Tempest: NSX-v external network supports multiple subnets

NSX-v at VIO-2.5 release supports external network with multiple subnets.
However, this feature requires network admin to configure the physcial router
to route the 2nd subnet CIDR to the OS environment.

In our QA environment, we are not able to configure physical router to route
the 2nd subnet, this test can only be executed at devstack environment.
And ONLY DO:

   1. PING to servers' floating-ip
   2. SSH sever using its floating-ip
   3. From server it can ping other server's private address.

For VIO or other environments, you need to have the right to change physcial
router, and change tempest.conf accoridingly.

3 tests in this module, so run this test sequencially.

Change-Id: I5fc0d137f5b95101bf7b9485a2ca03e61d13d766
This commit is contained in:
Alex Kang 2016-06-06 21:14:56 -07:00
parent 841f3a631b
commit fc05b43d32
4 changed files with 353 additions and 37 deletions

View File

@ -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

View File

@ -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']:

View File

@ -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

View File

@ -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 <tests>
"""
@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)