fix delete on termination behaviour for network interfaces

and add test cases for delete_on_termination preperty for NetworkInterface

Depends-On: I3d557dd95f442106c495249a5ef1d2ac36e6a2ea
Change-Id: Icf2b9739aaf87b4c9af13ad64a310081a68f776e
This commit is contained in:
Andrey Pavlov 2015-03-18 16:29:23 +03:00
parent 5480e9b850
commit 2801f9e687
11 changed files with 336 additions and 160 deletions

View File

@ -1650,12 +1650,14 @@ class VpcCloudController(CloudController):
@module_and_param_types(network_interface, 'eni_id',
'str',
'bool',
'sg_ids')
'sg_ids',
'dummy')
def modify_network_interface_attribute(self, context,
network_interface_id,
description=None,
source_dest_check=None,
security_group_id=None):
security_group_id=None,
attachment=None):
"""Modifies the specified attribute of the specified network interface.
@ -1668,6 +1670,9 @@ class VpcCloudController(CloudController):
means checking is disabled.
This value must be false for a NAT instance to perform NAT.
security_group_id [list of str]: List of secuirity groups to attach
attachment: Information about the interface attachment. If
modifying the 'delete on termination' attribute, you must
specify the ID of the interface attachment.
Returns:
true if the request succeeds.

View File

@ -23,7 +23,6 @@ from novaclient import exceptions as nova_exception
from oslo_config import cfg
from oslo_utils import timeutils
from ec2api.api import address as address_api
from ec2api.api import clients
from ec2api.api import common
from ec2api.api import ec2utils
@ -94,17 +93,6 @@ def terminate_instances(context, instance_id):
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'))
nova = clients.nova(context)
state_changes = []
for instance in instances:
@ -267,15 +255,15 @@ class ReservationDescriber(common.NonOpenstackItemsDescriber):
if not reservation_filters:
reservation_filters = None
instance_describer = InstanceDescriber()
formatted_instances = instance_describer.describe(
context, ids=ids, filter=instance_filters)
try:
instance_describer = InstanceDescriber()
formatted_instances = instance_describer.describe(
context, ids=ids, filter=instance_filters)
except exception.InvalidInstanceIDNotFound:
_remove_instances(context, instance_describer.obsolete_instances)
raise
# NOTE(ft): remove obsolete instances' DB items only, because
# network interfaces and addresses are cleaned during appropriate
# describe operations called inside current operation
_remove_instances(context, instance_describer.obsolete_instances,
purge_linked_items=False)
_remove_instances(context, instance_describer.obsolete_instances)
self.reservations = instance_describer.reservations.values()
self.instances = instance_describer.reservation_instances
@ -559,29 +547,26 @@ def _format_state_change(instance, os_instance):
}
def _remove_instances(context, instances, purge_linked_items=True):
def _remove_instances(context, instances):
if not instances:
return
ids = set([i['id'] for i in instances])
network_interfaces = collections.defaultdict(list)
if purge_linked_items:
# TODO(ft): implement search db items by os_id in DB layer
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'])
# TODO(ft): implement search db items by os_id in DB layer
for eni in db_api.get_items(context, 'eni'):
if 'instance_id' in eni and eni['instance_id'] in ids:
network_interfaces[eni['instance_id']].append(eni)
for instance_id in ids:
for eni in network_interfaces[instance_id]:
delete_on_termination = eni['delete_on_termination']
network_interface_api._detach_network_interface_item(context,
eni)
if delete_on_termination:
network_interface_api.delete_network_interface(context,
eni['id'])
db_api.delete_item(context, instance_id)
def _check_min_max_count(min_count, max_count):

View File

@ -308,17 +308,17 @@ def describe_network_interface_attribute(context, network_interface_id,
def modify_network_interface_attribute(context, network_interface_id,
description=None,
source_dest_check=None,
security_group_id=None):
# NOTE(Alex) Later more parameters will appear
security_group_id=None,
attachment=None):
params_count = (
int(description is not None) +
int(source_dest_check is not None) +
int(security_group_id is not None))
int(security_group_id is not None) +
int(attachment is not None))
if params_count != 1:
raise exception.InvalidParameterCombination(
'Multiple attributes specified')
network_interface = ec2utils.get_db_item(context, network_interface_id)
# TODO(Alex): Implement attachments
if description is not None:
network_interface['description'] = description
db_api.update_item(context, network_interface)
@ -335,6 +335,20 @@ def modify_network_interface_attribute(context, network_interface_id,
{'port': {'allowed_address_pairs': allowed}})
network_interface['source_dest_check'] = source_dest_check
db_api.update_item(context, network_interface)
if attachment:
attachment_id = attachment.get('attachment_id')
delete_on_termination = attachment.get('delete_on_termination')
if attachment_id is None or delete_on_termination is None:
raise exception.MissingParameter(
_('The request must contain the parameter attachment '
'deleteOnTermination'))
attachment_id_own = ec2utils.change_ec2_id_kind(
network_interface['id'], 'eni-attach')
if ('instance_id' not in network_interface
or attachment_id_own != attachment_id):
raise exception.InvalidAttachmentIDNotFound(id=attachment_id)
network_interface['delete_on_termination'] = delete_on_termination
db_api.update_item(context, network_interface)
return True

View File

@ -252,27 +252,29 @@ class AddressTest(base.EC2TestCase):
if not image_id:
raise self.skipException('aws image_id does not provided')
resp, data = self.client.CreateVpc(CidrBlock='10.2.0.0/20')
base_net = '10.3.0.0'
resp, data = self.client.CreateVpc(CidrBlock=base_net + '/20')
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
vpc_id = data['Vpc']['VpcId']
self.addResourceCleanUp(self.client.DeleteVpc, VpcId=vpc_id)
clean_vpc = self.addResourceCleanUp(self.client.DeleteVpc,
VpcId=vpc_id)
self.get_vpc_waiter().wait_available(vpc_id)
cidr = '10.2.0.0/24'
cidr = base_net + '/24'
resp, data = self.client.CreateSubnet(VpcId=vpc_id, CidrBlock=cidr,
AvailabilityZone=aws_zone)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
subnet_id = data['Subnet']['SubnetId']
self.addResourceCleanUp(self.client.DeleteSubnet,
SubnetId=subnet_id)
clean_subnet = self.addResourceCleanUp(self.client.DeleteSubnet,
SubnetId=subnet_id)
resp, data = self.client.RunInstances(
ImageId=image_id, InstanceType=instance_type, MinCount=1,
MaxCount=1, SubnetId=subnet_id)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
instance_id = data['Instances'][0]['InstanceId']
self.addResourceCleanUp(self.client.TerminateInstances,
InstanceIds=[instance_id])
clean_i = self.addResourceCleanUp(self.client.TerminateInstances,
InstanceIds=[instance_id])
self.get_instance_waiter().wait_available(instance_id,
final_set=('running'))
@ -282,8 +284,8 @@ class AddressTest(base.EC2TestCase):
resp, data = self.client.AllocateAddress(*[], **kwargs)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
alloc_id = data['AllocationId']
self.addResourceCleanUp(self.client.ReleaseAddress,
AllocationId=alloc_id)
clean_a = self.addResourceCleanUp(self.client.ReleaseAddress,
AllocationId=alloc_id)
resp, data = self.client.AssociateAddress(InstanceId=instance_id,
AllocationId=alloc_id)
@ -294,14 +296,14 @@ class AddressTest(base.EC2TestCase):
resp, data = self.client.CreateInternetGateway()
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
gw_id = data['InternetGateway']['InternetGatewayId']
self.addResourceCleanUp(self.client.DeleteInternetGateway,
InternetGatewayId=gw_id)
clean_ig = self.addResourceCleanUp(self.client.DeleteInternetGateway,
InternetGatewayId=gw_id)
resp, data = self.client.AttachInternetGateway(VpcId=vpc_id,
InternetGatewayId=gw_id)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.addResourceCleanUp(self.client.DetachInternetGateway,
VpcId=vpc_id,
InternetGatewayId=gw_id)
clean_aig = self.addResourceCleanUp(self.client.DetachInternetGateway,
VpcId=vpc_id,
InternetGatewayId=gw_id)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
resp, data = self.client.AssociateAddress(InstanceId=instance_id,
@ -323,6 +325,37 @@ class AddressTest(base.EC2TestCase):
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.assertIsNone(data['Addresses'][0].get('InstanceId'))
# NOTE(andrey-mp): cleanup
time.sleep(3)
resp, data = self.client.DetachInternetGateway(VpcId=vpc_id,
InternetGatewayId=gw_id)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.cancelResourceCleanUp(clean_aig)
resp, data = self.client.DeleteInternetGateway(InternetGatewayId=gw_id)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.cancelResourceCleanUp(clean_ig)
resp, data = self.client.ReleaseAddress(AllocationId=alloc_id)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.cancelResourceCleanUp(clean_a)
resp, data = self.client.TerminateInstances(InstanceIds=[instance_id])
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.cancelResourceCleanUp(clean_i)
self.get_instance_waiter().wait_delete(instance_id)
resp, data = self.client.DeleteSubnet(SubnetId=subnet_id)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.cancelResourceCleanUp(clean_subnet)
self.get_subnet_waiter().wait_delete(subnet_id)
resp, data = self.client.DeleteVpc(VpcId=vpc_id)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.cancelResourceCleanUp(clean_vpc)
self.get_vpc_waiter().wait_delete(vpc_id)
def test_associate_disassociate_standard_addresses(self):
instance_type = CONF.aws.instance_type
image_id = CONF.aws.image_id
@ -335,16 +368,16 @@ class AddressTest(base.EC2TestCase):
MaxCount=1)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
instance_id = data['Instances'][0]['InstanceId']
self.addResourceCleanUp(self.client.TerminateInstances,
InstanceIds=[instance_id])
clean_i = self.addResourceCleanUp(self.client.TerminateInstances,
InstanceIds=[instance_id])
self.get_instance_waiter().wait_available(instance_id,
final_set=('running'))
resp, data = self.client.AllocateAddress(*[], **{})
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
ip = data['PublicIp']
self.addResourceCleanUp(self.client.ReleaseAddress,
PublicIp=ip)
clean_a = self.addResourceCleanUp(self.client.ReleaseAddress,
PublicIp=ip)
resp, data = self.client.AssociateAddress(InstanceId=instance_id,
PublicIp=ip)
@ -360,3 +393,14 @@ class AddressTest(base.EC2TestCase):
resp, data = self.client.DescribeAddresses(*[], **{})
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.assertIsNone(data['Addresses'][0].get('InstanceId'))
time.sleep(3)
resp, data = self.client.ReleaseAddress(PublicIp=ip)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.cancelResourceCleanUp(clean_a)
resp, data = self.client.TerminateInstances(InstanceIds=[instance_id])
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.cancelResourceCleanUp(clean_i)
self.get_instance_waiter().wait_delete(instance_id)

View File

@ -245,13 +245,7 @@ class InstanceTest(base.EC2TestCase):
self.get_instance_waiter().wait_available(instance_id,
final_set=('running'))
resp, data = self.client.DescribeInstances(InstanceIds=[instance_id])
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
reservations = data.get('Reservations', [])
self.assertNotEmpty(reservations)
instances = reservations[0].get('Instances', [])
self.assertNotEmpty(instances)
instance = instances[0]
instance = self.get_instance(instance_id)
self.assertIsNotNone(instance.get('PublicIpAddress'))
self.assertIsNotNone(instance.get('PrivateIpAddress'))
self.assertNotEqual(instance.get('PublicIpAddress'),

View File

@ -45,13 +45,7 @@ class InstanceWithEBSTest(base.EC2TestCase):
self.get_instance_waiter().wait_available(instance_id,
final_set=('running'))
resp, data = self.client.DescribeInstances(InstanceIds=[instance_id])
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
reservations = data.get('Reservations', [])
self.assertNotEmpty(reservations)
instances = reservations[0].get('Instances', [])
self.assertNotEmpty(instances)
instance = instances[0]
instance = self.get_instance(instance_id)
self.assertEqual('ebs', instance.get('RootDeviceType'))
self.assertIsNotNone(instance.get('RootDeviceName'))

View File

@ -183,7 +183,7 @@ class InstanceInVPCTest(base.EC2TestCase):
"one subnet. Openstack can't do it without additional configuration."
"Worked only from Juno with parameter in config - "
"nova.conf/neutron/allow_duplicate_networks = True")
def test_create_instance_with_interfaces(self):
def test_create_instance_with_two_interfaces(self):
kwargs = {
'SubnetId': self.subnet_id,
}
@ -222,13 +222,7 @@ class InstanceInVPCTest(base.EC2TestCase):
self.get_instance_waiter().wait_available(instance_id,
final_set=('running'))
resp, data = self.client.DescribeInstances(InstanceIds=[instance_id])
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
reservations = data.get('Reservations', [])
self.assertNotEmpty(reservations)
instances = reservations[0].get('Instances', [])
self.assertNotEmpty(instances)
instance = instances[0]
instance = self.get_instance(instance_id)
nis = instance.get('NetworkInterfaces', [])
self.assertEqual(2, len(nis))
@ -256,13 +250,7 @@ class InstanceInVPCTest(base.EC2TestCase):
self.get_instance_waiter().wait_available(instance_id,
final_set=('running'))
resp, data = self.client.DescribeInstances(InstanceIds=[instance_id])
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
reservations = data.get('Reservations', [])
self.assertNotEmpty(reservations)
instances = reservations[0].get('Instances', [])
self.assertNotEmpty(instances)
instance = instances[0]
instance = self.get_instance(instance_id)
self.assertEqual(ip, instance['PrivateIpAddress'])
resp, data = self.client.TerminateInstances(InstanceIds=[instance_id])
@ -322,7 +310,27 @@ class InstanceInVPCTest(base.EC2TestCase):
NetworkInterfaceId=ni_id2)
self.get_network_interface_waiter().wait_available(ni_id2)
# NOTE(andrey-mp): A network interface may not specify a network
# interface ID and delete on termination as true
kwargs = {
'ImageId': CONF.aws.image_id,
'InstanceType': CONF.aws.instance_type,
'MinCount': 1,
'MaxCount': 1,
'NetworkInterfaces': [{'NetworkInterfaceId': ni_id1,
'DeviceIndex': 0,
'DeleteOnTermination': True}]
}
resp, data = self.client.RunInstances(*[], **kwargs)
if resp.status_code == 200:
self.addResourceCleanUp(
self.client.TerminateInstances,
InstanceIds=[data['Instances'][0]['InstanceId']])
self.assertEqual(400, resp.status_code)
self.assertEqual('InvalidParameterCombination', data['Error']['Code'])
if CONF.aws.run_incompatible_tests:
# NOTE(andrey-mp): Each network interface requires a device index.
kwargs = {
'ImageId': CONF.aws.image_id,
'InstanceType': CONF.aws.instance_type,

View File

@ -379,6 +379,27 @@ class NetworkInterfaceTest(base.EC2TestCase):
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.assertEqual(True, data['SourceDestCheck']['Value'])
kwargs = {
'NetworkInterfaceId': ni_id,
'Attachment': {
'AttachmentId': 'fake'
}
}
resp, data = self.client.ModifyNetworkInterfaceAttribute(*[], **kwargs)
self.assertEqual(400, resp.status_code, base.EC2ErrorConverter(data))
self.assertEqual('MissingParameter', data['Error']['Code'])
kwargs = {
'NetworkInterfaceId': ni_id,
'Attachment': {
'AttachmentId': 'eni-attach-ffffffff',
'DeleteOnTermination': True
}
}
resp, data = self.client.ModifyNetworkInterfaceAttribute(*[], **kwargs)
self.assertEqual(400, resp.status_code, base.EC2ErrorConverter(data))
self.assertEqual('InvalidAttachmentID.NotFound', data['Error']['Code'])
resp, data = self.client.DeleteNetworkInterface(
NetworkInterfaceId=ni_id)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
@ -409,8 +430,8 @@ class NetworkInterfaceTest(base.EC2TestCase):
MaxCount=1, SubnetId=self.subnet_id)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
instance_id = data['Instances'][0]['InstanceId']
self.addResourceCleanUp(self.client.TerminateInstances,
InstanceIds=[instance_id])
res_clean = self.addResourceCleanUp(self.client.TerminateInstances,
InstanceIds=[instance_id])
self.get_instance_waiter().wait_available(instance_id,
final_set=('running'))
@ -433,13 +454,7 @@ class NetworkInterfaceTest(base.EC2TestCase):
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
attachment_id = data['AttachmentId']
resp, data = self.client.DescribeInstances(InstanceIds=[instance_id])
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
reservations = data.get('Reservations', [])
self.assertNotEmpty(reservations)
instances = reservations[0].get('Instances', [])
self.assertNotEmpty(instances)
instance = instances[0]
instance = self.get_instance(instance_id)
nis = instance.get('NetworkInterfaces', [])
self.assertEqual(2, len(nis))
ids = [nis[0]['Attachment']['AttachmentId'],
@ -456,3 +471,148 @@ class NetworkInterfaceTest(base.EC2TestCase):
}
resp, data = self.client.DetachNetworkInterface(*[], **kwargs)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
resp, data = self.client.TerminateInstances(InstanceIds=[instance_id])
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.cancelResourceCleanUp(res_clean)
self.get_instance_waiter().wait_delete(instance_id)
def test_network_interfaces_are_not_deleted_on_termination(self):
instance_type = CONF.aws.instance_type
image_id = CONF.aws.image_id
if not image_id:
raise self.skipException('aws image_id does not provided')
resp, data = self.client.RunInstances(
ImageId=image_id, InstanceType=instance_type, MinCount=1,
MaxCount=1, SubnetId=self.subnet_id)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
instance_id = data['Instances'][0]['InstanceId']
res_clean = self.addResourceCleanUp(self.client.TerminateInstances,
InstanceIds=[instance_id])
self.get_instance_waiter().wait_available(instance_id,
final_set=('running'))
instance = self.get_instance(instance_id)
nis = instance.get('NetworkInterfaces', [])
self.assertEqual(1, len(nis))
self.assertTrue(nis[0]['Attachment']['DeleteOnTermination'])
ni_id = nis[0]['NetworkInterfaceId']
attachment_id = nis[0]['Attachment']['AttachmentId']
kwargs = {
'NetworkInterfaceId': ni_id,
'Attachment': {
'AttachmentId': attachment_id,
'DeleteOnTermination': False,
}
}
resp, data = self.client.ModifyNetworkInterfaceAttribute(*[], **kwargs)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
clean_ni = self.addResourceCleanUp(self.client.DeleteNetworkInterface,
NetworkInterfaceId=ni_id)
kwargs = {
'SubnetId': self.subnet_id,
}
resp, data = self.client.CreateNetworkInterface(*[], **kwargs)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
ni_id2 = data['NetworkInterface']['NetworkInterfaceId']
clean_ni2 = self.addResourceCleanUp(self.client.DeleteNetworkInterface,
NetworkInterfaceId=ni_id2)
self.get_network_interface_waiter().wait_available(ni_id2)
kwargs = {
'DeviceIndex': 2,
'InstanceId': instance_id,
'NetworkInterfaceId': ni_id2
}
resp, data = self.client.AttachNetworkInterface(*[], **kwargs)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
attachment_id = data['AttachmentId']
instance = self.get_instance(instance_id)
nis = instance.get('NetworkInterfaces', [])
self.assertEqual(2, len(nis))
ni = nis[0]
if ni['Attachment']['AttachmentId'] != attachment_id:
ni = nis[1]
self.assertEqual(attachment_id, ni['Attachment']['AttachmentId'])
self.assertFalse(ni['Attachment']['DeleteOnTermination'])
resp, data = self.client.TerminateInstances(InstanceIds=[instance_id])
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.cancelResourceCleanUp(res_clean)
self.get_instance_waiter().wait_delete(instance_id)
self.get_network_interface_waiter().wait_available(ni_id)
self.get_network_interface_waiter().wait_available(ni_id2)
resp, data = self.client.DeleteNetworkInterface(
NetworkInterfaceId=ni_id)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.cancelResourceCleanUp(clean_ni)
self.get_network_interface_waiter().wait_delete(ni_id)
resp, data = self.client.DeleteNetworkInterface(
NetworkInterfaceId=ni_id2)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.cancelResourceCleanUp(clean_ni2)
self.get_network_interface_waiter().wait_delete(ni_id2)
def test_network_interfaces_are_deleted_on_termination(self):
instance_type = CONF.aws.instance_type
image_id = CONF.aws.image_id
if not image_id:
raise self.skipException('aws image_id does not provided')
resp, data = self.client.RunInstances(
ImageId=image_id, InstanceType=instance_type, MinCount=1,
MaxCount=1, SubnetId=self.subnet_id)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
instance_id = data['Instances'][0]['InstanceId']
res_clean = self.addResourceCleanUp(self.client.TerminateInstances,
InstanceIds=[instance_id])
self.get_instance_waiter().wait_available(instance_id,
final_set=('running'))
instance = self.get_instance(instance_id)
nis = instance.get('NetworkInterfaces', [])
self.assertEqual(1, len(nis))
self.assertTrue(nis[0]['Attachment']['DeleteOnTermination'])
ni_id = nis[0]['NetworkInterfaceId']
kwargs = {
'SubnetId': self.subnet_id,
}
resp, data = self.client.CreateNetworkInterface(*[], **kwargs)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
ni_id2 = data['NetworkInterface']['NetworkInterfaceId']
self.addResourceCleanUp(self.client.DeleteNetworkInterface,
NetworkInterfaceId=ni_id2)
self.get_network_interface_waiter().wait_available(ni_id2)
kwargs = {
'DeviceIndex': 2,
'InstanceId': instance_id,
'NetworkInterfaceId': ni_id2
}
resp, data = self.client.AttachNetworkInterface(*[], **kwargs)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
attachment_id = data['AttachmentId']
kwargs = {
'NetworkInterfaceId': ni_id2,
'Attachment': {
'AttachmentId': attachment_id,
'DeleteOnTermination': True,
}
}
resp, data = self.client.ModifyNetworkInterfaceAttribute(*[], **kwargs)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
resp, data = self.client.TerminateInstances(InstanceIds=[instance_id])
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
self.cancelResourceCleanUp(res_clean)
self.get_instance_waiter().wait_delete(instance_id)
self.get_network_interface_waiter().wait_delete(ni_id)
self.get_network_interface_waiter().wait_delete(ni_id2)

View File

@ -25,7 +25,8 @@ LOG = log.getLogger(__name__)
class SubnetTest(base.EC2TestCase):
VPC_CIDR = '10.2.0.0/20'
BASE_CIDR = '10.2.0.0'
VPC_CIDR = BASE_CIDR + '/20'
vpc_id = None
@classmethod
@ -42,7 +43,7 @@ class SubnetTest(base.EC2TestCase):
cls.get_vpc_waiter().wait_available(cls.vpc_id)
def test_create_delete_subnet(self):
cidr = '10.2.0.0/24'
cidr = self.BASE_CIDR + '/24'
resp, data = self.client.CreateSubnet(VpcId=self.vpc_id,
CidrBlock=cidr)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
@ -75,7 +76,7 @@ class SubnetTest(base.EC2TestCase):
VpcId=vpc_id)
self.get_vpc_waiter().wait_available(vpc_id)
cidr = '10.2.0.0/24'
cidr = self.BASE_CIDR + '/24'
resp, data = self.client.CreateSubnet(VpcId=vpc_id,
CidrBlock=cidr)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
@ -99,7 +100,7 @@ class SubnetTest(base.EC2TestCase):
@testtools.skipUnless(CONF.aws.run_incompatible_tests,
"bug with overlapped subnets")
def test_create_overlapped_subnet(self):
cidr = '10.2.0.0/24'
cidr = self.BASE_CIDR + '/24'
resp, data = self.client.CreateSubnet(VpcId=self.vpc_id,
CidrBlock=cidr)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
@ -134,7 +135,7 @@ class SubnetTest(base.EC2TestCase):
self.assertEqual('InvalidSubnet.Range', data['Error']['Code'])
# NOTE(andrey-mp): bigger cidr than VPC has
cidr = '10.2.0.0/19'
cidr = self.BASE_CIDR + '/19'
resp, data = self.client.CreateSubnet(VpcId=self.vpc_id,
CidrBlock=cidr)
if resp.status_code == 200:
@ -144,7 +145,7 @@ class SubnetTest(base.EC2TestCase):
self.assertEqual('InvalidSubnet.Range', data['Error']['Code'])
# NOTE(andrey-mp): too small cidr
cidr = '10.2.0.0/29'
cidr = self.BASE_CIDR + '/29'
resp, data = self.client.CreateSubnet(VpcId=self.vpc_id,
CidrBlock=cidr)
if resp.status_code == 200:
@ -154,7 +155,7 @@ class SubnetTest(base.EC2TestCase):
self.assertEqual('InvalidSubnet.Range', data['Error']['Code'])
def test_describe_subnets_base(self):
cidr = '10.2.0.0/24'
cidr = self.BASE_CIDR + '/24'
resp, data = self.client.CreateSubnet(VpcId=self.vpc_id,
CidrBlock=cidr)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))
@ -179,7 +180,7 @@ class SubnetTest(base.EC2TestCase):
self.get_subnet_waiter().wait_delete(subnet_id)
def test_describe_subnets_filters(self):
cidr = '10.2.0.0/24'
cidr = self.BASE_CIDR + '/24'
resp, data = self.client.CreateSubnet(VpcId=self.vpc_id,
CidrBlock=cidr)
self.assertEqual(200, resp.status_code, base.EC2ErrorConverter(data))

View File

@ -546,18 +546,21 @@ class EC2TestCase(base.BaseTestCase):
# NOTE(andrey-mp): Helpers zone
def get_instance_bdm(self, instance_id, device_name):
"""
device_name=None means getting bdm of root instance device
"""
def get_instance(self, instance_id):
resp, data = self.client.DescribeInstances(
InstanceIds=[instance_id])
self.assertEqual(200, resp.status_code, EC2ErrorConverter(data))
self.assertEqual(1, len(data.get('Reservations', [])))
instances = data['Reservations'][0].get('Instances', [])
self.assertEqual(1, len(instances))
instance = instances[0]
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['RootDeviceName']
bdms = instance['BlockDeviceMappings']

View File

@ -641,9 +641,6 @@ class InstanceTestCase(base.ApiTestCase):
fake_state_change)]}))
self.db_api.get_items_by_ids.assert_called_once_with(
mock.ANY, set([fakes.ID_EC2_INSTANCE_1, fakes.ID_EC2_INSTANCE_2]))
(self.network_interface_api.
detach_network_interface.assert_called_once_with(
mock.ANY, fakes.ID_EC2_NETWORK_INTERFACE_2_ATTACH))
self.assertEqual(2, self.nova.servers.get.call_count)
self.nova.servers.get.assert_any_call(fakes.ID_OS_INSTANCE_1)
self.nova.servers.get.assert_any_call(fakes.ID_OS_INSTANCE_2)
@ -676,7 +673,7 @@ class InstanceTestCase(base.ApiTestCase):
self.nova.servers.get.side_effect = (
lambda ec2_id: fakes.OSInstance(ec2_id, vm_state='active'))
def do_check(mock_eni_list=[], detached_enis=[], deleted_enis=[]):
def do_check(mock_eni_list=[]):
self.set_mock_db_items(self.DB_FAKE_ENI,
*(self.DB_INSTANCES + mock_eni_list))
@ -686,39 +683,17 @@ class InstanceTestCase(base.ApiTestCase):
self.assertThat(
resp, matchers.DictMatches(ec2_terminate_instances_result))
detach_network_interface = (
self.network_interface_api.detach_network_interface)
self.assertEqual(len(detached_enis),
detach_network_interface.call_count)
for ec2_eni in detached_enis:
detach_network_interface.assert_any_call(
mock.ANY,
('eni-attach-%s' % ec2_eni['id'].split('-')[-1]))
self.assertFalse(self.db_api.delete_item.called)
detach_network_interface.reset_mock()
self.db_api.delete_item.reset_mock()
# NOTE(ft): 2 instances; the first has 2 correct ports;
# the second has the first port attached by EC2 API but later detached
# by OpenStack and the second port created through EC2 API but
# attached by OpenStack only
do_check(
mock_eni_list=[
self.DB_ATTACHED_ENIS[0], self.DB_ATTACHED_ENIS[1],
self.DB_ATTACHED_ENIS[2], self.DB_DETACHED_ENIS[3]],
detached_enis=[self.DB_ATTACHED_ENIS[1]],
deleted_enis=[self.DB_ATTACHED_ENIS[0],
self.DB_ATTACHED_ENIS[2]])
self.DB_ATTACHED_ENIS[2], self.DB_DETACHED_ENIS[3]])
# NOTE(ft): 2 instances: the first has the first port attached by
# OpenStack only, the second port is attached correctly;
# the second instance has one port created and attached by OpenStack
# only
do_check(
mock_eni_list=[self.DB_ATTACHED_ENIS[1]],
detached_enis=[self.DB_ATTACHED_ENIS[1]],
deleted_enis=[])
mock_eni_list=[self.DB_ATTACHED_ENIS[1]])
def test_terminate_instances_invalid_parameters(self):
self.assert_execution_error(
@ -997,7 +972,7 @@ class InstanceTestCase(base.ApiTestCase):
{'reservationSet': [fakes.EC2_RESERVATION_2]},
orderless_lists=True))
remove_instances.assert_called_once_with(
mock.ANY, [fakes.DB_INSTANCE_1], purge_linked_items=False)
mock.ANY, [fakes.DB_INSTANCE_1])
@mock.patch('ec2api.api.instance._format_instance')
def test_describe_instances_sorting(self, format_instance):
@ -1773,11 +1748,13 @@ class InstancePrivateTestCase(test_base.BaseTestCase):
do_check(True)
do_check(False)
@mock.patch('ec2api.api.network_interface.delete_network_interface')
@mock.patch('ec2api.api.network_interface._detach_network_interface_item')
@mock.patch('ec2api.api.address._disassociate_address_item')
@mock.patch('ec2api.db.api.IMPL')
def test_remove_instances(self, db_api, disassociate_address_item,
detach_network_interface_item):
detach_network_interface_item,
delete_network_interface):
fake_context = mock.Mock(service_catalog=[{'type': 'fake'}])
instances = [{'id': fakes.random_ec2_id('i')}
@ -1798,15 +1775,8 @@ class InstancePrivateTestCase(test_base.BaseTestCase):
for dummy in range(2))
instances_to_remove = instances[:2] + [instances[3]]
network_interfaces_of_removed_instances = {
instances[0]['id']: network_interfaces[0:2],
instances[1]['id']: network_interfaces[2:4],
instances[3]['id']: []}
network_interfaces_to_delete = [network_interfaces[0],
network_interfaces[1]]
network_interfaces_to_detach = [network_interfaces[2],
network_interfaces[3]]
addresses_to_dissassociate = [addresses[0]]
network_interfaces_to_delete = network_interfaces[0:2]
network_interfaces_to_detach = network_interfaces[0:4]
db_api.get_items.side_effect = tools.get_db_api_get_items(
*(network_interfaces + addresses))
@ -1816,9 +1786,8 @@ class InstancePrivateTestCase(test_base.BaseTestCase):
detach_network_interface_item.assert_any_call(fake_context,
eni)
for eni in network_interfaces_to_delete:
db_api.delete_item.assert_any_call(fake_context, eni['id'])
for addr in addresses_to_dissassociate:
disassociate_address_item.assert_any_call(fake_context, addr)
delete_network_interface.assert_any_call(fake_context,
eni['id'])
detach_network_interface_item.reset_mock()
db_api.reset_mock()
disassociate_address_item.reset_mock()
@ -1826,8 +1795,7 @@ class InstancePrivateTestCase(test_base.BaseTestCase):
instance_api._remove_instances(fake_context, instances_to_remove)
check_calls()
instance_api._remove_instances(fake_context, instances_to_remove,
network_interfaces_of_removed_instances)
instance_api._remove_instances(fake_context, instances_to_remove)
check_calls()
@mock.patch('ec2api.api.instance.novadb')