def allocate(**_): """This allocates an Elastic IP in the connected account.""" ec2_client = connection.EC2ConnectionClient().client() if _allocate_external_elasticip(): return ctx.logger.debug('Attempting to allocate elasticip.') kw = {} if ctx.node.properties.get('domain'): kw['domain'] = ctx.node.properties['domain'] try: address_object = ec2_client.allocate_address(**kw) except (boto.exception.EC2ResponseError, boto.exception.BotoServerError) as e: raise NonRecoverableError('{0}'.format(str(e))) if 'vpc' in address_object.domain: ctx.instance.runtime_properties[constants.ALLOCATION_ID] = \ address_object.allocation_id utils.set_external_resource_id(address_object.public_ip, ctx.instance, external=False)
def create(**_): """Creates an EC2 security group. """ ec2_client = connection.EC2ConnectionClient().client() for property_name in constants.SECURITY_GROUP_REQUIRED_PROPERTIES: utils.validate_node_property(property_name, ctx.node.properties) name = utils.get_resource_id() if _create_external_securitygroup(name): return ctx.logger.debug( 'Creating Security Group: {0}' .format(name)) create_args = dict( name=name, description=ctx.node.properties['description'], vpc_id=_get_connected_vpc() ) try: group_object = ec2_client.create_security_group(**create_args) except (boto.exception.EC2ResponseError, boto.exception.BotoServerError) as e: raise NonRecoverableError('{0}'.format(str(e))) _create_group_rules(group_object) utils.set_external_resource_id( group_object.id, ctx.instance, external=False)
def stop(**_): ec2_client = connection.EC2ConnectionClient().client() instance_id = \ utils.get_external_resource_id_or_raise( 'stop instance', ctx.instance) if _stop_external_instance(instance_id): return ctx.logger.debug( 'Attempting to stop EC2 Instance. {0}.)'.format(instance_id)) try: ec2_client.stop_instances(instance_id) except (boto.exception.EC2ResponseError, boto.exception.BotoServerError) as e: raise NonRecoverableError('{0}'.format(str(e))) ctx.logger.debug('Attempted to stop instance {0}.'.format(instance_id)) if _get_instance_state() == constants.INSTANCE_STATE_STOPPED: _unassign_runtime_properties( runtime_properties=constants.INSTANCE_INTERNAL_ATTRIBUTES, ctx_instance=ctx.instance) ctx.logger.info('Stopped instance {0}.'.format(instance_id)) else: return ctx.operation.retry( message='Waiting server to stop. Retrying...')
def start(**_): ec2_client = connection.EC2ConnectionClient().client() instance_id = \ utils.get_external_resource_id_or_raise( 'start instance', ctx.instance) if _start_external_instance(instance_id): return if _get_instance_state() == constants.INSTANCE_STATE_STARTED: _instance_started_assign_runtime_properties(instance_id) return ctx.logger.debug('Attempting to start instance: {0}.)'.format(instance_id)) try: ec2_client.start_instances(instance_id) except (boto.exception.EC2ResponseError, boto.exception.BotoServerError) as e: raise NonRecoverableError('{0}'.format(str(e))) ctx.logger.debug('Attempted to start instance {0}.'.format(instance_id)) if _get_instance_state() == constants.INSTANCE_STATE_STARTED: _instance_started_assign_runtime_properties(instance_id) else: return ctx.operation.retry( message='Waiting server to be running. Retrying...')
def create(args, **_): """Creates an EBS volume. """ ec2_client = connection.EC2ConnectionClient().client() for property_name in constants.VOLUME_REQUIRED_PROPERTIES: utils.validate_node_property(property_name, ctx.node.properties) if _create_external_volume(): return ctx.logger.debug('Creating EBS volume') create_volume_args = dict(size=ctx.node.properties['size'], zone=ctx.node.properties['zone']) create_volume_args.update(args) try: new_volume = ec2_client.create_volume(**create_volume_args) except (boto.exception.EC2ResponseError, boto.exception.BotoServerError) as e: raise NonRecoverableError('{0}'.format(str(e))) utils.set_external_resource_id(new_volume.id, ctx.instance, external=False)
def terminate(**_): ec2_client = connection.EC2ConnectionClient().client() instance_id = \ utils.get_external_resource_id_or_raise( 'terminate instance', ctx.instance) if _terminate_external_instance(instance_id): return ctx.logger.debug( 'Attempting to terminate EC2 Instance. {0}.)'.format(instance_id)) try: ec2_client.terminate_instances(instance_id) except (boto.exception.EC2ResponseError, boto.exception.BotoServerError) as e: raise NonRecoverableError('{0}'.format(str(e))) ctx.logger.debug('Attemped to terminate instance {0}'.format(instance_id)) if _get_instance_state() == \ constants.INSTANCE_STATE_TERMINATED: ctx.logger.info('Terminated instance: {0}.'.format(instance_id)) utils.unassign_runtime_property_from_resource( constants.EXTERNAL_RESOURCE_ID, ctx.instance) else: return ctx.operation.retry( message='Waiting server to terminate. Retrying...')
def _get_all_instances(list_of_instance_ids=None): """Returns a list of instance objects for a list of instance IDs. :returns a list of instance objects. :raises NonRecoverableError: If Boto errors. """ ec2_client = connection.EC2ConnectionClient().client() try: reservations = ec2_client.get_all_reservations(list_of_instance_ids) except boto.exception.EC2ResponseError as e: if 'InvalidInstanceID.NotFound' in e: instances = [ instance for res in ec2_client.get_all_reservations() for instance in res.instances ] utils.log_available_resources(instances) return None except boto.exception.BotoServerError as e: raise NonRecoverableError('{0}'.format(str(e))) instances = [] for reservation in reservations: for instance in reservation.instances: instances.append(instance) return instances
def associate(**_): """ Associates an Elastic IP created by Cloudify with an EC2 Instance that was also created by Cloudify. """ ec2_client = connection.EC2ConnectionClient().client() instance_id = \ utils.get_external_resource_id_or_raise( 'associate elasticip', ctx.source.instance) elasticip = \ utils.get_external_resource_id_or_raise( 'associate elasticip', ctx.target.instance) if _associate_external_elasticip_or_instance(elasticip): return ctx.logger.debug( 'Attempting to associate elasticip {0} and instance {1}.'.format( elasticip, instance_id)) try: ec2_client.associate_address(instance_id=instance_id, public_ip=elasticip) except (boto.exception.EC2ResponseError, boto.exception.BotoServerError) as e: raise NonRecoverableError('{0}'.format(str(e))) ctx.logger.info('Associated Elastic IP {0} with instance {1}.'.format( elasticip, instance_id)) ctx.source.instance.runtime_properties['public_ip_address'] = elasticip
def run_instances(**_): ec2_client = connection.EC2ConnectionClient().client() for property_name in constants.INSTANCE_REQUIRED_PROPERTIES: utils.validate_node_property(property_name, ctx.node.properties) if _create_external_instance(): return instance_parameters = _get_instance_parameters() ctx.logger.info( 'Attempting to create EC2 Instance with these API parameters: {0}.'. format(instance_parameters)) instance_id = _run_instances_if_needed(ec2_client, instance_parameters) instance = _get_instance_from_id(instance_id) if instance is None: return ctx.operation.retry( message='Waiting to verify that instance {0} ' 'has been added to your account.'.format(instance_id)) utils.set_external_resource_id(instance_id, ctx.instance, external=False)
def disassociate(**_): """ Disassocates an Elastic IP created by Cloudify from an EC2 Instance that was also created by Cloudify. """ ec2_client = connection.EC2ConnectionClient().client() instance_id = \ utils.get_external_resource_id_or_raise( 'disassociate elasticip', ctx.source.instance) elasticip = \ utils.get_external_resource_id_or_raise( 'disassociate elasticip', ctx.target.instance) if _disassociate_external_elasticip_or_instance(): return ctx.logger.debug('Disassociating Elastic IP {0}'.format(elasticip)) try: ec2_client.disassociate_address(public_ip=elasticip) except (boto.exception.EC2ResponseError, boto.exception.BotoServerError) as e: raise NonRecoverableError('{0}'.format(str(e))) utils.unassign_runtime_property_from_resource('public_ip_address', ctx.source.instance) ctx.logger.info('Disassociated Elastic IP {0} from instance {1}.'.format( elasticip, instance_id))
def test_create_group_rules_both_src_group_id_cidr(self): """ This tests that either src_group_id or cidr_ip is error is raised when neither is given. """ ec2_client = connection.EC2ConnectionClient().client() group = ec2_client.create_security_group( 'test_create_group_rules_both_src_group_id_or_cidr', 'this is test') test_properties = self.get_mock_properties() ctx = self.security_group_mock( 'test_create_group_rules_both_src_group_id_or_cidr', test_properties) current_ctx.set(ctx=ctx) group_object = ec2_client.create_security_group( 'dummy', 'this is test') ctx.node.properties['rules'][0]['src_group_id'] = group_object ex = self.assertRaises( NonRecoverableError, securitygroup._create_group_rules, group) self.assertIn( 'You need to pass either src_group_id OR cidr_ip.', ex.message)
def test_validation_use_external_not_in_account(self): """ Tests that an error is raised if you use external, but the key pair doesn't exist in your account. """ ctx = self.mock_ctx('test_validation_use_external_not_in_account') current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() kp = ec2_client.create_key_pair( 'test_validation_use_external_not_in_account') ctx.node.properties['use_external_resource'] = True ctx.node.properties['resource_id'] = kp.name kp = ec2_client.delete_key_pair( 'test_validation_use_external_not_in_account') key_path = \ self.create_dummy_key_path(ctx=ctx) self.addCleanup(os.remove, key_path) with open(key_path, 'w') as dummy_key: dummy_key.write('test') ex = self.assertRaises(NonRecoverableError, keypair.creation_validation, ctx=ctx) self.assertIn('the key pair does not exist in the account', ex.message) self.assertIn('InvalidKeyPair.NotFound', ex.message)
def test_aws_config_file_valid(self): client = connection.EC2ConnectionClient() config = client._get_aws_config_from_file() config_schema = constants.BOTO_CONFIG_SCHEMA for opt_group in config_schema.values(): for opt in opt_group: self.assertIn(opt, config)
def test_delete(self): """This tests that delete removes the runtime_properties""" test_properties = self.get_mock_properties() ctx = self.security_group_mock('test_delete', test_properties) current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() group = ec2_client.create_security_group('test', 'this is test') ctx.instance.runtime_properties['aws_resource_id'] = group.id securitygroup.delete(ctx=ctx) self.assertNotIn('aws_resource_id', ctx.instance.runtime_properties)
def test_get_key_pair_by_id(self): """ This tests that the _get_key_pair_by_id function returns the same keypair object. """ ctx = self.mock_ctx('test_get_key_pair_by_id') current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() kp = ec2_client.create_key_pair('test_get_key_pair_by_id_bad_id') output = keypair._get_key_pair_by_id(kp.name) self.assertEqual(output.name, kp.name)
def test_connect(self): """ this tests that a the correct region endpoint in returned by the connect function """ ctx = self.get_mock_context('test_connect') current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() self.assertTrue(type(ec2_client), EC2Connection) self.assertEqual(ec2_client.DefaultRegionEndpoint, 'ec2.us-east-1.amazonaws.com')
def test_validation_use_external(self): ctx = self.mock_ctx('test_validation_use_external') current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() kp = ec2_client.create_key_pair('test_validation_use_external') ctx.node.properties['use_external_resource'] = True ctx.node.properties['resource_id'] = kp.name ex = self.assertRaises(NonRecoverableError, keypair.creation_validation, ctx=ctx) self.assertIn('but the key file does not exist locally.', ex.message)
def test_save_key_pair_missing_property(self): """ This tests that _save_key_pair raises an error when private_key_path is not set. """ ctx = self.mock_ctx('test_save_key_pair_missing_property') current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() del (ctx.node.properties['private_key_path']) kp = ec2_client.create_key_pair('test_create_use_external') ex = self.assertRaises(NonRecoverableError, keypair._save_key_pair, kp) self.assertIn('Unable to get key file path, private_key_path not set', ex.message)
def test_delete(self): """ this tests that keypair delete removes the keypair from the account """ ctx = self.mock_ctx('test_delete') current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() kp = ec2_client.create_key_pair('test_delete') ctx.instance.runtime_properties['aws_resource_id'] = kp.name ctx.instance.runtime_properties['key_path'] = \ self.create_dummy_key_path(ctx=ctx) keypair.delete(ctx=ctx) self.assertEquals(None, ec2_client.get_key_pair(kp.name))
def start(start_retry_interval=30, private_key_path=None, **_): ec2_client = connection.EC2ConnectionClient().client() instance_id = \ utils.get_external_resource_id_or_raise( 'start instance', ctx.instance) if _start_external_instance(instance_id): return if _get_instance_state() == constants.INSTANCE_STATE_STARTED: if ctx.node.properties['use_password']: password_success = _retrieve_windows_pass( ec2_client=ec2_client, instance_id=instance_id, private_key_path=private_key_path) if not password_success: return ctx.operation.retry( message='Waiting for server to post generated password', retry_after=start_retry_interval) _instance_started_assign_runtime_properties_and_tag(instance_id) return ctx.logger.debug('Attempting to start instance: {0}.)'.format(instance_id)) try: ec2_client.start_instances(instance_id) except (boto.exception.EC2ResponseError, boto.exception.BotoServerError) as e: raise NonRecoverableError('{0}'.format(str(e))) ctx.logger.debug('Attempted to start instance {0}.'.format(instance_id)) if _get_instance_state() == constants.INSTANCE_STATE_STARTED: if ctx.node.properties['use_password']: password_success = _retrieve_windows_pass( ec2_client=ec2_client, instance_id=instance_id, private_key_path=private_key_path) if not password_success: return ctx.operation.retry( message='Waiting for server to post generated password', retry_after=start_retry_interval) _instance_started_assign_runtime_properties_and_tag(instance_id) else: return ctx.operation.retry( message='Waiting server to be running. Retrying...', retry_after=start_retry_interval)
def test_create_existing(self): """This tests that create creates the runtime_properties""" test_properties = self.get_mock_properties() ctx = self.security_group_mock('test_create_duplicate', test_properties) current_ctx.set(ctx=ctx) name = ctx.node.properties.get('resource_id') description = ctx.node.properties.get('description') ec2_client = connection.EC2ConnectionClient().client() group = ec2_client.create_security_group(name, description) ctx.node.properties['use_external_resource'] = True securitygroup.create(ctx=ctx) self.assertEqual(ctx.instance.runtime_properties['aws_resource_id'], group.id)
def test_create_use_external(self): """ This tests that the create keypair function adds the key_pair_name to runtime properties. """ ctx = self.mock_ctx('test_create_use_external') current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() kp = ec2_client.create_key_pair('test_create_use_external') ctx.node.properties['use_external_resource'] = True ctx.node.properties['resource_id'] = kp.name ex = self.assertRaises(NonRecoverableError, keypair.create, ctx=ctx) self.assertIn('External resource, but the key file does not exist', ex.message)
def test_get_all_groups(self): """ This tests that all created groups are returned by _get_all_security_groups """ test_properties = self.get_mock_properties() ctx = self.security_group_mock('test_get_all_groups', test_properties) current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() group = ec2_client.create_security_group('test_get_all_groups', 'this is test') ctx.instance.runtime_properties['aws_resource_id'] = group.id output = securitygroup._get_all_security_groups( list_of_group_ids=group.id) self.assertEqual(output[0].id, group.id)
def test_delete_use_external(self): """ This tests that delete function removes the runtime_properties for external resource. """ ctx = self.mock_ctx('test_delete_use_external') current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() kp = ec2_client.create_key_pair('test_delete_use_external') ctx.node.properties['use_external_resource'] = True ctx.node.properties['resource_id'] = kp.name ctx.instance.runtime_properties['aws_resource_id'] = kp.name keypair.delete(ctx=ctx) self.assertNotIn('aws_resource_id', ctx.instance.runtime_properties)
def test_validation_in_account(self): """ Tests that an error is raised if you don't use external, but the key does exist in your account. """ ctx = self.mock_ctx('test_validation_in_account') current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() kp = ec2_client.create_key_pair('test_validation_in_account') ctx.node.properties['use_external_resource'] = False ctx.node.properties['resource_id'] = kp.name ex = self.assertRaises(NonRecoverableError, keypair.creation_validation, ctx=ctx) self.assertIn('but the key pair exists in the account.', ex.message)
def test_run_instances_external_resource(self): """ this tests that the instance create function adds the runtime_properties """ ctx = self.mock_ctx('test_run_instances_external_resource') current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() reservation = ec2_client.run_instances( TEST_AMI_IMAGE_ID, instance_type=TEST_INSTANCE_TYPE) instance_id = reservation.instances[0].id ctx.node.properties['use_external_resource'] = True ctx.node.properties['resource_id'] = instance_id instance.run_instances(ctx=ctx) self.assertIn('aws_resource_id', ctx.instance.runtime_properties.keys())
def test_get_private_dns_name(self): """ This checks that _get_instance_attribute sets the correct runtime property and it is an FQDN """ ctx = self.mock_ctx('test_get_private_dns_name') current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() reservation = ec2_client.run_instances( TEST_AMI_IMAGE_ID, instance_type=TEST_INSTANCE_TYPE) ctx.instance.runtime_properties['aws_resource_id'] = \ reservation.instances[0].id property_name = 'private_dns_name' dns_name = instance._get_instance_attribute(property_name) self.assertRegexpMatches(dns_name, FQDN)
def test_create_duplicate(self): """This tests that when you give a name of an existing resource, a NonRecoverableError is raised. """ test_properties = self.get_mock_properties() ctx = self.security_group_mock( 'test_create_duplicate', test_properties) current_ctx.set(ctx=ctx) name = ctx.node.properties.get('resource_id') description = ctx.node.properties.get('description') ec2_client = connection.EC2ConnectionClient().client() ec2_client.create_security_group(name, description) ex = self.assertRaises( NonRecoverableError, securitygroup.create, ctx=ctx) self.assertIn('InvalidGroup.Duplicate', ex.message)
def _get_key_pair_by_id(key_pair_id): """Returns the key pair object for a given key pair id. :param key_pair_id: The ID of a keypair. :returns The boto keypair object. :raises NonRecoverableError: If EC2 finds no matching key pairs. """ ec2_client = connection.EC2ConnectionClient().client() try: key_pairs = ec2_client.get_all_key_pairs(keynames=key_pair_id) except (boto.exception.EC2ResponseError, boto.exception.BotoServerError) as e: raise NonRecoverableError('{0}'.format(str(e))) return key_pairs[0] if key_pairs else None
def test_delete_deleted(self): """This tests that security group delete raises an error when the group is already deleted. """ test_properties = self.get_mock_properties() ctx = self.security_group_mock( 'test_delete_deleted', test_properties) current_ctx.set(ctx=ctx) ec2_client = connection.EC2ConnectionClient().client() group = ec2_client.create_security_group('test_delete_deleted', 'this is test') ctx.instance.runtime_properties['aws_resource_id'] = group.id ec2_client.delete_security_group(group_id=group.id) ex = self.assertRaises( NonRecoverableError, securitygroup.delete, ctx=ctx) self.assertIn('does not exist in the account', ex.message)