diff --git a/vmware_nsx_tempest/tests/nsxv/scenario/test_neutron_resources_heat.py b/vmware_nsx_tempest/tests/nsxv/scenario/test_neutron_resources_heat.py new file mode 100644 index 0000000000..76580e39c9 --- /dev/null +++ b/vmware_nsx_tempest/tests/nsxv/scenario/test_neutron_resources_heat.py @@ -0,0 +1,252 @@ +# Copyright 2017 VMware 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 os +import re +import yaml + +from oslo_log import log as logging + +from tempest.api.orchestration import base +from tempest.common.utils import data_utils +from tempest import config +from tempest.lib import decorators +from tempest.scenario import manager +from tempest import test + +from vmware_nsx_tempest.services import nsxv_client + + +CONF = config.CONF + +LOG = logging.getLogger(__name__) +DIR_PATH = '/opt/stack/vmware-nsx/vmware_nsx_tempest/tests/' + + +class HeatSmokeTest(base.BaseOrchestrationTest, + manager.NetworkScenarioTest): + + """Deploy and Test Neutron Resources using HEAT. + + The script load the neutron resources from template and fully + validates successful deployment of all resources from the template. + The template consists of two toplogies with Shared and Exclusive router. + Tests will be common to toplogies (pls refer template for topo info)and + will be as below : + 1. verify created resources from template + 2. verify all created resouces from template + -->neutronDB-->NSXbackend + 3. check same network connectivity + 4. check cross network connectivity + """ + + def setUp(self): + super(HeatSmokeTest, self).setUp() + self.external_network = CONF.scenario.outside_world_servers + + @classmethod + def read_template(cls, name, ext='yaml'): + loc = ["templates", "%s.%s" % (name, ext)] + filepath = os.path.join(DIR_PATH, *loc) + if os.path.isfile(filepath): + with open(filepath, "r") as f: + content = f.read() + return content + else: + raise IOError("File %s not found " % filepath) + + @classmethod + def load_template(cls, name, ext='yaml'): + loc = ["templates", "%s.%s" % (name, ext)] + filepath = os.path.join(DIR_PATH, *loc) + if os.path.isfile(filepath): + with open(filepath, "r") as f: + return yaml.safe_load(f) + else: + raise IOError("File %s not found " % filepath) + + @classmethod + def resource_setup(cls): + super(HeatSmokeTest, cls).resource_setup() + cls.stack_name = data_utils.rand_name('heat') + try: + cls.neutron_basic_template = cls.load_template( + 'nsxv_neutron_smoke') + template = cls.read_template('nsxv_neutron_smoke') + except IOError as e: + LOG.exception(("file nsxv_neutron_smoke.yaml not found %(rsp)s") % + {'rsp': e}) + cls.stack_identifier = cls.create_stack(cls.stack_name, template) + cls.client.wait_for_stack_status(cls.stack_identifier, + 'CREATE_COMPLETE') + cls.stack_id = cls.stack_identifier.split('/')[1] + cls.resources = (cls.client.list_resources(cls.stack_identifier) + ['resources']) + cls.test_resources = {} + for resource in cls.resources: + cls.test_resources[resource['logical_resource_id']] = resource + + @classmethod + def setup_credentials(cls): + cls.set_network_resources() + super(HeatSmokeTest, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + super(HeatSmokeTest, cls).setup_clients() + cls.routers_client = cls.os.routers_client + cls.subnets_client = cls.os.subnets_client + manager_ip = re.search(r"(\d{1,3}\.){3}\d{1,3}", + CONF.nsxv.manager_uri).group(0) + cls.vsm = nsxv_client.VSMClient( + manager_ip, CONF.nsxv.user, CONF.nsxv.password) + + def _resource_list_check(self, resource): + # sorts out the resources and returns resource id + if resource == 'networks': + body = self.networks_client.list_networks() + component = 'OS::Neutron::Net' + elif resource == 'routers': + body = self.routers_client.list_routers() + component = 'OS::Neutron::Router' + print(body) + elif resource == 'servers': + body = self.servers_client.list_servers() + component = 'OS::Nova::Server' + resource_list_id = [res_list['id'] for res_list in body[resource]] + test_resource_list_id = [] + for _, resource in self.test_resources.items(): + if resource['resource_type'] == component: + test_resource_list_id.append(resource['physical_resource_id']) + for resource_id in test_resource_list_id: + self.assertIn(resource_id, resource_list_id) + return test_resource_list_id + + def _check_server_connectivity(self, floating_ip, address_list, + should_connect=True): + # checks server connectivity + private_key = self.get_stack_output(self.stack_identifier, + 'private_key') + ssh_source = self.get_remote_client(floating_ip, + private_key=private_key) + for remote_ip in address_list: + if should_connect: + msg = ("Timed out waiting for %s to become " + "reachable") % remote_ip + else: + msg = "ip address %s is reachable" % remote_ip + try: + self.assertTrue(self._check_remote_connectivity + (ssh_source, remote_ip, should_connect), + msg) + except Exception: + LOG.exception(("Unable to access %(dest)s via ssh to " + "floating-ip %(src)s") % + {'dest': remote_ip, 'src': floating_ip}) + raise + + @decorators.idempotent_id('f693a425-b018-4cde-96ab-cdd5b858e15c') + @test.attr(type=["smoke"]) + def test_created_resources(self): + """Verifies created resources from template .""" + for resource in self.resources: + msg = 'resource %s not create successfully' \ + % resource['logical_resource_id'] + self.assertEqual('CREATE_COMPLETE', resource['resource_status'], + msg) + self.assertIsInstance(resource, dict) + + @decorators.idempotent_id('3c3ccfcb-e50b-4372-82dc-d5b473acd506') + @test.attr(type=["smoke"]) + def test_created_network(self): + """Verifies created neutron networks.""" + network_id_list = self._resource_list_check(resource='networks') + for network_id in network_id_list: + body = self.networks_client.show_network(network_id) + self.assertEqual('True', str(body['network']['admin_state_up'])) + msg = 'newtwork %s not found' % body['network']['name'] + self.assertIsNotNone(self.vsm.get_logical_switch(network_id), msg) + + @decorators.idempotent_id('b3b103a7-69b2-42ea-a1b8-aa11cc551df9') + @test.attr(type=["smoke"]) + def test_created_router(self): + """Verifies created router.""" + router_id_list = self._resource_list_check(resource='routers') + for router_id in router_id_list: + body = self.routers_client.show_router(router_id) + self.assertEqual('True', str(body['router']['admin_state_up'])) + if (body['router']['router_type']) != 'shared': + router_edge_name = "%s-%s" % ( + body['router']['name'], body['router']['id']) + exc_edge = self.vsm.get_edge(router_edge_name) + msg = 'exc edge %s not found' % body['router']['name'] + self.assertTrue(exc_edge is not None, msg) + + @decorators.idempotent_id('2b29dfef-6d9f-4a70-9377-af432100ef10') + @test.attr(type=["smoke"]) + def test_created_server(self): + """Verifies created sever.""" + server_id_list = self._resource_list_check(resource='servers') + for server_id in server_id_list: + server = self.servers_client.show_server(server_id)['server'] + msg = 'server %s not active ' % (server) + self.assertEqual('ACTIVE', str(server['status']), msg) + + @decorators.idempotent_id('d937a607-aa5e-4cf1-bbf9-00044cbe7190') + @test.attr(type=["smoke"]) + def test_topo1_same_network_connectivity_(self): + """Verifies same network connnectivity for Topology 1 """ + address_list = [] + topo1_server1_floatingip = self.get_stack_output(self.stack_identifier, + 'topo1_server1_floatingip') + server4_private_ip = self.get_stack_output(self.stack_identifier, + 'topo1_server4_private_ip') + address_list.append(server4_private_ip) + LOG.info((" floating ip :%(rsp)s and private ip list : %(rsp1)s") % + {"rsp": topo1_server1_floatingip, "rsp1": address_list}) + self._check_server_connectivity(topo1_server1_floatingip, address_list, + should_connect=True) + + @decorators.idempotent_id('fdbc8b1a-755a-4b37-93e7-0a268e422f05') + @test.attr(type=["smoke"]) + def test_topo1_cross_network_connectivity(self): + """Verifies cross network connnectivity for Topology 1 """ + address_list = [] + topo1_server1_floatingip = self.get_stack_output( + self.stack_identifier, 'topo1_server1_floatingip') + server2_private_ip = self.get_stack_output(self.stack_identifier, + 'topo1_server2_private_ip') + server3_private_ip = self.get_stack_output(self.stack_identifier, + 'topo1_server3_private_ip') + address_list.append(server2_private_ip) + address_list.append(server3_private_ip) + LOG.info(("floating ip :%(rsp)s and private ip list : %(rsp1)s") % + {"rsp": topo1_server1_floatingip, "rsp1": address_list}) + self._check_server_connectivity(topo1_server1_floatingip, address_list, + should_connect=True) + + @decorators.idempotent_id('bcefd117-c55e-499d-a34b-653b8981f1c5') + @test.attr(type=["smoke"]) + def test_topo1_cross_external_connectivity(self): + """Verifies external network connnectivity for Topology 1 """ + address_list = [] + topo1_server1_floatingip = self.get_stack_output(self.stack_identifier, + 'topo1_server1_floatingip') + external_network = self.external_network[0] + address_list.append(external_network) + LOG.info(("floating ip :%(rsp)s and external ip : %(rsp1)s") % + {"rsp": topo1_server1_floatingip, "rsp1": address_list}) + self._check_server_connectivity(topo1_server1_floatingip, address_list, + should_connect=True) diff --git a/vmware_nsx_tempest/tests/templates/nsxv_neutron_smoke.yaml b/vmware_nsx_tempest/tests/templates/nsxv_neutron_smoke.yaml new file mode 100644 index 0000000000..edfde6a9ea --- /dev/null +++ b/vmware_nsx_tempest/tests/templates/nsxv_neutron_smoke.yaml @@ -0,0 +1,453 @@ +heat_template_version: 2013-05-23 + +description: > + Topology 1: + - 4 servers (Cirros)) + - 2 Logical Switches + - 1 Logical Router (Shared) + - 2 Security Group allowing HTTP + Topology 2: + - 2 servers (Cirros)) + - 2 Logical Switch + - 1 Logical Router (Exclusive) + - 1 Security Group allowing HTTP + +parameters: + + public_net: + label: Public Network ID for external connectivity + type: string + description: > + ID or name of public network + # Need to update this network UUID for each vPod. + default: ext-net + dmz_network: + default: ext-net + description: "External network" + type: string + ubuntu_image: + default: cirros + description: "Ubuntu image" + type: string + + +resources: + +# Topology1 + + heat_NAT_web_net: + type: OS::Neutron::Net + properties: + name: heat_NAT_web + + heat_NAT_web_subnet: + type: OS::Neutron::Subnet + properties: + network_id: { get_resource: heat_NAT_web_net } + cidr: 10.21.1.0/24 + dns_nameservers: [ "10.166.17.90" ] + + heat_NAT_db_net: + type: OS::Neutron::Net + properties: + name: heat_NAT_db + + heat_NAT_db_subnet: + type: OS::Neutron::Subnet + properties: + network_id: { get_resource: heat_NAT_db_net } + cidr: 10.21.2.0/24 + dns_nameservers: [ "10.166.17.90" ] + + my_key: + type: OS::Nova::KeyPair + properties: + save_private_key: true + name: my_key + + router: + type: OS::Neutron::Router + properties: + admin_state_up: true + name: heat_NAT_router + + router_gw: + type: OS::Neutron::RouterGateway + properties: + network_id: { get_param: public_net} + router_id: { get_resource: router } + + router_interface1: + type: OS::Neutron::RouterInterface + properties: + router_id: { get_resource: router } + subnet_id: { get_resource: heat_NAT_web_subnet } + + router_interface2: + type: OS::Neutron::RouterInterface + properties: + router_id: { get_resource: router } + subnet_id: { get_resource: heat_NAT_db_subnet } + + + heat_NAT_web_secgroup: + type: OS::Neutron::SecurityGroup + properties: + name: heat_NAT_web_secgroup + rules: + - protocol: tcp + remote_ip_prefix: 0.0.0.0/0 + port_range_min: 443 + port_range_max: 443 + - protocol: tcp + remote_ip_prefix: 0.0.0.0/0 + port_range_min: 22 + port_range_max: 22 + - protocol: icmp + remote_ip_prefix: 0.0.0.0/0 + + heat_NAT_db_secgroup: + type: OS::Neutron::SecurityGroup + properties: + name: heat_NAT_db_secgroup + rules: + - protocol: tcp + remote_mode: remote_group_id + remote_group_id: { get_resource: heat_NAT_web_secgroup } + port_range_min: 3307 + port_range_max: 3307 + - protocol: icmp + remote_ip_prefix: 0.0.0.0/0 + + server1_port: + type: OS::Neutron::Port + properties: + network_id: { get_resource: heat_NAT_web_net } + security_groups: + - { get_resource: heat_NAT_web_secgroup } + + server1_instance: + type: OS::Nova::Server + properties: + image: cirros + flavor: m1.tiny + key_name: { get_resource: my_key } + networks: + - port: { get_resource: server1_port } + + server1_floating_ip: + type: OS::Neutron::FloatingIP + properties: + floating_network_id: { get_param: public_net } + port_id: { get_resource: server1_port } + + + server2_port: + type: OS::Neutron::Port + properties: + network_id: { get_resource: heat_NAT_db_net } + security_groups: + - { get_resource: heat_NAT_db_secgroup } + + server2_instance: + type: OS::Nova::Server + properties: + image: cirros + flavor: m1.tiny + key_name: { get_resource: my_key } + networks: + - port: { get_resource: server2_port } + + server3_port: + type: OS::Neutron::Port + properties: + network_id: { get_resource: heat_NAT_db_net } + security_groups: + - { get_resource: heat_NAT_db_secgroup } + + + server3_instance: + type: OS::Nova::Server + properties: + image: cirros + flavor: m1.tiny + key_name: { get_resource: my_key } + networks: + - port: { get_resource: server3_port } + + server4_port: + type: OS::Neutron::Port + properties: + network_id: { get_resource: heat_NAT_web_net } + security_groups: + - { get_resource: heat_NAT_web_secgroup } + + server4_instance: + type: OS::Nova::Server + properties: + image: cirros + flavor: m1.tiny + key_name: { get_resource: my_key } + networks: + - port: { get_resource: server4_port } + +# Topology2 + + dmz_router: + properties: + admin_state_up: true + external_gateway_info: + network: + get_param: dmz_network + name: + Fn::Join: + - '_' + - [get_param: "OS::stack_name", "DmzGateway"] + value_specs: + router_type: exclusive + type: "OS::Neutron::Router" + floatingip_jump: + properties: + floating_network: + get_param: dmz_network + type: "OS::Neutron::FloatingIP" + floatingip_jump_association: + depends_on: + - floatingip_jump + - server_jump1 + - router_interface_subnet_mgmt_dmz + properties: + floating_ip: + get_resource: floatingip_jump + server_id: + get_resource: server_jump1 + type: "OS::Nova::FloatingIPAssociation" + network_mgmt: + properties: + admin_state_up: true + name: + Fn::Join: + - '_' + - [get_param: "OS::stack_name", "mgmt"] + shared: false + type: "OS::Neutron::Net" + network_mgmt2: + properties: + admin_state_up: true + name: + Fn::Join: + - '_' + - [get_param: "OS::stack_name", "mgmt2"] + shared: false + type: "OS::Neutron::Net" + port_dmz_jump: + depends_on: + - security_group + - subnet_mgmt + properties: + fixed_ips: + - ip_address: "50.0.0.10" + security_groups: + - get_resource: security_group + network_id: + get_resource: network_mgmt + type: "OS::Neutron::Port" + port_dmz_jump2: + depends_on: + - security_group + - subnet_mgmt + properties: + fixed_ips: + - ip_address: "60.0.0.10" + security_groups: + - get_resource: security_group + network_id: + get_resource: network_mgmt2 + type: "OS::Neutron::Port" + port_mgmt_dmz_router: + depends_on: + - security_group + - subnet_mgmt + properties: + fixed_ips: + - ip_address: "50.0.0.254" + network_id: + get_resource: network_mgmt + security_groups: + - get_resource: security_group + type: "OS::Neutron::Port" + router_interface_subnet_mgmt_dmz: + depends_on: + - dmz_router + - port_mgmt_dmz_router + properties: + port_id: + get_resource: port_mgmt_dmz_router + router_id: + get_resource: dmz_router + type: "OS::Neutron::RouterInterface" + port_mgmt_dmz_router2: + depends_on: + - security_group + - subnet_mgmt2 + properties: + fixed_ips: + - ip_address: "60.0.0.254" + network_id: + get_resource: network_mgmt2 + security_groups: + - get_resource: security_group + type: "OS::Neutron::Port" + router_interface_subnet_mgmt_dmz2: + depends_on: + - dmz_router + - port_mgmt_dmz_router2 + properties: + port_id: + get_resource: port_mgmt_dmz_router2 + router_id: + get_resource: dmz_router + type: "OS::Neutron::RouterInterface" + security_group: + properties: + description: "Allows all" + name: + Fn::Join: + - '_' + - [get_param: "OS::stack_name", "Permissive"] + rules: + - + direction: ingress + ethertype: IPv4 + port_range_max: 65535 + port_range_min: 1 + protocol: tcp + remote_ip_prefix: 0.0.0.0/0 + - + direction: ingress + ethertype: IPv4 + port_range_max: 65535 + port_range_min: 1 + protocol: udp + remote_ip_prefix: 0.0.0.0/0 + - + direction: ingress + ethertype: IPv4 + protocol: icmp + remote_ip_prefix: 0.0.0.0/0 + - + direction: egress + ethertype: IPv4 + port_range_max: 65535 + port_range_min: 1 + protocol: tcp + remote_ip_prefix: 0.0.0.0/0 + - + direction: egress + ethertype: IPv4 + port_range_max: 65535 + port_range_min: 1 + protocol: udp + remote_ip_prefix: 0.0.0.0/0 + - + direction: egress + ethertype: IPv4 + protocol: icmp + remote_ip_prefix: 0.0.0.0/0 + type: "OS::Neutron::SecurityGroup" + server_jump1: + depends_on: + - port_dmz_jump + properties: + diskConfig: MANUAL + flavor: m1.tiny + image: + get_param: ubuntu_image + key_name: { get_resource: my_key } + name: + Fn::Join: + - '_' + - [get_param: "OS::stack_name", "JumpServer1"] + networks: + - port: + get_resource: port_dmz_jump + networks: + - port: + get_resource: port_dmz_jump + type: "OS::Nova::Server" + subnet_mgmt: + depends_on: + - network_mgmt + properties: + allocation_pools: + - + end: "50.0.0.250" + start: "50.0.0.2" + cidr: 50.0.0.0/24 + dns_nameservers: + - "172.17.100.11" + enable_dhcp: true + ip_version: 4 + name: + Fn::Join: + - '_' + - [get_param: "OS::stack_name", "DMZSubnet"] + network_id: + get_resource: network_mgmt + type: "OS::Neutron::Subnet" + subnet_mgmt2: + depends_on: + - network_mgmt2 + properties: + allocation_pools: + - + end: "60.0.0.250" + start: "60.0.0.2" + cidr: 60.0.0.0/24 + dns_nameservers: + - "172.17.100.11" + enable_dhcp: true + ip_version: 4 + name: + Fn::Join: + - '_' + - [get_param: "OS::stack_name", "DMZSubnet2"] + network_id: + get_resource: network_mgmt2 + type: "OS::Neutron::Subnet" + server_jump2: + properties: + diskConfig: MANUAL + flavor: m1.tiny + image: + get_param: ubuntu_image + key_name: { get_resource: my_key } + name: + Fn::Join: + - '_' + - [get_param: "OS::stack_name", "JumpServer2"] + networks: + - port: + get_resource: port_dmz_jump2 + depends_on: [ port_dmz_jump2 ] + type: OS::Nova::Server + +outputs: + topo1_server1_floatingip: + description: Floating IP address of Topology1_Server1_floatingip + value: { get_attr: [ server1_floating_ip, floating_ip_address ] } + topo1_server1_private_ip: + description: Private IP address of the deployed compute instance + value: { get_attr: [server1_instance, networks, heat_NAT_web, 0] } + topo1_server2_private_ip: + description: Private IP address of the deployed compute instance + value: { get_attr: [server2_instance, networks, heat_NAT_db, 0] } + topo1_server3_private_ip: + description: Private IP address of the deployed compute instance + value: { get_attr: [server3_instance, networks, heat_NAT_db, 0] } + topo1_server4_private_ip: + description: Private IP address of the deployed compute instance + value: { get_attr: [server4_instance, networks, heat_NAT_web, 0] } + private_key: + description: Private key + value: { get_attr: [ my_key, private_key ] }