diff --git a/vmware_nsx_tempest/config.py b/vmware_nsx_tempest/config.py index 0673a3bff9..fc86971769 100644 --- a/vmware_nsx_tempest/config.py +++ b/vmware_nsx_tempest/config.py @@ -32,10 +32,17 @@ ScenarioGroup = [ " & DNS are local to provider's settings."), cfg.DictOpt('flat_alloc_pool_dict', default={}, - help=" Define flat network ip range." + help="Define flat network ip range." " required attributes are gateway, start, end" " and cidr. Example value: gateway:10.1.1.253," " start:10.1.1.30,end:10.1.1.49,cidr=10.1.1.0/24"), + cfg.DictOpt('xnet_multiple_subnets_dict', + default={}, + help="External network with multiple subnets." + " The primary subnet ip-range will be shrinked," + " This is for the 2nd subnet, required attrs:" + " start:10.1.1.31,end:10.1.1.33,cidr=10.1.2.0/24" + " AND limit to only 3 ip addresses defined."), ] network_group = config.network_group diff --git a/vmware_nsx_tempest/tests/nsxv/scenario/manager_topo_deployment.py b/vmware_nsx_tempest/tests/nsxv/scenario/manager_topo_deployment.py index 13e46086de..722cb03604 100644 --- a/vmware_nsx_tempest/tests/nsxv/scenario/manager_topo_deployment.py +++ b/vmware_nsx_tempest/tests/nsxv/scenario/manager_topo_deployment.py @@ -14,6 +14,7 @@ # under the License. import collections +from fixtures._fixtures import timeout as fixture_timeout import os import re import subprocess @@ -21,6 +22,7 @@ import time import traceback import net_resources +import netaddr from tempest.common.utils.linux import remote_client from tempest.common import waiters @@ -28,7 +30,6 @@ from tempest import config from tempest.scenario import manager from tempest import test -import netaddr from tempest.lib.common.utils import data_utils from tempest.lib import exceptions @@ -133,8 +134,8 @@ class TopoDeployScenarioManager(manager.NetworkScenarioTest): tenant_id = routers_client.tenant_id distributed = kwargs.pop('distributed', None) router_type = kwargs.pop('router_type', None) - if distributed in (True, False): - kwargs['distributed'] = distributed + if distributed: + kwargs['distributed'] = True elif router_type in ('shared', 'exclusive'): kwargs['router_type'] = router_type name = data_utils.rand_name(namestart) @@ -253,23 +254,25 @@ class TopoDeployScenarioManager(manager.NetworkScenarioTest): def setup_project_network(self, external_network_id, client_mgr=None, namestart=None, client=None, - tenant_id=None, cidr_offset=0): + tenant_id=None, cidr_offset=0, + **kwargs): """NOTE: Refer to create_networks@scenario/manager.py which might refer to public_router_id which we dont' want to use. The test class can define class variable tenant_router_attrs - to create different type of routers. + to create different type of routers, or overwrite with kwargs. """ - # namestart = namestart if namestart else 'topo-deploy-tenant' name = namestart or data_utils.rand_name('topo-deploy-tenant') client_mgr = client_mgr or self.manager - # _create_router() editing distributed and router_type - distributed = self.tenant_router_attrs.get('distributed') - router_type = self.tenant_router_attrs.get('router_type') - # child class use class var tenant_router_attrs to define - # tenant's router type. + # _create_router() edits distributed and router_type + # Child classes use class var tenant_router_attrs to define + # tenant's router type, however, caller can overwrite it with kwargs. + distributed = kwargs.get('distributed', + self.tenant_router_attrs.get('distributed')) + router_type = kwargs.get('router_type', + self.tenant_router_attrs.get('router_type')) net_router = self._create_router( client_mgr=client_mgr, tenant_id=tenant_id, namestart=name, @@ -460,6 +463,27 @@ class TopoDeployScenarioManager(manager.NetworkScenarioTest): def get_server_flavor(self): return CONF.compute.flavor_ref + # replaced by call_and_ignore_notfound_exc method + # at tempest/lib/common/utils/test_utils.py + def delete_wrapper(self, delete_thing, *args, **kwargs): + """Ignores NotFound exceptions for delete operations. + + @param delete_thing: delete method of a resource. method will be + executed as delete_thing(*args, **kwargs) + + """ + try: + delete_thing(*args, **kwargs) + except exceptions.NotFound: + # If the resource is already missing, mission accomplished. + pass + except fixture_timeout.TimeoutException: + # one more time + try: + delete_thing(*args, **kwargs) + except exceptions.NotFound: + pass + # common utilities def make_node_info(net_floatingip, username, password, @@ -690,7 +714,7 @@ def get_remote_client_by_password(client_ip, username, password): return ssh_client -def delete_all_servers(tenant_servers_client, trys=3): +def delete_all_servers(tenant_servers_client, trys=5): # try at least trys+1 time to delete servers, otherwise # network resources can not be deleted for s in tenant_servers_client.list_servers()['servers']: diff --git a/vmware_nsx_tempest/tests/nsxv/scenario/net_resources.py b/vmware_nsx_tempest/tests/nsxv/scenario/net_resources.py index 1355e653f3..64686e8b52 100644 --- a/vmware_nsx_tempest/tests/nsxv/scenario/net_resources.py +++ b/vmware_nsx_tempest/tests/nsxv/scenario/net_resources.py @@ -69,26 +69,14 @@ class DeletableRouter(n_resources.DeletableRouter): return self.add_interface(subnet) def add_interface(self, subnet): - # should not let subnet add interface to router as - # the router might be crated by admin. - try: - self.client.add_router_interface( - self.id, subnet_id=subnet.id) - except Exception: - x_method(self.client, 'add_router_interface_with_subnet_id', - self.id, subnet_id=subnet.id) + self.client.add_router_interface(self.id, subnet_id=subnet.id) self._subnets.add(subnet) def delete_subnet(self, subnet): return self.delete_interface(subnet) def delete_interface(self, subnet): - try: - self.client.remove_router_interface( - self.id, subnet_id=subnet.id) - except Exception: - x_method(self.client, 'remove_router_interface_with_subnet_id', - self.id, subnet_id=subnet.id) + self.client.remove_router_interface(self.id, subnet_id=subnet.id) self._subnets.remove(subnet) def update_extra_routes(self, nexthop, destination): @@ -110,13 +98,3 @@ class DeletableRouter(n_resources.DeletableRouter): for subnet in self._subnets.copy(): self.delete_interface(subnet) super(DeletableRouter, self).delete() - - -# Workaround solution -def x_method(target_obj, method_name, *args, **kwargs): - _method = getattr(target_obj, method_name, None) - if _method is None: - raise Exception("Method[%s] is not defined at instance[%s]" % - (method_name, str(target_obj))) - results = _method(*args, **kwargs) - return results diff --git a/vmware_nsx_tempest/tests/nsxv/scenario/test_xnet_multiple_subnets_basic_ops.py b/vmware_nsx_tempest/tests/nsxv/scenario/test_xnet_multiple_subnets_basic_ops.py new file mode 100644 index 0000000000..82bb62d5d9 --- /dev/null +++ b/vmware_nsx_tempest/tests/nsxv/scenario/test_xnet_multiple_subnets_basic_ops.py @@ -0,0 +1,307 @@ +# Copyright 2016 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 time + +from tempest.common import waiters +from tempest import config +from tempest import test + +from vmware_nsx_tempest.tests.nsxv.scenario import ( + manager_topo_deployment as dmgr) + +CONF = config.CONF + + +class TestXnetMultiSubnetsOps(dmgr.TopoDeployScenarioManager): + """Test NSX external network can support multiple subnets/cidrs. + + With multiple subnets, VMs get its floatingip from all subnets + attached to the external network. + + This test validates that VM can get its floatingip from all subnets, + and are reachable. However due to the physical network routing issue, + we can only validate at devstack environment: + + 1. VM's floatingip is pingable + 2. can ssh to VM's floatingip. + 3. from VM can ping other VMs' private address. + + If this test fail and were not able to revert to its original subnet + ip ranges, other tempest tests require floatingip's might FAIL. + + The test will shrink the primary subnet range to 3 ip addresses. + Note: the 1st one is already used by the router1@devstack. + + The 2nd subnet is set with CONF.scenario.xnet_multiple_subnets_dict, + and no-gateway is required. Make sure the 2nd CIRD is reachable by + your devstack. + + LIMITATION: + This test can only be done at devstack environment, other environment, + for example VIO can not be executed unless you can modify the physical + network to route the 2nd subnet cidr to the OS environment. + + This test validates data-path from the devstack host itself: + 1. Ping to floating-ips + 2. ssh to VM + 3. from VM ping other VMs' private ip address + + ATTENTION: + Because, this test consumes floatingip's so both subnets ip-ranges + will be used. NO OTHER TESTS should run when execute this test. + + Run this test module sequencially : + + ./run_tempest.sh -t + """ + + @classmethod + def skip_checks(cls): + super(TestXnetMultiSubnetsOps, cls).skip_checks() + if not CONF.scenario.xnet_multiple_subnets_dict: + msg = 'scenario.xnet_multiple_subnets_dict must be set.' + raise cls.skipException(msg) + if not CONF.network.public_network_id: + msg = ('network.public_network_id must be defined.') + raise cls.skipException(msg) + + @classmethod + def resource_setup(cls): + super(TestXnetMultiSubnetsOps, cls).resource_setup() + cls.xnet_subnets = [None, None] + cls.public_network_id = CONF.network.public_network_id + # primary user + cls.primary_tenant_id = cls.manager.networks_client.tenant_id + cls.floating_ips_client = cls.manager.floating_ips_client + cls.servers_client = cls.manager.servers_client + + @classmethod + def resource_cleanup(cls): + cls.remove_this_test_resources() + super(TestXnetMultiSubnetsOps, cls).resource_cleanup() + + @classmethod + def remove_this_test_resources(cls): + dmgr.delete_all_servers(cls.manager.servers_client) + subnets_client = cls.admin_manager.subnets_client + subnet_1 = cls.xnet_subnets[0] + subnet_2 = cls.xnet_subnets[1] + if subnet_2: + subnets_client.delete_subnet(subnet_2['id']) + cls.xnet_subnets[1] = None + if subnet_1: + subnets_client.update_subnet( + subnet_1['id'], + allocation_pools=subnet_1['allocation_pools']) + cls.xnet_subnets[0] = None + + @classmethod + def create_no_gateway_subnet(cls, network_id, cidr, allocation_pool, + ip_version=4, dns_nameservers=None, + name=None, client_mgr=None, **kwargs): + """Subnets, except the 1st one, no-gateway should be applied.""" + + client_mgr = client_mgr or cls.admin_manager + subnets_client = client_mgr.subnets_client + post_body = {'network_id': network_id, + 'cidr': cidr, + 'allocation_pools': [allocation_pool], + 'ip_version': ip_version, + 'gateway_ip': None, + 'enable_dhcp': False} + if name: + post_body['name'] = name + if dns_nameservers: + post_body['dns_nameservers'] = dns_nameservers + body = subnets_client.create_subnet(**post_body) + subnet_2 = subnets_client.show_subnet(body['subnet']['id']) + # no addCleanup, it is to be done at tearDown + return subnet_2['subnet'] + + def setUp(self): + """Create the 2nd subnet attached to public network. + + Idealy this is at class method. However we need to validate that + the public network and its subnets are correctly configured. + + External network/subnet configured here, so assert* can be called. + """ + super(TestXnetMultiSubnetsOps, self).setUp() + # only admin user can manage external network/subnets + networks_client = self.admin_manager.networks_client + subnets_client = self.admin_manager.subnets_client + self.sub2_dict = CONF.scenario.xnet_multiple_subnets_dict + # limited to only one subnet available when test started. + subnet_id_list = networks_client.show_network( + self.public_network_id)["network"]["subnets"] + self.assertEqual(1, len(subnet_id_list)) + subnet_1 = subnets_client.show_subnet( + subnet_id_list[0])["subnet"] + self.assertEqual(1, len(subnet_1["allocation_pools"])) + pool_start = subnet_1["allocation_pools"][0]["start"] + iprange = pool_start.split(".") + iprange[3] = str(int(iprange[3]) + 3) + pool_end = ".".join(iprange) + sub1_allocation = {'start': pool_start, 'end': pool_end} + self.xnet_subnets[0] = subnet_1 + # update the 1st subnet so it only has 3 ip addresses + subnet1 = subnets_client.update_subnet( + subnet_1['id'], + allocation_pools=[sub1_allocation])['subnet'] + alloc_pool1 = subnet1['allocation_pools'] + self.assertEqual(1, len(alloc_pool1)) + alloc_pool1 = alloc_pool1[0] + self.assertEqual(pool_start, alloc_pool1['start']) + self.assertEqual(pool_end, alloc_pool1['end']) + # create the 2nd subnet under external network + alloc_pool2 = {'start': self.sub2_dict['start'], + 'end': self.sub2_dict['end']} + dns_nameservers = subnet_1['dns_nameservers'] + subnet_2 = self.create_no_gateway_subnet( + subnet_1['network_id'], cidr=self.sub2_dict['cidr'], + allocation_pool=alloc_pool2, dns_nameservers=dns_nameservers, + name='public-xnet-subnet2') + self.xnet_subnets[1] = subnet_2 + self.my_network = None + self.user_sg = self._create_security_group( + security_groups_client=self.manager.security_groups_client, + namestart='xnet-subnets') + + def tearDown(self): + if self.my_network: + self.delete_floatingips_and_servers() + if self.my_network['router']: + self.delete_wrapper(self.my_network['router'].delete) + # Delete subnet - distributed router take longer time. + if self.my_network['subnet']: + self.delete_wrapper(self.my_network['subnet'].delete) + if self.my_network['network']: + self.delete_wrapper(self.my_network['network'].delete) + super(TestXnetMultiSubnetsOps, self).tearDown() + + def create_user_servers(self, num_servers=5): + network = self.my_network['network'] + user_sg = [{'name': self.user_sg['id']}] + self.my_network['servers'] = [] + server_id_list = [] + for num in range(0, num_servers): + vm_name = 'xnet-subnet-%d' % (num + 1) + sv = self.create_server_on_network( + network, + security_groups=user_sg, + name=vm_name, wait_on_boot=False) + self.my_network['servers'].append(sv) + server_id_list.append(sv['id']) + self.wait_for_servers_become_active(server_id_list, + self.servers_client) + + def wait_for_servers_become_active(self, server_id_list, + servers_client): + for server_id in server_id_list: + waiters.wait_for_server_status( + servers_client, server_id, 'ACTIVE') + + def create_floatingips_and_assign_to_servers(self): + self.my_network['floatingips'] = [] + self.fixed_ip_addresses = [] + for sv in self.my_network['servers']: + floatingip, sshc = self.create_floatingip_for_server(sv) + self.my_network['floatingips'].append(floatingip) + self.fixed_ip_addresses.append(floatingip.fixed_ip_address) + # check inside this tenant network, all VMs are reachable. + self.validate_all_servers_private_address_are_reachable( + sshc, self.fixed_ip_addresses) + + def create_floatingip_for_server(self, server): + # project/tenant create the server, not the ADMIN + username, password = self.get_image_userpass() + # Only admin can create resource with tenant_id attributes, so + # always providing the admin_manager as client to create_floatingip + # as scenario/manager.py always insert tenant_id attribe + # while creating the serve.. + floatingip = super(TestXnetMultiSubnetsOps, + self).create_floatingip_for_server( + server, + external_network_id=self.public_network_id, + client_mgr=self.admin_manager) + msg = ("Associate floatingip[%s] to server[%s]" + % (floatingip, server['name'])) + self._check_floatingip_connectivity( + floatingip, server, should_connect=True, msg=msg) + serv_fip = floatingip.floating_ip_address + dmgr.rm_sshkey(serv_fip) + ssh_client = dmgr.get_remote_client_by_password( + serv_fip, username, password) + return (floatingip, ssh_client) + + def delete_floatingips_and_servers(self): + for net_floatingip in self.my_network['floatingips']: + self.delete_wrapper(net_floatingip.delete) + fip_list = self.floating_ips_client.list_floatingips()['floatingips'] + if len(fip_list) > 0: + time.sleep(dmgr.WAITTIME_AFTER_DISASSOC_FLOATINGIP) + self.my_network['floatingips'] = [] + dmgr.delete_all_servers(self.servers_client) + + def validate_all_servers_private_address_are_reachable(self, + ssh_client, + ip_addresses): + for ip_addr in ip_addresses: + msg = "VM private address[%s] is not reachable." % ip_addr + reachable = dmgr.is_reachable(ssh_client, ip_addr) + self.assertTrue(reachable, msg) + + def _test_xnet_multiple_subnets_basic_ops(self, + router_type='exclusive', + distributed=None): + network, subnet, router = self.setup_project_network( + self.public_network_id, + client_mgr=self.admin_manager, + tenant_id=self.primary_tenant_id, + namestart='xnet-subnets', + router_type=router_type, distributed=distributed) + self.my_network = {'router': router, + 'subnet': subnet, + 'network': network, + 'servers': [], + 'floatingips': []} + self.create_user_servers() + self.create_floatingips_and_assign_to_servers() + self.delete_floatingips_and_servers() + + +class TestXnetMultiSubnetsOpsOnSharedRouter(TestXnetMultiSubnetsOps): + + @test.idempotent_id('e25d030f-7fdf-4500-bd55-4ed6f62c0a5c') + def test_xnet_multiple_subnets_basic_ops_on_shared_router(self): + return self._test_xnet_multiple_subnets_basic_ops( + 'shared', False) + + +class TestXnetMultiSubnetsOpsOnExclusiveRouter(TestXnetMultiSubnetsOps): + + @test.idempotent_id('5b09351a-0560-4555-99f0-a1f80d54d435') + def test_xnet_multiple_subnets_basic_ops_on_exclusive_router(self): + return self._test_xnet_multiple_subnets_basic_ops( + 'exclusive', False) + + +class TestXnetMultiSubnetsOpsOnDistributedRouter(TestXnetMultiSubnetsOps): + + @test.idempotent_id('9652d36b-8816-4212-a6e1-3a8b2580deee') + def test_xnet_multiple_subnets_basic_ops_on_distributed_router(self): + return self._test_xnet_multiple_subnets_basic_ops( + '', True)