Files
ec2-api/ec2api/tests/test_instance.py
Feodor Tersin fe949bf49c Port tests for instance volume manipulation
Change-Id: I41e61a5e6eb82515847cf0108366a7e72cb220af
2015-01-20 23:37:41 +04:00

1436 lines
70 KiB
Python

# Copyright 2014
# The Cloudscaling Group, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
import datetime
import itertools
import mock
from oslotest import base as test_base
from ec2api.api import instance as instance_api
from ec2api import exception
from ec2api.tests import base
from ec2api.tests import fakes
from ec2api.tests import matchers
from ec2api.tests import tools
class InstanceTestCase(base.ApiTestCase):
# TODO(ft): make negative tests on invalid parameters
def setUp(self):
super(InstanceTestCase, self).setUp()
network_interface_api_patcher = mock.patch(
'ec2api.api.instance.network_interface_api')
self.network_interface_api = network_interface_api_patcher.start()
self.addCleanup(network_interface_api_patcher.stop)
address_api_patcher = mock.patch('ec2api.api.address')
self.address_api = address_api_patcher.start()
self.addCleanup(address_api_patcher.stop)
security_group_api_patcher = mock.patch('ec2api.api.security_group')
self.security_group_api = security_group_api_patcher.start()
self.addCleanup(security_group_api_patcher.stop)
utils_generate_uid_patcher = (
mock.patch('ec2api.api.instance._utils_generate_uid'))
self.utils_generate_uid = utils_generate_uid_patcher.start()
self.addCleanup(utils_generate_uid_patcher.stop)
novadb_patcher = (mock.patch('ec2api.api.instance.novadb'))
self.novadb = novadb_patcher.start()
self.addCleanup(novadb_patcher.stop)
format_security_groups_ids_names = (
self.security_group_api.format_security_groups_ids_names)
format_security_groups_ids_names.return_value = {}
self.fake_flavor = mock.Mock()
self.fake_flavor.configure_mock(name='fake_flavor')
self.nova_flavors.get.return_value = self.fake_flavor
self.nova_flavors.list.return_value = [self.fake_flavor]
@mock.patch('ec2api.api.instance.InstanceEngineNeutron.'
'format_network_interfaces')
@mock.patch('ec2api.api.instance.InstanceEngineNeutron.'
'get_vpc_default_security_group_id')
def test_run_instances(self, get_vpc_default_security_group_id,
format_network_interfaces):
"""Run instance with various network interface settings."""
instance_api.instance_engine = (
instance_api.InstanceEngineNeutron())
self.db_api.get_item_by_id.side_effect = (
fakes.get_db_api_get_item_by_id(
{fakes.ID_EC2_SUBNET_1: fakes.DB_SUBNET_1,
fakes.ID_EC2_NETWORK_INTERFACE_1:
copy.deepcopy(fakes.DB_NETWORK_INTERFACE_1),
fakes.ID_EC2_IMAGE_1: fakes.DB_IMAGE_1}))
self.db_api.get_item_ids.side_effect = (
fakes.get_db_api_get_item_by_id({
(fakes.ID_OS_IMAGE_ARI_1,): [(fakes.ID_EC2_IMAGE_ARI_1,
fakes.ID_OS_IMAGE_ARI_1)],
(fakes.ID_OS_IMAGE_AKI_1,): [(fakes.ID_EC2_IMAGE_AKI_1,
fakes.ID_OS_IMAGE_AKI_1)]}))
self.glance.images.get.return_value = fakes.OSImage(fakes.OS_IMAGE_1)
self.network_interface_api.create_network_interface.return_value = (
{'networkInterface': fakes.EC2_NETWORK_INTERFACE_1})
self.db_api.add_item.return_value = fakes.DB_INSTANCE_1
self.nova_servers.create.return_value = (
fakes.OSInstance(
fakes.ID_OS_INSTANCE_1, {'id': 'fakeFlavorId'},
image={'id': fakes.ID_OS_IMAGE_1}))
self.novadb.instance_get_by_uuid.return_value = fakes.NOVADB_INSTANCE_1
self.novadb.block_device_mapping_get_all_by_instance.return_value = []
self.utils_generate_uid.return_value = fakes.ID_EC2_RESERVATION_1
get_vpc_default_security_group_id.return_value = None
def do_check(params, create_network_interface_kwargs=None,
delete_on_termination=None):
delete_port_on_termination = (
create_network_interface_kwargs is not None
if delete_on_termination is None
else delete_on_termination)
eni = fakes.gen_ec2_network_interface(
fakes.ID_EC2_NETWORK_INTERFACE_1,
fakes.EC2_SUBNET_1,
[fakes.IP_NETWORK_INTERFACE_1],
description=fakes.DESCRIPTION_NETWORK_INTERFACE_1,
ec2_instance_id=fakes.ID_EC2_INSTANCE_1,
delete_on_termination=delete_port_on_termination)
expected_reservation = fakes.gen_ec2_reservation(
fakes.ID_EC2_RESERVATION_1,
[fakes.gen_ec2_instance(
fakes.ID_EC2_INSTANCE_1,
private_ip_address=fakes.IP_NETWORK_INTERFACE_1,
ec2_network_interfaces=[eni],
image_id=fakes.ID_EC2_IMAGE_1,
kernel_id=fakes.ID_EC2_IMAGE_AKI_1,
ramdisk_id=fakes.ID_EC2_IMAGE_ARI_1)])
format_network_interfaces.return_value = {
fakes.ID_EC2_INSTANCE_1: [eni]}
params.update({'ImageId': fakes.ID_EC2_IMAGE_1,
'InstanceType': 'fake_flavor',
'MinCount': '1', 'MaxCount': '1'})
resp = self.execute('RunInstances', params)
self.assertEqual(200, resp['http_status_code'])
resp.pop('http_status_code')
self.assertThat(resp, matchers.DictMatches(expected_reservation))
if create_network_interface_kwargs is not None:
(self.network_interface_api.
create_network_interface.assert_called_once_with(
mock.ANY, fakes.ID_EC2_SUBNET_1,
**create_network_interface_kwargs))
self.nova_servers.create.assert_called_once_with(
'EC2 server', fakes.ID_OS_IMAGE_1, self.fake_flavor,
min_count=1, max_count=1,
kernel_id=None, ramdisk_id=None,
availability_zone=None,
block_device_mapping=None,
security_groups=None,
nics=[{'port-id': fakes.ID_OS_PORT_1}],
key_name=None, userdata=None)
self.db_api.add_item.assert_called_once_with(
mock.ANY, 'i', tools.purge_dict(fakes.DB_INSTANCE_1, ('id',)))
(self.network_interface_api.
_attach_network_interface_item.assert_called_once_with(
mock.ANY, fakes.DB_NETWORK_INTERFACE_1,
fakes.ID_EC2_INSTANCE_1,
delete_on_termination=delete_port_on_termination))
self.novadb.instance_get_by_uuid.assert_called_once_with(
mock.ANY, fakes.ID_OS_INSTANCE_1)
format_network_interfaces.assert_called_once_with(
mock.ANY, [[fakes.DB_NETWORK_INTERFACE_1]])
self.assertEqual(2, self.db_api.get_item_ids.call_count)
self.db_api.get_item_ids.assert_any_call(
mock.ANY, 'aki', (fakes.ID_OS_IMAGE_AKI_1,))
self.db_api.get_item_ids.assert_any_call(
mock.ANY, 'ari', (fakes.ID_OS_IMAGE_ARI_1,))
self.network_interface_api.reset_mock()
self.nova_servers.reset_mock()
self.db_api.reset_mock()
self.novadb.reset_mock()
format_network_interfaces.reset_mock()
do_check({'SubnetId': fakes.ID_EC2_SUBNET_1},
create_network_interface_kwargs={})
do_check({'SubnetId': fakes.ID_EC2_SUBNET_1,
'SecurityGroupId.1': fakes.ID_EC2_SECURITY_GROUP_1,
'SecurityGroupId.2': fakes.ID_EC2_SECURITY_GROUP_2},
create_network_interface_kwargs={
'security_group_id': [fakes.ID_EC2_SECURITY_GROUP_1,
fakes.ID_EC2_SECURITY_GROUP_2]})
do_check({'SubnetId': fakes.ID_EC2_SUBNET_1,
'PrivateIpAddress.1': fakes.IP_FIRST_SUBNET_1,
'PrivateIpAddress.2': fakes.IP_LAST_SUBNET_1},
create_network_interface_kwargs={
'private_ip_address': [fakes.IP_FIRST_SUBNET_1,
fakes.IP_LAST_SUBNET_1]})
do_check({'NetworkInterface.1.SubnetId': fakes.ID_EC2_SUBNET_1,
'NetworkInterface.1.SecurityGroupId.1':
fakes.ID_EC2_SECURITY_GROUP_1,
'NetworkInterface.1.PrivateIpAddress.1':
fakes.IP_FIRST_SUBNET_1},
create_network_interface_kwargs={
'security_group_id': [fakes.ID_EC2_SECURITY_GROUP_1],
'private_ip_address': [fakes.IP_FIRST_SUBNET_1]})
do_check({'NetworkInterface.1.SubnetId': fakes.ID_EC2_SUBNET_1,
'NetworkInterface.1.DeleteOnTermination': 'False'},
create_network_interface_kwargs={},
delete_on_termination=False)
do_check({'NetworkInterface.1.SubnetId': fakes.ID_EC2_SUBNET_1,
'NetworkInterface.1.SecurityGroupId.1':
fakes.ID_EC2_SECURITY_GROUP_1,
'NetworkInterface.1.DeleteOnTermination': 'False'},
create_network_interface_kwargs={
'security_group_id': [fakes.ID_EC2_SECURITY_GROUP_1]},
delete_on_termination=False)
do_check({'NetworkInterface.1.NetworkInterfaceId':
fakes.ID_EC2_NETWORK_INTERFACE_1})
@mock.patch('ec2api.api.instance.InstanceEngineNeutron.'
'format_network_interfaces')
@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,
format_network_interfaces):
"""Run 2 instances at once on 2 subnets in all combinations."""
instance_api.instance_engine = (
instance_api.InstanceEngineNeutron())
self._build_multiple_data_model()
self.glance.images.get.return_value = fakes.OSImage(fakes.OS_IMAGE_1)
get_vpc_default_security_group_id.return_value = None
format_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 = [
fakes.gen_ec2_instance(ec2_instance_id, launch_index=l_i,
ec2_network_interfaces=eni_pair)
for l_i, (ec2_instance_id, eni_pair) in enumerate(zip(
self.IDS_EC2_INSTANCE,
zip(*[iter(self.EC2_ATTACHED_ENIS)] * 2)))]
ec2_reservation = fakes.gen_ec2_reservation(fakes.ID_EC2_RESERVATION_1,
ec2_instances)
fakes_db_items = dict((eni['id'], eni)
for eni in self.DB_DETACHED_ENIS)
fakes_db_items.update({
fakes.ID_EC2_SUBNET_1: fakes.DB_SUBNET_1,
fakes.ID_EC2_SUBNET_2: fakes.DB_SUBNET_2})
self.db_api.get_item_by_id.side_effect = (
fakes.get_db_api_get_item_by_id(fakes_db_items))
self.network_interface_api.create_network_interface.side_effect = (
[{'networkInterface': eni}
for eni in self.EC2_DETACHED_ENIS])
self.nova_servers.create.side_effect = [
fakes.OSInstance(os_instance_id, {'id': 'fakeFlavorId'})
for os_instance_id in self.IDS_OS_INSTANCE]
self.novadb.instance_get_by_uuid.side_effect = self.NOVADB_INSTANCES
self.utils_generate_uid.return_value = fakes.ID_EC2_RESERVATION_1
self.db_api.add_item.side_effect = self.DB_INSTANCES
resp = self.execute(
'RunInstances',
{'ImageId': fakes.ID_EC2_IMAGE_1,
'InstanceType': 'fake_flavor',
'MinCount': '2',
'MaxCount': '2',
'NetworkInterface.1.SubnetId': fakes.ID_EC2_SUBNET_1,
'NetworkInterface.2.SubnetId': fakes.ID_EC2_SUBNET_2,
'NetworkInterface.2.DeleteOnTermination': 'False'})
self.assertEqual(200, resp['http_status_code'])
resp.pop('http_status_code')
self.assertThat(resp, matchers.DictMatches(ec2_reservation),
verbose=True)
self.network_interface_api.create_network_interface.assert_has_calls([
mock.call(mock.ANY, ec2_subnet_id)
for ec2_subnet_id in self.IDS_EC2_SUBNET_BY_PORT])
self.nova_servers.create.assert_has_calls([
mock.call(
'EC2 server', fakes.ID_OS_IMAGE_1, self.fake_flavor,
min_count=1, max_count=1,
kernel_id=None, ramdisk_id=None,
availability_zone=None,
block_device_mapping=None,
security_groups=None,
nics=[{'port-id': port_id}
for port_id in port_ids],
key_name=None, userdata=None)
for port_ids in zip(*[iter(self.IDS_OS_PORT)] * 2)])
(self.network_interface_api.
_attach_network_interface_item.assert_has_calls([
mock.call(mock.ANY, eni, ec2_instance_id,
delete_on_termination=dot)
for eni, ec2_instance_id, dot in zip(
self.DB_DETACHED_ENIS,
itertools.chain(*map(lambda i: [i] * 2,
self.IDS_EC2_INSTANCE)),
[True, False, True, False])]))
self.db_api.add_item.assert_has_calls([
mock.call(mock.ANY, 'i', tools.purge_dict(db_instance, ['id']))
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.InstanceEngineNeutron.'
'get_ec2_classic_os_network')
def test_run_instances_other_parameters(self, get_ec2_classic_os_network,
format_reservation,
parse_block_device_mapping):
self.glance.images.get.return_value = fakes.OSImage(fakes.OS_IMAGE_1)
get_ec2_classic_os_network.return_value = {'id': fakes.random_os_id()}
format_reservation.return_value = {}
parse_block_device_mapping.return_value = 'fake_bdm'
def do_check(engine, extra_kwargs={}, extra_db_instance={}):
instance_api.instance_engine = engine
resp = self.execute(
'RunInstances',
{'ImageId': fakes.ID_EC2_IMAGE_1,
'InstanceType': 'fake_flavor',
'MinCount': '1', 'MaxCount': '1',
'SecurityGroup.1': 'Default',
'Placement.AvailabilityZone': 'fake_zone',
'ClientToken': 'fake_client_token',
'BlockDeviceMapping.1.DeviceName': '/dev/vdd',
'BlockDeviceMapping.1.Ebs.SnapshotId': (
fakes.ID_EC2_SNAPSHOT_1),
'BlockDeviceMapping.1.Ebs.DeleteOnTermination': 'False'})
self.assertEqual(200, resp['http_status_code'])
self.nova_servers.create.assert_called_once_with(
mock.ANY, mock.ANY, mock.ANY, min_count=1, max_count=1,
userdata=None, kernel_id=None, ramdisk_id=None, key_name=None,
block_device_mapping='fake_bdm',
availability_zone='fake_zone', security_groups=['Default'],
**extra_kwargs)
self.nova_servers.reset_mock()
db_instance = {'os_id': mock.ANY,
'reservation_id': mock.ANY,
'launch_index': 0,
'client_token': 'fake_client_token'}
db_instance.update(extra_db_instance)
self.db_api.add_item.assert_called_once_with(
mock.ANY, 'i', db_instance)
self.db_api.reset_mock()
parse_block_device_mapping.assert_called_once_with(
mock.ANY,
[{'device_name': '/dev/vdd',
'ebs': {'snapshot_id': fakes.ID_EC2_SNAPSHOT_1,
'delete_on_termination': False}}],
self.glance.images.get.return_value)
parse_block_device_mapping.reset_mock()
do_check(
instance_api.InstanceEngineNeutron(),
extra_kwargs={
'nics': [
{'net-id': get_ec2_classic_os_network.return_value['id']}],
},
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(inst['os_id'])
for inst in instances]
format_reservation.return_value = {'key': 'value'}
# NOTE(ft): check select corresponding instance by client_token
self.db_api.get_items.return_value = [instances[0], instances[1]]
get_os_instances_by_instances.return_value = [os_instances[1]]
self.novadb.instance_get_by_uuid.return_value = 'novadb_instance'
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-1'})
self.assertEqual({'http_status_code': 200,
'key': 'value'},
resp)
format_reservation.assert_called_once_with(
mock.ANY, instances[1]['reservation_id'],
[(instances[1], os_instances[1], 'novadb_instance')],
'ec2_network_interfaces')
get_os_instances_by_instances.assert_called_once_with(
mock.ANY, {instances[1]['os_id']: instances[1]})
self.novadb.instance_get_by_uuid.assert_called_once_with(
mock.ANY, os_instances[1].id)
get_ec2_network_interfaces.assert_called_once_with(
mock.ANY, [instances[1]['id']])
# 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)
# 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()
self.novadb.reset_mock()
for inst in instances:
inst['reservation_id'] = instances[0]['reservation_id']
inst['client_token'] = 'client-token'
self.db_api.get_items.return_value = instances
get_os_instances_by_instances.return_value = [os_instances[0],
os_instances[2]]
self.novadb.instance_get_by_uuid.side_effect = ['novadb-instance-0',
'novadb-instance-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({'http_status_code': 200,
'key': 'value'},
resp)
format_reservation.assert_called_once_with(
mock.ANY, instances[0]['reservation_id'],
[(instances[0], os_instances[0], 'novadb-instance-0'),
(instances[2], os_instances[2], 'novadb-instance-2')],
'ec2_network_interfaces')
get_os_instances_by_instances.assert_called_once_with(
mock.ANY, dict((inst['os_id'], inst) for inst in instances))
self.assertEqual([mock.call(mock.ANY, os_instances[0].id),
mock.call(mock.ANY, os_instances[2].id)],
self.novadb.instance_get_by_uuid.mock_calls)
get_ec2_network_interfaces.assert_called_once_with(
mock.ANY, [instances[0]['id'], instances[2]['id']])
def test_run_instances_rollback(self):
instance_api.instance_engine = (
instance_api.InstanceEngineNeutron())
self.db_api.get_item_by_id.side_effect = (
fakes.get_db_api_get_item_by_id(
{fakes.ID_EC2_SUBNET_1: fakes.DB_SUBNET_1,
fakes.ID_EC2_NETWORK_INTERFACE_1:
fakes.DB_NETWORK_INTERFACE_1}))
self.db_api.get_item_ids.return_value = [
(fakes.ID_EC2_IMAGE_1, fakes.ID_OS_IMAGE_1)]
self.glance.images.get.return_value = fakes.OSImage(fakes.OS_IMAGE_1)
self.network_interface_api.create_network_interface.return_value = (
{'networkInterface': fakes.EC2_NETWORK_INTERFACE_1})
self.db_api.add_item.return_value = fakes.DB_INSTANCE_1
self.utils_generate_uid.return_value = fakes.ID_EC2_RESERVATION_1
self.nova_servers.create.return_value = (
fakes.OSInstance(fakes.ID_OS_INSTANCE_1, {'id': 'fakeFlavorId'},
image={'id': fakes.ID_OS_IMAGE_1}))
self.novadb.instance_get_by_uuid.side_effect = Exception()
def do_check(params, new_port=True, delete_on_termination=None):
params.update({'ImageId': fakes.ID_EC2_IMAGE_1,
'InstanceType': 'fake_flavor',
'MinCount': '1', 'MaxCount': '1'})
self.execute('RunInstances', params)
# TODO(ft): check sequence of calling
# neutron update port must be the first
if new_port:
(self.network_interface_api.
delete_network_interface.assert_called_once_with(
mock.ANY,
network_interface_id=fakes.ID_EC2_NETWORK_INTERFACE_1))
else:
self.neutron.update_port.assert_called_once_with(
fakes.ID_OS_PORT_1,
{'port': {'device_id': '',
'device_owner': ''}})
self.nova_servers.delete.assert_called_once_with(
fakes.ID_OS_INSTANCE_1)
self.db_api.delete_item.assert_called_once_with(
mock.ANY, fakes.ID_EC2_INSTANCE_1)
self.network_interface_api.reset_mock()
self.neutron.reset_mock()
self.nova_servers.reset_mock()
self.db_api.reset_mock()
do_check({'SubnetId': fakes.ID_EC2_SUBNET_1})
do_check({'NetworkInterface.1.SubnetId': fakes.ID_EC2_SUBNET_1})
do_check({'NetworkInterface.1.SubnetId': fakes.ID_EC2_SUBNET_1,
'NetworkInterface.1.DeleteOnTermination': 'False'},
delete_on_termination=False)
do_check({'NetworkInterface.1.NetworkInterfaceId':
fakes.ID_EC2_NETWORK_INTERFACE_1},
new_port=False)
def test_run_instances_invalid_parameters(self):
resp = self.execute('RunInstances',
{'ImageId': fakes.ID_EC2_IMAGE_1,
'MinCount': '0', 'MaxCount': '0'})
self.assertEqual(400, resp['http_status_code'])
self.assertEqual('InvalidParameterValue', resp['Error']['Code'])
resp = self.execute('RunInstances',
{'ImageId': fakes.ID_EC2_IMAGE_1,
'MinCount': '1', 'MaxCount': '0'})
self.assertEqual(400, resp['http_status_code'])
self.assertEqual('InvalidParameterValue', resp['Error']['Code'])
resp = self.execute('RunInstances',
{'ImageId': fakes.ID_EC2_IMAGE_1,
'MinCount': '0', 'MaxCount': '1'})
self.assertEqual(400, resp['http_status_code'])
self.assertEqual('InvalidParameterValue', resp['Error']['Code'])
resp = self.execute('RunInstances',
{'ImageId': fakes.ID_EC2_IMAGE_1,
'MinCount': '2', 'MaxCount': '1'})
self.assertEqual(400, resp['http_status_code'])
self.assertEqual('InvalidParameterValue', resp['Error']['Code'])
@mock.patch.object(fakes.OSInstance, 'delete', autospec=True)
@mock.patch.object(fakes.OSInstance, 'get', autospec=True)
def test_terminate_instances(self, os_instance_get, os_instance_delete):
"""Terminate 2 instances in one request."""
instance_api.instance_engine = (
instance_api.InstanceEngineNeutron())
self.db_api.get_items_by_ids.return_value = [fakes.DB_INSTANCE_1,
fakes.DB_INSTANCE_2]
self.nova_servers.get.side_effect = [fakes.OS_INSTANCE_1,
fakes.OS_INSTANCE_2]
self.db_api.get_items.side_effect = fakes.get_db_api_get_items(
{'eni': [fakes.DB_NETWORK_INTERFACE_1,
fakes.DB_NETWORK_INTERFACE_2],
'eipalloc': [fakes.DB_ADDRESS_1, fakes.DB_ADDRESS_2]})
resp = self.execute('TerminateInstances',
{'InstanceId.1': fakes.ID_EC2_INSTANCE_1,
'InstanceId.2': fakes.ID_EC2_INSTANCE_2})
self.assertEqual(200, resp['http_status_code'])
resp.pop('http_status_code')
fake_state_change = {'previousState': {'code': 0,
'name': 'pending'},
'currentState': {'code': 0,
'name': 'pending'}}
self.assertThat(
resp,
matchers.DictMatches(
{'instancesSet': [
tools.update_dict({'instanceId': fakes.ID_EC2_INSTANCE_1},
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, 'i',
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)
self.assertEqual(
0, self.address_api.dissassociate_address_item.call_count)
self.assertEqual(2, self.db_api.delete_item.call_count)
for inst_id in (fakes.ID_EC2_INSTANCE_1, fakes.ID_EC2_INSTANCE_2):
self.db_api.delete_item.assert_any_call(mock.ANY, inst_id)
self.assertEqual(2, os_instance_delete.call_count)
self.assertEqual(2, os_instance_get.call_count)
for call_num, inst_id in enumerate([fakes.OS_INSTANCE_1,
fakes.OS_INSTANCE_2]):
self.assertEqual(mock.call(inst_id),
os_instance_delete.call_args_list[call_num])
self.assertEqual(mock.call(inst_id),
os_instance_get.call_args_list[call_num])
def test_terminate_instances_multiple_networks(self):
"""Terminate an instance with various combinations of ports."""
self._build_multiple_data_model()
fake_state_change = {'previousState': {'code': 16,
'name': 'running'},
'currentState': {'code': 16,
'name': 'running'}}
ec2_terminate_instances_result = {
'instancesSet': [
tools.update_dict({'instanceId': fakes.ID_EC2_INSTANCE_1},
fake_state_change),
tools.update_dict({'instanceId': fakes.ID_EC2_INSTANCE_2},
fake_state_change)]}
self.db_api.get_items_by_ids.return_value = self.DB_INSTANCES
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=[]):
self.db_api.get_items.side_effect = fakes.get_db_api_get_items({
'eni': copy.deepcopy(mock_eni_list) + [self.DB_FAKE_ENI],
'eipalloc': []})
resp = self.execute('TerminateInstances',
{'InstanceId.1': fakes.ID_EC2_INSTANCE_1,
'InstanceId.2': fakes.ID_EC2_INSTANCE_2})
self.assertEqual(200, resp['http_status_code'])
resp.pop('http_status_code')
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.assertEqual(len(deleted_enis) + 2,
self.db_api.delete_item.call_count)
for eni in deleted_enis:
self.db_api.delete_item.assert_any_call(mock.ANY, eni['id'])
for inst_id in (fakes.ID_EC2_INSTANCE_1, fakes.ID_EC2_INSTANCE_2):
self.db_api.delete_item.assert_any_call(mock.ANY, inst_id)
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]])
# 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=[])
def test_terminate_instances_invalid_parameters(self):
resp = self.execute('TerminateInstances',
{'InstanceId.1': fakes.random_ec2_id('i')})
self.assertEqual(400, resp['http_status_code'])
self.assertEqual('InvalidInstanceID.NotFound', resp['Error']['Code'])
@mock.patch('ec2api.api.instance._get_os_instances_by_instances')
def _test_instances_operation(self, operation, os_instance_operation,
valid_state, invalid_state,
get_os_instances_by_instances):
os_instance_1 = copy.deepcopy(fakes.OS_INSTANCE_1)
os_instance_2 = copy.deepcopy(fakes.OS_INSTANCE_2)
for inst in (os_instance_1, os_instance_2):
setattr(inst, 'OS-EXT-STS:vm_state', valid_state)
self.db_api.get_items_by_ids.return_value = [fakes.DB_INSTANCE_1,
fakes.DB_INSTANCE_2]
get_os_instances_by_instances.return_value = [os_instance_1,
os_instance_2]
resp = self.execute(operation,
{'InstanceId.1': fakes.ID_EC2_INSTANCE_1,
'InstanceId.2': fakes.ID_EC2_INSTANCE_2})
self.assertEqual({'http_status_code': 200,
'return': True},
resp)
self.assertEqual([mock.call(os_instance_1), mock.call(os_instance_2)],
os_instance_operation.mock_calls)
self.db_api.get_items_by_ids.assert_called_once_with(
mock.ANY, 'i', set([fakes.ID_EC2_INSTANCE_1,
fakes.ID_EC2_INSTANCE_2]))
get_os_instances_by_instances.assert_called_once_with(
mock.ANY, [fakes.DB_INSTANCE_1, fakes.DB_INSTANCE_2], exactly=True)
setattr(os_instance_2, 'OS-EXT-STS:vm_state', invalid_state)
os_instance_operation.reset_mock()
resp = self.execute('StartInstances',
{'InstanceId.1': fakes.ID_EC2_INSTANCE_1,
'InstanceId.2': fakes.ID_EC2_INSTANCE_2})
self.assertEqual(400, resp['http_status_code'])
self.assertEqual('IncorrectInstanceState', resp['Error']['Code'])
self.assertEqual(0, os_instance_operation.call_count)
@mock.patch.object(fakes.OSInstance, 'start', autospec=True)
def test_start_instances(self, os_instance_start):
self._test_instances_operation('StartInstances', os_instance_start,
instance_api.vm_states_STOPPED,
instance_api.vm_states_ACTIVE)
@mock.patch.object(fakes.OSInstance, 'stop', autospec=True)
def test_stop_instances(self, os_instance_stop):
self._test_instances_operation('StopInstances', os_instance_stop,
instance_api.vm_states_ACTIVE,
instance_api.vm_states_STOPPED)
@mock.patch.object(fakes.OSInstance, 'reboot', autospec=True)
def test_reboot_instances(self, os_instance_reboot):
self._test_instances_operation('RebootInstances', os_instance_reboot,
instance_api.vm_states_ACTIVE,
instance_api.vm_states_BUILDING)
@mock.patch('ec2api.openstack.common.timeutils.utcnow')
def _test_instance_get_operation(self, operation, getter, key, utcnow):
self.db_api.get_item_by_id.return_value = fakes.DB_INSTANCE_2
self.nova_servers.get.return_value = fakes.OS_INSTANCE_2
getter.return_value = 'fake_data'
utcnow.return_value = datetime.datetime(2015, 1, 19, 23, 34, 45, 123)
resp = self.execute(operation,
{'InstanceId': fakes.ID_EC2_INSTANCE_2})
self.assertEqual({'http_status_code': 200,
'instanceId': fakes.ID_EC2_INSTANCE_2,
'timestamp': '2015-01-19T23:34:45.000Z',
key: 'fake_data'},
resp)
self.db_api.get_item_by_id.assert_called_once_with(
mock.ANY, 'i', fakes.ID_EC2_INSTANCE_2)
self.nova_servers.get.assert_called_once_with(fakes.ID_OS_INSTANCE_2)
getter.assert_called_once_with(fakes.OS_INSTANCE_2)
@mock.patch.object(fakes.OSInstance, 'get_password', autospec=True)
def test_get_password_data(self, get_password):
self._test_instance_get_operation('GetPasswordData',
get_password, 'passwordData')
@mock.patch.object(fakes.OSInstance, 'get_console_output', autospec=True)
def test_console_output(self, get_console_output):
self._test_instance_get_operation('GetConsoleOutput',
get_console_output, 'output')
def test_describe_instances(self):
"""Describe 2 instances, one of which is vpc instance."""
instance_api.instance_engine = (
instance_api.InstanceEngineNeutron())
self.db_api.get_items.side_effect = (
fakes.get_db_api_get_items(
{'eni': [fakes.DB_NETWORK_INTERFACE_1,
fakes.DB_NETWORK_INTERFACE_2],
'i': [fakes.DB_INSTANCE_1, fakes.DB_INSTANCE_2],
'ami': [fakes.DB_IMAGE_1, fakes.DB_IMAGE_2],
'vol': [fakes.DB_VOLUME_1, fakes.DB_VOLUME_2,
fakes.DB_VOLUME_3]}))
self.db_api.get_item_ids.side_effect = (
fakes.get_db_api_get_item_by_id({
(fakes.ID_OS_IMAGE_ARI_1,): [(fakes.ID_EC2_IMAGE_ARI_1,
fakes.ID_OS_IMAGE_ARI_1)],
(fakes.ID_OS_IMAGE_AKI_1,): [(fakes.ID_EC2_IMAGE_AKI_1,
fakes.ID_OS_IMAGE_AKI_1)]}))
self.nova_servers.list.return_value = [fakes.OS_INSTANCE_1,
fakes.OS_INSTANCE_2]
self.novadb.instance_get_by_uuid.side_effect = (
fakes.get_db_api_get_items({
fakes.ID_OS_INSTANCE_1: fakes.NOVADB_INSTANCE_1,
fakes.ID_OS_INSTANCE_2: fakes.NOVADB_INSTANCE_2}))
self.novadb.block_device_mapping_get_all_by_instance.side_effect = (
fakes.get_db_api_get_items({
fakes.ID_OS_INSTANCE_1: fakes.NOVADB_BDM_INSTANCE_1,
fakes.ID_OS_INSTANCE_2: fakes.NOVADB_BDM_INSTANCE_2}))
self.network_interface_api.describe_network_interfaces.side_effect = (
lambda *args, **kwargs: copy.deepcopy({
'networkInterfaceSet': [fakes.EC2_NETWORK_INTERFACE_1,
fakes.EC2_NETWORK_INTERFACE_2]}))
resp = self.execute('DescribeInstances', {})
self.assertEqual(200, resp['http_status_code'])
resp.pop('http_status_code')
self.assertThat(resp, matchers.DictMatches(
{'reservationSet': [fakes.EC2_RESERVATION_1,
fakes.EC2_RESERVATION_2]},
orderless_lists=True))
resp = self.execute('DescribeInstances',
{'Filter.1.Name': 'key-name',
'Filter.1.Value.1': 'a',
'Filter.1.Value.2': 'b',
'Filter.2.Name': 'client-token',
'Filter.2.Value.1': 'a string'})
self.assertEqual({'http_status_code': 200,
'reservationSet': []},
resp)
self.db_api.get_items_by_ids.return_value = [fakes.DB_INSTANCE_2]
resp = self.execute('DescribeInstances', {'InstanceId.1':
fakes.ID_EC2_INSTANCE_2})
self.assertEqual(200, resp['http_status_code'])
resp.pop('http_status_code')
self.assertThat(resp, matchers.DictMatches(
{'reservationSet': [fakes.EC2_RESERVATION_2]},
orderless_lists=True))
def test_describe_instances_ec2_classic(self):
instance_api.instance_engine = (
instance_api.InstanceEngineNova())
self.db_api.get_items.side_effect = (
fakes.get_db_api_get_items(
{'i': [fakes.DB_INSTANCE_2],
'ami': [fakes.DB_IMAGE_1, fakes.DB_IMAGE_2],
'vol': [fakes.DB_VOLUME_1, fakes.DB_VOLUME_2,
fakes.DB_VOLUME_3]}))
self.nova_servers.list.return_value = [fakes.OS_INSTANCE_2]
self.novadb.instance_get_by_uuid.return_value = (
fakes.NOVADB_INSTANCE_2)
self.novadb.block_device_mapping_get_all_by_instance.return_value = (
fakes.NOVADB_BDM_INSTANCE_2)
resp = self.execute('DescribeInstances', {})
self.assertEqual(200, resp['http_status_code'])
resp.pop('http_status_code')
self.assertThat(resp, matchers.DictMatches(
{'reservationSet': [fakes.EC2_RESERVATION_2]},
orderless_lists=True))
def test_describe_instances_mutliple_networks(self):
"""Describe 2 instances with various combinations of network."""
instance_api.instance_engine = (
instance_api.InstanceEngineNeutron())
self._build_multiple_data_model()
self.db_api.get_items.side_effect = (
fakes.get_db_api_get_items(
{'i': self.DB_INSTANCES,
'ami': [],
'vol': []}))
self.novadb.instance_get_by_uuid.side_effect = (
fakes.get_db_api_get_items(
dict((os_id, novadb_instance)
for os_id, novadb_instance in zip(
self.IDS_OS_INSTANCE,
self.NOVADB_INSTANCES))))
describe_network_interfaces = (
self.network_interface_api.describe_network_interfaces)
def do_check(ips_by_instance=[], ec2_enis_by_instance=[],
ec2_instance_ips=[]):
describe_network_interfaces.return_value = copy.deepcopy(
{'networkInterfaceSet': list(
itertools.chain(*ec2_enis_by_instance))})
self.nova_servers.list.return_value = [
fakes.OSInstance(
os_id, {'id': 'fakeFlavorId'},
addresses=dict((subnet_name,
[{'addr': addr,
'version': 4,
'OS-EXT-IPS:type': 'fixed'}])
for subnet_name, addr in ips))
for os_id, ips in zip(
self.IDS_OS_INSTANCE,
ips_by_instance)]
resp = self.execute('DescribeInstances', {})
self.assertEqual(200, resp['http_status_code'])
resp.pop('http_status_code')
instances = [fakes.gen_ec2_instance(
inst_id, launch_index=l_i, private_ip_address=ip,
ec2_network_interfaces=enis)
for l_i, (inst_id, ip, enis) in enumerate(zip(
self.IDS_EC2_INSTANCE,
ec2_instance_ips,
ec2_enis_by_instance))]
reservation_set = [fakes.gen_ec2_reservation(
fakes.ID_EC2_RESERVATION_1, instances)]
self.assertThat({'reservationSet': reservation_set},
matchers.DictMatches(resp, orderless_lists=True),
verbose=True)
def ip_info(ind):
return (self.EC2_ATTACHED_ENIS[ind]['subnetId'],
self.EC2_ATTACHED_ENIS[ind]['privateIpAddress'])
# 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(
ips_by_instance=[[ip_info(0), ip_info(1)], [ip_info(3)]],
ec2_enis_by_instance=[
[self.EC2_ATTACHED_ENIS[0], self.EC2_ATTACHED_ENIS[1]],
[]],
ec2_instance_ips=[fakes.IP_FIRST_SUBNET_1, fakes.IP_LAST_SUBNET_2])
# 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(
ips_by_instance=[[ip_info(0), ip_info(1)], [ip_info(3)]],
ec2_enis_by_instance=[[self.EC2_ATTACHED_ENIS[1]], []],
ec2_instance_ips=[fakes.IP_FIRST_SUBNET_2, fakes.IP_LAST_SUBNET_2])
@mock.patch('ec2api.api.instance._remove_instances')
def test_describe_instances_auto_remove(self, remove_instances):
instance_api.instance_engine = (
instance_api.InstanceEngineNova())
self.db_api.get_items.side_effect = (
fakes.get_db_api_get_items(
{'i': [fakes.DB_INSTANCE_1, fakes.DB_INSTANCE_2],
'ami': [],
'vol': [fakes.DB_VOLUME_2]}))
self.nova_servers.list.return_value = [fakes.OS_INSTANCE_2]
self.novadb.instance_get_by_uuid.return_value = (
fakes.NOVADB_INSTANCE_2)
self.novadb.block_device_mapping_get_all_by_instance.return_value = (
fakes.NOVADB_BDM_INSTANCE_2)
resp = self.execute('DescribeInstances', {})
self.assertThat(resp,
matchers.DictMatches(
{'http_status_code': 200,
'reservationSet': [fakes.EC2_RESERVATION_2]},
orderless_lists=True))
remove_instances.assert_called_once_with(
mock.ANY, [fakes.DB_INSTANCE_1])
def test_describe_instances_invalid_parameters(self):
resp = self.execute('DescribeInstances', {'InstanceId.1':
fakes.random_ec2_id('i')})
self.assertEqual(400, resp['http_status_code'])
self.assertEqual('InvalidInstanceID.NotFound', resp['Error']['Code'])
self.db_api.get_items_by_ids.return_value = [fakes.DB_INSTANCE_2]
resp = self.execute('DescribeInstances',
{'InstanceId.1': fakes.ID_EC2_INSTANCE_2,
'InstanceId.2': fakes.random_ec2_id('i')})
self.assertEqual(400, resp['http_status_code'])
self.assertEqual('InvalidInstanceID.NotFound', resp['Error']['Code'])
def test_describe_instance_attributes(self):
self.db_api.get_item_by_id.side_effect = (
fakes.get_db_api_get_item_by_id({
fakes.ID_EC2_INSTANCE_1: fakes.DB_INSTANCE_1,
fakes.ID_EC2_INSTANCE_2: fakes.DB_INSTANCE_2}))
self.db_api.get_item_ids.side_effect = (
fakes.get_db_api_get_item_by_id({
(fakes.ID_OS_IMAGE_AKI_1,): [(fakes.ID_EC2_IMAGE_AKI_1,
fakes.ID_OS_IMAGE_AKI_1)],
(fakes.ID_OS_IMAGE_ARI_1,): [(fakes.ID_EC2_IMAGE_ARI_1,
fakes.ID_OS_IMAGE_ARI_1)]}))
self.nova_servers.get.side_effect = (
fakes.get_by_1st_arg_getter({
fakes.ID_OS_INSTANCE_1: fakes.OS_INSTANCE_1,
fakes.ID_OS_INSTANCE_2: fakes.OS_INSTANCE_2}))
self.novadb.instance_get_by_uuid.side_effect = (
fakes.get_db_api_get_items({
fakes.ID_OS_INSTANCE_1: fakes.NOVADB_INSTANCE_1,
fakes.ID_OS_INSTANCE_2: fakes.NOVADB_INSTANCE_2}))
self.novadb.block_device_mapping_get_all_by_instance.side_effect = (
fakes.get_db_api_get_items({
fakes.ID_OS_INSTANCE_1: fakes.NOVADB_BDM_INSTANCE_1,
fakes.ID_OS_INSTANCE_2: fakes.NOVADB_BDM_INSTANCE_2}))
self.cinder.volumes.get.return_value = fakes.CinderVolume(
fakes.OS_VOLUME_2)
self.db_api.get_items.return_value = [fakes.DB_VOLUME_2]
def do_check(instance_id, attribute, expected):
resp = self.execute('DescribeInstanceAttribute',
{'InstanceId': instance_id,
'Attribute': attribute})
expected.update({'http_status_code': 200,
'instanceId': instance_id})
self.assertThat(resp, matchers.DictMatches(expected))
do_check(fakes.ID_EC2_INSTANCE_2, 'blockDeviceMapping',
{'rootDeviceType': 'ebs',
'blockDeviceMapping': (
fakes.EC2_INSTANCE_2['blockDeviceMapping'])})
do_check(fakes.ID_EC2_INSTANCE_2, 'disableApiTermination',
{'disableApiTermination': {'value': False}})
do_check(fakes.ID_EC2_INSTANCE_2, 'groupSet',
{'groupSet': fakes.EC2_RESERVATION_2['groupSet']})
do_check(fakes.ID_EC2_INSTANCE_2, 'instanceInitiatedShutdownBehavior',
{'instanceInitiatedShutdownBehavior': {'value': 'stop'}})
do_check(fakes.ID_EC2_INSTANCE_2, 'instanceType',
{'instanceType': {'value': 'fake_flavor'}})
do_check(fakes.ID_EC2_INSTANCE_1, 'kernel',
{'kernel': {'value': fakes.ID_EC2_IMAGE_AKI_1}})
do_check(fakes.ID_EC2_INSTANCE_1, 'ramdisk',
{'ramdisk': {'value': fakes.ID_EC2_IMAGE_ARI_1}})
do_check(fakes.ID_EC2_INSTANCE_2, 'rootDeviceName',
{'rootDeviceName': {
'value': fakes.ROOT_DEVICE_NAME_INSTANCE_2}})
do_check(fakes.ID_EC2_INSTANCE_2, 'userData',
{'userData': {'value': fakes.USER_DATA_INSTANCE_2}})
def _build_multiple_data_model(self):
# NOTE(ft): generate necessary fake data
# We need 4 detached ports in 2 subnets.
# Sequence of all ports list is s1i1, s2i1, s1i2, s2i2,
# where sNiM - port info of instance iM on subnet sN.
# We generate port ids but use subnet and instance ids since
# fakes contain enough ids for subnets an instances, but not for ports.
instances_count = 2
subnets_count = 2
ports_count = instances_count * subnets_count
ids_ec2_eni = [fakes.random_ec2_id('eni') for _ in range(ports_count)]
ids_os_port = [fakes.random_os_id() for _ in range(ports_count)]
ids_ec2_subnet = (fakes.ID_EC2_SUBNET_1, fakes.ID_EC2_SUBNET_2)
ids_ec2_subnet_by_port = ids_ec2_subnet * 2
ips = (fakes.IP_FIRST_SUBNET_1, fakes.IP_FIRST_SUBNET_2,
fakes.IP_LAST_SUBNET_1, fakes.IP_LAST_SUBNET_2)
ids_ec2_instance = [fakes.ID_EC2_INSTANCE_1, fakes.ID_EC2_INSTANCE_2]
ids_ec2_instance_by_port = list(
itertools.chain(*map(lambda i: [i] * subnets_count,
ids_ec2_instance)))
ids_os_instance = [fakes.ID_OS_INSTANCE_1, fakes.ID_OS_INSTANCE_2]
dots_by_port = [True, False] * instances_count
db_attached_enis = [
fakes.gen_db_network_interface(
ec2_id, os_id, fakes.ID_EC2_VPC_1,
subnet_ec2_id, ip,
instance_id=instance_ec2_id,
delete_on_termination=dot)
for ec2_id, os_id, subnet_ec2_id, ip, instance_ec2_id, dot in zip(
ids_ec2_eni,
ids_os_port,
ids_ec2_subnet_by_port,
ips,
ids_ec2_instance_by_port,
dots_by_port)]
db_detached_enis = [
fakes.gen_db_network_interface(
ec2_id, os_id, fakes.ID_EC2_VPC_1,
subnet_ec2_id, ip)
for ec2_id, os_id, subnet_ec2_id, ip in zip(
ids_ec2_eni,
ids_os_port,
ids_ec2_subnet_by_port,
ips)]
ec2_attached_enis = [
fakes.gen_ec2_network_interface(
db_eni['id'],
None, # ec2_subnet
[db_eni['private_ip_address']],
ec2_instance_id=ec2_instance_id,
delete_on_termination=dot,
ec2_subnet_id=ec2_subnet_id,
ec2_vpc_id=fakes.ID_EC2_VPC_1)
for db_eni, dot, ec2_subnet_id, ec2_instance_id in zip(
db_attached_enis,
dots_by_port,
ids_ec2_subnet_by_port,
ids_ec2_instance_by_port)]
ec2_detached_enis = [
fakes.gen_ec2_network_interface(
db_eni['id'],
None, # ec2_subnet
[db_eni['private_ip_address']],
ec2_subnet_id=ec2_subnet_id,
ec2_vpc_id=fakes.ID_EC2_VPC_1)
for db_eni, ec2_subnet_id in zip(
db_detached_enis,
ids_ec2_subnet_by_port)]
db_instances = [
{'id': db_id,
'os_id': os_id,
'vpc_id': fakes.ID_EC2_VPC_1,
'reservation_id': fakes.ID_EC2_RESERVATION_1,
'launch_index': l_i}
for l_i, (db_id, os_id) in enumerate(zip(
ids_ec2_instance,
ids_os_instance))]
novadb_instances = [
{'kernel_id': None,
'ramdisk_id': None,
'root_device_name': '/dev/vda',
'hostname': ec2_id}
for ec2_id in ids_ec2_instance]
self.IDS_EC2_SUBNET = ids_ec2_subnet
self.IDS_OS_PORT = ids_os_port
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
self.NOVADB_INSTANCES = novadb_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,
# format_network_interfaces, get_os_instances_by_instances, remove_instances,
# format_reservation, _is_ebs_instance
class InstancePrivateTestCase(test_base.BaseTestCase):
@mock.patch('glanceclient.client.Client')
@mock.patch('ec2api.db.api.IMPL')
def test_parse_image_parameters(self, db_api, glance):
fake_context = mock.Mock(service_catalog=[{'type': 'fake'}])
image_id = fakes.random_ec2_id('ami')
os_image_id = fakes.random_os_id()
db_api.get_public_items.return_value = [{'id': image_id,
'os_id': os_image_id}]
os_image = fakes.OSImage({
'id': fakes.random_os_id(),
'owner': fakes.ID_OS_PROJECT,
'is_public': True,
'status': None,
'container_format': 'ami',
'name': 'fake_name',
'properties': {}})
glance.return_value.images.get.return_value = os_image
self.assertRaises(exception.ImageNotActive,
instance_api._parse_image_parameters,
fake_context, image_id, None, None)
os_image.status = 'active'
os_image.properties['image_state'] = 'decrypting'
self.assertRaises(exception.ImageNotActive,
instance_api._parse_image_parameters,
fake_context, image_id, None, None)
@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 = fakes.get_db_api_get_item_by_id({
fakes.ID_EC2_VOLUME_1: fakes.DB_VOLUME_1,
fakes.ID_EC2_VOLUME_2: fakes.DB_VOLUME_2,
fakes.ID_EC2_VOLUME_3: fakes.DB_VOLUME_3,
fakes.ID_EC2_SNAPSHOT_1: fakes.DB_SNAPSHOT_1,
fakes.ID_EC2_SNAPSHOT_2: fakes.DB_SNAPSHOT_2})
res = instance_api._parse_block_device_mapping(
fake_context, [], os_image)
self.assertEqual([], res)
res = instance_api._parse_block_device_mapping(
fake_context, [{'device_name': '/dev/vdf',
'ebs': {'snapshot_id': fakes.ID_EC2_SNAPSHOT_1}},
{'device_name': '/dev/vdg',
'ebs': {'snapshot_id': fakes.ID_EC2_SNAPSHOT_2,
'volume_size': 111,
'delete_on_termination': False}},
{'device_name': '/dev/vdh',
'ebs': {'snapshot_id': fakes.ID_EC2_VOLUME_1}},
{'device_name': '/dev/vdi',
'ebs': {'snapshot_id': fakes.ID_EC2_VOLUME_2,
'delete_on_termination': True}},
{'device_name': '/dev/sdb1',
'ebs': {'volume_size': 55}}],
os_image)
self.assertThat(
res,
matchers.ListMatches([{'device_name': '/dev/vdf',
'snapshot_id': fakes.ID_OS_SNAPSHOT_1,
'delete_on_termination': True},
{'device_name': '/dev/vdg',
'snapshot_id': fakes.ID_OS_SNAPSHOT_2,
'volume_size': 111,
'delete_on_termination': False},
{'device_name': '/dev/vdh',
'volume_id': fakes.ID_OS_VOLUME_1,
'delete_on_termination': True},
{'device_name': '/dev/vdi',
'volume_id': fakes.ID_OS_VOLUME_2,
'delete_on_termination': True},
{'device_name': '/dev/sdb1',
'snapshot_id': fakes.ID_OS_SNAPSHOT_1,
'volume_size': 55,
'volume_id': None,
'delete_on_termination': None,
'virtual_name': None,
'no_device': None}],
orderless_lists=True))
@mock.patch('ec2api.api.instance.novadb')
@mock.patch('novaclient.v1_1.client.Client')
@mock.patch('ec2api.db.api.IMPL')
def test_format_instance(self, db_api, nova, novadb):
fake_context = mock.Mock(service_catalog=[{'type': 'fake'}])
fake_flavor = mock.Mock()
fake_flavor.configure_mock(name='fake_flavor')
nova.return_value.flavors.get.return_value = fake_flavor
instance = {'id': fakes.random_ec2_id('i'),
'os_id': fakes.random_os_id(),
'launch_index': 0}
os_instance = fakes.OSInstance(instance['os_id'],
flavor={'id': 'fakeFlavorId'})
novadb_instance = {'kernel_id': None,
'ramdisk_id': None,
'hostname': instance['id']}
setattr(os_instance, 'OS-EXT-STS:vm_state',
instance_api.vm_states_ACTIVE)
formatted_instance = instance_api._format_instance(
fake_context, instance, os_instance, novadb_instance, [], {})
self.assertEqual({'name': instance_api.inst_state_RUNNING,
'code': instance_api.inst_state_RUNNING_CODE},
formatted_instance['instanceState'])
setattr(os_instance, 'OS-EXT-STS:vm_state',
instance_api.vm_states_STOPPED)
formatted_instance = instance_api._format_instance(
fake_context, instance, os_instance, novadb_instance, [], {})
self.assertEqual({'name': instance_api.inst_state_STOPPED,
'code': instance_api.inst_state_STOPPED_CODE},
formatted_instance['instanceState'])
os_instance.image = {'id': fakes.random_os_id()}
formatted_instance = instance_api._format_instance(
fake_context, instance, os_instance, novadb_instance, [], {})
db_api.add_item_id.assert_called_once_with(
mock.ANY, 'ami', os_instance.image['id'])
@mock.patch('cinderclient.v1.client.Client')
@mock.patch('ec2api.api.instance.novadb')
def test_format_instance_bdm(self, novadb, cinder):
cinder.return_value.volumes.get.return_value = (
mock.Mock(status='attached', attachments={'device': 'fake'}))
id_os_instance_1 = fakes.random_os_id()
id_os_instance_2 = fakes.random_os_id()
novadb.block_device_mapping_get_all_by_instance.side_effect = (
fakes.get_db_api_get_items({
id_os_instance_1: [{'device_name': '/dev/sdb1',
'delete_on_termination': False,
'snapshot_id': '1',
'volume_id': '2',
'no_device': False},
{'device_name': '/dev/sdb2',
'delete_on_termination': False,
'snapshot_id': None,
'volume_id': '3',
'volume_size': 1,
'no_device': False},
{'device_name': '/dev/sdb3',
'delete_on_termination': True,
'snapshot_id': '4',
'volume_id': '5',
'no_device': False},
{'device_name': '/dev/sdb4',
'delete_on_termination': False,
'snapshot_id': '6',
'volume_id': '7',
'no_device': False},
{'device_name': '/dev/sdb5',
'delete_on_termination': False,
'snapshot_id': '8',
'volume_id': '9',
'volume_size': 0,
'no_device': False},
{'device_name': '/dev/sdb6',
'delete_on_termination': False,
'snapshot_id': '10',
'volume_id': '11',
'volume_size': 1,
'no_device': False},
{'device_name': '/dev/sdb7',
'snapshot_id': None,
'volume_id': None,
'no_device': True},
{'device_name': '/dev/sdb8',
'snapshot_id': None,
'volume_id': None,
'virtual_name': 'swap',
'no_device': False},
{'device_name': '/dev/sdb9',
'snapshot_id': None,
'volume_id': None,
'virtual_name': 'ephemeral3',
'no_device': False}],
id_os_instance_2: [{'device_name': 'vda',
'delete_on_termination': False,
'snapshot_id': '1',
'volume_id': '21',
'no_device': False}]
}))
db_volumes_1 = {'2': {'id': 'vol-00000002'},
'3': {'id': 'vol-00000003'},
'5': {'id': 'vol-00000005'},
'7': {'id': 'vol-00000007'},
'9': {'id': 'vol-00000009'},
'11': {'id': 'vol-0000000b'}}
fake_context = mock.Mock(service_catalog=[{'type': 'fake'}])
result = {}
instance_api._cloud_format_instance_bdm(
fake_context, id_os_instance_1, '/dev/sdb1', result, db_volumes_1)
self.assertThat(
result,
matchers.DictMatches({
'rootDeviceType': 'ebs',
'blockDeviceMapping': [
{'deviceName': '/dev/sdb1',
'ebs': {'status': 'attached',
'deleteOnTermination': False,
'volumeId': 'vol-00000002',
'attachTime': '',
}},
{'deviceName': '/dev/sdb2',
'ebs': {'status': 'attached',
'deleteOnTermination': False,
'volumeId': 'vol-00000003',
'attachTime': '',
}},
{'deviceName': '/dev/sdb3',
'ebs': {'status': 'attached',
'deleteOnTermination': True,
'volumeId': 'vol-00000005',
'attachTime': '',
}},
{'deviceName': '/dev/sdb4',
'ebs': {'status': 'attached',
'deleteOnTermination': False,
'volumeId': 'vol-00000007',
'attachTime': '',
}},
{'deviceName': '/dev/sdb5',
'ebs': {'status': 'attached',
'deleteOnTermination': False,
'volumeId': 'vol-00000009',
'attachTime': '',
}},
{'deviceName': '/dev/sdb6',
'ebs': {'status': 'attached',
'deleteOnTermination': False,
'volumeId': 'vol-0000000b',
'attachTime': '', }}]},
orderless_lists=True))
result = {}
with mock.patch('ec2api.db.api.IMPL') as db_api:
db_api.get_items.return_value = [{'id': 'vol-00000015',
'os_id': '21'}]
instance_api._cloud_format_instance_bdm(
fake_context, id_os_instance_2, '/dev/sdc1', result)
self.assertThat(
result,
matchers.DictMatches({
'rootDeviceType': 'instance-store',
'blockDeviceMapping': [
{'deviceName': 'vda',
'ebs': {'status': 'attached',
'deleteOnTermination': False,
'volumeId': 'vol-00000015',
'attachTime': '', }}]}))
@mock.patch('cinderclient.v1.client.Client')
@mock.patch('ec2api.api.instance.novadb')
def test_format_instance_bdm_while_attaching_volume(self, novadb, cinder):
cinder.return_value.volumes.get.return_value = (
mock.Mock(status='attaching'))
id_os_instance = fakes.random_os_id()
novadb.block_device_mapping_get_all_by_instance.return_value = (
[{'device_name': '/dev/sdb1',
'delete_on_termination': False,
'snapshot_id': '1',
'volume_id': '2',
'no_device': False}])
fake_context = mock.Mock(service_catalog=[{'type': 'fake'}])
result = {}
instance_api._cloud_format_instance_bdm(
fake_context, id_os_instance, '/dev/vda', result,
{'2': {'id': 'vol-00000002'}})
self.assertThat(
result,
matchers.DictMatches({
'rootDeviceType': 'instance-store',
'blockDeviceMapping': [
{'deviceName': '/dev/sdb1',
'ebs': {'status': 'attaching',
'deleteOnTermination': False,
'volumeId': 'vol-00000002',
'attachTime': '', }}]}))
def test_block_device_strip_dev(self):
self.assertEqual(
instance_api._block_device_strip_dev('/dev/sda'), 'sda')
self.assertEqual(instance_api._block_device_strip_dev('sda'), 'sda')
def test_block_device_prepend_dev(self):
mapping = ['/dev/sda', 'sdb', 'sdc', 'sdd', 'sde']
expected = ['/dev/sda', '/dev/sdb', '/dev/sdc', '/dev/sdd', '/dev/sde']
for m, e in zip(mapping, expected):
prepended = instance_api._block_device_prepend_dev(m)
self.assertEqual(e, prepended)