diff options
author | Steven Pisarski <s.pisarski@cablelabs.com> | 2017-10-30 15:24:00 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@opnfv.org> | 2017-10-30 15:24:00 +0000 |
commit | 0dfca494ef7c2778babfac48d9b701953860b54f (patch) | |
tree | 777d30f3ecfca3f4bc689dfaa24c4bcd2b2fc634 | |
parent | ff6141ac35b4e0abd551c4d1f08a1e62ab538ec3 (diff) | |
parent | 6b7307374f6cfcad9eec8986aba8fdd8e08c8574 (diff) |
Merge "Return OpenStackVolume and OpenStackVolumeType instances from heat."
-rw-r--r-- | docs/how-to-use/APITests.rst | 15 | ||||
-rw-r--r-- | docs/how-to-use/IntegrationTests.rst | 39 | ||||
-rw-r--r-- | docs/how-to-use/UnitTests.rst | 9 | ||||
-rw-r--r-- | snaps/openstack/create_stack.py | 75 | ||||
-rw-r--r-- | snaps/openstack/tests/create_stack_tests.py | 98 | ||||
-rw-r--r-- | snaps/openstack/tests/heat/volume_heat_template.yaml | 51 | ||||
-rw-r--r-- | snaps/openstack/utils/heat_utils.py | 97 | ||||
-rw-r--r-- | snaps/openstack/utils/settings_utils.py | 46 | ||||
-rw-r--r-- | snaps/openstack/utils/tests/heat_utils_tests.py | 120 | ||||
-rw-r--r-- | snaps/openstack/utils/tests/settings_utils_tests.py | 47 | ||||
-rw-r--r-- | snaps/test_suite_builder.py | 19 |
11 files changed, 562 insertions, 54 deletions
diff --git a/docs/how-to-use/APITests.rst b/docs/how-to-use/APITests.rst index 3ac272b..fbd7e67 100644 --- a/docs/how-to-use/APITests.rst +++ b/docs/how-to-use/APITests.rst @@ -447,6 +447,21 @@ heat_utils_tests.py - HeatUtilsCreateComplexStackTests | | | by Heat | +---------------------------------------+---------------+-----------------------------------------------------------+ +heat_utils_tests.py - HeatUtilsVolumeTests +------------------------------------------ + ++---------------------------------------+---------------+-----------------------------------------------------------+ +| Test Name | Heat API | Description | ++=======================================+===============+===========================================================+ +| test_create_vol_with_stack | 1 | Tests ability of the function | +| | | heat_utils.get_stack_volumes() to return the correct | +| | | Volume domain objects deployed with Heat | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_create_vol_types_with_stack | 1 | Tests ability of the function | +| | | heat_utils.get_stack_volumes_types() to return the correct| +| | | VolumeType domain objects deployed with Heat | ++---------------------------------------+---------------+-----------------------------------------------------------+ + settings_utils_tests.py - SettingsUtilsNetworkingTests ------------------------------------------------------ diff --git a/docs/how-to-use/IntegrationTests.rst b/docs/how-to-use/IntegrationTests.rst index 5a734ef..538c9c0 100644 --- a/docs/how-to-use/IntegrationTests.rst +++ b/docs/how-to-use/IntegrationTests.rst @@ -360,34 +360,49 @@ create_stack_tests.py - CreateStackSuccessTests ----------------------------------------------- +---------------------------------------+---------------+-----------------------------------------------------------+ -| Test Name | Neutron API | Description | +| Test Name | Heat API | Description | +=======================================+===============+===========================================================+ -| test_create_stack_template_file | 2 | Ensures that a Heat stack can be created with a file-based| +| test_create_stack_template_file | 1 | Ensures that a Heat stack can be created with a file-based| | | | Heat template file | +---------------------------------------+---------------+-----------------------------------------------------------+ -| test_create_stack_template_dict | 2 | Ensures that a Heat stack can be created with a dictionary| +| test_create_stack_template_dict | 1 | Ensures that a Heat stack can be created with a dictionary| | | | Heat template | +---------------------------------------+---------------+-----------------------------------------------------------+ -| test_create_delete_stack | 2 | Ensures that a Heat stack can be created and deleted | +| test_create_delete_stack | 1 | Ensures that a Heat stack can be created and deleted | | | | while having clean() called 2x without an exception | +---------------------------------------+---------------+-----------------------------------------------------------+ -| test_create_same_stack | 2 | Ensures that a Heat stack with the same name cannot be | +| test_create_same_stack | 1 | Ensures that a Heat stack with the same name cannot be | | | | created 2x | +---------------------------------------+---------------+-----------------------------------------------------------+ -| test_retrieve_network_creators | 2 | Ensures that an OpenStackHeatStack instance can return an | +| test_retrieve_network_creators | 1 | Ensures that an OpenStackHeatStack instance can return an | | | | OpenStackNetwork instance configured as deployed | +---------------------------------------+---------------+-----------------------------------------------------------+ -| test_retrieve_vm_inst_creators | 2 | Ensures that an OpenStackHeatStack instance can return an | +| test_retrieve_vm_inst_creators | 1 | Ensures that an OpenStackHeatStack instance can return an | | | | OpenStackVmInstance instance configured as deployed | +---------------------------------------+---------------+-----------------------------------------------------------+ +create_stack_tests.py - CreateStackVolumeTests +---------------------------------------------- + ++---------------------------------------+---------------+-----------------------------------------------------------+ +| Test Name | Heat API | Description | ++=======================================+===============+===========================================================+ +| test_retrieve_volume_creator | 1 | Ensures that an OpenStackHeatStack instance can return a | +| | | OpenStackVolume instance that it was responsible for | +| | | deploying | ++---------------------------------------+---------------+-----------------------------------------------------------+ +| test_retrieve_volume_type_creator | 1 | Ensures that an OpenStackHeatStack instance can return a | +| | | OpenStackVolumeType instance that it was responsible for | +| | | deploying | ++---------------------------------------+---------------+-----------------------------------------------------------+ + create_stack_tests.py - CreateComplexStackTests ----------------------------------------------- +---------------------------------------+---------------+-----------------------------------------------------------+ -| Test Name | Neutron API | Description | +| Test Name | Heat API | Description | +=======================================+===============+===========================================================+ -| test_connect_via_ssh_heat_vm | 2 | Ensures that two OpenStackHeatStack instances can return | +| test_connect_via_ssh_heat_vm | 1 | Ensures that two OpenStackHeatStack instances can return | | | | OpenStackVmInstance instances one configured with a | | | | floating IP and keypair and can be access via SSH | +---------------------------------------+---------------+-----------------------------------------------------------+ @@ -396,12 +411,12 @@ create_stack_tests.py - CreateStackNegativeTests ------------------------------------------------ +----------------------------------------+---------------+-----------------------------------------------------------+ -| Test Name | Neutron API | Description | +| Test Name | Heat API | Description | +========================================+===============+===========================================================+ -| test_missing_dependencies | 2 | Ensures that a Heat template fails to deploy when expected| +| test_missing_dependencies | 1 | Ensures that a Heat template fails to deploy when expected| | | | dependencies are missing | +----------------------------------------+---------------+-----------------------------------------------------------+ -| test_bad_stack_file | 2 | Ensures that a Heat template fails to deploy when the Heat| +| test_bad_stack_file | 1 | Ensures that a Heat template fails to deploy when the Heat| | | | template file does not exist | +----------------------------------------+---------------+-----------------------------------------------------------+ diff --git a/docs/how-to-use/UnitTests.rst b/docs/how-to-use/UnitTests.rst index f6f52b5..3cb26db 100644 --- a/docs/how-to-use/UnitTests.rst +++ b/docs/how-to-use/UnitTests.rst @@ -293,3 +293,12 @@ VmInstDomainObjectTests Ensures that all required members are included when constructing a VmInst domain object + +SettingsUtilsVolumeTests +------------------------ + +Ensures that the settings_utils.py#create_volume_settings() function properly +maps a snaps.domain.Volume object correctly to a +snaps.openstack.create_volume.VolumeSettings object as well as a +snaps.domain.VolumeType object to a +snaps.openstack.create_volume.VolumeSettings object diff --git a/snaps/openstack/create_stack.py b/snaps/openstack/create_stack.py index 22edb65..d8f9d15 100644 --- a/snaps/openstack/create_stack.py +++ b/snaps/openstack/create_stack.py @@ -19,8 +19,11 @@ import time from heatclient.exc import HTTPNotFound from snaps.openstack.create_instance import OpenStackVmInstance +from snaps.openstack.create_volume import OpenStackVolume +from snaps.openstack.create_volume_type import OpenStackVolumeType from snaps.openstack.openstack_creator import OpenStackCloudObject -from snaps.openstack.utils import nova_utils, settings_utils, glance_utils +from snaps.openstack.utils import ( + nova_utils, settings_utils, glance_utils, cinder_utils) from snaps.openstack.create_network import OpenStackNetwork from snaps.openstack.utils import heat_utils, neutron_utils @@ -95,25 +98,22 @@ class OpenStackHeatStack(OpenStackCloudObject, object): self.initialize() if self.__stack: - logger.info('Found stack with name - ' + self.stack_settings.name) + logger.info('Found stack with name - %s', self.stack_settings.name) return self.__stack else: self.__stack = heat_utils.create_stack(self.__heat_cli, self.stack_settings) logger.info( - 'Created stack with name - ' + self.stack_settings.name) + 'Created stack with name - %s', self.stack_settings.name) if self.__stack and self.stack_complete(block=True): - logger.info( - 'Stack is now active with name - ' + - self.stack_settings.name) + logger.info('Stack is now active with name - %s', + self.stack_settings.name) return self.__stack else: status = heat_utils.get_stack_status_reason(self.__heat_cli, self.__stack.id) - logger.error( - 'ERROR: STACK CREATION FAILED: ' + status) - raise StackCreationError( - 'Failure while creating stack') + logger.error('ERROR: STACK CREATION FAILED: %s', status) + raise StackCreationError('Failure while creating stack') def clean(self): """ @@ -122,7 +122,7 @@ class OpenStackHeatStack(OpenStackCloudObject, object): """ if self.__stack: try: - logger.info('Deleting stack - %s' + self.__stack.name) + logger.info('Deleting stack - %s', self.__stack.name) heat_utils.delete_stack(self.__heat_cli, self.__stack) try: @@ -265,6 +265,59 @@ class OpenStackHeatStack(OpenStackCloudObject, object): return out + def get_volume_creators(self): + """ + Returns a list of Volume creator objects as configured by the heat + template + :return: list() of OpenStackVolume objects + """ + + out = list() + cinder = cinder_utils.cinder_client(self._os_creds) + + volumes = heat_utils.get_stack_volumes( + self.__heat_cli, cinder, self.__stack) + + for volume in volumes: + settings = settings_utils.create_volume_settings(volume) + creator = OpenStackVolume(self._os_creds, settings) + out.append(creator) + + try: + creator.initialize() + except Exception as e: + logger.error( + 'Unexpected error initializing volume creator - %s', e) + + return out + + def get_volume_type_creators(self): + """ + Returns a list of VolumeType creator objects as configured by the heat + template + :return: list() of OpenStackVolumeType objects + """ + + out = list() + cinder = cinder_utils.cinder_client(self._os_creds) + + vol_types = heat_utils.get_stack_volume_types( + self.__heat_cli, cinder, self.__stack) + + for volume in vol_types: + settings = settings_utils.create_volume_type_settings(volume) + creator = OpenStackVolumeType(self._os_creds, settings) + out.append(creator) + + try: + creator.initialize() + except Exception as e: + logger.error( + 'Unexpected error initializing volume type creator - %s', + e) + + return out + def _stack_status_check(self, expected_status_code, block, timeout, poll_interval, fail_status): """ diff --git a/snaps/openstack/tests/create_stack_tests.py b/snaps/openstack/tests/create_stack_tests.py index d96462a..a2b2215 100644 --- a/snaps/openstack/tests/create_stack_tests.py +++ b/snaps/openstack/tests/create_stack_tests.py @@ -394,7 +394,7 @@ class CreateStackSuccessTests(OSIntegrationTestCase): nova, vm_inst_creators[0].get_vm_inst().id)) -class CreateComplexStackTests(OSIntegrationTestCase): +class CreateStackFloatingIpTests(OSIntegrationTestCase): """ Tests for the CreateStack class defined in create_stack.py """ @@ -493,6 +493,102 @@ class CreateComplexStackTests(OSIntegrationTestCase): self.assertEqual(0, len(vm_settings.floating_ip_settings)) +class CreateStackVolumeTests(OSIntegrationTestCase): + """ + Tests for the CreateStack class defined in create_stack.py + """ + + def setUp(self): + """ + Instantiates the CreateStack object that is responsible for downloading + and creating an OS stack file within OpenStack + """ + super(self.__class__, self).__start__() + + self.guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + + self.heat_creds = self.admin_os_creds + self.heat_creds.project_name = self.admin_os_creds.project_name + + self.heat_cli = heat_utils.heat_client(self.heat_creds) + self.stack_creator = None + + self.volume_name = self.guid + '-volume' + self.volume_type_name = self.guid + '-volume-type' + + self.env_values = { + 'volume_name': self.volume_name, + 'volume_type_name': self.volume_type_name} + + self.heat_tmplt_path = pkg_resources.resource_filename( + 'snaps.openstack.tests.heat', 'volume_heat_template.yaml') + + stack_settings = StackSettings( + name=self.__class__.__name__ + '-' + str(self.guid) + '-stack', + template_path=self.heat_tmplt_path, + env_values=self.env_values) + self.stack_creator = create_stack.OpenStackHeatStack( + self.heat_creds, stack_settings) + self.created_stack = self.stack_creator.create() + self.assertIsNotNone(self.created_stack) + + def tearDown(self): + """ + Cleans the stack and downloaded stack file + """ + if self.stack_creator: + try: + self.stack_creator.clean() + except: + pass + + super(self.__class__, self).__clean__() + + def test_retrieve_volume_creator(self): + """ + Tests the creation of an OpenStack stack from Heat template file and + the retrieval of an OpenStackVolume creator/state machine instance + """ + volume_creators = self.stack_creator.get_volume_creators() + self.assertEqual(1, len(volume_creators)) + + creator = volume_creators[0] + self.assertEqual(self.volume_name, creator.volume_settings.name) + self.assertEqual(self.volume_name, creator.get_volume().name) + self.assertEqual(self.volume_type_name, + creator.volume_settings.type_name) + self.assertEqual(self.volume_type_name, creator.get_volume().type) + self.assertEqual(1, creator.volume_settings.size) + self.assertEqual(1, creator.get_volume().size) + + def test_retrieve_volume_type_creator(self): + """ + Tests the creation of an OpenStack stack from Heat template file and + the retrieval of an OpenStackVolume creator/state machine instance + """ + volume_type_creators = self.stack_creator.get_volume_type_creators() + self.assertEqual(1, len(volume_type_creators)) + + creator = volume_type_creators[0] + self.assertIsNotNone(creator) + + volume_type = creator.get_volume_type() + self.assertIsNotNone(volume_type) + + self.assertEqual(self.volume_type_name, volume_type.name) + self.assertTrue(volume_type.public) + self.assertIsNone(volume_type.qos_spec) + + encryption = volume_type.encryption + self.assertIsNotNone(encryption) + self.assertIsNone(encryption.cipher) + self.assertEqual('front-end', encryption.control_location) + self.assertIsNone(encryption.key_size) + self.assertEqual(u'nova.volume.encryptors.luks.LuksEncryptor', + encryption.provider) + self.assertEqual(volume_type.id, encryption.volume_type_id) + + class CreateStackNegativeTests(OSIntegrationTestCase): """ Negative test cases for the CreateStack class diff --git a/snaps/openstack/tests/heat/volume_heat_template.yaml b/snaps/openstack/tests/heat/volume_heat_template.yaml new file mode 100644 index 0000000..7b6e55a --- /dev/null +++ b/snaps/openstack/tests/heat/volume_heat_template.yaml @@ -0,0 +1,51 @@ +############################################################################## +# Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs") +# and others. 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. +############################################################################## +heat_template_version: 2015-04-30 + +description: Simple template to deploy a single volume with encryption + +parameters: + volume_name: + type: string + label: Volume name + description: The name of the volume + default: test-vol-name + volume_type_name: + type: string + label: Volume Type name + description: The name of the volume type + default: test-vol-type-name + +resources: + volume_type: + type: OS::Cinder::VolumeType + properties: + name: { get_param: volume_type_name } + + encryption_vol_type: + type: OS::Cinder::EncryptedVolumeType + properties: + provider: nova.volume.encryptors.luks.LuksEncryptor + control_location: front-end + volume_type: { get_resource: volume_type } + + volume: + type: OS::Cinder::Volume + properties: + name: { get_param: volume_name } + size: 1 + volume_type: { get_resource: encryption_vol_type } diff --git a/snaps/openstack/utils/heat_utils.py b/snaps/openstack/utils/heat_utils.py index 8b9395b..f2d4efd 100644 --- a/snaps/openstack/utils/heat_utils.py +++ b/snaps/openstack/utils/heat_utils.py @@ -23,7 +23,8 @@ from oslo_serialization import jsonutils from snaps import file_utils from snaps.domain.stack import Stack, Resource, Output -from snaps.openstack.utils import keystone_utils, neutron_utils, nova_utils +from snaps.openstack.utils import keystone_utils, neutron_utils, nova_utils, \ + cinder_utils __author__ = 'spisarski' @@ -140,20 +141,24 @@ def __get_os_resources(heat_cli, stack): return heat_cli.resources.list(stack.id) -def get_resources(heat_cli, stack): +def get_resources(heat_cli, stack, res_type=None): """ Returns all of the OpenStack resource objects for a given stack :param heat_cli: the OpenStack heat client :param stack: the SNAPS-OO Stack domain object - :return: a list + :param res_type: the type name to filter + :return: a list of Resource domain objects """ os_resources = __get_os_resources(heat_cli, stack) if os_resources: out = list() for os_resource in os_resources: - out.append(Resource(resource_type=os_resource.resource_type, - resource_id=os_resource.physical_resource_id)) + if ((res_type and os_resource.resource_type == res_type) + or not res_type): + out.append(Resource( + resource_type=os_resource.resource_type, + resource_id=os_resource.physical_resource_id)) return out @@ -163,7 +168,7 @@ def get_outputs(heat_cli, stack): for given stack :param heat_cli: the OpenStack heat client :param stack: the SNAPS-OO Stack domain object - :return: a list + :return: a list of Output domain objects """ out = list() @@ -182,46 +187,86 @@ def get_outputs(heat_cli, stack): def get_stack_networks(heat_cli, neutron, stack): """ - Returns an instance of NetworkSettings for each network owned by this stack + Returns a list of Network domain objects deployed by this stack :param heat_cli: the OpenStack heat client object :param neutron: the OpenStack neutron client object :param stack: the SNAPS-OO Stack domain object - :return: a list of NetworkSettings + :return: a list of Network objects """ out = list() - resources = get_resources(heat_cli, stack) + resources = get_resources(heat_cli, stack, 'OS::Neutron::Net') for resource in resources: - if resource.type == 'OS::Neutron::Net': - network = neutron_utils.get_network_by_id( - neutron, resource.id) - if network: - out.append(network) + network = neutron_utils.get_network_by_id( + neutron, resource.id) + if network: + out.append(network) return out def get_stack_servers(heat_cli, nova, stack): """ - Returns an instance of NetworkSettings for each network owned by this stack + Returns a list of VMInst domain objects associated with a Stack :param heat_cli: the OpenStack heat client object :param nova: the OpenStack nova client object :param stack: the SNAPS-OO Stack domain object - :return: a list of NetworkSettings + :return: a list of VMInst domain objects """ out = list() - resources = get_resources(heat_cli, stack) + resources = get_resources(heat_cli, stack, 'OS::Nova::Server') for resource in resources: - if resource.type == 'OS::Nova::Server': - try: - server = nova_utils.get_server_object_by_id( - nova, resource.id) - if server: - out.append(server) - except NotFound: - logger.warn( - 'VmInst cannot be located with ID %s', resource.id) + try: + server = nova_utils.get_server_object_by_id(nova, resource.id) + if server: + out.append(server) + except NotFound: + logger.warn('VmInst cannot be located with ID %s', resource.id) + + return out + + +def get_stack_volumes(heat_cli, cinder, stack): + """ + Returns an instance of NetworkSettings for each network owned by this stack + :param heat_cli: the OpenStack heat client object + :param cinder: the OpenStack cinder client object + :param stack: the SNAPS-OO Stack domain object + :return: a list of Volume domain objects + """ + + out = list() + resources = get_resources(heat_cli, stack, 'OS::Cinder::Volume') + for resource in resources: + try: + server = cinder_utils.get_volume_by_id(cinder, resource.id) + if server: + out.append(server) + except NotFound: + logger.warn('Volume cannot be located with ID %s', resource.id) + + return out + + +def get_stack_volume_types(heat_cli, cinder, stack): + """ + Returns an instance of NetworkSettings for each network owned by this stack + :param heat_cli: the OpenStack heat client object + :param cinder: the OpenStack cinder client object + :param stack: the SNAPS-OO Stack domain object + :return: a list of VolumeType domain objects + """ + + out = list() + resources = get_resources(heat_cli, stack, 'OS::Cinder::VolumeType') + for resource in resources: + try: + vol_type = cinder_utils.get_volume_type_by_id(cinder, resource.id) + if vol_type: + out.append(vol_type) + except NotFound: + logger.warn('VolumeType cannot be located with ID %s', resource.id) return out diff --git a/snaps/openstack/utils/settings_utils.py b/snaps/openstack/utils/settings_utils.py index 7f00075..7169319 100644 --- a/snaps/openstack/utils/settings_utils.py +++ b/snaps/openstack/utils/settings_utils.py @@ -20,6 +20,9 @@ from snaps.openstack.create_instance import ( from snaps.openstack.create_keypairs import KeypairSettings from snaps.openstack.create_network import ( PortSettings, SubnetSettings, NetworkSettings) +from snaps.openstack.create_volume import VolumeSettings +from snaps.openstack.create_volume_type import ( + VolumeTypeSettings, VolumeTypeEncryptionSettings, ControlLocation) from snaps.openstack.utils import ( neutron_utils, nova_utils, heat_utils, glance_utils) @@ -63,6 +66,49 @@ def create_subnet_settings(neutron, network): return out +def create_volume_settings(volume): + """ + Returns a VolumeSettings object + :param volume: a SNAPS-OO Volume object + """ + + return VolumeSettings( + name=volume.name, description=volume.description, + size=volume.size, type_name=volume.type, + availability_zone=volume.availability_zone, + multi_attach=volume.multi_attach) + + +def create_volume_type_settings(volume_type): + """ + Returns a VolumeTypeSettings object + :param volume_type: a SNAPS-OO VolumeType object + """ + + control = None + if volume_type.encryption: + if (volume_type.encryption.control_location + == ControlLocation.front_end.value): + control = ControlLocation.front_end + else: + control = ControlLocation.back_end + + encrypt_settings = VolumeTypeEncryptionSettings( + name=volume_type.encryption.__class__, + provider_class=volume_type.encryption.provider, + control_location=control, + cipher=volume_type.encryption.cipher, + key_size=volume_type.encryption.key_size) + + qos_spec_name = None + if volume_type.qos_spec: + qos_spec_name = volume_type.qos_spec.name + + return VolumeTypeSettings( + name=volume_type.name, encryption=encrypt_settings, + qos_spec_name=qos_spec_name, public=volume_type.public) + + def create_vm_inst_settings(nova, neutron, server): """ Returns a NetworkSettings object diff --git a/snaps/openstack/utils/tests/heat_utils_tests.py b/snaps/openstack/utils/tests/heat_utils_tests.py index 6f75f89..4f58613 100644 --- a/snaps/openstack/utils/tests/heat_utils_tests.py +++ b/snaps/openstack/utils/tests/heat_utils_tests.py @@ -27,7 +27,8 @@ from snaps.openstack.create_stack import StackSettings from snaps.openstack.tests import openstack_tests from snaps.openstack.tests.os_source_file_test import OSComponentTestCase from snaps.openstack.utils import ( - heat_utils, neutron_utils, nova_utils, settings_utils, glance_utils) + heat_utils, neutron_utils, nova_utils, settings_utils, glance_utils, + cinder_utils) __author__ = 'spisarski' @@ -460,3 +461,120 @@ class HeatUtilsCreateComplexStackTests(OSComponentTestCase): priv_key_key='private_key') self.assertIsNotNone(keypair2_settings) self.assertEqual(self.keypair_name, keypair2_settings.name) + + +class HeatUtilsVolumeTests(OSComponentTestCase): + """ + Test Heat volume functionality + """ + + def setUp(self): + """ + Instantiates OpenStack instances that cannot be spawned by Heat + """ + guid = self.__class__.__name__ + '-' + str(uuid.uuid4()) + stack_name = guid + '-stack' + self.volume_name = guid + '-vol' + self.volume_type_name = guid + '-vol-type' + + env_values = { + 'volume_name': self.volume_name, + 'volume_type_name': self.volume_type_name} + + heat_tmplt_path = pkg_resources.resource_filename( + 'snaps.openstack.tests.heat', 'volume_heat_template.yaml') + self.stack_settings = StackSettings( + name=stack_name, template_path=heat_tmplt_path, + env_values=env_values) + self.stack = None + self.heat_client = heat_utils.heat_client(self.os_creds) + self.cinder = cinder_utils.cinder_client(self.os_creds) + + def tearDown(self): + """ + Cleans the image and downloaded image file + """ + if self.stack: + try: + heat_utils.delete_stack(self.heat_client, self.stack) + except: + pass + + def test_create_vol_with_stack(self): + """ + Tests the creation of an OpenStack volume with Heat. + """ + self.stack = heat_utils.create_stack( + self.heat_client, self.stack_settings) + + # Wait until stack deployment has completed + end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT + is_active = False + while time.time() < end_time: + status = heat_utils.get_stack_status(self.heat_client, + self.stack.id) + if status == create_stack.STATUS_CREATE_COMPLETE: + is_active = True + break + elif status == create_stack.STATUS_CREATE_FAILED: + is_active = False + break + + time.sleep(3) + + self.assertTrue(is_active) + + volumes = heat_utils.get_stack_volumes( + self.heat_client, self.cinder, self.stack) + + self.assertEqual(1, len(volumes)) + + volume = volumes[0] + self.assertEqual(self.volume_name, volume.name) + self.assertEqual(self.volume_type_name, volume.type) + self.assertEqual(1, volume.size) + self.assertEqual(False, volume.multi_attach) + + def test_create_vol_types_with_stack(self): + """ + Tests the creation of an OpenStack volume with Heat. + """ + self.stack = heat_utils.create_stack( + self.heat_client, self.stack_settings) + + # Wait until stack deployment has completed + end_time = time.time() + create_stack.STACK_COMPLETE_TIMEOUT + is_active = False + while time.time() < end_time: + status = heat_utils.get_stack_status(self.heat_client, + self.stack.id) + if status == create_stack.STATUS_CREATE_COMPLETE: + is_active = True + break + elif status == create_stack.STATUS_CREATE_FAILED: + is_active = False + break + + time.sleep(3) + + self.assertTrue(is_active) + + volume_types = heat_utils.get_stack_volume_types( + self.heat_client, self.cinder, self.stack) + + self.assertEqual(1, len(volume_types)) + + volume_type = volume_types[0] + + self.assertEqual(self.volume_type_name, volume_type.name) + self.assertTrue(volume_type.public) + self.assertIsNone(volume_type.qos_spec) + + encryption = volume_type.encryption + self.assertIsNotNone(encryption) + self.assertIsNone(encryption.cipher) + self.assertEqual('front-end', encryption.control_location) + self.assertIsNone(encryption.key_size) + self.assertEqual(u'nova.volume.encryptors.luks.LuksEncryptor', + encryption.provider) + self.assertEqual(volume_type.id, encryption.volume_type_id) diff --git a/snaps/openstack/utils/tests/settings_utils_tests.py b/snaps/openstack/utils/tests/settings_utils_tests.py index f84e6a0..cb14039 100644 --- a/snaps/openstack/utils/tests/settings_utils_tests.py +++ b/snaps/openstack/utils/tests/settings_utils_tests.py @@ -13,14 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. import logging +import unittest + import os import uuid +from snaps.domain.volume import ( + Volume, VolumeType, VolumeTypeEncryption, QoSSpec) from snaps.openstack import ( create_image, create_network, create_router, create_flavor, create_keypairs, create_instance) from snaps.openstack.create_network import ( NetworkSettings, OpenStackNetwork, SubnetSettings) +from snaps.openstack.create_qos import Consumer from snaps.openstack.create_security_group import ( SecurityGroupRuleSettings, Direction, Protocol, OpenStackSecurityGroup, SecurityGroupSettings) @@ -339,3 +344,45 @@ class SettingsUtilsVmInstTests(OSComponentTestCase): self.assertIsNotNone(derived_image_settings) self.assertEqual(self.image_creator.image_settings.name, derived_image_settings.name) + + +class SettingsUtilsVolumeTests(unittest.TestCase): + """ + Exercises the settings_utils.py functions around volumes + """ + + def test_vol_settings_from_vol(self): + volume = Volume( + name='vol-name', volume_id='vol-id', description='desc', size=99, + vol_type='vol-type', availability_zone='zone1', multi_attach=True) + settings = settings_utils.create_volume_settings(volume) + self.assertEqual(volume.name, settings.name) + self.assertEqual(volume.description, settings.description) + self.assertEqual(volume.size, settings.size) + self.assertEqual(volume.type, settings.type_name) + self.assertEqual(volume.availability_zone, settings.availability_zone) + self.assertEqual(volume.multi_attach, settings.multi_attach) + + def test_vol_type_settings_from_vol(self): + encryption = VolumeTypeEncryption( + volume_encryption_id='vol-encrypt-id', volume_type_id='vol-typ-id', + control_location='front-end', provider='FooClass', cipher='1', + key_size=1) + qos_spec = QoSSpec(name='qos-spec-name', spec_id='qos-spec-id', + consumer=Consumer.back_end) + volume_type = VolumeType( + name='vol-type-name', volume_type_id='vol-type-id', public=True, + encryption=encryption, qos_spec=qos_spec) + + settings = settings_utils.create_volume_type_settings(volume_type) + self.assertEqual(volume_type.name, settings.name) + self.assertEqual(volume_type.public, settings.public) + + encrypt_settings = settings.encryption + self.assertIsNotNone(encrypt_settings) + self.assertEqual(encryption.control_location, + encrypt_settings.control_location.value) + self.assertEqual(encryption.cipher, encrypt_settings.cipher) + self.assertEqual(encryption.key_size, encrypt_settings.key_size) + + self.assertEqual(qos_spec.name, settings.qos_spec_name) diff --git a/snaps/test_suite_builder.py b/snaps/test_suite_builder.py index f06b027..77a8a2a 100644 --- a/snaps/test_suite_builder.py +++ b/snaps/test_suite_builder.py @@ -68,7 +68,7 @@ from snaps.openstack.tests.create_security_group_tests import ( SecurityGroupSettingsUnitTests) from snaps.openstack.tests.create_stack_tests import ( StackSettingsUnitTests, CreateStackSuccessTests, CreateStackNegativeTests, - CreateComplexStackTests) + CreateStackFloatingIpTests, CreateStackVolumeTests) from snaps.openstack.tests.create_user_tests import ( UserSettingsUnitTests, CreateUserSuccessTests) from snaps.openstack.tests.create_volume_tests import ( @@ -88,7 +88,7 @@ from snaps.openstack.utils.tests.glance_utils_tests import ( GlanceSmokeTests, GlanceUtilsTests) from snaps.openstack.utils.tests.heat_utils_tests import ( HeatSmokeTests, HeatUtilsCreateSimpleStackTests, - HeatUtilsCreateComplexStackTests) + HeatUtilsCreateComplexStackTests, HeatUtilsVolumeTests) from snaps.openstack.utils.tests.keystone_utils_tests import ( KeystoneSmokeTests, KeystoneUtilsTests) from snaps.openstack.utils.tests.neutron_utils_tests import ( @@ -98,6 +98,8 @@ from snaps.openstack.utils.tests.neutron_utils_tests import ( from snaps.openstack.utils.tests.nova_utils_tests import ( NovaSmokeTests, NovaUtilsKeypairTests, NovaUtilsFlavorTests, NovaUtilsInstanceTests, NovaUtilsInstanceVolumeTests) +from snaps.openstack.utils.tests.settings_utils_tests import ( + SettingsUtilsVolumeTests) from snaps.provisioning.tests.ansible_utils_tests import ( AnsibleProvisioningTests) from snaps.tests.file_utils_tests import FileUtilsTests @@ -198,6 +200,8 @@ def add_unit_tests(suite): VolumeTypeSettingsUnitTests)) suite.addTest(unittest.TestLoader().loadTestsFromTestCase( VolumeSettingsUnitTests)) + suite.addTest(unittest.TestLoader().loadTestsFromTestCase( + SettingsUtilsVolumeTests)) def add_openstack_client_tests(suite, os_creds, ext_net_name, @@ -322,6 +326,10 @@ def add_openstack_api_tests(suite, os_creds, ext_net_name, use_keystone=True, ext_net_name=ext_net_name, log_level=log_level, image_metadata=image_metadata)) suite.addTest(OSComponentTestCase.parameterize( + HeatUtilsVolumeTests, os_creds=os_creds, + ext_net_name=ext_net_name, log_level=log_level, + image_metadata=image_metadata)) + suite.addTest(OSComponentTestCase.parameterize( CinderUtilsQoSTests, os_creds=os_creds, ext_net_name=ext_net_name, log_level=log_level, image_metadata=image_metadata)) @@ -503,6 +511,11 @@ def add_openstack_integration_tests(suite, os_creds, ext_net_name, flavor_metadata=flavor_metadata, image_metadata=image_metadata, log_level=log_level)) suite.addTest(OSIntegrationTestCase.parameterize( + CreateStackVolumeTests, os_creds=os_creds, ext_net_name=ext_net_name, + use_keystone=use_keystone, + flavor_metadata=flavor_metadata, image_metadata=image_metadata, + log_level=log_level)) + suite.addTest(OSIntegrationTestCase.parameterize( CreateStackNegativeTests, os_creds=os_creds, ext_net_name=ext_net_name, use_keystone=use_keystone, flavor_metadata=flavor_metadata, image_metadata=image_metadata, @@ -515,7 +528,7 @@ def add_openstack_integration_tests(suite, os_creds, ext_net_name, flavor_metadata=flavor_metadata, image_metadata=image_metadata, log_level=log_level)) suite.addTest(OSIntegrationTestCase.parameterize( - CreateComplexStackTests, os_creds=os_creds, + CreateStackFloatingIpTests, os_creds=os_creds, ext_net_name=ext_net_name, use_keystone=use_keystone, flavor_metadata=flavor_metadata, image_metadata=image_metadata, log_level=log_level)) |