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:
parent
5480e9b850
commit
2801f9e687
@ -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.
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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'),
|
||||
|
@ -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'))
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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']
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user