omni/nova/tests/unit/virt/ec2/test_ec2.py

547 lines
23 KiB
Python

"""
Copyright 2016 Platform9 Systems Inc.
All Rights Reserved.
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 base64
import contextlib
import boto
import mock
from moto import mock_cloudwatch
from moto import mock_ec2_deprecated
from oslo_utils import uuidutils
from nova.compute import task_states
from nova import context
from nova import exception
from nova.image.glance import GlanceImageServiceV2
from nova import objects
from nova import test
from nova.tests.unit import fake_instance
from nova.tests.unit import matchers
from nova.virt.ec2 import EC2Driver
class EC2DriverTestCase(test.NoDBTestCase):
@mock_ec2_deprecated
@mock_cloudwatch
def setUp(self):
super(EC2DriverTestCase, self).setUp()
self.fake_access_key = 'aws_access_key'
self.fake_secret_key = 'aws_secret_key'
self.region_name = 'us-east-1'
self.region = boto.ec2.get_region(self.region_name)
self.flags(access_key=self.fake_access_key,
secret_key=self.fake_secret_key,
# Region name cannot be fake
region_name=self.region_name,
group='AWS')
self.flags(api_servers=['http://localhost:9292'], group='glance')
self.flags(rabbit_port='5672')
self.conn = EC2Driver(None, False)
self.type_data = None
self.project_id = 'fake'
self.user_id = 'fake'
self.instance_node = None
self.uuid = None
self.instance = None
self.context = context.RequestContext(self.user_id, self.project_id)
self.fake_vpc_conn = boto.connect_vpc(
aws_access_key_id=self.fake_access_key,
aws_secret_access_key=self.fake_secret_key)
self.fake_ec2_conn = boto.ec2.EC2Connection(
aws_access_key_id=self.fake_access_key,
aws_secret_access_key=self.fake_secret_key,
region=self.region)
def tearDown(self):
super(EC2DriverTestCase, self).tearDown()
def reset(self):
instance_list = self.conn.ec2_conn.get_only_instances()
# terminated instances are considered deleted and hence ignore them
instance_id_list = [
x.id for x in instance_list if x.state != 'terminated'
]
if len(instance_id_list) > 0:
self.conn.ec2_conn.stop_instances(
instance_ids=instance_id_list, force=True)
self.conn.ec2_conn.terminate_instances(
instance_ids=instance_id_list)
self.type_data = None
self.instance = None
self.uuid = None
self.instance_node = None
def _get_instance_flavor_details(self):
return {'memory_mb': 2048.0,
'root_gb': 0,
'deleted_at': None,
'name': 't2.small',
'deleted': 0,
'created_at': None,
'ephemeral_gb': 0,
'updated_at': None,
'disabled': False,
'vcpus': 1,
'extra_specs': {},
'swap': 0,
'rxtx_factor': 1.0,
'is_public': True,
'flavorid': '1',
'vcpu_weight': None,
'id': 2}
def _create_instance(self, key_name=None, key_data=None, user_data=None):
uuid = uuidutils.generate_uuid()
self.type_data = self._get_instance_flavor_details()
values = {'name': 'fake_instance',
'id': 1,
'uuid': uuid,
'project_id': self.project_id,
'user_id': self.user_id,
'kernel_id': 'fake_kernel_id',
'ramdisk_id': 'fake_ramdisk_id',
'flavor': objects.flavor.Flavor(**self.type_data),
'node': 'fake_node',
'memory_mb': self.type_data['memory_mb'],
'root_gb': self.type_data['root_gb'],
'ephemeral_gb': self.type_data['ephemeral_gb'],
'vpcus': self.type_data['vcpus'],
'swap': self.type_data['swap'],
'expected_attrs': ['system_metadata', 'metadata'],
'display_name': 'fake_instance'}
if key_name and key_data:
values['key_name'] = key_name
values['key_data'] = key_data
if user_data:
values['user_data'] = user_data
self.instance_node = 'fake_node'
self.uuid = uuid
self.instance = fake_instance.fake_instance_obj(self.context, **values)
def _create_network(self):
self.vpc = self.fake_vpc_conn.create_vpc('192.168.100.0/24')
self.subnet = self.fake_vpc_conn.create_subnet(self.vpc.id,
'192.168.100.0/24')
self.subnet_id = self.subnet.id
def _create_nova_vm(self):
self.conn.spawn(self.context, self.instance, None, injected_files=[],
admin_password=None, network_info=None,
block_device_info=None)
@mock_ec2_deprecated
def _create_vm_in_aws_nova(self):
self._create_instance()
self._create_network()
with contextlib.nested(
mock.patch.object(EC2Driver, '_get_image_ami_id_from_meta'),
mock.patch.object(EC2Driver, '_process_network_info'),
mock.patch.object(EC2Driver, '_get_instance_sec_grps'),
) as (mock_image, mock_network, mock_secgrp):
mock_image.return_value = 'ami-1234abc'
mock_network.return_value = (self.subnet_id, '192.168.10.5', None,
None)
mock_secgrp.return_value = []
self._create_nova_vm()
@mock_ec2_deprecated
def test_list_instances(self):
for _ in range(0, 5):
self.conn.ec2_conn.run_instances('ami-1234abc')
fake_list = self.conn.list_instances()
self.assertEqual(5, len(fake_list))
self.reset()
@mock_ec2_deprecated
def test_add_ssh_keys_key_exists(self):
fake_key = 'fake_key'
fake_key_data = 'abcdefgh'
self.conn.ec2_conn.import_key_pair(fake_key, fake_key_data)
with contextlib.nested(
mock.patch.object(boto.ec2.EC2Connection, 'get_key_pair'),
mock.patch.object(boto.ec2.EC2Connection, 'import_key_pair'),
) as (fake_get, fake_import):
fake_get.return_value = True
self.conn._add_ssh_keys(fake_key, fake_key_data)
fake_get.assert_called_once_with(fake_key)
fake_import.assert_not_called()
@mock_ec2_deprecated
def test_add_ssh_keys_key_absent(self):
fake_key = 'fake_key'
fake_key_data = 'abcdefgh'
with contextlib.nested(
mock.patch.object(boto.ec2.EC2Connection, 'get_key_pair'),
mock.patch.object(boto.ec2.EC2Connection, 'import_key_pair'),
) as (fake_get, fake_import):
fake_get.return_value = False
self.conn._add_ssh_keys(fake_key, fake_key_data)
fake_get.assert_called_once_with(fake_key)
fake_import.assert_called_once_with(fake_key, fake_key_data)
def test_process_network_info(self):
fake_network_info = [{
'profile': {},
'ovs_interfaceid': None,
'preserve_on_delete': False,
'network': {
'bridge': None,
'subnets': [{
'ips': [{'meta': {},
'version': 4,
'type': 'fixed',
'floating_ips': [],
'address': u'192.168.100.5'}],
'version': 4,
'meta': {},
'dns': [],
'routes': [],
'cidr': u'192.168.100.0/24',
'gateway': {'meta': {},
'version': 4,
'type': 'gateway',
'address': u'192.168.100.1'}}],
'meta': {'injected': True,
'tenant_id': '135b1a036a51414ea1f989ab59fefde5'},
'id': '4f8ad58d-de60-4b52-94ba-8b988a9b7f33',
'label': 'test'},
'devname': 'tapa9a90cf6-62',
'vnic_type': 'normal',
'qbh_params': None,
'meta': {},
'details': '{"subnet_id": "subnet-0107db5a",'
' "ip_address": "192.168.100.5"}',
'address': 'fa:16:3e:23:65:2c',
'active': True,
'type': 'vip_type_a',
'id': 'a9a90cf6-627c-46f3-829d-c5a2ae07aaf0',
'qbg_params': None
}]
aws_subnet_id, aws_fixed_ip, port_id, network_id = \
self.conn._process_network_info(fake_network_info)
self.assertEqual(aws_subnet_id, 'subnet-0107db5a')
self.assertEqual(aws_fixed_ip, '192.168.100.5')
self.assertEqual(port_id, 'a9a90cf6-627c-46f3-829d-c5a2ae07aaf0')
self.assertEqual(network_id, '4f8ad58d-de60-4b52-94ba-8b988a9b7f33')
@mock_ec2_deprecated
def test_spawn(self):
self._create_instance()
self._create_network()
with contextlib.nested(
mock.patch.object(EC2Driver, '_get_image_ami_id_from_meta'),
mock.patch.object(EC2Driver, '_process_network_info'),
mock.patch.object(EC2Driver, '_get_instance_sec_grps'),
) as (mock_image, mock_network, mock_secgrp):
mock_image.return_value = 'ami-1234abc'
mock_network.return_value = (self.subnet_id, '192.168.10.5', None,
None)
mock_secgrp.return_value = []
self._create_nova_vm()
fake_instances = self.fake_ec2_conn.get_only_instances()
self.assertEqual(len(fake_instances), 1)
inst = fake_instances[0]
self.assertEqual(inst.vpc_id, self.vpc.id)
self.assertEqual(self.subnet_id, inst.subnet_id)
self.assertEqual(inst.tags['Name'], 'fake_instance')
self.assertEqual(inst.tags['openstack_id'], self.uuid)
self.assertEqual(inst.image_id, 'ami-1234abc')
self.assertEqual(inst.region.name, self.region_name)
self.assertEqual(inst.key_name, 'None')
self.assertEqual(inst.instance_type, 't2.small')
self.reset()
@mock_ec2_deprecated
def test_spawn_with_key(self):
self._create_instance(key_name='fake_key', key_data='fake_key_data')
self._create_network()
with contextlib.nested(
mock.patch.object(EC2Driver, '_get_image_ami_id_from_meta'),
mock.patch.object(EC2Driver, '_process_network_info'),
mock.patch.object(EC2Driver, '_get_instance_sec_grps'),
) as (mock_image, mock_network, mock_secgrp):
mock_image.return_value = 'ami-1234abc'
mock_network.return_value = (self.subnet_id, '192.168.10.5', None,
None)
mock_secgrp.return_value = []
self._create_nova_vm()
fake_instances = self.fake_ec2_conn.get_only_instances()
self.assertEqual(len(fake_instances), 1)
inst = fake_instances[0]
self.assertEqual(inst.key_name, 'fake_key')
self.reset()
@mock_ec2_deprecated
def test_spawn_with_userdata(self):
userdata = '''
#cloud-config
password: password
'''
b64encoded = base64.b64encode(userdata)
self._create_instance(user_data=b64encoded)
self._create_network()
with contextlib.nested(
mock.patch.object(EC2Driver, '_get_image_ami_id_from_meta'),
mock.patch.object(EC2Driver, '_process_network_info'),
mock.patch.object(EC2Driver, '_get_instance_sec_grps'),
) as (mock_image, mock_network, mock_secgrp):
mock_image.return_value = 'ami-1234abc'
mock_network.return_value = (self.subnet_id, '192.168.10.5', None,
None)
mock_secgrp.return_value = []
fake_run_instance_op = self.fake_ec2_conn.run_instances(
'ami-1234abc')
boto.ec2.EC2Connection.run_instances = mock.Mock()
boto.ec2.EC2Connection.run_instances.return_value = \
fake_run_instance_op
self._create_nova_vm()
fake_instances = self.fake_ec2_conn.get_only_instances()
self.assertEqual(len(fake_instances), 1)
boto.ec2.EC2Connection.run_instances.assert_called_once_with(
instance_type='t2.small', key_name=None,
image_id='ami-1234abc', user_data=userdata,
subnet_id=self.subnet_id, private_ip_address='192.168.10.5',
security_group_ids=[])
self.reset()
@mock_ec2_deprecated
def test_spawn_with_network_error(self):
self._create_instance()
with contextlib.nested(
mock.patch.object(EC2Driver, '_get_image_ami_id_from_meta'),
mock.patch.object(EC2Driver, '_process_network_info'),
mock.patch.object(EC2Driver, '_get_instance_sec_grps'),
) as (mock_image, mock_network, mock_secgrp):
mock_image.return_value = 'ami-1234abc'
mock_network.return_value = (None, None, None, None)
mock_secgrp.return_value = []
self.assertRaises(exception.BuildAbortException,
self._create_nova_vm)
self.reset()
@mock_ec2_deprecated
def test_spawn_with_network_error_from_aws(self):
self._create_instance()
with contextlib.nested(
mock.patch.object(EC2Driver, '_get_image_ami_id_from_meta'),
mock.patch.object(EC2Driver, '_process_network_info'),
mock.patch.object(EC2Driver, '_get_instance_sec_grps'),
) as (mock_image, mock_network, mock_secgrp):
mock_image.return_value = 'ami-1234abc'
mock_network.return_value = (None, '192.168.10.5', None, None)
mock_secgrp.return_value = []
self.assertRaises(exception.BuildAbortException,
self._create_nova_vm)
self.reset()
@mock_ec2_deprecated
def test_spawn_with_image_error(self):
self._create_instance()
self._create_network()
with contextlib.nested(
mock.patch.object(EC2Driver, '_get_image_ami_id_from_meta'),
mock.patch.object(EC2Driver, '_process_network_info'),
mock.patch.object(EC2Driver, '_get_instance_sec_grps'),
) as (mock_image, mock_network, mock_secgrp):
mock_image.side_effect = exception.BuildAbortException('fake')
mock_network.return_value = ('subnet-1234abc', '192.168.10.5',
None, None)
mock_secgrp.return_value = []
self.assertRaises(exception.BuildAbortException,
self._create_nova_vm)
self.reset()
@mock_ec2_deprecated
def test_snapshot(self):
self._create_vm_in_aws_nova()
GlanceImageServiceV2.update = mock.Mock()
expected_calls = [{'args': (),
'kwargs': {
'task_state': task_states.IMAGE_UPLOADING,
'expected_state': task_states.IMAGE_SNAPSHOT}}]
func_call_matcher = matchers.FunctionCallMatcher(expected_calls)
self.conn.snapshot(self.context, self.instance, 'test-snapshot',
func_call_matcher.call)
self.assertIsNone(func_call_matcher.match())
_, snapshot_name, metadata = GlanceImageServiceV2.update.call_args[0]
aws_imgs = self.fake_ec2_conn.get_all_images()
self.assertEqual(1, len(aws_imgs))
aws_img = aws_imgs[0]
self.assertEqual(snapshot_name, 'test-snapshot')
self.assertEqual(aws_img.name, 'test-snapshot')
self.assertEqual(aws_img.id, metadata['properties']['ec2_image_id'])
self.reset()
@mock_ec2_deprecated
def test_snapshot_instance_not_found(self):
boto.ec2.EC2Connection.create_image = mock.Mock()
self._create_instance()
GlanceImageServiceV2.update = mock.Mock()
expected_calls = [{'args': (),
'kwargs': {
'task_state': task_states.IMAGE_UPLOADING,
'expected_state': task_states.IMAGE_SNAPSHOT}}]
func_call_matcher = matchers.FunctionCallMatcher(expected_calls)
self.assertRaises(exception.InstanceNotFound, self.conn.snapshot,
self.context, self.instance, 'test-snapshot',
func_call_matcher.call)
boto.ec2.EC2Connection.create_image.assert_not_called()
self.reset()
@mock_ec2_deprecated
def test_reboot_soft(self):
boto.ec2.EC2Connection.reboot_instances = mock.Mock()
self._create_vm_in_aws_nova()
fake_inst = self.fake_ec2_conn.get_only_instances()[0]
self.conn.reboot(self.context, self.instance, None, 'SOFT', None, None)
boto.ec2.EC2Connection.reboot_instances.assert_called_once_with(
instance_ids=[fake_inst.id], dry_run=False)
self.reset()
@mock_ec2_deprecated
def test_reboot_hard(self):
self._create_vm_in_aws_nova()
fake_inst = self.fake_ec2_conn.get_only_instances()[0]
boto.ec2.EC2Connection.stop_instances = mock.Mock()
boto.ec2.EC2Connection.start_instances = mock.Mock()
EC2Driver._wait_for_state = mock.Mock()
self.conn.reboot(self.context, self.instance, None, 'HARD', None, None)
boto.ec2.EC2Connection.stop_instances.assert_called_once_with(
instance_ids=[fake_inst.id], force=False, dry_run=False)
boto.ec2.EC2Connection.start_instances.assert_called_once_with(
instance_ids=[fake_inst.id], dry_run=False)
wait_state_calls = EC2Driver._wait_for_state.call_args_list
self.assertEqual(2, len(wait_state_calls))
self.assertEqual('stopped', wait_state_calls[0][0][2])
self.assertEqual(fake_inst.id, wait_state_calls[0][0][1])
self.assertEqual('running', wait_state_calls[1][0][2])
self.assertEqual(fake_inst.id, wait_state_calls[0][0][1])
self.reset()
@mock_ec2_deprecated
def test_reboot_instance_not_found(self):
self._create_instance()
boto.ec2.EC2Connection.stop_instances = mock.Mock()
self.assertRaises(exception.InstanceNotFound, self.conn.reboot,
self.context, self.instance, None, 'SOFT', None,
None)
boto.ec2.EC2Connection.stop_instances.assert_not_called()
self.reset()
@mock_ec2_deprecated
def test_power_off(self):
self._create_vm_in_aws_nova()
fake_inst = self.fake_ec2_conn.get_only_instances()[0]
self.assertEqual(fake_inst.state, 'running')
self.conn.power_off(self.instance)
fake_inst = self.fake_ec2_conn.get_only_instances()[0]
self.assertEqual(fake_inst.state, 'stopped')
self.reset()
@mock_ec2_deprecated
def test_power_off_instance_not_found(self):
self._create_instance()
self.assertRaises(exception.InstanceNotFound, self.conn.power_off,
self.instance)
self.reset()
@mock_ec2_deprecated
def test_power_on(self):
self._create_vm_in_aws_nova()
fake_inst = self.fake_ec2_conn.get_only_instances()[0]
self.fake_ec2_conn.stop_instances(instance_ids=[fake_inst.id])
self.conn.power_on(self.context, self.instance, None, None)
fake_inst = self.fake_ec2_conn.get_only_instances()[0]
self.assertEqual(fake_inst.state, 'running')
self.reset()
@mock_ec2_deprecated
def test_power_on_instance_not_found(self):
self._create_instance()
self.assertRaises(exception.InstanceNotFound, self.conn.power_on,
self.context, self.instance, None, None)
self.reset()
@mock_ec2_deprecated
def test_destroy(self):
self._create_vm_in_aws_nova()
self.conn.destroy(self.context, self.instance, None, None)
fake_instance = self.fake_ec2_conn.get_only_instances()[0]
self.assertEqual('terminated', fake_instance.state)
self.reset()
@mock_ec2_deprecated
def test_destroy_instance_not_found(self):
self._create_instance()
with contextlib.nested(
mock.patch.object(boto.ec2.EC2Connection, 'stop_instances'),
mock.patch.object(boto.ec2.EC2Connection, 'terminate_instances'),
mock.patch.object(EC2Driver, '_wait_for_state'),
) as (fake_stop, fake_terminate, fake_wait):
self.conn.destroy(self.context, self.instance, None, None)
fake_stop.assert_not_called()
fake_terminate.assert_not_called()
fake_wait.assert_not_called()
self.reset()
@mock_ec2_deprecated
def test_destory_instance_terminated_on_aws(self):
self._create_vm_in_aws_nova()
fake_instances = self.fake_ec2_conn.get_only_instances()
self.fake_ec2_conn.stop_instances(instance_ids=[fake_instances[0].id])
self.fake_ec2_conn.terminate_instances(
instance_ids=[fake_instances[0].id])
with contextlib.nested(
mock.patch.object(boto.ec2.EC2Connection, 'stop_instances'),
mock.patch.object(boto.ec2.EC2Connection, 'terminate_instances'),
mock.patch.object(EC2Driver, '_wait_for_state'),
) as (fake_stop, fake_terminate, fake_wait):
self.conn.destroy(self.context, self.instance, None, None)
fake_stop.assert_not_called()
fake_terminate.assert_not_called()
fake_wait.assert_not_called()
self.reset()
@mock_ec2_deprecated
def test_destroy_instance_shut_down_on_aws(self):
self._create_vm_in_aws_nova()
fake_instances = self.fake_ec2_conn.get_only_instances()
self.fake_ec2_conn.stop_instances(instance_ids=[fake_instances[0].id])
with contextlib.nested(
mock.patch.object(boto.ec2.EC2Connection, 'stop_instances'),
mock.patch.object(boto.ec2.EC2Connection, 'terminate_instances'),
mock.patch.object(EC2Driver, '_wait_for_state'),
) as (fake_stop, fake_terminate, fake_wait):
self.conn.destroy(self.context, self.instance, None, None)
fake_stop.assert_not_called()
fake_terminate.assert_called_once_with(
instance_ids=[fake_instances[0].id])
self.reset()
@mock_ec2_deprecated
def test_get_info(self):
self._create_vm_in_aws_nova()
vm_info = self.conn.get_info(self.instance)
self.assertEqual(0, vm_info.state)
self.assertEqual(self.instance.id, vm_info.id)
self.reset()
@mock_ec2_deprecated
def test_get_info_instance_not_found(self):
self._create_instance()
self.assertRaises(exception.InstanceNotFound, self.conn.get_info,
self.instance)
self.reset()