|
19 | 19 | ''' |
20 | 20 |
|
21 | 21 | # Common |
| 22 | +from Crypto.PublicKey import RSA |
22 | 23 | from collections import defaultdict |
23 | 24 | import json |
| 25 | +import os |
24 | 26 |
|
25 | 27 | # Cloudify |
26 | 28 | from cloudify import compute |
|
29 | 31 | from cloudify_awssdk.common import decorators, utils |
30 | 32 | from cloudify_awssdk.common.constants import EXTERNAL_RESOURCE_ID |
31 | 33 | from cloudify_awssdk.ec2 import EC2Base |
| 34 | +from cloudify_awssdk.ec2.decrypt import decrypt_password |
| 35 | + |
32 | 36 | # Boto |
33 | 37 | from botocore.exceptions import ClientError, ParamValidationError |
34 | 38 |
|
|
46 | 50 | GROUP_TYPE = 'cloudify.nodes.aws.ec2.SecurityGroup' |
47 | 51 | NETWORK_INTERFACE_TYPE = 'cloudify.nodes.aws.ec2.Interface' |
48 | 52 | SUBNET_TYPE = 'cloudify.nodes.aws.ec2.Subnet' |
| 53 | +KEY_TYPE = 'cloudify.nodes.aws.ec2.Keypair' |
49 | 54 | GROUPIDS = 'SecurityGroupIds' |
50 | 55 | NETWORK_INTERFACES = 'NetworkInterfaces' |
51 | 56 | SUBNET_ID = 'SubnetId' |
@@ -145,6 +150,17 @@ def modify_instance_attribute(self, params): |
145 | 150 | self.logger.debug('Response: {0}'.format(res)) |
146 | 151 | return res |
147 | 152 |
|
| 153 | + def get_password(self, params): |
| 154 | + ''' |
| 155 | + Modify attribute of AWS EC2 Instances. |
| 156 | + ''' |
| 157 | + self.logger.debug( |
| 158 | + 'Getting {0} password with parameters: {1}'.format( |
| 159 | + self.type_name, params)) |
| 160 | + res = self.client.get_password_data(**params) |
| 161 | + self.logger.debug('Response: {0}'.format(res)) |
| 162 | + return res |
| 163 | + |
148 | 164 |
|
149 | 165 | @decorators.aws_resource(EC2Instances, resource_type=RESOURCE_TYPE) |
150 | 166 | def prepare(ctx, iface, resource_config, **_): |
@@ -265,6 +281,10 @@ def start(ctx, iface, resource_config, **_): |
265 | 281 | ctx.instance.runtime_properties['ip'] = ip |
266 | 282 | ctx.instance.runtime_properties['public_ip_address'] = pip |
267 | 283 | ctx.instance.runtime_properties['private_ip_address'] = ip |
| 284 | + if not _handle_password(iface): |
| 285 | + raise OperationRetry( |
| 286 | + '{0} ID# {1} password is empty.'.format( |
| 287 | + iface.type_name, iface.resource_id)) |
268 | 288 | return |
269 | 289 |
|
270 | 290 | elif ctx.operation.retry_number == 0: |
@@ -412,3 +432,37 @@ def _handle_userdata(existing_userdata): |
412 | 432 | [existing_userdata, install_agent_userdata]) |
413 | 433 |
|
414 | 434 | return final_userdata |
| 435 | + |
| 436 | + |
| 437 | +def _handle_password(iface): |
| 438 | + if not ctx.node.properties['use_password']: |
| 439 | + return True |
| 440 | + key_data = ctx.node.properties['agent_config'].get('key') |
| 441 | + if not key_data: |
| 442 | + rel = utils.find_rel_by_node_type(ctx.instance, KEY_TYPE) |
| 443 | + if rel: |
| 444 | + key_data = \ |
| 445 | + rel.target.instance.runtime_properties.get( |
| 446 | + 'create_response', {}).get('KeyMaterial') |
| 447 | + if not key_data: |
| 448 | + raise NonRecoverableError( |
| 449 | + 'No key_data was provided in agent config property or rel.') |
| 450 | + if os.path.exists(key_data): |
| 451 | + with open(key_data, 'r') as outfile: |
| 452 | + key_data = outfile.readlines() |
| 453 | + password_data = iface.get_password( |
| 454 | + { |
| 455 | + 'InstanceId': ctx.instance.runtime_properties[EXTERNAL_RESOURCE_ID] |
| 456 | + } |
| 457 | + ) |
| 458 | + if not isinstance(password_data, dict): |
| 459 | + return False |
| 460 | + encrypted_password = password_data.get('PasswordData') |
| 461 | + if not encrypted_password: |
| 462 | + ctx.logger.error('password_data is {0}'.format(password_data)) |
| 463 | + return False |
| 464 | + key = RSA.importKey(key_data) |
| 465 | + password = decrypt_password(key, encrypted_password) |
| 466 | + ctx.instance.runtime_properties['password'] = \ |
| 467 | + password |
| 468 | + return True |
0 commit comments