Refactor running of instances
- get a single copy of key algorithms - remove remains of detaching of network interfaces during cleaning on crash - use describe_instances to format run_instances result - use describe_instances for idempotent run Change-Id: Ica01a15f6146f39c0100902604d438968efc41a5
This commit is contained in:
parent
4b2924c02e
commit
092e1947c1
|
@ -21,6 +21,7 @@ import re
|
|||
|
||||
from novaclient import exceptions as nova_exception
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from ec2api.api import clients
|
||||
|
@ -31,8 +32,9 @@ from ec2api.api import security_group as security_group_api
|
|||
from ec2api import context as ec2_context
|
||||
from ec2api.db import api as db_api
|
||||
from ec2api import exception
|
||||
from ec2api.i18n import _
|
||||
from ec2api.i18n import _, _LE
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
ec2_opts = [
|
||||
cfg.BoolOpt('ec2_private_dns_show_ip',
|
||||
|
@ -72,24 +74,99 @@ def run_instances(context, image_id, min_count, max_count,
|
|||
_check_min_max_count(min_count, max_count)
|
||||
|
||||
if client_token:
|
||||
idempotent_run = _get_idempotent_run(context, client_token)
|
||||
if idempotent_run:
|
||||
return idempotent_run
|
||||
reservations = describe_instances(context,
|
||||
filter=[{'name': 'client-token',
|
||||
'value': [client_token]}])
|
||||
if reservations['reservationSet']:
|
||||
if len(reservations['reservationSet']) > 1:
|
||||
LOG.error(_LE('describe_instances has returned %s '
|
||||
'reservations, but 1 is expected.') %
|
||||
len(reservations['reservationSet']))
|
||||
LOG.error(_LE('Requested instances client token: %s') %
|
||||
client_token)
|
||||
LOG.error(_LE('Result: %s') % reservations)
|
||||
return reservations['reservationSet'][0]
|
||||
|
||||
os_image, os_kernel_id, os_ramdisk_id = _parse_image_parameters(
|
||||
context, image_id, kernel_id, ramdisk_id)
|
||||
|
||||
nova = clients.nova(context)
|
||||
try:
|
||||
os_flavor = next(f for f in nova.flavors.list()
|
||||
if f.name == instance_type)
|
||||
except StopIteration:
|
||||
raise exception.InvalidParameterValue(value=instance_type,
|
||||
parameter='InstanceType')
|
||||
|
||||
bdm = _parse_block_device_mapping(context, block_device_mapping)
|
||||
availability_zone = (placement or {}).get('availability_zone')
|
||||
if user_data:
|
||||
user_data = base64.b64decode(user_data)
|
||||
|
||||
return instance_engine.run_instances(
|
||||
context, image_id, min_count, max_count,
|
||||
key_name, security_group_id,
|
||||
security_group, user_data, instance_type,
|
||||
placement, kernel_id, ramdisk_id,
|
||||
block_device_mapping, monitoring,
|
||||
subnet_id, disable_api_termination,
|
||||
instance_initiated_shutdown_behavior,
|
||||
private_ip_address, client_token,
|
||||
network_interface, iam_instance_profile,
|
||||
ebs_optimized)
|
||||
vpc_id, launch_context = instance_engine.get_vpc_and_build_launch_context(
|
||||
context, security_group,
|
||||
subnet_id, private_ip_address, security_group_id, network_interface,
|
||||
multiple_instances=max_count > 1)
|
||||
|
||||
ec2_reservation_id = _generate_reservation_id()
|
||||
instance_ids = []
|
||||
with common.OnCrashCleaner() as cleaner:
|
||||
# NOTE(ft): create Neutron's ports manually and run instances one
|
||||
# by one to have a chance to:
|
||||
# process individual network interface options like security_group
|
||||
# or private_ip_addresses (Nova's create_instances receives only
|
||||
# one fixed_ip for subnet)
|
||||
# set dhcp options to port
|
||||
# add corresponding OS ids of network interfaces to our DB
|
||||
# TODO(ft): we should lock created network interfaces to prevent
|
||||
# their usage or deleting
|
||||
|
||||
# TODO(ft): do correct error messages on create failures. For
|
||||
# example, overlimit, ip lack, ip overlapping, etc
|
||||
for launch_index in range(max_count):
|
||||
if launch_index >= min_count:
|
||||
cleaner.approveChanges()
|
||||
|
||||
extra_params = (
|
||||
instance_engine.get_launch_extra_parameters(
|
||||
context, cleaner, launch_context))
|
||||
|
||||
os_instance = nova.servers.create(
|
||||
'%s-%s' % (ec2_reservation_id, launch_index),
|
||||
os_image.id, os_flavor,
|
||||
min_count=1, max_count=1,
|
||||
kernel_id=os_kernel_id, ramdisk_id=os_ramdisk_id,
|
||||
availability_zone=availability_zone,
|
||||
block_device_mapping=bdm,
|
||||
key_name=key_name, userdata=user_data,
|
||||
**extra_params)
|
||||
cleaner.addCleanup(nova.servers.delete, os_instance.id)
|
||||
|
||||
instance = {'os_id': os_instance.id,
|
||||
'vpc_id': vpc_id,
|
||||
'reservation_id': ec2_reservation_id,
|
||||
'launch_index': launch_index}
|
||||
if client_token:
|
||||
instance['client_token'] = client_token
|
||||
|
||||
instance = db_api.add_item(context, 'i', instance)
|
||||
cleaner.addCleanup(db_api.delete_item, context, instance['id'])
|
||||
instance_ids.append(instance['id'])
|
||||
|
||||
nova.servers.update(os_instance, name=instance['id'])
|
||||
|
||||
instance_engine.post_launch_action(
|
||||
context, cleaner, launch_context, instance['id'])
|
||||
|
||||
ec2_reservations = describe_instances(context, instance_ids)
|
||||
reservation_count = len(ec2_reservations['reservationSet'])
|
||||
if reservation_count != 1:
|
||||
LOG.error(_LE('describe_instances has returned %s reservations, '
|
||||
'but 1 is expected.') % reservation_count)
|
||||
LOG.error(_LE('Requested instances IDs: %s') % instance_ids)
|
||||
LOG.error(_LE('Result: %s') % ec2_reservations)
|
||||
return (ec2_reservations['reservationSet'][0]
|
||||
if reservation_count else None)
|
||||
|
||||
|
||||
def terminate_instances(context, instance_id):
|
||||
|
@ -229,9 +306,9 @@ class ReservationDescriber(common.NonOpenstackItemsDescriber):
|
|||
if i['instanceId'] in self.suitable_instances]
|
||||
if not formatted_instances:
|
||||
return None
|
||||
return _format_reservation_body(self.context, reservation,
|
||||
formatted_instances,
|
||||
self.os_groups.get(reservation['id']))
|
||||
return _format_reservation(self.context, reservation,
|
||||
formatted_instances,
|
||||
self.os_groups.get(reservation['id']))
|
||||
|
||||
def get_db_items(self):
|
||||
return self.reservations
|
||||
|
@ -380,31 +457,7 @@ def describe_instance_attribute(context, instance_id, attribute):
|
|||
return result
|
||||
|
||||
|
||||
def _get_idempotent_run(context, 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
|
||||
nova = clients.nova(ec2_context.get_os_admin_context())
|
||||
os_instances = _get_os_instances_by_instances(context, instances.values(),
|
||||
nova=nova)
|
||||
instances_info = []
|
||||
instance_ids = []
|
||||
for os_instance in os_instances:
|
||||
instance = instances[os_instance.id]
|
||||
instances_info.append((instance, os_instance,))
|
||||
instance_ids.append(instance['id'])
|
||||
if not instances_info:
|
||||
return
|
||||
ec2_network_interfaces = (
|
||||
instance_engine.get_ec2_network_interfaces(context, instance_ids))
|
||||
return _format_reservation(context, instance['reservation_id'],
|
||||
instances_info, ec2_network_interfaces)
|
||||
|
||||
|
||||
def _format_reservation_body(context, reservation, formatted_instances,
|
||||
os_groups):
|
||||
def _format_reservation(context, reservation, formatted_instances, os_groups):
|
||||
return {
|
||||
'reservationId': reservation['id'],
|
||||
'ownerId': reservation['owner_id'],
|
||||
|
@ -415,23 +468,6 @@ def _format_reservation_body(context, reservation, formatted_instances,
|
|||
}
|
||||
|
||||
|
||||
def _format_reservation(context, reservation_id, instances_info,
|
||||
ec2_network_interfaces, image_ids={}):
|
||||
formatted_instances = []
|
||||
for (instance, os_instance) in instances_info:
|
||||
ec2_instance = _format_instance(
|
||||
context, instance, os_instance,
|
||||
ec2_network_interfaces.get(instance['id']), image_ids)
|
||||
formatted_instances.append(ec2_instance)
|
||||
|
||||
reservation = {'id': reservation_id,
|
||||
'owner_id': os_instance.tenant_id}
|
||||
return _format_reservation_body(
|
||||
context, reservation, formatted_instances,
|
||||
(None if instance['vpc_id'] else
|
||||
getattr(os_instance, 'security_groups', [])))
|
||||
|
||||
|
||||
def _format_instance(context, instance, os_instance, ec2_network_interfaces,
|
||||
image_ids, volumes=None, os_volumes=None):
|
||||
ec2_instance = {
|
||||
|
@ -588,7 +624,7 @@ def _parse_image_parameters(context, image_id, kernel_id, ramdisk_id):
|
|||
return os_image, os_kernel_id, os_ramdisk_id
|
||||
|
||||
|
||||
def _parse_block_device_mapping(context, block_device_mapping, os_image):
|
||||
def _parse_block_device_mapping(context, block_device_mapping):
|
||||
# TODO(ft): check block_device_mapping structure
|
||||
bdm = {}
|
||||
for args_bd in (block_device_mapping or []):
|
||||
|
@ -696,139 +732,64 @@ def _generate_reservation_id():
|
|||
|
||||
class InstanceEngineNeutron(object):
|
||||
|
||||
def run_instances(self, context, image_id, min_count, max_count,
|
||||
key_name=None, security_group_id=None,
|
||||
security_group=None, user_data=None, instance_type=None,
|
||||
placement=None, kernel_id=None, ramdisk_id=None,
|
||||
block_device_mapping=None, monitoring=None,
|
||||
subnet_id=None, disable_api_termination=None,
|
||||
instance_initiated_shutdown_behavior=None,
|
||||
private_ip_address=None, client_token=None,
|
||||
network_interface=None, iam_instance_profile=None,
|
||||
ebs_optimized=None):
|
||||
os_image, os_kernel_id, os_ramdisk_id = _parse_image_parameters(
|
||||
context, image_id, kernel_id, ramdisk_id)
|
||||
|
||||
nova = clients.nova(context)
|
||||
os_flavor = next((f for f in nova.flavors.list()
|
||||
if f.name == instance_type), None)
|
||||
if not os_flavor:
|
||||
raise exception.InvalidParameterValue(value=instance_type,
|
||||
parameter='InstanceType')
|
||||
|
||||
bdm = _parse_block_device_mapping(context, block_device_mapping,
|
||||
os_image)
|
||||
|
||||
def get_vpc_and_build_launch_context(
|
||||
self, context, security_group,
|
||||
subnet_id, private_ip_address, security_group_id,
|
||||
network_interface, multiple_instances):
|
||||
# TODO(ft): support auto_assign_floating_ip
|
||||
|
||||
(security_groups_names,
|
||||
vpc_network_parameters) = self.merge_network_interface_parameters(
|
||||
vpc_network_parameters = self.merge_network_interface_parameters(
|
||||
security_group,
|
||||
subnet_id, private_ip_address, security_group_id,
|
||||
network_interface)
|
||||
|
||||
self.check_network_interface_parameters(vpc_network_parameters,
|
||||
max_count > 1)
|
||||
multiple_instances)
|
||||
|
||||
(vpc_id, network_data) = self.parse_network_interface_parameters(
|
||||
context, vpc_network_parameters)
|
||||
launch_context = {'vpc_id': vpc_id,
|
||||
'network_data': network_data,
|
||||
'security_groups': security_group}
|
||||
|
||||
# NOTE(ft): workaround for Launchpad Bug #1384347 in Icehouse
|
||||
if not security_groups_names and vpc_network_parameters:
|
||||
security_groups_names = self.get_vpc_default_security_group_id(
|
||||
context, vpc_id)
|
||||
if not security_group and vpc_network_parameters:
|
||||
launch_context['security_groups'] = (
|
||||
self.get_vpc_default_security_group_id(context, vpc_id))
|
||||
|
||||
neutron = clients.neutron(context)
|
||||
if not vpc_id:
|
||||
ec2_classic_nics = [
|
||||
neutron = clients.neutron(context)
|
||||
launch_context['ec2_classic_nics'] = [
|
||||
{'net-id': self.get_ec2_classic_os_network(context,
|
||||
neutron)['id']}]
|
||||
|
||||
instance_ids = []
|
||||
instances_info = []
|
||||
ec2_reservation_id = _generate_reservation_id()
|
||||
return vpc_id, launch_context
|
||||
|
||||
with common.OnCrashCleaner() as cleaner:
|
||||
# NOTE(ft): create Neutron's ports manually and run instances one
|
||||
# by one to have a chance to:
|
||||
# process individual network interface options like security_group
|
||||
# or private_ip_addresses (Nova's create_instances receives only
|
||||
# one fixed_ip for subnet)
|
||||
# set dhcp options to port
|
||||
# add corresponding OS ids of network interfaces to our DB
|
||||
# TODO(ft): we should lock created network interfaces to prevent
|
||||
# their usage or deleting
|
||||
def get_launch_extra_parameters(self, context, cleaner, launch_context):
|
||||
if 'ec2_classic_nics' in launch_context:
|
||||
nics = launch_context['ec2_classic_nics']
|
||||
else:
|
||||
network_data = launch_context['network_data']
|
||||
self.create_network_interfaces(context, cleaner, network_data)
|
||||
nics = [{'port-id': data['network_interface']['os_id']}
|
||||
for data in network_data]
|
||||
return {'security_groups': launch_context['security_groups'],
|
||||
'nics': nics}
|
||||
|
||||
# TODO(ft): do correct error messages on create failures. For
|
||||
# example, overlimit, ip lack, ip overlapping, etc
|
||||
for launch_index in range(max_count):
|
||||
if launch_index >= min_count:
|
||||
cleaner.approveChanges()
|
||||
|
||||
self.create_network_interfaces(context, cleaner, network_data)
|
||||
nics = ([{'port-id': data['network_interface']['os_id']}
|
||||
for data in network_data]
|
||||
if vpc_id else
|
||||
ec2_classic_nics)
|
||||
|
||||
os_instance = nova.servers.create(
|
||||
'%s-%s' % (ec2_reservation_id, launch_index),
|
||||
os_image.id, os_flavor,
|
||||
min_count=1, max_count=1,
|
||||
kernel_id=os_kernel_id, ramdisk_id=os_ramdisk_id,
|
||||
availability_zone=(
|
||||
(placement or {}).get('availability_zone')),
|
||||
block_device_mapping=bdm,
|
||||
security_groups=security_groups_names,
|
||||
nics=nics,
|
||||
key_name=key_name, userdata=user_data)
|
||||
cleaner.addCleanup(nova.servers.delete, os_instance.id)
|
||||
|
||||
for data in network_data:
|
||||
if data.get('detach_on_crash'):
|
||||
cleaner.addCleanup(neutron.update_port,
|
||||
data['network_interface']['os_id'],
|
||||
{'port': {'device_id': '',
|
||||
'device_owner': ''}})
|
||||
|
||||
instance = {'os_id': os_instance.id,
|
||||
'vpc_id': vpc_id,
|
||||
'reservation_id': ec2_reservation_id,
|
||||
'launch_index': launch_index}
|
||||
if client_token:
|
||||
instance['client_token'] = client_token
|
||||
instance = db_api.add_item(context, 'i', instance)
|
||||
cleaner.addCleanup(db_api.delete_item, context, instance['id'])
|
||||
instance_ids.append(instance['id'])
|
||||
|
||||
nova.servers.update(os_instance, name=instance['id'])
|
||||
|
||||
for data in network_data:
|
||||
# TODO(ft): implement update items in DB layer to prevent
|
||||
# record by record modification
|
||||
# Alternatively a create_network_interface sub-function can
|
||||
# set attach_time at once
|
||||
network_interface_api._attach_network_interface_item(
|
||||
context, data['network_interface'], instance['id'],
|
||||
data['device_index'],
|
||||
delete_on_termination=data['delete_on_termination'])
|
||||
cleaner.addCleanup(
|
||||
network_interface_api._detach_network_interface_item,
|
||||
context, data['network_interface'])
|
||||
|
||||
instances_info.append((instance, os_instance))
|
||||
|
||||
# NOTE(ft): we don't reuse network interface objects received from
|
||||
# create_network_interfaces because they don't contain attachment info
|
||||
ec2_network_interfaces = (self.get_ec2_network_interfaces(
|
||||
context, instance_ids=instance_ids))
|
||||
# NOTE(ft): since os_instance is created with regular Nova client,
|
||||
# it doesn't contain enough info to get an instance in EC2 format
|
||||
# completely, nevertheless we use it to get rid of additional requests
|
||||
# and reduce code complexity
|
||||
return _format_reservation(context, ec2_reservation_id, instances_info,
|
||||
ec2_network_interfaces,
|
||||
image_ids={os_image.id: image_id})
|
||||
def post_launch_action(self, context, cleaner, launch_context,
|
||||
instance_id):
|
||||
for data in launch_context['network_data']:
|
||||
# TODO(ft): implement update items in DB layer to prevent
|
||||
# record by record modification
|
||||
# Alternatively a create_network_interface sub-function can
|
||||
# set attach_time at once
|
||||
network_interface_api._attach_network_interface_item(
|
||||
context, data['network_interface'], instance_id,
|
||||
data['device_index'],
|
||||
delete_on_termination=data['delete_on_termination'])
|
||||
cleaner.addCleanup(
|
||||
network_interface_api._detach_network_interface_item,
|
||||
context, data['network_interface'])
|
||||
|
||||
def get_ec2_network_interfaces(self, context, instance_ids=None):
|
||||
# NOTE(ft): we would be glad to use filters with this describe
|
||||
|
@ -895,7 +856,7 @@ class InstanceEngineNeutron(object):
|
|||
param['private_ip_address'] = private_ip_address
|
||||
if security_group_ids:
|
||||
param['security_group_id'] = security_group_ids
|
||||
return None, [param]
|
||||
return [param]
|
||||
elif private_ip_address:
|
||||
msg = _('Specifying an IP address is only valid for VPC instances '
|
||||
'and thus requires a subnet in which to launch')
|
||||
|
@ -905,7 +866,7 @@ class InstanceEngineNeutron(object):
|
|||
raise exception.InvalidParameterCombination(msg)
|
||||
else:
|
||||
# NOTE(ft): only one of this variables is not empty
|
||||
return security_group_names, network_interfaces
|
||||
return network_interfaces
|
||||
|
||||
def check_network_interface_parameters(self, params, multiple_instances):
|
||||
# NOTE(ft): we ignore associate_public_ip_address
|
||||
|
@ -970,7 +931,6 @@ class InstanceEngineNeutron(object):
|
|||
network_interface_ids.add(ec2_eni_id)
|
||||
network_data.append({'device_index': param['device_index'],
|
||||
'network_interface': network_interface,
|
||||
'detach_on_crash': True,
|
||||
'delete_on_termination': False})
|
||||
else:
|
||||
subnet = ec2utils.get_db_item(context, param['subnet_id'],
|
||||
|
@ -1055,70 +1015,20 @@ class InstanceEngineNeutron(object):
|
|||
|
||||
class InstanceEngineNova(object):
|
||||
|
||||
def run_instances(self, context, image_id, min_count, max_count,
|
||||
key_name=None, security_group_id=None,
|
||||
security_group=None, user_data=None, instance_type=None,
|
||||
placement=None, kernel_id=None, ramdisk_id=None,
|
||||
block_device_mapping=None, monitoring=None,
|
||||
subnet_id=None, disable_api_termination=None,
|
||||
instance_initiated_shutdown_behavior=None,
|
||||
private_ip_address=None, client_token=None,
|
||||
network_interface=None, iam_instance_profile=None,
|
||||
ebs_optimized=None):
|
||||
os_image, os_kernel_id, os_ramdisk_id = _parse_image_parameters(
|
||||
context, image_id, kernel_id, ramdisk_id)
|
||||
def get_vpc_and_build_launch_context(
|
||||
self, context, security_group,
|
||||
subnet_id, private_ip_address, security_group_id,
|
||||
network_interface, multiple_instances):
|
||||
# TODO(ft): check emptiness of vpc related parameters
|
||||
|
||||
nova = clients.nova(context)
|
||||
os_flavor = next((f for f in nova.flavors.list()
|
||||
if f.name == instance_type), None)
|
||||
if not os_flavor:
|
||||
raise exception.InvalidParameterValue(value=instance_type,
|
||||
parameter='InstanceType')
|
||||
return None, {'security_groups': security_group}
|
||||
|
||||
bdm = _parse_block_device_mapping(context, block_device_mapping,
|
||||
os_image)
|
||||
def get_launch_extra_parameters(self, context, cleaner, launch_context):
|
||||
return {'security_groups': launch_context['security_groups']}
|
||||
|
||||
# TODO(ft): support auto_assign_floating_ip
|
||||
|
||||
instances_info = []
|
||||
ec2_reservation_id = _generate_reservation_id()
|
||||
|
||||
# TODO(ft): do correct error messages on create failures. For
|
||||
# example, overlimit, ip lack, ip overlapping, etc
|
||||
with common.OnCrashCleaner() as cleaner:
|
||||
for index in range(max_count):
|
||||
if index >= min_count:
|
||||
cleaner.approveChanges()
|
||||
|
||||
os_instance = nova.servers.create(
|
||||
'%s-%s' % (ec2_reservation_id, index),
|
||||
os_image.id, os_flavor,
|
||||
min_count=1, max_count=1,
|
||||
kernel_id=os_kernel_id, ramdisk_id=os_ramdisk_id,
|
||||
availability_zone=(
|
||||
placement or {}).get('availability_zone'),
|
||||
block_device_mapping=bdm,
|
||||
security_groups=security_group,
|
||||
key_name=key_name, userdata=user_data)
|
||||
cleaner.addCleanup(nova.servers.delete, os_instance.id)
|
||||
|
||||
instance = {'os_id': os_instance.id,
|
||||
'reservation_id': ec2_reservation_id,
|
||||
'launch_index': index}
|
||||
if client_token:
|
||||
instance['client_token'] = client_token
|
||||
instance = db_api.add_item(context, 'i', instance)
|
||||
cleaner.addCleanup(db_api.delete_item, context, instance['id'])
|
||||
|
||||
nova.servers.update(os_instance, name=instance['id'])
|
||||
instances_info.append((instance, os_instance))
|
||||
|
||||
# NOTE(ft): since os_instance is created with regular Nova client,
|
||||
# it doesn't contain enough info to get an instance in EC2 format
|
||||
# completely, nevertheless we use it to get rid of additional requests
|
||||
# and reduce code complexity
|
||||
return _format_reservation(context, ec2_reservation_id, instances_info,
|
||||
{}, image_ids={os_image.id: image_id})
|
||||
def post_launch_action(self, context, cleaner, launch_context,
|
||||
instance_id):
|
||||
pass
|
||||
|
||||
def get_ec2_network_interfaces(self, context, instance_ids=None):
|
||||
return {}
|
||||
|
|
|
@ -79,12 +79,11 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
self.nova.flavors.get.return_value = self.fake_flavor
|
||||
self.nova.flavors.list.return_value = [self.fake_flavor]
|
||||
|
||||
@mock.patch('ec2api.api.instance.InstanceEngineNeutron.'
|
||||
'get_ec2_network_interfaces')
|
||||
@mock.patch('ec2api.api.instance.describe_instances')
|
||||
@mock.patch('ec2api.api.instance.InstanceEngineNeutron.'
|
||||
'get_vpc_default_security_group_id')
|
||||
def test_run_instances(self, get_vpc_default_security_group_id,
|
||||
get_ec2_network_interfaces):
|
||||
describe_instances):
|
||||
"""Run instance with various network interface settings."""
|
||||
instance_api.instance_engine = (
|
||||
instance_api.InstanceEngineNeutron())
|
||||
|
@ -130,8 +129,8 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
reservation_id=fakes.ID_EC2_RESERVATION_1),
|
||||
{'privateDnsName': None},
|
||||
['rootDeviceType', 'rootDeviceName'])])
|
||||
get_ec2_network_interfaces.return_value = {
|
||||
fakes.ID_EC2_INSTANCE_1: [eni]}
|
||||
describe_instances.return_value = {
|
||||
'reservationSet': [expected_reservation]}
|
||||
|
||||
params.update({'ImageId': fakes.ID_EC2_IMAGE_1,
|
||||
'InstanceType': 'fake_flavor',
|
||||
|
@ -161,13 +160,13 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
mock.ANY, fakes.DB_NETWORK_INTERFACE_1,
|
||||
fakes.ID_EC2_INSTANCE_1, 0,
|
||||
delete_on_termination=delete_port_on_termination))
|
||||
get_ec2_network_interfaces.assert_called_once_with(
|
||||
mock.ANY, instance_ids=[fakes.ID_EC2_INSTANCE_1])
|
||||
describe_instances.assert_called_once_with(
|
||||
mock.ANY, [fakes.ID_EC2_INSTANCE_1])
|
||||
|
||||
self.network_interface_api.reset_mock()
|
||||
self.nova.servers.reset_mock()
|
||||
self.db_api.reset_mock()
|
||||
get_ec2_network_interfaces.reset_mock()
|
||||
describe_instances.reset_mock()
|
||||
|
||||
do_check({'SubnetId': fakes.ID_EC2_SUBNET_1},
|
||||
create_network_interface_kwargs={})
|
||||
|
@ -210,13 +209,12 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
'NetworkInterface.1.NetworkInterfaceId': (
|
||||
fakes.ID_EC2_NETWORK_INTERFACE_1)})
|
||||
|
||||
@mock.patch('ec2api.api.instance.InstanceEngineNeutron.'
|
||||
'get_ec2_network_interfaces')
|
||||
@mock.patch('ec2api.api.instance.describe_instances')
|
||||
@mock.patch('ec2api.api.instance.InstanceEngineNeutron.'
|
||||
'get_vpc_default_security_group_id')
|
||||
def test_run_instances_multiple_networks(self,
|
||||
get_vpc_default_security_group_id,
|
||||
get_ec2_network_interfaces):
|
||||
describe_instances):
|
||||
"""Run 2 instances at once on 2 subnets in all combinations."""
|
||||
instance_api.instance_engine = (
|
||||
instance_api.InstanceEngineNeutron())
|
||||
|
@ -225,11 +223,6 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
self.glance.images.get.return_value = fakes.OSImage(fakes.OS_IMAGE_1)
|
||||
get_vpc_default_security_group_id.return_value = None
|
||||
|
||||
get_ec2_network_interfaces.return_value = dict(
|
||||
(ec2_instance_id, list(eni_pair))
|
||||
for ec2_instance_id, eni_pair in zip(
|
||||
self.IDS_EC2_INSTANCE,
|
||||
zip(*[iter(self.EC2_ATTACHED_ENIS)] * 2)))
|
||||
ec2_instances = [
|
||||
tools.patch_dict(
|
||||
fakes.gen_ec2_instance(
|
||||
|
@ -243,6 +236,7 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
zip(*[iter(self.EC2_ATTACHED_ENIS)] * 2)))]
|
||||
ec2_reservation = fakes.gen_ec2_reservation(fakes.ID_EC2_RESERVATION_1,
|
||||
ec2_instances)
|
||||
describe_instances.return_value = {'reservationSet': [ec2_reservation]}
|
||||
|
||||
self.set_mock_db_items(
|
||||
fakes.DB_IMAGE_1, fakes.DB_SUBNET_1, fakes.DB_SUBNET_2,
|
||||
|
@ -305,11 +299,11 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
for db_instance in self.DB_INSTANCES])
|
||||
|
||||
@mock.patch('ec2api.api.instance._parse_block_device_mapping')
|
||||
@mock.patch('ec2api.api.instance._format_reservation')
|
||||
@mock.patch('ec2api.api.instance.describe_instances')
|
||||
@mock.patch('ec2api.api.instance.InstanceEngineNeutron.'
|
||||
'get_ec2_classic_os_network')
|
||||
def test_run_instances_other_parameters(self, get_ec2_classic_os_network,
|
||||
format_reservation,
|
||||
describe_instances,
|
||||
parse_block_device_mapping):
|
||||
self.set_mock_db_items(
|
||||
fakes.DB_IMAGE_1, fakes.DB_IMAGE_AKI_1, fakes.DB_IMAGE_ARI_1)
|
||||
|
@ -319,13 +313,15 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
fakes.ID_OS_IMAGE_AKI_1: fakes.OSImage(fakes.OS_IMAGE_AKI_1),
|
||||
fakes.ID_OS_IMAGE_ARI_1: fakes.OSImage(fakes.OS_IMAGE_ARI_1)}))
|
||||
get_ec2_classic_os_network.return_value = {'id': fakes.random_os_id()}
|
||||
format_reservation.return_value = {}
|
||||
user_data = base64.b64decode(fakes.USER_DATA_INSTANCE_2)
|
||||
parse_block_device_mapping.return_value = 'fake_bdm'
|
||||
|
||||
def do_check(engine, extra_kwargs={}, extra_db_instance={}):
|
||||
instance_api.instance_engine = engine
|
||||
|
||||
user_data = base64.b64decode(fakes.USER_DATA_INSTANCE_2)
|
||||
describe_instances.side_effect = [
|
||||
{'reservationSet': []},
|
||||
{'reservationSet': [{'foo': 'bar'}]}]
|
||||
|
||||
self.execute(
|
||||
'RunInstances',
|
||||
|
@ -352,6 +348,7 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
**extra_kwargs)
|
||||
self.nova.servers.reset_mock()
|
||||
db_instance = {'os_id': mock.ANY,
|
||||
'vpc_id': None,
|
||||
'reservation_id': mock.ANY,
|
||||
'launch_index': 0,
|
||||
'client_token': 'fake_client_token'}
|
||||
|
@ -363,8 +360,7 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
mock.ANY,
|
||||
[{'device_name': '/dev/vdd',
|
||||
'ebs': {'snapshot_id': fakes.ID_EC2_SNAPSHOT_1,
|
||||
'delete_on_termination': False}}],
|
||||
fakes.OSImage(fakes.OS_IMAGE_1))
|
||||
'delete_on_termination': False}}])
|
||||
parse_block_device_mapping.reset_mock()
|
||||
|
||||
do_check(
|
||||
|
@ -376,27 +372,13 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
extra_db_instance={'vpc_id': None})
|
||||
do_check(instance_api.InstanceEngineNova())
|
||||
|
||||
@mock.patch('ec2api.api.instance._format_reservation')
|
||||
@mock.patch('ec2api.api.instance._get_os_instances_by_instances')
|
||||
def test_idempotent_run(self, get_os_instances_by_instances,
|
||||
format_reservation):
|
||||
instance_engine = mock.MagicMock()
|
||||
instance_api.instance_engine = instance_engine
|
||||
get_ec2_network_interfaces = instance_engine.get_ec2_network_interfaces
|
||||
|
||||
instances = [{'id': fakes.random_ec2_id('i'),
|
||||
'os_id': fakes.random_os_id(),
|
||||
'reservation_id': fakes.random_ec2_id('r'),
|
||||
'client_token': 'client-token-%s' % ind}
|
||||
for ind in range(3)]
|
||||
os_instances = [fakes.OSInstance_full({'id': inst['os_id']})
|
||||
for inst in instances]
|
||||
format_reservation.return_value = {'key': 'value'}
|
||||
@mock.patch('ec2api.api.instance.describe_instances')
|
||||
def test_idempotent_run(self, describe_instances):
|
||||
self.set_mock_db_items()
|
||||
|
||||
# NOTE(ft): check select corresponding instance by client_token
|
||||
self.set_mock_db_items(instances[0], instances[1])
|
||||
get_os_instances_by_instances.return_value = [os_instances[1]]
|
||||
get_ec2_network_interfaces.return_value = 'ec2_network_interfaces'
|
||||
describe_instances.return_value = {
|
||||
'reservationSet': [{'key': 'value'}]}
|
||||
|
||||
resp = self.execute('RunInstances',
|
||||
{'MinCount': '1', 'MaxCount': '1',
|
||||
|
@ -404,64 +386,20 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
'InstanceType': 'fake_flavor',
|
||||
'ClientToken': 'client-token-1'})
|
||||
self.assertEqual({'key': 'value'}, resp)
|
||||
format_reservation.assert_called_once_with(
|
||||
mock.ANY, instances[1]['reservation_id'],
|
||||
[(instances[1], os_instances[1])],
|
||||
'ec2_network_interfaces')
|
||||
get_os_instances_by_instances.assert_called_once_with(
|
||||
mock.ANY, instances[1:2], nova=self.nova_admin)
|
||||
get_ec2_network_interfaces.assert_called_once_with(
|
||||
mock.ANY, [instances[1]['id']])
|
||||
describe_instances.assert_called_once_with(
|
||||
mock.ANY, filter=[{'name': 'client-token',
|
||||
'value': ['client-token-1']}])
|
||||
|
||||
# NOTE(ft): check pass to general run_instances logic if no
|
||||
# corresponding client_token is found
|
||||
instance_engine.run_instances.return_value = {}
|
||||
resp = self.execute('RunInstances',
|
||||
{'MinCount': '1', 'MaxCount': '1',
|
||||
'ImageId': fakes.ID_EC2_IMAGE_1,
|
||||
'InstanceType': 'fake_flavor',
|
||||
'ClientToken': 'client-token-2'})
|
||||
self.assertTrue(instance_engine.run_instances.called)
|
||||
describe_instances.return_value = {'reservationSet': []}
|
||||
|
||||
# NOTE(ft): check pass to general run_instances logic if no more
|
||||
# corresponding OS instance exists
|
||||
instance_engine.reset_mock()
|
||||
get_os_instances_by_instances.return_value = []
|
||||
resp = self.execute('RunInstances',
|
||||
{'MinCount': '1', 'MaxCount': '1',
|
||||
'ImageId': fakes.ID_EC2_IMAGE_1,
|
||||
'InstanceType': 'fake_flavor',
|
||||
'ClientToken': 'client-token-1'})
|
||||
self.assertTrue(instance_engine.run_instances.called)
|
||||
|
||||
# NOTE(ft): check case for several instances with same client_token,
|
||||
# but one no more exists in OS
|
||||
format_reservation.reset_mock()
|
||||
get_os_instances_by_instances.reset_mock()
|
||||
instance_engine.reset_mock()
|
||||
for inst in instances:
|
||||
inst['reservation_id'] = instances[0]['reservation_id']
|
||||
inst['client_token'] = 'client-token'
|
||||
self.set_mock_db_items(*instances)
|
||||
get_os_instances_by_instances.return_value = [os_instances[0],
|
||||
os_instances[2]]
|
||||
get_ec2_network_interfaces.return_value = 'ec2_network_interfaces'
|
||||
|
||||
resp = self.execute('RunInstances',
|
||||
{'MinCount': '1', 'MaxCount': '1',
|
||||
'ImageId': fakes.ID_EC2_IMAGE_1,
|
||||
'InstanceType': 'fake_flavor',
|
||||
'ClientToken': 'client-token'})
|
||||
self.assertEqual({'key': 'value'}, resp)
|
||||
format_reservation.assert_called_once_with(
|
||||
mock.ANY, instances[0]['reservation_id'],
|
||||
[(instances[0], os_instances[0]),
|
||||
(instances[2], os_instances[2])],
|
||||
'ec2_network_interfaces')
|
||||
self.assert_any_call(get_os_instances_by_instances, mock.ANY,
|
||||
instances, nova=self.nova_admin)
|
||||
get_ec2_network_interfaces.assert_called_once_with(
|
||||
mock.ANY, [instances[0]['id'], instances[2]['id']])
|
||||
self.assert_execution_error(
|
||||
'InvalidAMIID.NotFound', 'RunInstances',
|
||||
{'MinCount': '1', 'MaxCount': '1',
|
||||
'ImageId': fakes.ID_EC2_IMAGE_1,
|
||||
'InstanceType': 'fake_flavor',
|
||||
'ClientToken': 'client-token-2'})
|
||||
|
||||
def test_run_instances_rollback(self):
|
||||
instance_api.instance_engine = (
|
||||
|
@ -482,7 +420,7 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
_attach_network_interface_item.side_effect) = Exception()
|
||||
|
||||
@tools.screen_unexpected_exception_logs
|
||||
def do_check(params, new_port=True, delete_on_termination=None):
|
||||
def do_check(params, new_port=True):
|
||||
mock_manager = mock.MagicMock()
|
||||
mock_manager.attach_mock(self.network_interface_api,
|
||||
'network_interface_api')
|
||||
|
@ -495,15 +433,7 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
self.assert_execution_error(
|
||||
self.ANY_EXECUTE_ERROR, 'RunInstances', params)
|
||||
|
||||
calls = []
|
||||
if not new_port:
|
||||
calls.append(
|
||||
mock.call.neutron.update_port(
|
||||
fakes.ID_OS_PORT_1,
|
||||
{'port': {'device_id': '',
|
||||
'device_owner': ''}}))
|
||||
calls.append(
|
||||
mock.call.nova_servers.delete(fakes.ID_OS_INSTANCE_1))
|
||||
calls = [mock.call.nova_servers.delete(fakes.ID_OS_INSTANCE_1)]
|
||||
if new_port:
|
||||
calls.append(
|
||||
mock.call.network_interface_api.delete_network_interface(
|
||||
|
@ -525,19 +455,15 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
|
||||
do_check({'NetworkInterface.1.DeviceIndex': '0',
|
||||
'NetworkInterface.1.SubnetId': fakes.ID_EC2_SUBNET_1,
|
||||
'NetworkInterface.1.DeleteOnTermination': 'False'},
|
||||
delete_on_termination=False)
|
||||
'NetworkInterface.1.DeleteOnTermination': 'False'})
|
||||
|
||||
do_check({'NetworkInterface.1.DeviceIndex': '0',
|
||||
'NetworkInterface.1.NetworkInterfaceId': (
|
||||
fakes.ID_EC2_NETWORK_INTERFACE_1)},
|
||||
new_port=False)
|
||||
|
||||
@mock.patch('ec2api.api.instance.InstanceEngineNeutron.'
|
||||
'get_ec2_network_interfaces')
|
||||
@mock.patch('ec2api.api.instance._format_reservation')
|
||||
def test_run_instances_multiply_rollback(self, format_reservation,
|
||||
get_ec2_network_interfaces):
|
||||
@mock.patch('ec2api.api.instance.describe_instances')
|
||||
def test_run_instances_multiply_rollback(self, describe_instances):
|
||||
instances = [{'id': fakes.random_ec2_id('i'),
|
||||
'os_id': fakes.random_os_id()}
|
||||
for dummy in range(3)]
|
||||
|
@ -553,7 +479,6 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
self.glance.images.get.return_value = fakes.OSImage(fakes.OS_IMAGE_1)
|
||||
|
||||
self.utils_generate_uid.return_value = fakes.ID_EC2_RESERVATION_1
|
||||
get_ec2_network_interfaces.return_value = []
|
||||
|
||||
def do_check(engine):
|
||||
instance_api.instance_engine = engine
|
||||
|
@ -563,24 +488,19 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
for eni in network_interfaces]
|
||||
self.db_api.add_item.side_effect = instances
|
||||
self.nova.servers.create.side_effect = os_instances
|
||||
format_reservation.side_effect = (
|
||||
lambda _context, r_id, instance_info, *args, **kwargs: (
|
||||
{'reservationId': r_id,
|
||||
'instancesSet': [
|
||||
{'instanceId': inst['id']}
|
||||
for inst, _os_inst in instance_info]}))
|
||||
expected_reservation = {
|
||||
'reservationId': fakes.ID_EC2_RESERVATION_1,
|
||||
'instancesSet': [{'instanceId': inst['id']}
|
||||
for inst in instances[:2]]}
|
||||
describe_instances.return_value = {
|
||||
'reservationSet': [expected_reservation]}
|
||||
|
||||
resp = self.execute('RunInstances',
|
||||
{'ImageId': fakes.ID_EC2_IMAGE_1,
|
||||
'InstanceType': 'fake_flavor',
|
||||
'MinCount': '2', 'MaxCount': '3',
|
||||
'SubnetId': fakes.ID_EC2_SUBNET_1})
|
||||
self.assertThat(resp,
|
||||
matchers.DictMatches(
|
||||
{'reservationId': fakes.ID_EC2_RESERVATION_1,
|
||||
'instancesSet': [
|
||||
{'instanceId': inst['id']}
|
||||
for inst in instances[:2]]}))
|
||||
self.assertThat(resp, matchers.DictMatches(expected_reservation))
|
||||
|
||||
self.nova.servers.delete.assert_called_once_with(
|
||||
instances[2]['os_id'])
|
||||
|
@ -626,10 +546,7 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
"""Terminate 2 instances in one request."""
|
||||
instance_api.instance_engine = (
|
||||
instance_api.InstanceEngineNeutron())
|
||||
self.set_mock_db_items(
|
||||
fakes.DB_INSTANCE_1, fakes.DB_INSTANCE_2,
|
||||
fakes.DB_NETWORK_INTERFACE_1, fakes.DB_NETWORK_INTERFACE_2,
|
||||
fakes.DB_ADDRESS_1, fakes.DB_ADDRESS_2)
|
||||
self.set_mock_db_items(fakes.DB_INSTANCE_1, fakes.DB_INSTANCE_2)
|
||||
os_instances = [fakes.OSInstance(fakes.OS_INSTANCE_1),
|
||||
fakes.OSInstance(fakes.OS_INSTANCE_2)]
|
||||
self.nova.servers.get.side_effect = os_instances
|
||||
|
@ -650,13 +567,9 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
fake_state_change),
|
||||
tools.update_dict({'instanceId': fakes.ID_EC2_INSTANCE_2},
|
||||
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.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)
|
||||
self.assertEqual(
|
||||
0, self.address_api.dissassociate_address_item.call_count)
|
||||
self.assertFalse(self.db_api.delete_item.called)
|
||||
self.assertEqual(2, os_instance_delete.call_count)
|
||||
self.assertEqual(2, os_instance_get.call_count)
|
||||
|
@ -684,27 +597,15 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
lambda ec2_id: fakes.OSInstance({'id': ec2_id,
|
||||
'vm_state': 'active'}))
|
||||
|
||||
def do_check(mock_eni_list=[]):
|
||||
self.set_mock_db_items(self.DB_FAKE_ENI,
|
||||
*(self.DB_INSTANCES + mock_eni_list))
|
||||
self.set_mock_db_items(*self.DB_INSTANCES)
|
||||
|
||||
resp = self.execute('TerminateInstances',
|
||||
{'InstanceId.1': fakes.ID_EC2_INSTANCE_1,
|
||||
'InstanceId.2': fakes.ID_EC2_INSTANCE_2})
|
||||
resp = self.execute('TerminateInstances',
|
||||
{'InstanceId.1': fakes.ID_EC2_INSTANCE_1,
|
||||
'InstanceId.2': fakes.ID_EC2_INSTANCE_2})
|
||||
|
||||
self.assertThat(
|
||||
resp, matchers.DictMatches(ec2_terminate_instances_result))
|
||||
self.assertFalse(self.db_api.delete_item.called)
|
||||
|
||||
self.db_api.delete_item.reset_mock()
|
||||
|
||||
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]])
|
||||
|
||||
do_check(
|
||||
mock_eni_list=[self.DB_ATTACHED_ENIS[1]])
|
||||
self.assertThat(
|
||||
resp, matchers.DictMatches(ec2_terminate_instances_result))
|
||||
self.assertFalse(self.db_api.delete_item.called)
|
||||
|
||||
def test_terminate_instances_invalid_parameters(self):
|
||||
self.assert_execution_error(
|
||||
|
@ -1150,24 +1051,11 @@ class InstanceTestCase(base.ApiTestCase):
|
|||
self.IDS_OS_INSTANCE = ids_os_instance
|
||||
self.IDS_EC2_INSTANCE = ids_ec2_instance
|
||||
self.IDS_EC2_SUBNET_BY_PORT = ids_ec2_subnet_by_port
|
||||
self.DB_ATTACHED_ENIS = db_attached_enis
|
||||
self.DB_DETACHED_ENIS = db_detached_enis
|
||||
self.EC2_ATTACHED_ENIS = ec2_attached_enis
|
||||
self.EC2_DETACHED_ENIS = ec2_detached_enis
|
||||
self.DB_INSTANCES = db_instances
|
||||
|
||||
# NOTE(ft): additional fake data to check filtering, etc
|
||||
self.DB_FAKE_ENI = fakes.gen_db_network_interface(
|
||||
fakes.random_ec2_id('eni'), fakes.random_os_id(),
|
||||
fakes.ID_EC2_VPC_1, fakes.ID_EC2_SUBNET_2,
|
||||
'fake_ip')
|
||||
ec2_fake_eni = fakes.gen_ec2_network_interface(
|
||||
self.DB_FAKE_ENI['id'],
|
||||
fakes.EC2_SUBNET_2, ['fake_ip'])
|
||||
self.OS_FAKE_PORT = fakes.gen_os_port(
|
||||
fakes.random_os_id(), ec2_fake_eni,
|
||||
fakes.ID_OS_SUBNET_2, ['fake_ip'])
|
||||
|
||||
|
||||
# TODO(ft): add tests for get_vpc_default_security_group_id,
|
||||
|
||||
|
@ -1227,41 +1115,40 @@ class InstancePrivateTestCase(test_base.BaseTestCase):
|
|||
None, None, None, ['sg-1'], None)
|
||||
|
||||
self.assertEqual(
|
||||
(None, [{'device_index': 0,
|
||||
'subnet_id': 'subnet-1'}]),
|
||||
([{'device_index': 0,
|
||||
'subnet_id': 'subnet-1'}]),
|
||||
engine.merge_network_interface_parameters(
|
||||
None, 'subnet-1', None, None, None))
|
||||
self.assertEqual(
|
||||
(None, [{'device_index': 0,
|
||||
'subnet_id': 'subnet-1',
|
||||
'private_ip_address': '10.10.10.10'}]),
|
||||
([{'device_index': 0,
|
||||
'subnet_id': 'subnet-1',
|
||||
'private_ip_address': '10.10.10.10'}]),
|
||||
engine.merge_network_interface_parameters(
|
||||
None, 'subnet-1', '10.10.10.10', None, None))
|
||||
self.assertEqual(
|
||||
(None, [{'device_index': 0,
|
||||
'subnet_id': 'subnet-1',
|
||||
'private_ip_address': '10.10.10.10',
|
||||
'security_group_id': ['sg-1']}]),
|
||||
([{'device_index': 0,
|
||||
'subnet_id': 'subnet-1',
|
||||
'private_ip_address': '10.10.10.10',
|
||||
'security_group_id': ['sg-1']}]),
|
||||
engine.merge_network_interface_parameters(
|
||||
None, 'subnet-1', '10.10.10.10', ['sg-1'], None))
|
||||
self.assertEqual(
|
||||
(None, [{'device_index': 0,
|
||||
'subnet_id': 'subnet-1',
|
||||
'security_group_id': ['sg-1']}]),
|
||||
([{'device_index': 0,
|
||||
'subnet_id': 'subnet-1',
|
||||
'security_group_id': ['sg-1']}]),
|
||||
engine.merge_network_interface_parameters(
|
||||
None, 'subnet-1', None, ['sg-1'], None))
|
||||
|
||||
self.assertEqual(
|
||||
(None, [{'device_index': 0,
|
||||
'subnet_id': 'subnet-1'}]),
|
||||
([{'device_index': 0,
|
||||
'subnet_id': 'subnet-1'}]),
|
||||
engine.merge_network_interface_parameters(
|
||||
None, None, None, None,
|
||||
[{'device_index': 0, 'subnet_id': 'subnet-1'}]))
|
||||
self.assertEqual(
|
||||
(['default'], []),
|
||||
engine.merge_network_interface_parameters(
|
||||
['default'], None, None, None, None))
|
||||
self.assertEqual((None, []),
|
||||
self.assertEqual([],
|
||||
engine.merge_network_interface_parameters(
|
||||
['default'], None, None, None, None))
|
||||
self.assertEqual([],
|
||||
engine.merge_network_interface_parameters(
|
||||
None, None, None, None, None))
|
||||
|
||||
|
@ -1374,7 +1261,6 @@ class InstancePrivateTestCase(test_base.BaseTestCase):
|
|||
'delete_on_termination': False},
|
||||
{'device_index': 1,
|
||||
'network_interface': fakes.DB_NETWORK_INTERFACE_1,
|
||||
'detach_on_crash': True,
|
||||
'delete_on_termination': False}]),
|
||||
resp)
|
||||
resp = engine.parse_network_interface_parameters(
|
||||
|
@ -1472,14 +1358,12 @@ class InstancePrivateTestCase(test_base.BaseTestCase):
|
|||
@mock.patch('ec2api.db.api.IMPL')
|
||||
def test_parse_block_device_mapping(self, db_api):
|
||||
fake_context = mock.Mock(service_catalog=[{'type': 'fake'}])
|
||||
os_image = fakes.OSImage(fakes.OS_IMAGE_1)
|
||||
|
||||
db_api.get_item_by_id.side_effect = tools.get_db_api_get_item_by_id(
|
||||
fakes.DB_VOLUME_1, fakes.DB_VOLUME_2, fakes.DB_VOLUME_3,
|
||||
fakes.DB_SNAPSHOT_1, fakes.DB_SNAPSHOT_2)
|
||||
|
||||
res = instance_api._parse_block_device_mapping(
|
||||
fake_context, [], os_image)
|
||||
res = instance_api._parse_block_device_mapping(fake_context, [])
|
||||
self.assertEqual({}, res)
|
||||
|
||||
res = instance_api._parse_block_device_mapping(
|
||||
|
@ -1495,8 +1379,7 @@ class InstancePrivateTestCase(test_base.BaseTestCase):
|
|||
'ebs': {'snapshot_id': fakes.ID_EC2_VOLUME_2,
|
||||
'delete_on_termination': True}},
|
||||
{'device_name': '/dev/sdb1',
|
||||
'ebs': {'volume_size': 55}}],
|
||||
os_image)
|
||||
'ebs': {'volume_size': 55}}])
|
||||
self.assertThat(
|
||||
res,
|
||||
matchers.DictMatches(
|
||||
|
@ -1717,10 +1600,8 @@ class InstancePrivateTestCase(test_base.BaseTestCase):
|
|||
|
||||
@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,
|
||||
def test_remove_instances(self, db_api, detach_network_interface_item,
|
||||
delete_network_interface):
|
||||
fake_context = mock.Mock(service_catalog=[{'type': 'fake'}])
|
||||
|
||||
|
@ -1735,35 +1616,22 @@ class InstancePrivateTestCase(test_base.BaseTestCase):
|
|||
[[{'id': fakes.random_ec2_id('i')}] * 2])))]
|
||||
network_interfaces.extend({'id': fakes.random_ec2_id('eni')}
|
||||
for dummy in range(2))
|
||||
addresses = [{'id': fakes.random_ec2_id('eipalloc'),
|
||||
'network_interface_id': eni['id']}
|
||||
for eni in network_interfaces[::2]]
|
||||
addresses.extend({'id': fakes.random_ec2_id('eipalloc')}
|
||||
for dummy in range(2))
|
||||
|
||||
instances_to_remove = instances[:2] + [instances[3]]
|
||||
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))
|
||||
|
||||
def check_calls():
|
||||
for eni in network_interfaces_to_detach:
|
||||
detach_network_interface_item.assert_any_call(fake_context,
|
||||
eni)
|
||||
for eni in network_interfaces_to_delete:
|
||||
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()
|
||||
*network_interfaces)
|
||||
|
||||
instance_api._remove_instances(fake_context, instances_to_remove)
|
||||
check_calls()
|
||||
|
||||
instance_api._remove_instances(fake_context, instances_to_remove)
|
||||
check_calls()
|
||||
for eni in network_interfaces_to_detach:
|
||||
detach_network_interface_item.assert_any_call(fake_context,
|
||||
eni)
|
||||
for eni in network_interfaces_to_delete:
|
||||
delete_network_interface.assert_any_call(fake_context,
|
||||
eni['id'])
|
||||
|
||||
@mock.patch('cinderclient.client.Client')
|
||||
def test_get_os_volumes(self, cinder):
|
||||
|
|
Loading…
Reference in New Issue