# Copyright 2014 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import logging import sys import time import traceback import botocore.exceptions from oslo_log import log import six from tempest.lib import base from tempest.lib import exceptions import testtools from ec2api.tests.functional import botocoreclient from ec2api.tests.functional import config as cfg CONF = cfg.CONF LOG = log.getLogger(__name__) logging.getLogger('botocore').setLevel(logging.INFO) logging.getLogger( 'botocore.vendored.requests.packages.urllib3.connectionpool' ).setLevel(logging.WARNING) logging.getLogger('paramiko.transport').setLevel(logging.WARNING) class EC2Waiter(object): def __init__(self, wait_func): self.wait_func = wait_func self.default_timeout = CONF.aws.build_timeout self.default_check_interval = CONF.aws.build_interval def _state_wait(self, f, f_args=None, f_kwargs=None, final_set=set(), error_set=('error')): if not isinstance(final_set, set): final_set = set((final_set,)) if not isinstance(error_set, set): error_set = set((error_set,)) interval = self.default_check_interval start_time = time.time() args = f_args if f_args is not None else [] kwargs = f_kwargs if f_kwargs is not None else {} try: old_status = status = f(*args, **kwargs) except exceptions.NotFound: old_status = status = "NotFound" while True: if status != old_status: LOG.info('State transition "%s" ==> "%s" %d second', old_status, status, time.time() - start_time) if status in final_set: return status if error_set is not None and status in error_set: raise testtools.TestCase.failureException( 'State changes to error state! ' 'While waiting for %s at "%s"' % (final_set, status)) dtime = time.time() - start_time if dtime > self.default_timeout: raise testtools.TestCase.failureException( 'State change timeout exceeded! ' '(%ds) While waiting for %s at "%s"' % (dtime, final_set, status)) time.sleep(interval) interval += self.default_check_interval old_status = status try: status = f(*args, **kwargs) except exceptions.NotFound: status = "NotFound" def _state_wait_gone(self, f, f_args=None, f_kwargs=None): interval = self.default_check_interval start_time = time.time() args = f_args if f_args is not None else [] kwargs = f_kwargs if f_kwargs is not None else {} try: old_status = status = f(*args, **kwargs) while True: if status != old_status: LOG.info('State transition "%s" ==> "%s" %d second', old_status, status, time.time() - start_time) dtime = time.time() - start_time if dtime > self.default_timeout: raise testtools.TestCase.failureException( "State change timeout exceeded while waiting" " for deleting") time.sleep(interval) interval += self.default_check_interval old_status = status status = f(*args, **kwargs) except exceptions.NotFound: pass def wait_available(self, obj_id, final_set=('available')): self._state_wait(self.wait_func, f_args=[obj_id], final_set=final_set) def wait_delete(self, obj_id): self._state_wait_gone(self.wait_func, f_args=[obj_id]) def wait_no_exception(self, *args, **kwargs): interval = self.default_check_interval start_time = time.time() while True: try: self.wait_func(*args, **kwargs) return except Exception: pass dtime = time.time() - start_time if dtime > self.default_timeout: raise testtools.TestCase.failureException( "Timeout exceeded while waiting") time.sleep(interval) interval += self.default_check_interval def wait_for_result(self, *args, **kwargs): interval = self.default_check_interval start_time = time.time() while True: result = self.wait_func(*args, **kwargs) if result: return result dtime = time.time() - start_time if dtime > self.default_timeout: raise testtools.TestCase.failureException( "Timeout exceeded while waiting") time.sleep(interval) interval += self.default_check_interval def safe_setup(f): """A decorator used to wrap the setUpClass for safe setup.""" def decorator(cls): try: f(cls) except Exception as se: exc_info = sys.exc_info() LOG.exception("setUpClass failed: %s" % se) try: cls.tearDownClass() except Exception as te: LOG.exception("tearDownClass failed: %s" % te) six.reraise(*exc_info) return decorator def get_device_name_prefix(device_name): """Return device name without device number. /dev/sda1 -> /dev/sd /dev/vda -> /dev/vd """ dev_num_pos = 0 while '0' <= device_name[dev_num_pos - 1] <= '9': dev_num_pos -= 1 return device_name[:dev_num_pos - 1] class TesterStateHolder(object): ec2_client = None _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super(TesterStateHolder, cls).__new__( cls, *args, **kwargs) return cls._instance _ec2_enabled = None _vpc_enabled = None def get_ec2_enabled(self): if self._ec2_enabled is None: self._fill_attributes() return self._ec2_enabled def get_vpc_enabled(self): if self._vpc_enabled is None: self._fill_attributes() return self._vpc_enabled def _fill_attributes(self): self._ec2_enabled = False self._vpc_enabled = False data = self.ec2_client.describe_account_attributes() for item in data.get('AccountAttributes', []): if item['AttributeName'] == 'supported-platforms': for value in item['AttributeValues']: if value['AttributeValue'] == 'VPC': self._vpc_enabled = True if value['AttributeValue'] == 'EC2': self._ec2_enabled = True def skip_without_ec2(*args, **kwargs): """A decorator useful to skip tests if EC2-classic is not supported.""" def decorator(f): @functools.wraps(f) def wrapper(self, *func_args, **func_kwargs): if not TesterStateHolder().get_ec2_enabled(): msg = "Skipped because EC2-classic is not enabled" raise testtools.TestCase.skipException(msg) return f(self, *func_args, **func_kwargs) return wrapper return decorator def skip_without_vpc(*args, **kwargs): """A decorator useful to skip tests if VPC is not supported.""" def decorator(f): @functools.wraps(f) def wrapper(self, *func_args, **func_kwargs): if not TesterStateHolder().get_vpc_enabled(): msg = "Skipped because VPC is disabled" raise testtools.TestCase.skipException(msg) return f(self, *func_args, **func_kwargs) return wrapper return decorator def check_network_feature_enabled(ext_name): ext_list = CONF['network-feature-enabled']['api_extensions'] if 'all' not in ext_list and ext_name not in ext_list: msg = ("Skipped network test as %s is not available" % ext_name) raise testtools.TestCase.skipException(msg) def skip_without_network_feature(ext_name, *args, **kwargs): """A decorator useful to skip tests without specified network extension.""" def decorator(f): @functools.wraps(f) def wrapper(self, *func_args, **func_kwargs): check_network_feature_enabled(ext_name) return f(self, *func_args, **func_kwargs) return wrapper return decorator class EC2TestCase(base.BaseTestCase): """Recommended to use as base class for boto related test.""" # The trash contains cleanup functions and paramaters in tuples # (function, *args, **kwargs) _global_resource_trash_bin = {} _global_sequence = -1 @classmethod @safe_setup def setUpClass(cls): super(EC2TestCase, cls).setUpClass() if not CONF.service_available.ec2api: raise cls.skipException("ec2api is disabled") cls.client = botocoreclient.get_ec2_client( CONF.aws.ec2_url, CONF.aws.aws_region, CONF.aws.aws_access, CONF.aws.aws_secret, CONF.aws.ca_bundle) cls.s3_client = botocoreclient.get_s3_client( CONF.aws.s3_url, CONF.aws.aws_region, CONF.aws.aws_access, CONF.aws.aws_secret, CONF.aws.ca_bundle) TesterStateHolder().ec2_client = cls.client @classmethod def addResourceCleanUpStatic(cls, function, *args, **kwargs): """Adds CleanUp callable, used by tearDownClass. Recommended to a use (deep)copy on the mutable args. """ tb = traceback.extract_stack(limit=2) cls._global_sequence = cls._global_sequence + 1 cls._global_resource_trash_bin[cls._global_sequence] = (function, args, kwargs, tb[0]) return cls._global_sequence def setUp(self): super(EC2TestCase, self).setUp() self._resource_trash_bin = {} self._sequence = -1 def tearDown(self): fail_count = self.cleanUp(self._resource_trash_bin) super(EC2TestCase, self).tearDown() if fail_count: raise exceptions.TempestException("%d cleanUp operation failed" % fail_count) def addResourceCleanUp(self, function, *args, **kwargs): """Adds CleanUp callable, used by tearDown. Recommended to a use (deep)copy on the mutable args. """ tb = traceback.extract_stack(limit=2)[0] self._sequence = self._sequence + 1 self._resource_trash_bin[self._sequence] = (function, args, kwargs, tb) LOG.debug("For cleaning up: %s\n From: %s" % (self.friendly_function_call_str(function, *args, **kwargs), str((tb[0], tb[1], tb[2])))) return self._sequence def cancelResourceCleanUp(self, key): """Cancel Clean up request.""" del self._resource_trash_bin[key] # NOTE(andrey-mp): if ERROR in responce_code then treat object as deleted _VALID_CLEANUP_ERRORS = [ 'NotFound', 'Gateway.NotAttached' ] # NOTE(andrey-mp): function must return boolean - should we retry # deleting or not _HOOKED_CLEANUP_ERRORS = { ('delete_vpc', 'DependencyViolation'): ( 'delete_vpc_failed', lambda kwargs: kwargs['VpcId']) } _CLEANUP_WAITERS = { 'delete_vpc': ( 'get_vpc_waiter', lambda kwargs: kwargs['VpcId']), 'delete_subnet': ( 'get_subnet_waiter', lambda kwargs: kwargs['SubnetId']), 'delete_network_interface': ( 'get_network_interface_waiter', lambda kwargs: kwargs['NetworkInterfaceId']), 'terminate_instances': ( 'get_instance_waiter', lambda kwargs: kwargs['InstanceIds'][0]), 'delete_volume': ( 'get_volume_waiter', lambda kwargs: kwargs['VolumeId']), 'detach_volume': ( 'get_volume_attachment_waiter', lambda kwargs: kwargs['VolumeId']), 'delete_snapshot': ( 'get_snapshot_waiter', lambda kwargs: kwargs['SnapshotId']), 'deregister_image': ( 'get_image_waiter', lambda kwargs: kwargs['ImageId']), 'detach_vpn_gateway': ( 'get_vpn_gateway_attachment_waiter', lambda kwargs: kwargs['VpnGatewayId']), 'delete_vpn_connection': ( 'get_vpn_connection_waiter', lambda kwargs: kwargs['VpnConnectionId']), 'delete_customer_gateway': ( 'get_customer_gateway_waiter', lambda kwargs: kwargs['CustomerGatewayId']), 'delete_vpn_gateway': ( 'get_vpn_gateway_waiter', lambda kwargs: kwargs['VpnGatewayId']), 'disassociate_address': ( 'get_address_assoc_waiter', lambda kwargs: kwargs), } @classmethod def tearDownClass(cls): fail_count = cls.cleanUp(cls._global_resource_trash_bin) super(EC2TestCase, cls).tearDownClass() if fail_count: raise exceptions.TempestException("%d cleanUp operation failed" % fail_count) @classmethod def cleanUp(cls, trash_bin): """Calls the callables added by addResourceCleanUp, when you overwire this function dont't forget to call this too. """ fail_count = 0 trash_keys = sorted(trash_bin, reverse=True) for key in trash_keys: (function, pos_args, kw_args, tb) = trash_bin[key] try: LOG.debug("Cleaning up: %s\n From: %s" % (cls.friendly_function_call_str(function, *pos_args, **kw_args), str((tb[0], tb[1], tb[2])))) res = cls.cleanUpItem(function, pos_args, kw_args) if not res: fail_count += 1 LOG.error('Failure in cleanup for: %s' % str(kw_args)) except BaseException: fail_count += 1 LOG.exception('Failure in cleanup for: %s' % str(kw_args)) finally: del trash_bin[key] return fail_count @classmethod def cleanUpItem(cls, function, pos_args, kw_args): attempts_left = 10 interval = 1 deleted = False while not deleted and attempts_left > 0: try: function(*pos_args, **kw_args) deleted = True key = function.__name__ if key in cls._CLEANUP_WAITERS: (waiter, obj_id) = cls._CLEANUP_WAITERS[key] waiter = getattr(cls, waiter) obj_id = obj_id(kw_args) try: waiter().wait_delete(obj_id) except botocore.exceptions.ClientError as e: LOG.exception('Exception occured in cleanup waiting') return False except botocore.exceptions.ClientError as e: error_code = e.response['Error']['Code'] for err in cls._VALID_CLEANUP_ERRORS: if err in error_code: deleted = True break else: hook_res = False key = (function.__name__, error_code) if key in cls._HOOKED_CLEANUP_ERRORS: (hook, obj_id) = cls._HOOKED_CLEANUP_ERRORS[key] hook = getattr(cls, hook) obj_id = obj_id(kw_args) hook_res = hook(obj_id) if not hook_res: LOG.error('Cleanup failed: %s', e, exc_info=True) return False LOG.error('Retrying cleanup due to: %s', e) time.sleep(interval) attempts_left -= 1 interval += 1 return deleted @classmethod def friendly_function_name_simple(cls, call_able): name = "" if hasattr(call_able, "im_class"): name += call_able.im_class.__name__ + "." name += call_able.__name__ return name @classmethod def friendly_function_call_str(cls, call_able, *args, **kwargs): string = cls.friendly_function_name_simple(call_able) string += "(" + ", ".join(map(str, args)) if len(kwargs): if len(args): string += ", " string += ", ".join("=".join(map(str, (key, value))) for (key, value) in kwargs.items()) return string + ")" @classmethod def _vpc_get_state(cls, vpc_id): try: data = cls.client.describe_vpcs(VpcIds=[vpc_id]) if not data['Vpcs']: raise exceptions.NotFound() return data['Vpcs'][0]['State'] except botocore.exceptions.ClientError: error_code = sys.exc_info()[1].response['Error']['Code'] if error_code == 'InvalidVpcID.NotFound': raise exceptions.NotFound() raise @classmethod def get_vpc_waiter(cls): return EC2Waiter(cls._vpc_get_state) @classmethod def _subnet_get_state(cls, subnet_id): try: data = cls.client.describe_subnets(SubnetIds=[subnet_id]) if not data['Subnets']: raise exceptions.NotFound() return data['Subnets'][0]['State'] except botocore.exceptions.ClientError: error_code = sys.exc_info()[1].response['Error']['Code'] if error_code == 'InvalidSubnetID.NotFound': raise exceptions.NotFound() raise @classmethod def get_subnet_waiter(cls): return EC2Waiter(cls._subnet_get_state) @classmethod def _address_assoc_get_state(cls, kwargs): try: ip = kwargs.get('PublicIp') alloc_id = kwargs.get('AllocationId') assoc_id = kwargs.get('AssociationId') if ip: data = cls.client.describe_addresses(PublicIps=[ip]) elif alloc_id: data = cls.client.describe_addresses(AllocationIds=[alloc_id]) elif assoc_id: data = cls.client.describe_addresses( Filters=[{'Name': 'association-id', 'Values': [assoc_id]}]) LOG.debug('Addresses: %s' % str(data.get('Addresses'))) if ('Addresses' in data and len(data['Addresses']) == 1 and data['Addresses'][0].get('InstanceId')): return 'available' raise exceptions.NotFound() except botocore.exceptions.ClientError: raise exceptions.NotFound() @classmethod def get_address_assoc_waiter(cls): return EC2Waiter(cls._address_assoc_get_state) @classmethod def _instance_get_state(cls, instance_id): try: data = cls.client.describe_instances(InstanceIds=[instance_id]) if not data['Reservations']: raise exceptions.NotFound() if not data['Reservations'][0]['Instances']: raise exceptions.NotFound() state = data['Reservations'][0]['Instances'][0]['State']['Name'] if state != 'terminated': return state raise exceptions.NotFound() except botocore.exceptions.ClientError: error_code = sys.exc_info()[1].response['Error']['Code'] if error_code == 'InvalidInstanceID.NotFound': raise exceptions.NotFound() raise @classmethod def get_instance_waiter(cls): return EC2Waiter(cls._instance_get_state) @classmethod def _network_interface_get_state(cls, ni_id): try: data = cls.client.describe_network_interfaces( NetworkInterfaceIds=[ni_id]) if not data['NetworkInterfaces']: raise exceptions.NotFound() return data['NetworkInterfaces'][0]['Status'] except botocore.exceptions.ClientError: error_code = sys.exc_info()[1].response['Error']['Code'] if error_code == 'InvalidNetworkInterfaceID.NotFound': raise exceptions.NotFound() raise @classmethod def get_network_interface_waiter(cls): return EC2Waiter(cls._network_interface_get_state) @classmethod def _volume_get_state(cls, volume_id): try: data = cls.client.describe_volumes(VolumeIds=[volume_id]) if not data['Volumes']: raise exceptions.NotFound() return data['Volumes'][0]['State'] except botocore.exceptions.ClientError: error_code = sys.exc_info()[1].response['Error']['Code'] if error_code == 'InvalidVolume.NotFound': raise exceptions.NotFound() raise @classmethod def get_volume_waiter(cls): return EC2Waiter(cls._volume_get_state) @classmethod def _volume_attachment_get_state(cls, volume_id): try: data = cls.client.describe_volumes(VolumeIds=[volume_id]) volume = data['Volumes'][0] if 'Attachments' in volume and len(volume['Attachments']) > 0: return volume['Attachments'][0]['State'] raise exceptions.NotFound() except botocore.exceptions.ClientError: error_code = sys.exc_info()[1].response['Error']['Code'] if error_code == 'InvalidVolume.NotFound': raise exceptions.NotFound() raise @classmethod def get_volume_attachment_waiter(cls): return EC2Waiter(cls._volume_attachment_get_state) @classmethod def _snapshot_get_state(cls, snapshot_id): try: data = cls.client.describe_snapshots(SnapshotIds=[snapshot_id]) if not data['Snapshots']: raise exceptions.NotFound() return data['Snapshots'][0]['State'] except botocore.exceptions.ClientError: error_code = sys.exc_info()[1].response['Error']['Code'] if error_code == 'InvalidSnapshot.NotFound': raise exceptions.NotFound() raise @classmethod def get_snapshot_waiter(cls): return EC2Waiter(cls._snapshot_get_state) @classmethod def _image_get_state(cls, image_id): try: data = cls.client.describe_images(ImageIds=[image_id]) if not data['Images']: raise exceptions.NotFound() return data['Images'][0]['State'] except botocore.exceptions.ClientError: error_code = sys.exc_info()[1].response['Error']['Code'] if error_code == 'InvalidAMIID.NotFound': raise exceptions.NotFound() raise @classmethod def get_image_waiter(cls): return EC2Waiter(cls._image_get_state) @classmethod def _vpn_gateway_get_attachment_state(cls, vpn_gateway_id): try: data = cls.client.describe_vpn_gateways( VpnGatewayIds=[vpn_gateway_id]) attachments = data['VpnGateways'][0].get('VpcAttachments') if (not attachments or attachments[0]['State'] == 'detached'): raise exceptions.NotFound() return attachments[0]['State'] except botocore.exceptions.ClientError as ex: error_code = ex.response['Error']['Code'] if error_code == 'InvalidVpnGatewayID.NotFound': raise exceptions.NotFound() raise @classmethod def get_vpn_gateway_attachment_waiter(cls): return EC2Waiter(cls._vpn_gateway_get_attachment_state) @classmethod def _vpn_object_get_state(cls, func, kwargs, data_key, error_not_found): # NOTE(andrey-mp): use this for vpn_connection, vpn_gateway, # customer_gateway due to similar states try: data = func(**kwargs) if not data[data_key]: raise exceptions.NotFound() if data[data_key][0]['State'] == 'deleted': raise exceptions.NotFound() return data[data_key][0]['State'] except botocore.exceptions.ClientError as ex: error_code = ex.response['Error']['Code'] if error_code == error_not_found: raise exceptions.NotFound() raise @classmethod def _vpn_connection_get_state(cls, vpn_connection_id): return cls._vpn_object_get_state( cls.client.describe_vpn_connections, {'VpnConnectionIds': [vpn_connection_id]}, 'VpnConnections', 'InvalidVpnConnectionID.NotFound') @classmethod def get_vpn_connection_waiter(cls): return EC2Waiter(cls._vpn_connection_get_state) @classmethod def _customer_gateway_get_state(cls, customer_gateway_id): return cls._vpn_object_get_state( cls.client.describe_customer_gateways, {'CustomerGatewayIds': [customer_gateway_id]}, 'CustomerGateways', 'InvalidCustomerGatewayID.NotFound') @classmethod def get_customer_gateway_waiter(cls): return EC2Waiter(cls._customer_gateway_get_state) @classmethod def _vpn_gateway_get_state(cls, vpn_gateway_id): return cls._vpn_object_get_state( cls.client.describe_vpn_gateways, {'VpnGatewayIds': [vpn_gateway_id]}, 'VpnGateways', 'InvalidVpnGatewayID.NotFound') @classmethod def get_vpn_gateway_waiter(cls): return EC2Waiter(cls._vpn_gateway_get_state) @classmethod def _vpn_connection_get_route_state(cls, vpn_connection_id, destination_cidr_block=None): try: data = cls.client.describe_vpn_connections( VpnConnectionIds=[vpn_connection_id]) try: route = next( r for r in data['VpnConnections'][0]['Routes'] if r['DestinationCidrBlock'] == destination_cidr_block) except StopIteration: raise exceptions.NotFound() if route['State'] == 'deleted': raise exceptions.NotFound() return route['State'] except botocore.exceptions.ClientError as ex: error_code = ex.response['Error']['Code'] if error_code == 'InvalidVpnGatewayID.NotFound': raise exceptions.NotFound() raise @classmethod def get_vpn_connection_route_waiter(cls, destination_cidr_block): return EC2Waiter( functools.partial(cls._vpn_connection_get_route_state, destination_cidr_block=destination_cidr_block)) @classmethod def _vpn_connection_get_tunnel_up_state(cls, vpn_connection_id): data = cls.client.describe_vpn_connections( VpnConnectionIds=[vpn_connection_id]) for item in data['VpnConnections'][0].get('VgwTelemetry', []): if 'UP' == item['Status']: return 'UP' raise exceptions.NotFound() @classmethod def get_vpn_connection_tunnel_waiter(cls): return EC2Waiter(cls._vpn_connection_get_tunnel_up_state) @classmethod def delete_vpc_failed(cls, vpc_id): try: LOG.warning('VpnGateways: ' + str(cls.client.describe_vpn_gateways( Filters=[{'Name': 'attachment.vpc-id', 'Values': [vpc_id]}] )['VpnGateways'])) LOG.warning('RouteTables: ' + str(cls.client.describe_route_tables( Filters=[{'Name': 'vpc-id', 'Values': [vpc_id]}] )['RouteTables'])) return True except Exception: LOG.exception('Error occured during "delete_vpc_failed" hook') return False def assertEmpty(self, list_obj, msg=None): self.assertTrue(len(list_obj) == 0, msg) def assertNotEmpty(self, list_obj, msg=None): self.assertTrue(len(list_obj) > 0, msg) def assertRaises(self, error_code, fn, rollback_fn=None, **kwargs): try: fn_data = fn(**kwargs) if rollback_fn: try: rollback_fn(fn_data) except Exception: LOG.exception('Rollback failed') msg = ("%s hasn't returned exception for params %s" % (str(fn.__name__), str(kwargs))) raise self.failureException(msg) except botocore.exceptions.ClientError as e: self.assertEqual(error_code, e.response['Error']['Code']) # NOTE(andrey-mp): Helpers zone def get_instance(self, instance_id): data = self.client.describe_instances(InstanceIds=[instance_id]) self.assertEqual(1, len(data.get('Reservations', []))) instances = data['Reservations'][0].get('Instances', []) self.assertEqual(1, len(instances)) return instances[0] def get_instance_bdm(self, instance_id, device_name): """ device_name=None means getting bdm of root instance device """ instance = self.get_instance(instance_id) if not device_name: device_name = instance.get('RootDeviceName') if not device_name: return None bdms = instance.get('BlockDeviceMappings') if bdms is None: return None bdt = [bdt for bdt in bdms if bdt['DeviceName'] == device_name] return None if len(bdt) == 0 else bdt[0] def run_instance(self, clean_dict=None, **kwargs): kwargs.setdefault('ImageId', CONF.aws.image_id) kwargs.setdefault('InstanceType', CONF.aws.instance_type) kwargs.setdefault('Placement', {'AvailabilityZone': CONF.aws.aws_zone}) kwargs['MinCount'] = 1 kwargs['MaxCount'] = 1 data = self.client.run_instances(*[], **kwargs) instance_id = data['Instances'][0]['InstanceId'] res_clean = self.addResourceCleanUp(self.client.terminate_instances, InstanceIds=[instance_id]) self.get_instance_waiter().wait_available(instance_id, final_set=('running')) if clean_dict is not None: clean_dict['instance'] = res_clean return instance_id def create_vpc_and_subnet(self, cidr): data = self.client.create_vpc(CidrBlock=cidr) vpc_id = data['Vpc']['VpcId'] self.addResourceCleanUp(self.client.delete_vpc, VpcId=vpc_id) self.get_vpc_waiter().wait_available(vpc_id) data = self.client.create_subnet(VpcId=vpc_id, CidrBlock=cidr, AvailabilityZone=CONF.aws.aws_zone) subnet_id = data['Subnet']['SubnetId'] self.addResourceCleanUp(self.client.delete_subnet, SubnetId=subnet_id) return vpc_id, subnet_id def prepare_route(self, vpc_id, gw_id): data = self.client.describe_route_tables( Filters=[{'Name': 'vpc-id', 'Values': [vpc_id]}]) self.assertEqual(1, len(data['RouteTables'])) route_table_id = data['RouteTables'][0]['RouteTableId'] kwargs = { 'DestinationCidrBlock': '0.0.0.0/0', 'RouteTableId': route_table_id, 'GatewayId': gw_id } self.client.create_route(*[], **kwargs) return route_table_id def create_and_attach_internet_gateway(self, vpc_id): data = self.client.create_internet_gateway() gw_id = data['InternetGateway']['InternetGatewayId'] self.addResourceCleanUp(self.client.delete_internet_gateway, InternetGatewayId=gw_id) data = self.client.attach_internet_gateway(VpcId=vpc_id, InternetGatewayId=gw_id) self.addResourceCleanUp(self.client.detach_internet_gateway, VpcId=vpc_id, InternetGatewayId=gw_id) return gw_id