Filters in describe instances

Change-Id: If5f2e7c37f49df07bfa973dba3ac87a087477a89
This commit is contained in:
Feodor Tersin 2014-11-27 19:07:14 +04:00
parent 960d472332
commit 3c400fb115
6 changed files with 281 additions and 213 deletions

View File

@ -22,6 +22,7 @@ import re
from novaclient import exceptions as nova_exception
from oslo.config import cfg
from ec2api.api import address as address_api
from ec2api.api import clients
from ec2api.api import ec2utils
from ec2api.api import network_interface as network_interface_api
@ -48,6 +49,45 @@ CONF.register_opts(ec2_opts)
# TODO(ft): implement DeviceIndex
INSTANCE_FILTER_MAP = {
'block-device-mapping.device-name': ['blockDeviceMapping',
'deviceName'],
'client-token': 'clientToken',
'dns-name': 'dnsName',
'image-id': 'imageId',
'instance-id': 'instanceId',
'instance-type': 'instanceType',
'ip-address': 'ipAddress',
'kernel-id': 'kernelId',
'key-name': 'keyName',
'launch-index': 'amiLaunchIndex',
'launch-time': 'launchTime',
'private-dns-name': 'privateDnsName',
'private-ip-address': 'privateIpAddress',
'ramdisk-id': 'ramdiskId',
'root-device-name': 'rootDeviceName',
'root-device-type': 'rootDeviceType',
'subnet-id': ['networkInterfaceSet', 'subnetId'],
'vpc-id': ['networkInterfaceSet', 'vpcId'],
'network-interface.description': ['networkInterfaceSet',
'description'],
'network-interface.subnet-id': ['networkInterfaceSet', 'subnetId'],
'network-interface.vpc-id': ['networkInterfaceSet', 'vpcId'],
'network-interface.network-interface.id': ['networkInterfaceSet',
'networkInterfaceId'],
'network-interface.owner-id': ['networkInterfaceSet', 'ownerId'],
'network-interface.requester-managed': ['networkInterfaceSet',
'requesterManaged'],
'network-interface.status': ['networkInterfaceSet', 'status'],
'network-interface.mac-address': ['networkInterfaceSet', 'macAddress'],
'network-interface.source-destination-check': ['networkInterfaceSet',
'sourceDestCheck'],
}
RESERVATION_FILTER_MAP = {
'reservation-id': 'reservationId',
'owner-id': 'ownerId',
}
def run_instances(context, image_id, min_count, max_count,
key_name=None, security_group_id=None,
@ -103,7 +143,7 @@ def run_instances(context, image_id, min_count, max_count,
security_groups_names = _get_vpc_default_security_group_id(
context, vpc_id)
instances_infos = []
instances_info = []
ec2_reservation_id = _generate_reservation_id()
# TODO(ft): Process min and max counts on running errors accordingly to
@ -183,112 +223,114 @@ def run_instances(context, image_id, min_count, max_count,
novadb_instance = novadb.instance_get_by_uuid(context,
os_instance.id)
instances_infos.append((instance, os_instance, novadb_instance,
network_interfaces,))
instances_info.append((instance, os_instance, novadb_instance,))
common_nw_info = _get_common_nw_info(context)
return _format_reservation(context, ec2_reservation_id, instances_infos,
common_nw_info)
# NOTE(ft): we cann't use describe_network_interfaces at this stage
# because network interfaces are not attached yet
ec2_network_interfaces = _format_network_interfaces(
context, instance_network_interfaces)
return _format_reservation(context, ec2_reservation_id, instances_info,
ec2_network_interfaces)
def terminate_instances(context, instance_id):
instance_ids = set(instance_id or [])
instance_ids = set(instance_id)
instances = ec2utils.get_db_items(context, 'i', instance_ids)
# TODO(ft): implement search db items in DB layer
network_interfaces = collections.defaultdict(list)
for eni in db_api.get_items(context, 'eni'):
if eni.get('instance_id') in instance_ids:
if eni['delete_on_termination']:
network_interfaces[eni['instance_id']].append(eni)
else:
network_interface_api.detach_network_interface(
context,
ec2utils.change_ec2_id_kind(eni['id'], 'eni-attach'))
_remove_instances(context, instances, network_interfaces)
nova = clients.nova(context)
os_instances_ids = [instance['os_id'] for instance in instances]
os_instances = nova.servers.list(search_opts={'id': os_instances_ids})
# TODO(ft): implement search db items by os_id in DB layer
network_interfaces = db_api.get_items(context, 'eni')
# NOTE(ft): detach port before terminating instance to prevent
# Nova deletes it
neutron = clients.neutron(context)
os_ports = neutron.list_ports(device_id=os_instances_ids)['ports']
for network_interface in network_interfaces:
if (network_interface.get('instance_id') not in instance_ids or
network_interface['delete_on_termination']):
continue
# NOTE(ft): detach ports only really attached to the instance
instance = next(i for i in instances
if i['id'] == network_interface['instance_id'])
os_port = next((p for p in os_ports
if p['id'] == network_interface['os_id']), None)
if (os_port and os_port['device_id'] == instance['os_id']):
neutron.update_port(os_port['id'],
{'port': {'device_id': '',
'device_owner': ''}})
network_interface_api._detach_network_interface_item(
context, network_interface)
for os_instance in os_instances:
nova.servers.delete(os_instance.id)
for instance in instances:
db_api.delete_item(context, instance['id'])
for network_interface in network_interfaces:
if (network_interface.get('instance_id') not in instance_ids or
not network_interface['delete_on_termination']):
continue
db_api.delete_item(context, network_interface['id'])
state_changes = []
for inst in instances:
state_change = _format_state_change(
inst,
next((i for i in os_instances if i.id == inst['os_id']), None))
for instance in instances:
try:
os_instance = nova.servers.get(instance['os_id'])
except nova_exception.NotFound:
os_instance = None
else:
os_instance.delete()
state_change = _format_state_change(instance, os_instance)
state_changes.append(state_change)
return {'instancesSet': state_changes}
def describe_instances(context, instance_id=None, filter=None, **kwargs):
def describe_instances(context, instance_id=None, filter=None,
max_results=None, next_token=None):
instances = ec2utils.get_db_items(context, 'i', instance_id)
if instance_id:
os_instances = _get_os_instances_by_instances(context, instances)
else:
os_instances = clients.nova(context).servers.list()
instances = db_api.get_items(context, 'i')
instances_by_os_id = dict((i['os_id'], i) for i in instances)
nova = clients.nova(context)
os_instances = nova.servers.list()
# TODO(ft): implement search db items by os_id in DB layer
network_interfaces = collections.defaultdict(list)
for eni in db_api.get_items(context, 'eni'):
if 'instance_id' in eni:
network_interfaces[eni['instance_id']].append(eni)
reservations = _prepare_reservations(
context, os_instances, instances_by_os_id, network_interfaces)
common_nw_info = _get_common_nw_info(context)
reservations = collections.defaultdict(list)
for os_instance in os_instances:
novadb_instance = novadb.instance_get_by_uuid(context, os_instance.id)
instance = instances_by_os_id.pop(os_instance.id, None)
if not instance:
instance = db_api.add_item(
context, 'i',
{'os_id': os_instance.id,
'vpc_id': None,
'reservation_id': novadb_instance['reservation_id'],
'launch_index': novadb_instance['launch_index']})
reservations[instance['reservation_id']].append(
(instance, os_instance, novadb_instance,))
_remove_instances(context, instances_by_os_id.itervalues())
ec2_network_interfaces = _get_ec2_network_interfaces(context, instance_id)
reservation_filters = []
instance_filters = []
for f in filter or []:
if f.get('name') in RESERVATION_FILTER_MAP:
reservation_filters.append(f)
else:
instance_filters.append(f)
ec2_reservations = []
for reservation_id, instances_infos in reservations.iteritems():
ec2_reservations.append(_format_reservation(
context, reservation_id, instances_infos, common_nw_info))
for reservation_id, instances_info in reservations.iteritems():
ec2_reservation = _format_reservation(
context, reservation_id, instances_info,
ec2_network_interfaces, instance_filters)
if (ec2_reservation['instancesSet'] and
not utils.filtered_out(ec2_reservation, reservation_filters,
RESERVATION_FILTER_MAP)):
ec2_reservations.append(ec2_reservation)
return {'reservationSet': ec2_reservations}
def reboot_instances(context, instance_id):
os_instances = _get_os_instances(context, instance_id)
for os_instance in os_instances:
os_instance.reboot()
return True
return _foreach_instance(context, instance_id,
lambda instance: instance.reboot())
def stop_instances(context, instance_id, force=False):
os_instances = _get_os_instances(context, instance_id)
for os_instance in os_instances:
os_instance.stop()
return True
return _foreach_instance(context, instance_id,
lambda instance: instance.stop())
def start_instances(context, instance_id):
os_instances = _get_os_instances(context, instance_id)
for os_instance in os_instances:
os_instance.start()
return True
return _foreach_instance(context, instance_id,
lambda instance: instance.start())
def describe_instance_attribute(context, instance_id, attribute):
os_instance = None
novadb_instance = None
instance = db_api.get_item_by_id(context, 'i', instance_id)
nova = clients.nova(context)
os_instance = nova.servers.get(instance['os_id'])
novadb_instance = novadb.instance_get_by_uuid(context, os_instance.id)
def _format_attr_block_device_mapping(result):
root_device_name = _cloud_format_instance_root_device_name(
@ -351,82 +393,43 @@ def describe_instance_attribute(context, instance_id, attribute):
# TODO(ft): clarify an exact AWS error
raise exception.InvalidAttribute(attr=attribute)
instance = db_api.get_item_by_id(context, 'i', instance_id)
nova = clients.nova(context)
os_instance = nova.servers.get(instance['os_id'])
novadb_instance = novadb.instance_get_by_uuid(context, os_instance.id)
result = {'instance_id': instance_id}
fn(result)
return result
def _get_idempotent_run(context, client_token):
instances = db_api.get_items(context, 'i')
instances = [i for i in instances
if i.get('client_token') == client_token]
# TODO(ft): implement search in DB layer
instances = dict((i['os_id'], i) for i in db_api.get_items(context, 'i')
if i.get('client_token') == client_token)
if not instances:
return
os_instances = clients.nova(context).servers.list(
search_opts={'id': [i['os_id'] for i in instances]})
os_instances = dict((i.id, i) for i in os_instances)
# TODO(ft): implement search db items by os_id in DB layer
instance_ids = set(i['id'] for i in instances)
network_interfaces = collections.defaultdict(list)
for eni in db_api.get_items(context, 'eni'):
if 'instance_id' in eni and eni['instance_id'] in instance_ids:
network_interfaces[eni['instance_id']].append(eni)
instances_infos = []
for instance in instances:
os_instance = os_instances.get(instance['os_id'])
if not os_instance:
continue
novadb_instance = novadb.instance_get_by_uuid(context, os_instance.id)
instances_infos.append((instance, os_instance, novadb_instance,
network_interfaces[instance['id']],))
common_nw_info = _get_common_nw_info(context)
return _format_reservation(context, instance['reservation_id'],
instances_infos, common_nw_info)
def _prepare_reservations(context, os_instances, instances_by_os_id,
network_interfaces):
reservations = {}
os_instances = _get_os_instances_by_instances(context, instances)
instances_info = []
instance_ids = []
for os_instance in os_instances:
instance = instances.pop(os_instance['id'])
novadb_instance = novadb.instance_get_by_uuid(context, os_instance.id)
if os_instance.id in instances_by_os_id:
instance = instances_by_os_id.get(os_instance.id)
reservation_id = instance['reservation_id']
else:
reservation_id = novadb_instance['reservation_id']
instance = db_api.add_item(
context, 'i',
{'os_id': os_instance.id,
'vpc_id': None,
'reservation_id': reservation_id,
'launch_index': novadb_instance['launch_index']})
if reservation_id not in reservations:
reservations[reservation_id] = []
reservations[reservation_id].append(
(instance, os_instance, novadb_instance,
network_interfaces[instance['id']],))
return reservations
instances_info.append((instance, os_instance, novadb_instance,))
instance_ids.append(instance['id'])
if instances:
_remove_instances(context, instances.itervalues())
if not instances_info:
return
ec2_network_interfaces = _get_ec2_network_interfaces(context, instance_ids)
return _format_reservation(context, instance['reservation_id'],
instances_info, ec2_network_interfaces)
def _format_reservation(context, reservation_id, instances_infos,
common_nw_info):
os_ports, addresses, security_groups = common_nw_info
def _format_reservation(context, reservation_id, instances_info,
ec2_network_interfaces, filters=None):
ec2_instances = []
for (instance, os_instance, novadb_instance,
network_interfaces) in instances_infos:
ports_info = [(eni,
os_ports[eni['os_id']],
addresses.get(eni['id'], []))
for eni in network_interfaces
if eni['os_id'] in os_ports]
ec2_instances.append(
_format_instance(context, instance, os_instance,
novadb_instance, ports_info, security_groups))
for (instance, os_instance, novadb_instance) in instances_info:
ec2_instance = _format_instance(
context, instance, os_instance, novadb_instance,
ec2_network_interfaces.get(instance['id']))
if not utils.filtered_out(ec2_instance, filters, INSTANCE_FILTER_MAP):
ec2_instances.append(ec2_instance)
ec2_reservation = {'reservationId': reservation_id,
'ownerId': os_instance.tenant_id,
'instancesSet': ec2_instances}
@ -437,7 +440,7 @@ def _format_reservation(context, reservation_id, instances_infos,
def _format_instance(context, instance, os_instance, novadb_instance,
ports_info, security_groups):
ec2_network_interfaces):
ec2_instance = {}
ec2_instance['instanceId'] = instance['id']
image_uuid = os_instance.image['id'] if os_instance.image else ''
@ -490,40 +493,32 @@ def _format_instance(context, instance, os_instance, novadb_instance,
'availabilityZone': getattr(os_instance,
'OS-EXT-AZ:availability_zone')
}
if not ports_info:
if not ec2_network_interfaces:
# TODO(ft): boto uses 2010-08-31 version of AWS protocol
# which doesn't contain groupSet element in an instance
# We should support different versions of output data
# ec2_instance['groupSet'] = _format_group_set(
# context, os_instance.security_groups)
return ec2_instance
ec2_network_interfaces = []
for network_interface, os_port, addresses in ports_info:
ec2_network_interface = (
network_interface_api._format_network_interface(
context, network_interface, os_port, addresses,
security_groups=security_groups))
attachment = ec2_network_interface.get('attachment')
if attachment:
attachment.pop('instanceId', None)
attachment.pop('instanceOwnerId', None)
ec2_network_interfaces.append(ec2_network_interface)
ec2_instance['networkInterfaceSet'] = ec2_network_interfaces
# NOTE(ft): get instance's subnet by instance's privateIpAddress
instance_ip = ec2_instance['privateIpAddress']
network_interface = None
for ((network_interface, os_port, _addresses),
ec2_network_interface) in zip(ports_info, ec2_network_interfaces):
if instance_ip in (ip['ip_address']
for ip in os_port['fixed_ips']):
ec2_instance['subnetId'] = network_interface['subnet_id']
# TODO(ft): boto uses 2010-08-31 version of AWS protocol
# which doesn't contain groupSet element in an instance
# We should support different versions of output data
# ec2_instance['groupSet'] = ec2_network_interface['groupSet']
break
if network_interface:
ec2_instance['vpcId'] = network_interface['vpc_id']
main_ec2_network_interface = None
for ec2_network_interface in ec2_network_interfaces:
ec2_network_interface['attachment'].pop('instanceId')
ec2_network_interface['attachment'].pop('instanceOwnerId')
if (not main_ec2_network_interface and
any(address['privateIpAddress'] == instance_ip
for address in
ec2_network_interface['privateIpAddressesSet'])):
main_ec2_network_interface = ec2_network_interface
ec2_instance['networkInterfaceSet'] = ec2_network_interfaces
if main_ec2_network_interface:
ec2_instance['subnetId'] = main_ec2_network_interface['subnetId']
# TODO(ft): boto uses 2010-08-31 version of AWS protocol
# which doesn't contain groupSet element in an instance
# We should support different versions of output data
# ec2_instance['groupSet'] = main_ec2_network_interface['groupSet']
ec2_instance['vpcId'] = ec2_network_interface['vpcId']
return ec2_instance
@ -544,13 +539,32 @@ def _format_state_change(instance, os_instance):
}
def _get_common_nw_info(context):
def _get_ec2_network_interfaces(context, instance_ids=None):
ec2_network_interfaces = collections.defaultdict(list)
if not instance_ids:
network_interface_ids = None
else:
# TODO(ft): implement search db items in DB layer
network_interface_ids = [
eni['id'] for eni in db_api.get_items(context, 'eni')
if eni.get('instance_id') in instance_ids]
enis = network_interface_api.describe_network_interfaces(
context,
network_interface_id=network_interface_ids)['networkInterfaceSet']
for eni in enis:
if eni['status'] == 'in-use':
ec2_network_interfaces[eni['attachment']['instanceId']].append(eni)
return ec2_network_interfaces
def _format_network_interfaces(context, instances_network_interfaces):
neutron = clients.neutron(context)
os_ports = neutron.list_ports()['ports']
os_ports = dict((p['id'], p) for p in os_ports)
os_floating_ips = neutron.list_floatingips()['floatingips']
# TODO(ft): reuse following code from network_interface_api
os_floating_ips = neutron.list_floatingips(fields=['id'])['floatingips']
os_floating_ip_ids = set(ip['id'] for ip in os_floating_ips)
addresses = collections.defaultdict(list)
for address in db_api.get_items(context, 'eipalloc'):
@ -561,7 +575,40 @@ def _get_common_nw_info(context):
security_groups = security_group_api._format_security_groups_ids_names(
context)
return os_ports, addresses, security_groups
ec2_network_interfaces = collections.defaultdict(list)
for network_interfaces in instances_network_interfaces:
for network_interface in network_interfaces:
ec2_eni = network_interface_api._format_network_interface(
context, network_interface,
os_ports[network_interface['os_id']],
addresses[network_interface['id']], security_groups)
ec2_network_interfaces[
network_interface['instance_id']].append(ec2_eni)
return ec2_network_interfaces
def _remove_instances(context, instances, network_interfaces=None):
if network_interfaces is None:
# TODO(ft): implement search db items by os_id in DB layer
network_interfaces = collections.defaultdict(list)
for eni in db_api.get_items(context, 'eni'):
if 'instance_id' in eni:
network_interfaces[eni['instance_id']].append(eni)
addresses = db_api.get_items(context, 'eipalloc')
addresses = dict((a['network_interface_id'], a) for a in addresses
if 'network_interface_id' in a)
for instance in instances:
for eni in network_interfaces[instance['id']]:
if eni['delete_on_termination']:
address = addresses.get(eni['id'])
if address:
address_api._disassociate_address_item(context, address)
db_api.delete_item(context, eni['id'])
else:
network_interface_api._detach_network_interface_item(context,
eni)
db_api.delete_item(context, instance['id'])
def _check_min_max_count(min_count, max_count):
@ -832,21 +879,27 @@ def _get_ip_info_for_instance(os_instance):
return fixed_ip, fixed_ip6, floating_ip
def _get_os_instances(context, instance_ids):
def _foreach_instance(context, instance_ids, func):
instances = ec2utils.get_db_items(context, 'i', instance_ids)
os_instances = _get_os_instances_by_instances(context, instances,
exactly=True)
for os_instance in os_instances:
func(os_instance)
return True
def _get_os_instances_by_instances(context, instances, exactly=False):
nova = clients.nova(context)
os_instances_ids = [instance['os_id'] for instance in instances]
os_instances = nova.servers.list(search_opts={'id': os_instances_ids})
os_instances = []
for instance in instances:
try:
os_instances.append(nova.servers.get(instance['os_id']))
except nova_exception.NotFound:
if exactly:
raise exception.InvalidInstanceIDNotFound(i_id=instance['id'])
if len(instances) > len(os_instances):
raise exception.InvalidInstanceIDNotFound(
instance_id=any(i['id'] for i in instances
if all(i['os_id'] != os_i.id
for os_i in os_instances)))
return os_instances
# NOTE(ft): following functions are copied from various parts of Nova
_dev = re.compile('^/dev/')

View File

@ -354,12 +354,11 @@ def attach_network_interface(context, network_interface_id,
network_interface['id'], 'eni-attach')}
def detach_network_interface(context, attachment_id,
force=None):
network_interface = db_api.get_item_by_id(context, 'eni', attachment_id)
def detach_network_interface(context, attachment_id, force=None):
network_interface = db_api.get_item_by_id(
context, 'eni', ec2utils.change_ec2_id_kind(attachment_id, 'eni'))
if 'instance_id' not in network_interface:
raise exception.InvalidAttachmentIDNotFound(
attachment_id=attachment_id)
raise exception.InvalidAttachmentIDNotFound(attach_id=attachment_id)
# TODO(Alex) Check that device index is not 0 (when we support it) and
# forbid detaching.
neutron = clients.neutron(context)

View File

@ -56,9 +56,8 @@ def filtered_out(item, filters, filter_map):
value=filter['name'], parameter='filter',
reason='invalid filter')
if type(filter_name) is list:
value_set = item.get(filter_name[0])
if value_set is not None:
values = [value[filter_name[1]] for value in value_set]
value_set = item.get(filter_name[0], [])
values = [value[filter_name[1]] for value in value_set]
else:
values = [item.get(filter_name)]
if not values:

View File

@ -168,7 +168,7 @@ class NovaDbInstanceNotFound(EC2NotFound):
class InvalidInstanceIDNotFound(EC2NotFound):
ec2_code = 'InvalidInstanceID.NotFound'
msg_fmt = _("The instance ID '%(instance_id)s' does not exist")
msg_fmt = _("The instance ID '%(i_id)s' does not exist")
class InvalidVpcIDNotFound(EC2NotFound):
@ -194,7 +194,7 @@ class InvalidNetworkInterfaceIDNotFound(EC2NotFound):
class InvalidAttachmentIDNotFound(EC2NotFound):
ec2_code = 'InvalidAttachmentID.NotFound'
msg_fmt = _("Attachment %(eni-attach_id)s could not "
msg_fmt = _("Attachment %(attach_id)s could not "
"be found.")

View File

@ -540,6 +540,9 @@ class OSInstance(object):
def get(self):
None
def delete(self):
None
OS_INSTANCE_1 = OSInstance(
ID_OS_INSTANCE_1, {'id': 'fakeFlavorId'},

View File

@ -339,16 +339,35 @@ class InstanceTestCase(base.ApiTestCase):
fakes.EC2_NETWORK_INTERFACE_1['networkInterfaceId']},
new_port=False)
def test_terminate_instances(self):
@mock.patch('ec2api.api.address._disassociate_address_item')
@mock.patch('ec2api.api.network_interface.detach_network_interface')
def test_terminate_instances(self, detach_network_interface,
dissassociate_address_item):
"""Terminate 2 instances in one request."""
self.db_api.get_items_by_ids.return_value = [fakes.DB_INSTANCE_1,
fakes.DB_INSTANCE_2]
self.nova_servers.list.return_value = [fakes.OS_INSTANCE_1,
self.nova_servers.get.side_effect = [fakes.OS_INSTANCE_1,
fakes.OS_INSTANCE_2]
self.db_api.get_items.return_value = (
[copy.deepcopy(fakes.DB_NETWORK_INTERFACE_1),
copy.deepcopy(fakes.DB_NETWORK_INTERFACE_2)])
self.neutron.list_ports.return_value = {'ports': [fakes.OS_PORT_2]}
self.db_api.get_items.side_effect = fakes.get_db_api_get_items(
{'eni': [copy.deepcopy(fakes.DB_NETWORK_INTERFACE_1),
copy.deepcopy(fakes.DB_NETWORK_INTERFACE_2)],
'eipalloc': [copy.deepcopy(fakes.DB_ADDRESS_1),
copy.deepcopy(fakes.DB_ADDRESS_2)]})
instance1_delete_patcher = mock.patch.object(fakes.OS_INSTANCE_1,
'delete')
instance1_delete = instance1_delete_patcher.start()
self.addCleanup(instance1_delete.stop)
instance2_delete_patcher = mock.patch.object(fakes.OS_INSTANCE_2,
'delete')
instance2_delete = instance2_delete_patcher.start()
self.addCleanup(instance2_delete.stop)
instance1_get_patcher = mock.patch.object(fakes.OS_INSTANCE_1, 'get')
instance1_get = instance1_get_patcher.start()
self.addCleanup(instance1_get.stop)
instance2_get_patcher = mock.patch.object(fakes.OS_INSTANCE_2, 'get')
instance2_get = instance2_get_patcher.start()
self.addCleanup(instance2_get.stop)
resp = self.execute('TerminateInstances',
{'InstanceId.1': fakes.ID_EC2_INSTANCE_1,
@ -370,26 +389,21 @@ class InstanceTestCase(base.ApiTestCase):
self.db_api.get_items_by_ids.assert_called_once_with(
mock.ANY, 'i',
set([fakes.ID_EC2_INSTANCE_1, fakes.ID_EC2_INSTANCE_2]))
self.nova_servers.list.assert_called_once_with(
search_opts={'id': [fakes.ID_OS_INSTANCE_1,
fakes.ID_OS_INSTANCE_2]})
self._assert_list_ports_is_called_with_filter(
[fakes.ID_OS_INSTANCE_1, fakes.ID_OS_INSTANCE_2])
self.db_api.get_items.assert_called_once_with(mock.ANY, 'eni')
self.neutron.update_port.assert_called_once_with(
fakes.ID_OS_PORT_2,
{'port': {'device_id': '',
'device_owner': ''}})
self.db_api.update_item.assert_called_once_with(
mock.ANY, tools.purge_dict(fakes.DB_NETWORK_INTERFACE_2,
('instance_id', 'attach_time',
'delete_on_termination')))
self.nova_servers.delete.assert_any_call(fakes.ID_OS_INSTANCE_1)
self.nova_servers.delete.assert_any_call(fakes.ID_OS_INSTANCE_2)
self.assertEqual(2, self.nova_servers.delete.call_count)
self.assertEqual(2, self.db_api.get_items.call_count)
self.db_api.get_items.assert_any_call(mock.ANY, 'eni')
self.db_api.get_items.assert_any_call(mock.ANY, 'eipalloc')
detach_network_interface.assert_called_once_with(
mock.ANY, fakes.ID_EC2_NETWORK_INTERFACE_2_ATTACH)
self.nova_servers.get.assert_any_call(fakes.ID_OS_INSTANCE_1)
self.nova_servers.get.assert_any_call(fakes.ID_OS_INSTANCE_2)
self.assertEqual(0, dissassociate_address_item.call_count)
self.assertEqual(2, self.db_api.delete_item.call_count)
for inst_id in (fakes.ID_EC2_INSTANCE_1, fakes.ID_EC2_INSTANCE_2):
self.db_api.delete_item.assert_any_call(mock.ANY, inst_id)
self.assertEqual(2, self.db_api.delete_item.call_count)
instance1_delete.assert_called_once_with()
instance2_delete.assert_called_once_with()
instance1_get.assert_called_once_with()
instance2_get.assert_called_once_with()
# TODO(ft): restore test after finish extraction of Nova EC2 API
def _test_terminate_instances_multiple_networks(self):