diff options
Diffstat (limited to 'snaps/openstack')
-rw-r--r-- | snaps/openstack/create_stack.py | 3 | ||||
-rw-r--r-- | snaps/openstack/tests/create_stack_tests.py | 108 | ||||
-rw-r--r-- | snaps/openstack/tests/heat/agent-group.yaml | 115 | ||||
-rw-r--r-- | snaps/openstack/tests/heat/agent.yaml | 110 | ||||
-rw-r--r-- | snaps/openstack/utils/heat_utils.py | 54 | ||||
-rw-r--r-- | snaps/openstack/utils/tests/heat_utils_tests.py | 4 |
6 files changed, 373 insertions, 21 deletions
diff --git a/snaps/openstack/create_stack.py b/snaps/openstack/create_stack.py index 74fde9d..d4a6b10 100644 --- a/snaps/openstack/create_stack.py +++ b/snaps/openstack/create_stack.py @@ -467,7 +467,8 @@ class OpenStackHeatStack(OpenStackCloudObject, object): return False if fail_status and status == fail_status: - resources = heat_utils.get_resources(self.__heat_cli, self.__stack) + resources = heat_utils.get_resources( + self.__heat_cli, self.__stack.id) logger.error('Stack %s failed', self.__stack.name) for resource in resources: if (resource.status != diff --git a/snaps/openstack/tests/create_stack_tests.py b/snaps/openstack/tests/create_stack_tests.py index 2db89e5..6041735 100644 --- a/snaps/openstack/tests/create_stack_tests.py +++ b/snaps/openstack/tests/create_stack_tests.py @@ -506,6 +506,112 @@ class CreateStackFloatingIpTests(OSIntegrationTestCase): self.assertEqual(0, len(vm_settings.floating_ip_settings)) +class CreateStackNestedResourceTests(OSIntegrationTestCase): + """ + Tests to ensure that nested heat templates work + """ + + def setUp(self): + + 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.image_creator = OpenStackImage( + self.heat_creds, openstack_tests.cirros_image_settings( + name=self.guid + '-image', + image_metadata=self.image_metadata)) + self.image_creator.create() + + self.flavor_creator = OpenStackFlavor( + self.admin_os_creds, + FlavorConfig( + name=self.guid + '-flavor-name', ram=256, disk=10, vcpus=1)) + self.flavor_creator.create() + + env_values = { + 'public_network': self.ext_net_name, + 'agent_image': self.image_creator.image_settings.name, + 'agent_flavor': self.flavor_creator.flavor_settings.name, + } + + heat_tmplt_path = pkg_resources.resource_filename( + 'snaps.openstack.tests.heat', 'agent-group.yaml') + heat_resource_path = pkg_resources.resource_filename( + 'snaps.openstack.tests.heat', 'agent.yaml') + + stack_settings = StackConfig( + name=self.__class__.__name__ + '-' + str(self.guid) + '-stack', + template_path=heat_tmplt_path, + resource_files=[heat_resource_path], + env_values=env_values) + + self.stack_creator = OpenStackHeatStack( + self.heat_creds, stack_settings, + [self.image_creator.image_settings]) + + self.vm_inst_creators = list() + + def tearDown(self): + """ + Cleans the stack and downloaded stack file + """ + if self.stack_creator: + try: + self.stack_creator.clean() + except: + pass + + if self.image_creator: + try: + self.image_creator.clean() + except: + pass + + if self.flavor_creator: + try: + self.flavor_creator.clean() + except: + pass + + for vm_inst_creator in self.vm_inst_creators: + try: + keypair_settings = vm_inst_creator.keypair_settings + if keypair_settings and keypair_settings.private_filepath: + expanded_path = os.path.expanduser( + keypair_settings.private_filepath) + os.chmod(expanded_path, 0o755) + os.remove(expanded_path) + except: + pass + + super(self.__class__, self).__clean__() + + def test_nested(self): + """ + Tests the creation of an OpenStack stack from Heat template file and + the retrieval of two VM instance creators and attempt to connect via + SSH to the first one with a floating IP. + """ + created_stack = self.stack_creator.create() + self.assertIsNotNone(created_stack) + + self.vm_inst_creators = self.stack_creator.get_vm_inst_creators( + heat_keypair_option='private_key') + self.assertIsNotNone(self.vm_inst_creators) + self.assertEqual(1, len(self.vm_inst_creators)) + + for vm_inst_creator in self.vm_inst_creators: + self.assertTrue( + create_instance_tests.validate_ssh_client(vm_inst_creator)) + + class CreateStackRouterTests(OSIntegrationTestCase): """ Tests for the CreateStack class defined in create_stack.py where the @@ -1056,7 +1162,7 @@ class CreateStackFailureTests(OSIntegrationTestCase): self.stack_creator.create() except StackError: resources = heat_utils.get_resources( - self.heat_cli, self.stack_creator.get_stack()) + self.heat_cli, self.stack_creator.get_stack().id) found = False for resource in resources: diff --git a/snaps/openstack/tests/heat/agent-group.yaml b/snaps/openstack/tests/heat/agent-group.yaml new file mode 100644 index 0000000..540ea93 --- /dev/null +++ b/snaps/openstack/tests/heat/agent-group.yaml @@ -0,0 +1,115 @@ +############################################################################## +# 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: 2013-05-23 + +parameters: + public_network: + type: string + constraints: + - custom_constraint: neutron.network + agent_flavor: + type: string + agent_image: + type: string + volume_size: + type: number + description: Size of the volume to be created. + default: 1 + constraints: + - range: { min: 1, max: 1024 } + description: must be between 1 and 1024 Gb. + agent_count: + type: number + default: 1 + constraints: + - range: { min: 1, max: 512 } + description: must be between 1 and 512 agents. + availability_zone: + type: string + default: nova + +resources: + slaves: + type: OS::Heat::ResourceGroup + depends_on: [subnet, network_router_interface, + open_security_group, key_pair] + properties: + count: {get_param: agent_count} + resource_def: { + type: "agent.yaml", + properties: { + public_network: {get_param: public_network}, + agent_network: {get_resource: network}, + flavor: {get_param: agent_flavor}, + image: {get_param: agent_image}, + availability_zone: {get_param: availability_zone}, + open_security_group: {get_resource: open_security_group}, + key_name: {get_resource: key_pair}, + volume_size: {get_param: volume_size} + } + } + + network: + type: OS::Neutron::Net + properties: + name: network + + subnet: + type: OS::Neutron::Subnet + properties: + network_id: { get_resource: network } + cidr: 172.16.0.0/16 + gateway_ip: 172.16.0.1 + + network_router: + type: OS::Neutron::Router + properties: + external_gateway_info: + network: { get_param: public_network } + + network_router_interface: + type: OS::Neutron::RouterInterface + properties: + router_id: { get_resource: network_router } + subnet_id: { get_resource: subnet } + + key_pair: + type: OS::Nova::KeyPair + properties: + save_private_key: true + name: agent_keypair + + open_security_group: + type: OS::Neutron::SecurityGroup + properties: + description: An open security group to allow all access to the slaves + rules: + - remote_ip_prefix: 0.0.0.0/0 + protocol: tcp + port_range_min: 22 + port_range_max: 22 + - remote_ip_prefix: 0.0.0.0/0 + protocol: icmp + +outputs: + slave_ips: { + description: "Slave addresses", + value: { get_attr: [ slaves, agent_ip] } + } + private_key: + description: "SSH Private Key" + value: { get_attr: [ key_pair, private_key ]} diff --git a/snaps/openstack/tests/heat/agent.yaml b/snaps/openstack/tests/heat/agent.yaml new file mode 100644 index 0000000..014b14f --- /dev/null +++ b/snaps/openstack/tests/heat/agent.yaml @@ -0,0 +1,110 @@ +############################################################################## +# 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: 2013-05-23 + +parameters: + flavor: + type: string + default: test + image: + type: string + default: 'Ubuntu 16.04' + key_name: + type: string + default: test_key + username: + type: string + default: test_user + open_security_group: + type: string + volume_size: + type: number + description: Size of the volume to be created. + default: 1 + constraints: + - range: { min: 1, max: 1024 } + description: must be between 1 and 1024 Gb. + agent_network: + type: string + constraints: + - custom_constraint: neutron.network + public_network: + type: string + constraints: + - custom_constraint: neutron.network + availability_zone: + type: string + default: nova + +resources: + agent: + type: "OS::Nova::Server" + properties: + name: agent + image: { get_param: image } + flavor: { get_param: flavor } + key_name: { get_param: key_name } + networks: + - port: { get_resource: agent_port } + user_data: { get_resource: agent_config } + user_data_format: RAW + availability_zone: { get_param: availability_zone} + + agent_config: + type: "OS::Heat::CloudConfig" + properties: + cloud_config: + users: + - name: { get_param: username } + groups: users + shell: /bin/bash + sudo: "ALL=(ALL) NOPASSWD:ALL" + ssh_authorized_keys: + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEbnDiqZ8RjQJJzJPf074J41XlYED+zYBzaUZ5UkkUquXzymyUmoWaFBXJP+XPu4Ns44U/S8614+JxGk96tjUdJlIjL0Ag8HP6KLtTNCabucKcEASpgJIVWqJvE3E9upZLIEiTGsF8I8S67T2qq1J1uvtxyeZmyjm7NMamjyFXE53dhR2EHqSutyKK1CK74NkRY9wr3qWUIt35kLdKSVSfrr4gOOicDALbIRu77skHIvrjt+wK1VWphBdMg6ytuq5mIE6pjWAU3Gwl4aTxOU0z43ARzCLq8HVf8s/dKjYMj8plNqaIfceMbaEUqpNHv/xbvtGNG7N0aB/a4pkUQL07 + - default + package_update: false + package_upgrade: false + manage_etc_hosts: localhost + + agent_port: + type: "OS::Neutron::Port" + properties: + network_id: { get_param: agent_network } + security_groups: + - { get_param: open_security_group } + + floating_ip: + type: OS::Neutron::FloatingIP + properties: + floating_network_id: { get_param: public_network } + port_id: { get_resource: agent_port } + + agent_volume: + type: OS::Cinder::Volume + properties: + size: { get_param: volume_size } + + agent_volume_att: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: { get_resource: agent } + volume_id: { get_resource: agent_volume} + +outputs: + agent_ip: + description: The floating IP address of the agent on the public network + value: { get_attr: [ floating_ip, floating_ip_address ] } diff --git a/snaps/openstack/utils/heat_utils.py b/snaps/openstack/utils/heat_utils.py index 8e49c53..e440717 100644 --- a/snaps/openstack/utils/heat_utils.py +++ b/snaps/openstack/utils/heat_utils.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import logging +import os import yaml from heatclient.client import Client @@ -117,8 +118,15 @@ def create_stack(heat_cli, stack_settings): if stack_settings.env_values: args['parameters'] = stack_settings.env_values - if stack_settings.files: - args['files'] = stack_settings.files + if stack_settings.resource_files: + resources = dict() + for res_file in stack_settings.resource_files: + heat_resource_contents = file_utils.read_file(res_file) + base_filename = os.path.basename(res_file) + + if heat_resource_contents and base_filename: + resources[base_filename] = heat_resource_contents + args['files'] = resources stack = heat_cli.stacks.create(**args) @@ -134,25 +142,25 @@ def delete_stack(heat_cli, stack): heat_cli.stacks.delete(stack.id) -def __get_os_resources(heat_cli, stack): +def __get_os_resources(heat_cli, res_id): """ 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 + :param res_id: the resource ID :return: a list """ - return heat_cli.resources.list(stack.id) + return heat_cli.resources.list(res_id) -def get_resources(heat_cli, stack, res_type=None): +def get_resources(heat_cli, res_id, 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 + :param res_id: the SNAPS-OO Stack domain object :param res_type: the type name to filter :return: a list of Resource domain objects """ - os_resources = __get_os_resources(heat_cli, stack) + os_resources = __get_os_resources(heat_cli, res_id) if os_resources: out = list() @@ -201,7 +209,7 @@ def get_stack_networks(heat_cli, neutron, stack): """ out = list() - resources = get_resources(heat_cli, stack, 'OS::Neutron::Net') + resources = get_resources(heat_cli, stack.id, 'OS::Neutron::Net') for resource in resources: network = neutron_utils.get_network_by_id(neutron, resource.id) if network: @@ -220,7 +228,7 @@ def get_stack_routers(heat_cli, neutron, stack): """ out = list() - resources = get_resources(heat_cli, stack, 'OS::Neutron::Router') + resources = get_resources(heat_cli, stack.id, 'OS::Neutron::Router') for resource in resources: router = neutron_utils.get_router_by_id(neutron, resource.id) if router: @@ -239,7 +247,7 @@ def get_stack_security_groups(heat_cli, neutron, stack): """ out = list() - resources = get_resources(heat_cli, stack, 'OS::Neutron::SecurityGroup') + resources = get_resources(heat_cli, stack.id, 'OS::Neutron::SecurityGroup') for resource in resources: security_group = neutron_utils.get_security_group_by_id( neutron, resource.id) @@ -260,8 +268,8 @@ def get_stack_servers(heat_cli, nova, neutron, stack): """ out = list() - resources = get_resources(heat_cli, stack, 'OS::Nova::Server') - for resource in resources: + srvr_res = get_resources(heat_cli, stack.id, 'OS::Nova::Server') + for resource in srvr_res: try: server = nova_utils.get_server_object_by_id( nova, neutron, resource.id) @@ -270,6 +278,18 @@ def get_stack_servers(heat_cli, nova, neutron, stack): except NotFound: logger.warn('VmInst cannot be located with ID %s', resource.id) + res_grps = get_resources(heat_cli, stack.id, 'OS::Heat::ResourceGroup') + for res_grp in res_grps: + res_ress = get_resources(heat_cli, res_grp.id) + for res_res in res_ress: + res_res_srvrs = get_resources( + heat_cli, res_res.id, 'OS::Nova::Server') + for res_srvr in res_res_srvrs: + server = nova_utils.get_server_object_by_id( + nova, neutron, res_srvr.id) + if server: + out.append(server) + return out @@ -283,7 +303,7 @@ def get_stack_keypairs(heat_cli, nova, stack): """ out = list() - resources = get_resources(heat_cli, stack, 'OS::Nova::KeyPair') + resources = get_resources(heat_cli, stack.id, 'OS::Nova::KeyPair') for resource in resources: try: keypair = nova_utils.get_keypair_by_id(nova, resource.id) @@ -305,7 +325,7 @@ def get_stack_volumes(heat_cli, cinder, stack): """ out = list() - resources = get_resources(heat_cli, stack, 'OS::Cinder::Volume') + resources = get_resources(heat_cli, stack.id, 'OS::Cinder::Volume') for resource in resources: try: server = cinder_utils.get_volume_by_id(cinder, resource.id) @@ -327,7 +347,7 @@ def get_stack_volume_types(heat_cli, cinder, stack): """ out = list() - resources = get_resources(heat_cli, stack, 'OS::Cinder::VolumeType') + resources = get_resources(heat_cli, stack.id, 'OS::Cinder::VolumeType') for resource in resources: try: vol_type = cinder_utils.get_volume_type_by_id(cinder, resource.id) @@ -350,7 +370,7 @@ def get_stack_flavors(heat_cli, nova, stack): """ out = list() - resources = get_resources(heat_cli, stack, 'OS::Nova::Flavor') + resources = get_resources(heat_cli, stack.id, 'OS::Nova::Flavor') for resource in resources: try: flavor = nova_utils.get_flavor_by_id(nova, resource.id) diff --git a/snaps/openstack/utils/tests/heat_utils_tests.py b/snaps/openstack/utils/tests/heat_utils_tests.py index 7d43adf..67fbdec 100644 --- a/snaps/openstack/utils/tests/heat_utils_tests.py +++ b/snaps/openstack/utils/tests/heat_utils_tests.py @@ -164,7 +164,7 @@ class HeatUtilsCreateSimpleStackTests(OSComponentTestCase): self.stack1.id) self.assertEqual(self.stack1, stack_query_3) - resources = heat_utils.get_resources(self.heat_client, self.stack1) + resources = heat_utils.get_resources(self.heat_client, self.stack1.id) self.assertIsNotNone(resources) self.assertEqual(4, len(resources)) @@ -359,7 +359,7 @@ class HeatUtilsCreateComplexStackTests(OSComponentTestCase): Tests that a heat template with floating IPs and can have the proper settings derived from settings_utils.py. """ - resources = heat_utils.get_resources(self.heat_client, self.stack) + resources = heat_utils.get_resources(self.heat_client, self.stack.id) self.assertIsNotNone(resources) self.assertEqual(12, len(resources)) |