init release of vmware-nsx tempest tests in tempest external plugin
installation procedure & tech-notes at vmware_nsx_tempest/README.rst With this plugin method, vmware_nsx_tempest tests can be treated as tempest tests and executed under tempest environment. Fix nsxv_client to support multiple transport zones. Change-Id: Id103c0ce03d75749fe6295108db48493f565b05a Implements: blueprint vmware-nsx-tempest-plugin
This commit is contained in:
parent
8785d5fdde
commit
00c98dce02
@ -39,6 +39,9 @@ vmware_nsx.neutron.nsxv.router_type_drivers =
|
||||
shared = vmware_nsx.plugins.nsx_v.drivers.shared_router_driver:RouterSharedDriver
|
||||
distributed = vmware_nsx.plugins.nsx_v.drivers.distributed_router_driver:RouterDistributedDriver
|
||||
exclusive = vmware_nsx.plugins.nsx_v.drivers.exclusive_router_driver:RouterExclusiveDriver
|
||||
tempest.test_plugins =
|
||||
vmware-nsx-tempest-plugin = vmware_nsx_tempest.plugin:VMwareNsxTempestPlugin
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
build-dir = doc/build
|
||||
|
87
vmware_nsx_tempest/README.rst
Normal file
87
vmware_nsx_tempest/README.rst
Normal file
@ -0,0 +1,87 @@
|
||||
Welcome!
|
||||
========
|
||||
vmware_nsx_tempest is a plugin module to openstack tempest project.
|
||||
|
||||
If you are not familiar with tempest, please refer to:
|
||||
|
||||
http://docs.openstack.org/developer/tempest
|
||||
|
||||
It is implemented with tempest external plugin. The official design
|
||||
sepcification is at https://review.openstack.org/#/c/184992/
|
||||
|
||||
vmware_nsx_tempest development and execution guide
|
||||
==================================================
|
||||
|
||||
vmware_nsx_tempest hosts vmware_nsx's functional api and scenario tests.
|
||||
|
||||
All vmware_nsx_tempest tests are in "master" branch. For this reason,
|
||||
it is recommended to have your own developer version of vmware-nsx repo
|
||||
installed outside the devstack folder, /opt/stack/.
|
||||
|
||||
For example at /opt/devtest folder. In doing so, you can install
|
||||
editable vmware-nsx repo under tempest VENV environemnt.
|
||||
|
||||
Installation:
|
||||
-------------
|
||||
|
||||
Installed at your own development env, for example /opt/devtest/:
|
||||
|
||||
cd /opt/devtest
|
||||
git clone https://github.com/openstack/vmware-nsx
|
||||
|
||||
Assume the tempest directory is at /opt/devtest/os-tempest.
|
||||
|
||||
cd /opt/devtest/os-tempest
|
||||
source .venv/bin/activate
|
||||
pip install -e /opt/devtest/vmware-nsx/
|
||||
|
||||
run command
|
||||
pip show vmware-nsx
|
||||
and you should observe the following statements:
|
||||
Location: /opt/devtest/vmware-nsx
|
||||
and under section of Entry-points:
|
||||
[tempest.test_plugins]
|
||||
vmware-nsx-tempest-plugin = vmware_nsx_tempest.plugin:VMwareNsxTempestPlugin
|
||||
|
||||
Validate installed vmware_nsx_tempest succesfully do:
|
||||
|
||||
cd /opt/devtest/os-tempest
|
||||
tools/with_venv.sh testr list-tests vmware_nsx_tempest.*l2_gateway
|
||||
|
||||
if no test lists created, your installation failed.
|
||||
|
||||
Execution:
|
||||
----------
|
||||
|
||||
vmware_nsx_tempest tests are tempest tests, you need to
|
||||
run from tempest directory. For example, to run only l2-gateway tests:
|
||||
|
||||
cd /opt/devtest/os-tempest
|
||||
./run_tempest.sh -t vmware_nsx_tempest.*test_l2_gateway
|
||||
./run_tempest.sh -d vmware_nsx_tempest.tests.nsxv.api.test_l2_gateway_connection.L2GatewayConnectionTest.test_csuld_single_device_interface_vlan
|
||||
|
||||
TechNote on vmware_nsx_tempest:
|
||||
-------------------------------
|
||||
|
||||
vmware_nsx_tempest is a plugin to tempest, not neutron, nor vmware_nsx.
|
||||
|
||||
It is defined by tempest.test_plugins.
|
||||
|
||||
Modules within vmware_nsx_tempest can not see resources defined
|
||||
by vmware_nsx. Commands like following are not acceptable, unless
|
||||
vmware_nsx is installed in your tempest environment.
|
||||
|
||||
from vmware_nsx._i18n import _LI, _LE
|
||||
import vmware_nsx.shell.admin.plugins.common.utils as admin_utils
|
||||
|
||||
TechNote on logging:
|
||||
--------------------
|
||||
tempest repo itself does not enforce LOG complying to _i18n.
|
||||
So for tempest tests for vmware-nsx, that is vmware_nsx_tempest, should
|
||||
use LOG.debug() command.
|
||||
|
||||
If you need to log other than debug level, please do this:
|
||||
|
||||
from vmware_nsx_tempest._i18n import _LI, _LE, _LW, _LC
|
||||
|
||||
Customize it depending on the log level your scripts will use.
|
19
vmware_nsx_tempest/__init__.py
Normal file
19
vmware_nsx_tempest/__init__.py
Normal file
@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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 pbr.version
|
||||
|
||||
|
||||
__version__ = pbr.version.VersionInfo(
|
||||
'vmware_nsx_tempest').version_string()
|
42
vmware_nsx_tempest/_i18n.py
Normal file
42
vmware_nsx_tempest/_i18n.py
Normal file
@ -0,0 +1,42 @@
|
||||
# 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 oslo_i18n
|
||||
|
||||
DOMAIN = "vmware-nsx-tempest"
|
||||
|
||||
_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN)
|
||||
|
||||
# The primary translation function using the well-known name "_"
|
||||
_ = _translators.primary
|
||||
|
||||
# The contextual translation function using the name "_C"
|
||||
_C = _translators.contextual_form
|
||||
|
||||
# The plural translation function using the name "_P"
|
||||
_P = _translators.plural_form
|
||||
|
||||
# Translators for log levels.
|
||||
#
|
||||
# The abbreviated names are meant to reflect the usual use of a short
|
||||
# name like '_'. The "L" is for "log" and the other letter comes from
|
||||
# the level.
|
||||
_LI = _translators.log_info
|
||||
_LW = _translators.log_warning
|
||||
_LE = _translators.log_error
|
||||
_LC = _translators.log_critical
|
||||
|
||||
|
||||
def get_available_languages():
|
||||
return oslo_i18n.get_available_languages(DOMAIN)
|
102
vmware_nsx_tempest/config.py
Normal file
102
vmware_nsx_tempest/config.py
Normal file
@ -0,0 +1,102 @@
|
||||
# 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
from tempest import config
|
||||
|
||||
scenario_group = config.scenario_group
|
||||
ScenarioGroup = [
|
||||
cfg.FloatOpt('waitfor_disassoc',
|
||||
default=15.0,
|
||||
help="Wait for seconds after disassociation."),
|
||||
cfg.FloatOpt('waitfor_assoc',
|
||||
default=5.0,
|
||||
help="Waitfor seconds after association."),
|
||||
cfg.FloatOpt('waitfor_connectivity',
|
||||
default=120.0,
|
||||
help="Wait for seconds to become connected."),
|
||||
cfg.ListOpt('outside_world_servers',
|
||||
default=["8.8.8.8", "8.8.4.4"],
|
||||
help="List of servers reside outside of openstack env."
|
||||
" which is used to test default gateway behavior"
|
||||
" when VMs are under logical routers,"
|
||||
" & DNS are local to provider's settings."),
|
||||
cfg.DictOpt('flat_alloc_pool_dict',
|
||||
default={},
|
||||
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"),
|
||||
]
|
||||
|
||||
network_group = config.network_group
|
||||
NetworkGroup = [
|
||||
cfg.StrOpt('l2gw_switch',
|
||||
default='',
|
||||
help="Distributed Virtual Portgroup to create VLAN port."),
|
||||
cfg.DictOpt('l2gw_switch_dict',
|
||||
default={},
|
||||
help="dict version of l2gw_switch:"
|
||||
"device_name:,interfaces:,segmentation_id:,"),
|
||||
]
|
||||
|
||||
nsxv_group = cfg.OptGroup(name='nsxv',
|
||||
title="NSX-v Configuration Options")
|
||||
NSXvGroup = [
|
||||
cfg.StrOpt('manager_uri',
|
||||
default='https://10.0.0.10',
|
||||
help="NSX-v manager ip address"),
|
||||
cfg.StrOpt('user',
|
||||
default='admin',
|
||||
help="NSX-v manager username"),
|
||||
cfg.StrOpt('password',
|
||||
default='default',
|
||||
help="NSX-v manager password"),
|
||||
cfg.StrOpt('vdn_scope_id',
|
||||
default='vdnscope-1',
|
||||
help="NSX-v vdn scope id"),
|
||||
cfg.DictOpt('flat_alloc_pool_dict',
|
||||
default={},
|
||||
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.StrOpt('vlan_physical_network',
|
||||
default='',
|
||||
help="physval_network to create vlan."),
|
||||
cfg.IntOpt('provider_vlan_id',
|
||||
default=888,
|
||||
help="The default vlan_id for admin vlan."),
|
||||
]
|
||||
|
||||
|
||||
l2gw_group = cfg.OptGroup(name='l2gw',
|
||||
title="l2-gateway Configuration Options")
|
||||
L2gwGroup = [
|
||||
cfg.DictOpt('vlan_subnet_ipv4_dict',
|
||||
default={},
|
||||
help="Tenant's VLAN subnet cdir to connect to l2gw/VXLAN."
|
||||
" Example: cidr=192.168.99.0/24,start:192.168.99.41"
|
||||
" ,end:192.168.99.50,gateway=192.168.99.253"),
|
||||
cfg.StrOpt('device_one_vlan',
|
||||
default="",
|
||||
help="l2g2 device with one VLAN"
|
||||
" l2gw-1::dvportgroup-14420|3845"),
|
||||
cfg.StrOpt('device_multiple_vlans',
|
||||
default="",
|
||||
help="l2gw device with multiple VLANs"
|
||||
" l2gw-x::dvportgroup-14429|3880#3381#3382"),
|
||||
cfg.StrOpt('multiple_interfaces_multiple_vlans',
|
||||
default="",
|
||||
help="l2gw multiple devices, interface has multiple VLANs"
|
||||
" m-ifs::dvportgroup-144|138#246;dvportgroup-155|339"),
|
||||
]
|
58
vmware_nsx_tempest/plugin.py
Normal file
58
vmware_nsx_tempest/plugin.py
Normal file
@ -0,0 +1,58 @@
|
||||
# Copyright 2015 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
|
||||
|
||||
from tempest import config
|
||||
from tempest.test_discover import plugins
|
||||
|
||||
from vmware_nsx_tempest import config as config_nsx
|
||||
|
||||
|
||||
class VMwareNsxTempestPlugin(plugins.TempestPlugin):
|
||||
|
||||
"""Our addon configuration is defined at vmware_nsx_tempest/config.py
|
||||
|
||||
1. register_opts() to register group/opts to Tempest
|
||||
2. get_opt_lists() to pass config to Tempest
|
||||
|
||||
The official plugin is defined at
|
||||
http://docs.openstack.org/developer/tempest/plugin.html
|
||||
"""
|
||||
|
||||
def load_tests(self):
|
||||
mydir = os.path.dirname(os.path.abspath(__file__))
|
||||
base_path = os.path.split(mydir)[0]
|
||||
test_dir = "vmware_nsx_tempest/tests"
|
||||
test_fullpath = os.path.join(base_path, test_dir)
|
||||
return (test_fullpath, base_path)
|
||||
|
||||
def register_opts(self, conf):
|
||||
config.register_opt_group(
|
||||
conf,
|
||||
config_nsx.scenario_group, config_nsx.ScenarioGroup)
|
||||
config.register_opt_group(
|
||||
conf,
|
||||
config_nsx.network_group, config_nsx.NetworkGroup)
|
||||
config.register_opt_group(
|
||||
conf,
|
||||
config_nsx.nsxv_group, config_nsx.NSXvGroup)
|
||||
config.register_opt_group(
|
||||
conf,
|
||||
config_nsx.l2gw_group, config_nsx.L2gwGroup)
|
||||
|
||||
def get_opt_lists(self):
|
||||
return [(config_nsx.scenario_group.name, config_nsx.scenario_group)]
|
30
vmware_nsx_tempest/services/README.rst
Normal file
30
vmware_nsx_tempest/services/README.rst
Normal file
@ -0,0 +1,30 @@
|
||||
This folder contains services for managing NSX-t, NSX-v and
|
||||
neutron sub-services not yet migrating to tempest-lib.
|
||||
|
||||
Services provided:
|
||||
|
||||
# Openstack tempest service clients
|
||||
l2_gateway_client.py
|
||||
based on tempest BaseNetworkClient implements client APIs to manage
|
||||
neutron l2-gateway resources
|
||||
|
||||
l2_gateway_connection_client.py
|
||||
based on tempest BaseNetworkClient implements client APIs to manage
|
||||
neutron l2-gateway-connection resources
|
||||
|
||||
lbv1_client.py
|
||||
based on tempest BaseNetworkClient implements client APIs to manage
|
||||
neutron v1 load-balancer resources
|
||||
|
||||
network_client_base.py
|
||||
due to tempest network services are in the process of migrating to
|
||||
tempest-lib, some features to be used by tests are not in
|
||||
BaseNetworkClient. Inherent here and used by all vmware-nsx-tempest
|
||||
client for now.
|
||||
|
||||
# NSXv speific services
|
||||
nsxv_client.py which it has API ops on the following NSX-v components
|
||||
- Logical switch (Tenant network)
|
||||
- Edge (Service edge, DHCP edge, and VDR edge)
|
||||
- DFW firewall rules (Security group)
|
||||
- SpoofGuard
|
0
vmware_nsx_tempest/services/__init__.py
Normal file
0
vmware_nsx_tempest/services/__init__.py
Normal file
94
vmware_nsx_tempest/services/base_l2gw.py
Normal file
94
vmware_nsx_tempest/services/base_l2gw.py
Normal file
@ -0,0 +1,94 @@
|
||||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
SEGMENTATION_ID_DELIMITER = "#"
|
||||
INTERFACE_SEG_ID_DELIMITER = "|"
|
||||
DEVICE_INTERFACE_DELIMITER = "::"
|
||||
DEVICE_DELIMITER = ","
|
||||
INTERFACE_DELIMITER = ";"
|
||||
"""
|
||||
Sample for providing input for gateway creation in config is noted below
|
||||
Options provide flexibility to user to create l2gateway
|
||||
For single device ,single interface with single vlan
|
||||
l2gw_switch = device_name1::int_name1|vlan1
|
||||
For single device multiple interfaces with single or multiple vlans
|
||||
l2gw_switch = device_name1::int_name1|vlan1#vlan2;int_name2|vlan3
|
||||
For multiple devices with mutiple interfaces having single or mutiple vlan
|
||||
l2gw_switch = device_n1::int_n1|vlan1,device_n2::int_n2|vlan2#vlan3
|
||||
"""
|
||||
|
||||
|
||||
def get_interface(interfaces):
|
||||
interface_dict = []
|
||||
for interface in interfaces:
|
||||
if INTERFACE_SEG_ID_DELIMITER in interface:
|
||||
int_name = interface.split(INTERFACE_SEG_ID_DELIMITER)[0]
|
||||
segid = interface.split(INTERFACE_SEG_ID_DELIMITER)[1]
|
||||
if SEGMENTATION_ID_DELIMITER in segid:
|
||||
segid = segid.split(SEGMENTATION_ID_DELIMITER)
|
||||
else:
|
||||
segid = [segid]
|
||||
interface_detail = {'name': int_name, 'segmentation_id': segid}
|
||||
else:
|
||||
interface_detail = {'name': interface}
|
||||
interface_dict.append(interface_detail)
|
||||
return interface_dict
|
||||
|
||||
|
||||
def get_device_interface(device_name, interface):
|
||||
if INTERFACE_DELIMITER in interface:
|
||||
interface_dict = interface.split(INTERFACE_DELIMITER)
|
||||
interfaces = get_interface(interface_dict)
|
||||
else:
|
||||
interfaces = get_interface([interface])
|
||||
device = {'device_name': device_name,
|
||||
'interfaces': interfaces}
|
||||
return device
|
||||
|
||||
|
||||
def get_l2gw_body(l2gw_conf):
|
||||
device_dict = []
|
||||
devices = l2gw_conf.split(DEVICE_DELIMITER)
|
||||
for device in devices:
|
||||
if DEVICE_INTERFACE_DELIMITER in device:
|
||||
device_name = device.split(DEVICE_INTERFACE_DELIMITER)[0]
|
||||
interface = device.split(DEVICE_INTERFACE_DELIMITER)[1]
|
||||
device = get_device_interface(device_name, interface)
|
||||
device_dict.append(device)
|
||||
body = {'devices': device_dict}
|
||||
return body
|
||||
|
||||
|
||||
def form_dict_devices(devices):
|
||||
seg_ids = []
|
||||
devices1 = dict()
|
||||
int_seg = []
|
||||
for device in devices:
|
||||
device_name = device['device_name']
|
||||
interfaces = device['interfaces']
|
||||
for interface in interfaces:
|
||||
interface_name = interface['name']
|
||||
int_seg.append(interface_name)
|
||||
seg_id = interface['segmentation_id']
|
||||
if type(seg_id) is list:
|
||||
for segid in seg_id:
|
||||
seg_ids.append(segid)
|
||||
else:
|
||||
seg_ids.append(seg_id)
|
||||
int_seg.append(seg_id)
|
||||
devices1.setdefault(device_name, []).append(int_seg)
|
||||
int_seg = []
|
||||
return devices1
|
67
vmware_nsx_tempest/services/l2_gateway_client.py
Normal file
67
vmware_nsx_tempest/services/l2_gateway_client.py
Normal file
@ -0,0 +1,67 @@
|
||||
# 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.
|
||||
|
||||
from tempest.services.network.json import base
|
||||
|
||||
|
||||
class L2GatewayClient(base.BaseNetworkClient):
|
||||
resource = 'l2_gateway'
|
||||
resource_plural = 'l2_gateways'
|
||||
path = 'l2-gateways'
|
||||
resource_base_path = '/%s' % path
|
||||
resource_object_path = '/%s/%%s' % path
|
||||
|
||||
def create_l2_gateway(self, **kwargs):
|
||||
uri = self.resource_base_path
|
||||
post_data = {self.resource: kwargs}
|
||||
return self.create_resource(uri, post_data)
|
||||
|
||||
def update_l2_gateway(self, l2_gateway_id, **kwargs):
|
||||
uri = self.resource_object_path % l2_gateway_id
|
||||
post_data = {self.resource: kwargs}
|
||||
return self.update_resource(uri, post_data)
|
||||
|
||||
def show_l2_gateway(self, l2_gateway_id, **fields):
|
||||
uri = self.resource_object_path % l2_gateway_id
|
||||
return self.show_resource(uri, **fields)
|
||||
|
||||
def delete_l2_gateway(self, l2_gateway_id):
|
||||
uri = self.resource_object_path % l2_gateway_id
|
||||
return self.delete_resource(uri)
|
||||
|
||||
def list_l2_gateways(self, **filters):
|
||||
uri = self.resource_base_path
|
||||
return self.list_resources(uri, **filters)
|
||||
|
||||
|
||||
def get_client(client_mgr):
|
||||
"""create a l2-gateway client from manager or networks_client
|
||||
|
||||
For itempest user:
|
||||
from itempest import load_our_solar_system as osn
|
||||
from vmware_nsx_tempest.services import l2_gateway_client
|
||||
l2gw_client = l2_gateway_client.get_client(osn.adm.manager)
|
||||
For tempest user:
|
||||
l2gw_client = l2_gateway_client.get_client(osn.adm)
|
||||
"""
|
||||
manager = getattr(client_mgr, 'manager', client_mgr)
|
||||
net_client = getattr(manager, 'networks_client')
|
||||
try:
|
||||
_params = manager.default_params_with_timeout_values.copy()
|
||||
except Exception:
|
||||
_params = {}
|
||||
client = L2GatewayClient(net_client.auth_provider,
|
||||
net_client.service,
|
||||
net_client.region,
|
||||
net_client.endpoint_type,
|
||||
**_params)
|
||||
return client
|
67
vmware_nsx_tempest/services/l2_gateway_connection_client.py
Normal file
67
vmware_nsx_tempest/services/l2_gateway_connection_client.py
Normal file
@ -0,0 +1,67 @@
|
||||
# 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.
|
||||
|
||||
from tempest.services.network.json import base
|
||||
|
||||
|
||||
class L2GatewayConnectionClient(base.BaseNetworkClient):
|
||||
resource = 'l2_gateway_connection'
|
||||
resource_plural = 'l2_gateway_connections'
|
||||
path = 'l2-gateway-connections'
|
||||
resource_base_path = '/%s' % path
|
||||
resource_object_path = '/%s/%%s' % path
|
||||
|
||||
def create_l2_gateway_connection(self, **kwargs):
|
||||
uri = self.resource_base_path
|
||||
post_data = {self.resource: kwargs}
|
||||
return self.create_resource(uri, post_data)
|
||||
|
||||
def update_l2_gateway_connection(self, l2_gateway_id, **kwargs):
|
||||
uri = self.resource_object_path % l2_gateway_id
|
||||
post_data = {self.resource: kwargs}
|
||||
return self.update_resource(uri, post_data)
|
||||
|
||||
def show_l2_gateway_connection(self, l2_gateway_id, **fields):
|
||||
uri = self.resource_object_path % l2_gateway_id
|
||||
return self.show_resource(uri, **fields)
|
||||
|
||||
def delete_l2_gateway_connection(self, l2_gateway_id):
|
||||
uri = self.resource_object_path % l2_gateway_id
|
||||
return self.delete_resource(uri)
|
||||
|
||||
def list_l2_gateway_connections(self, **filters):
|
||||
uri = self.resource_base_path
|
||||
return self.list_resources(uri, **filters)
|
||||
|
||||
|
||||
def get_client(client_mgr):
|
||||
"""create a l2-gateway client from manager or networks_client
|
||||
|
||||
For itempest user:
|
||||
from itempest import load_our_solar_system as osn
|
||||
from vmware_nsx_tempest.services import l2_gateway_connection_client
|
||||
l2gwc_client = l2_gateway_connection_client.get_client(osn.adm.manager)
|
||||
For tempest user:
|
||||
l2gwc_client = l2_gateway_connection_client.get_client(cls.os_adm)
|
||||
"""
|
||||
manager = getattr(client_mgr, 'manager', client_mgr)
|
||||
net_client = getattr(manager, 'networks_client')
|
||||
try:
|
||||
_params = manager.default_params_with_timeout_values.copy()
|
||||
except Exception:
|
||||
_params = {}
|
||||
client = L2GatewayConnectionClient(net_client.auth_provider,
|
||||
net_client.service,
|
||||
net_client.region,
|
||||
net_client.endpoint_type,
|
||||
**_params)
|
||||
return client
|
319
vmware_nsx_tempest/services/load_balancer_v1_client.py
Normal file
319
vmware_nsx_tempest/services/load_balancer_v1_client.py
Normal file
@ -0,0 +1,319 @@
|
||||
# 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_lib.common.utils import misc as misc_utils
|
||||
from tempest_lib import exceptions as lib_exc
|
||||
|
||||
from tempest import exceptions
|
||||
from vmware_nsx_tempest.services import network_client_base as base
|
||||
|
||||
POOL_RID = 'pools'
|
||||
VIP_RID = 'vips'
|
||||
HEALTHMONITOR_RID = 'health_monitors'
|
||||
MEMBER_RID = 'members'
|
||||
|
||||
|
||||
class LoadBalancerV1Client(base.BaseNetworkClient):
|
||||
|
||||
def _list_lb(self, lb_resource, **filters):
|
||||
resource_name_s, resource_name_p = _g_resource_namelist(lb_resource)
|
||||
req_uri = '/lb/%s' % (resource_name_p)
|
||||
return self.list_resources(req_uri, **filters)
|
||||
|
||||
def _show_lb(self, lb_resource, resource_id, **fields):
|
||||
resource_name_s, resource_name_p = _g_resource_namelist(lb_resource)
|
||||
req_uri = '/lb/%s/%s' % (resource_name_p, resource_id)
|
||||
return self.show_resource(req_uri, **fields)
|
||||
|
||||
def _delete_lb(self, lb_resource, resource_id):
|
||||
resource_name_s, resource_name_p = _g_resource_namelist(lb_resource)
|
||||
req_uri = '/lb/%s/%s' % (resource_name_p, resource_id)
|
||||
return self.delete_resource(req_uri)
|
||||
|
||||
def _create_lb(self, lb_resource, **kwargs):
|
||||
resource_name_s, resource_name_p = _g_resource_namelist(lb_resource)
|
||||
req_uri = '/lb/%s' % (resource_name_p)
|
||||
post_body = {resource_name_s: kwargs}
|
||||
return self.create_resource(req_uri, post_body)
|
||||
|
||||
def _update_lb(self, lb_resource, resource_id, **kwargs):
|
||||
resource_name_s, resource_name_p = _g_resource_namelist(lb_resource)
|
||||
req_uri = '/lb/%s/%s' % (resource_name_p, resource_id)
|
||||
post_body = {resource_name_s: kwargs}
|
||||
return self.update_resource(req_uri, post_body)
|
||||
|
||||
def show_agent_hosting_pool(self, pool_id):
|
||||
"""Get loadbalancer agent hosting a pool."""
|
||||
req_uri = "/lb/pools/%s/loadbalancer-agent" % (pool_id)
|
||||
return self.show_resource(req_uri)
|
||||
|
||||
def associate_health_monitor_with_pool(self, health_monitor_id, pool_id):
|
||||
"""Create a mapping between a health monitor and a pool."""
|
||||
post_body = {'health_monitor': {'id': health_monitor_id}}
|
||||
req_uri = '/lb/pools/%s/%s' % (pool_id, HEALTHMONITOR_RID)
|
||||
return self.create_resource(req_uri, post_body)
|
||||
|
||||
def create_health_monitor(self, **kwargs):
|
||||
"""Create a health monitor."""
|
||||
create_kwargs = dict(
|
||||
type=kwargs.pop('type', 'TCP'),
|
||||
max_retries=kwargs.pop('nax_retries', 3),
|
||||
timeout=kwargs.pop('timeout', 1),
|
||||
delay=kwargs.pop('delay', 4),
|
||||
)
|
||||
create_kwargs.update(**kwargs)
|
||||
return self._create_lb(HEALTHMONITOR_RID, **create_kwargs)
|
||||
|
||||
def delete_health_monitor(self, health_monitor_id):
|
||||
"""Delete a given health monitor."""
|
||||
return self._delete_lb(HEALTHMONITOR_RID, health_monitor_id)
|
||||
|
||||
def disassociate_health_monitor_with_pool(self, health_monitor_id,
|
||||
pool_id):
|
||||
"""Remove a mapping from a health monitor to a pool."""
|
||||
req_uri = ('/lb/pools/%s/%s/%s'
|
||||
% (pool_id, HEALTHMONITOR_RID, health_monitor_id))
|
||||
return self.delete_resource(req_uri)
|
||||
|
||||
def list_health_monitors(self, **filters):
|
||||
"""List health monitors that belong to a given tenant."""
|
||||
return self._list_lb(HEALTHMONITOR_RID, **filters)
|
||||
|
||||
def show_health_monitor(self, health_monitor_id):
|
||||
"""Show information of a given health monitor."""
|
||||
return self._show_lb(HEALTHMONITOR_RID, health_monitor_id)
|
||||
|
||||
def update_health_monitor(self, health_monitor_id,
|
||||
show_then_update=False, **kwargs):
|
||||
"""Update a given health monitor."""
|
||||
body = (self.show_health_monitor(health_monitor_id)['health_monitor']
|
||||
if show_then_update else {})
|
||||
body.update(**kwargs)
|
||||
return self._update_lb(HEALTHMONITOR_RID,
|
||||
health_monitor_id, **body)
|
||||
|
||||
# tempest create_member(self,protocol_port, pool, ip_version)
|
||||
# we use pool_id
|
||||
def create_member(self, protocol_port, pool_id,
|
||||
ip_version=4, **kwargs):
|
||||
"""Create a member."""
|
||||
create_kwargs = dict(
|
||||
protocol_port=protocol_port,
|
||||
pool_id=pool_id,
|
||||
address=("fd00:abcd" if ip_version == 6 else "10.0.9.46"),
|
||||
)
|
||||
create_kwargs.update(**kwargs)
|
||||
return self._create_lb(MEMBER_RID, **create_kwargs)
|
||||
|
||||
def delete_member(self, member_id):
|
||||
"""Delete a given member."""
|
||||
return self._delete_lb(MEMBER_RID, member_id)
|
||||
|
||||
def list_members(self, **filters):
|
||||
"""List members that belong to a given tenant."""
|
||||
return self._list_lb(MEMBER_RID, **filters)
|
||||
|
||||
def show_member(self, member_id):
|
||||
"""Show information of a given member."""
|
||||
return self._show_lb(MEMBER_RID, member_id)
|
||||
|
||||
def update_member(self, member_id,
|
||||
show_then_update=False, **kwargs):
|
||||
"""Update a given member."""
|
||||
body = (self.show_member(member_id)['member']
|
||||
if show_then_update else {})
|
||||
body.update(**kwargs)
|
||||
return self._update_lb(MEMBER_RID, member_id, **body)
|
||||
|
||||
def create_pool(self, name, lb_method, protocol, subnet_id,
|
||||
**kwargs):
|
||||
"""Create a pool."""
|
||||
lb_method = lb_method or 'ROUND_ROBIN'
|
||||
protocol = protocol or 'HTTP'
|
||||
create_kwargs = dict(
|
||||
name=name, lb_method=lb_method,
|
||||
protocol=protocol, subnet_id=subnet_id,
|
||||
)
|
||||
create_kwargs.update(kwargs)
|
||||
return self._create_lb(POOL_RID, **create_kwargs)
|
||||
|
||||
def delete_pool(self, pool_id):
|
||||
"""Delete a given pool."""
|
||||
return self._delete_lb(POOL_RID, pool_id)
|
||||
|
||||
def list_pools(self, **filters):
|
||||
"""List pools that belong to a given tenant."""
|
||||
return self._list_lb(POOL_RID, **filters)
|
||||
|
||||
def list_lb_pool_stats(self, pool_id, **filters):
|
||||
"""Retrieve stats for a given pool."""
|
||||
req_uri = '/lb/pools/%s/stats' % (pool_id)
|
||||
return self.list_resources(req_uri, **filters)
|
||||
|
||||
def list_pool_on_agents(self, **filters):
|
||||
"""List the pools on a loadbalancer agent."""
|
||||
pass
|
||||
|
||||
def show_pool(self, pool_id):
|
||||
"""Show information of a given pool."""
|
||||
return self._show_lb(POOL_RID, pool_id)
|
||||
|
||||
def update_pool(self, pool_id, show_then_update=False, **kwargs):
|
||||
"""Update a given pool."""
|
||||
body = (self.show_pool(pool_id)['pool']
|
||||
if show_then_update else {})
|
||||
body.update(**kwargs)
|
||||
return self._update_lb(POOL_RID, pool_id, **body)
|
||||
|
||||
def create_vip(self, pool_id, **kwargs):
|
||||
"""Create a vip."""
|
||||
create_kwargs = dict(
|
||||
pool_id=pool_id,
|
||||
protocol=kwargs.pop('protocol', 'HTTP'),
|
||||
protocol_port=kwargs.pop('protocol_port', 80),
|
||||
name=kwargs.pop('name', None),
|
||||
address=kwargs.pop('address', None),
|
||||
)
|
||||
for k in create_kwargs.keys():
|
||||
if create_kwargs[k] is None:
|
||||
create_kwargs.pop(k)
|
||||
create_kwargs.update(**kwargs)
|
||||
# subnet_id needed to create vip
|
||||
return self._create_lb(VIP_RID, **create_kwargs)
|
||||
|
||||
def delete_vip(self, vip_id):
|
||||
"""Delete a given vip."""
|
||||
return self._delete_lb(VIP_RID, vip_id)
|
||||
|
||||
def list_vips(self, **filters):
|
||||
"""List vips that belong to a given tenant."""
|
||||
return self._list_lb(VIP_RID, **filters)
|
||||
|
||||
def show_vip(self, vip_id):
|
||||
"""Show information of a given vip."""
|
||||
return self._show_lb(VIP_RID, vip_id)
|
||||
|
||||
def update_vip(self, vip_id, show_then_update=False, **kwargs):
|
||||
"""Update a given vip."""
|
||||
body = (self.show_vip(vip_id)['vip']
|
||||
if show_then_update else {})
|
||||
body.update(**kwargs)
|
||||
return self._update_lb(VIP_RID, vip_id, **body)
|
||||
|
||||
# Following 3 methods are specifically to load-balancer V1 client.
|
||||
# They are being implemented by the pareant tempest_lib.common.rest_client
|
||||
# with different calling signatures, only id, no resoure_type. Because,
|
||||
# starting in Liberty release, each resource should have its own client.
|
||||
# Since V1 is deprecated, we are not going to change it, and
|
||||
# copy following 2 methods for V1 LB client only.
|
||||
def wait_for_resource_deletion(self, resource_type, id, client=None):
|
||||
"""Waits for a resource to be deleted."""
|
||||
start_time = int(time.time())
|
||||
while True:
|
||||
if self.is_resource_deleted(resource_type, id, client=client):
|
||||
return
|
||||
if int(time.time()) - start_time >= self.build_timeout:
|
||||
raise exceptions.TimeoutException
|
||||
time.sleep(self.build_interval)
|
||||
|
||||
def is_resource_deleted(self, resource_type, id, client=None):
|
||||
if client is None:
|
||||
client = self
|
||||
method = 'show_' + resource_type
|
||||
try:
|
||||
getattr(client, method)(id)
|
||||
except AttributeError:
|
||||
raise Exception("Unknown resource type %s " % resource_type)
|
||||
except lib_exc.NotFound:
|
||||
return True
|
||||
return False
|
||||
|
||||
def wait_for_resource_status(self, fetch, status, interval=None,
|
||||
timeout=None):
|
||||
"""This has different calling signature then rest_client.
|
||||
|
||||
@summary: Waits for a network resource to reach a status
|
||||
@param fetch: the callable to be used to query the resource status
|
||||
@type fecth: callable that takes no parameters and returns the resource
|
||||
@param status: the status that the resource has to reach
|
||||
@type status: String
|
||||
@param interval: the number of seconds to wait between each status
|
||||
query
|
||||
@type interval: Integer
|
||||
@param timeout: the maximum number of seconds to wait for the resource
|
||||
to reach the desired status
|
||||
@type timeout: Integer
|
||||
"""
|
||||
if not interval:
|
||||
interval = self.build_interval
|
||||
if not timeout:
|
||||
timeout = self.build_timeout
|
||||
start_time = time.time()
|
||||
|
||||
while time.time() - start_time <= timeout:
|
||||
resource = fetch()
|
||||
if resource['status'] == status:
|
||||
return
|
||||
time.sleep(interval)
|
||||
|
||||
# At this point, the wait has timed out
|
||||
message = 'Resource %s' % (str(resource))
|
||||
message += ' failed to reach status %s' % status
|
||||
message += ' (current: %s)' % resource['status']
|
||||
message += ' within the required time %s' % timeout
|
||||
caller = misc_utils.find_test_caller()
|
||||
if caller:
|
||||
message = '(%s) %s' % (caller, message)
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
|
||||
def _g_resource_namelist(lb_resource):
|
||||
if lb_resource[-1] == 's':
|
||||
return (lb_resource[:-1], lb_resource)
|
||||
return (lb_resource, lb_resource + "s")
|
||||
|
||||
|
||||
def destroy_tenant_lb(lbv1_client):
|
||||
for o in lbv1_client.list_members():
|
||||
lbv1_client.delete_member(o['id'])
|
||||
for o in lbv1_client.list_health_monitors():
|
||||
lbv1_client.delete_health_monitor(o['id'])
|
||||
for o in lbv1_client.list_vips():
|
||||
lbv1_client.delete_vip(o['id'])
|
||||
for o in lbv1_client.list_pools():
|
||||
lbv1_client.delete_pool(o['id'])
|
||||
|
||||
|
||||
def get_client(client_mgr):
|
||||
"""create a v1 load balancer client
|
||||
|
||||
For itempest user:
|
||||
from itempest import load_our_solar_system as osn
|
||||
from vmware_nsx_tempest.services import load_balancer_v1_client
|
||||
lbv1 = load_balancer_v1_client.get_client(osn.adm.manager)
|
||||
For tempest user:
|
||||
lbv1 = load_balancer_v1_client.get_client(cls.os_adm)
|
||||
"""
|
||||
manager = getattr(client_mgr, 'manager', client_mgr)
|
||||
net_client = getattr(manager, 'networks_client')
|
||||
try:
|
||||
_params = manager.default_params_with_timeout_values.copy()
|
||||
except Exception:
|
||||
_params = {}
|
||||
client = LoadBalancerV1Client(net_client.auth_provider,
|
||||
net_client.service,
|
||||
net_client.region,
|
||||
net_client.endpoint_type,
|
||||
**_params)
|
||||
return client
|
42
vmware_nsx_tempest/services/network_client_base.py
Normal file
42
vmware_nsx_tempest/services/network_client_base.py
Normal file
@ -0,0 +1,42 @@
|
||||
# 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.
|
||||
|
||||
from tempest.services.network.json import base
|
||||
|
||||
|
||||
# netowrk/json/base.py does not include thoese method in network_client
|
||||
class BaseNetworkClient(base.BaseNetworkClient):
|
||||
def __init__(self, auth_provider, service, region,
|
||||
endpoint_type=None, build_interval=None, build_timeout=None,
|
||||
disable_ssl_certificate_validation=None, ca_certs=None,
|
||||
trace_requests=None, **kwargs):
|
||||
dsca = disable_ssl_certificate_validation
|
||||
super(base.BaseNetworkClient, self).__init__(
|
||||
auth_provider, service, region,
|
||||
endpoint_type=endpoint_type,
|
||||
build_interval=build_interval,
|
||||
build_timeout=build_timeout,
|
||||
disable_ssl_certificate_validation=dsca,
|
||||
ca_certs=ca_certs,
|
||||
trace_requests=trace_requests)
|
||||
|
||||
|
||||
default_params = {
|
||||
'disable_ssl_certificate_validation': True,
|
||||
'ca_certs': None,
|
||||
'trace_requests': ''}
|
||||
default_params_2 = {
|
||||
'catalog_type': 'network',
|
||||
'region': 'nova',
|
||||
'endpoint_type': 'publicURL',
|
||||
'build_timeout': 300,
|
||||
'build_interval': 1}
|
263
vmware_nsx_tempest/services/nsxv_client.py
Normal file
263
vmware_nsx_tempest/services/nsxv_client.py
Normal file
@ -0,0 +1,263 @@
|
||||
# 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 base64
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
import requests
|
||||
|
||||
from tempest import config
|
||||
|
||||
import vmware_nsx_tempest.services.utils as utils
|
||||
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
CONF = config.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VSMClient(object):
|
||||
"""NSX-v client.
|
||||
|
||||
The client provides the API operations on its components.
|
||||
The purpose of this rest client is to query backend components after
|
||||
issuing corresponding API calls from OpenStack. This is to make sure
|
||||
the API calls has been realized on the NSX-v backend.
|
||||
"""
|
||||
API_VERSION = "2.0"
|
||||
|
||||
def __init__(self, host, username, password, *args, **kwargs):
|
||||
self.force = True if 'force' in kwargs else False
|
||||
self.host = host
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.version = None
|
||||
self.endpoint = None
|
||||
self.content_type = "application/json"
|
||||
self.accept_type = "application/json"
|
||||
self.verify = False
|
||||
self.secure = True
|
||||
self.interface = "json"
|
||||
self.url = None
|
||||
self.headers = None
|
||||
self.api_version = VSMClient.API_VERSION
|
||||
self.default_scope_id = None
|
||||
|
||||
self.__set_headers()
|
||||
self._version = self.get_vsm_version()
|
||||
|
||||
def __set_endpoint(self, endpoint):
|
||||
self.endpoint = endpoint
|
||||
|
||||
def get_endpoint(self):
|
||||
return self.endpoint
|
||||
|
||||
def __set_content_type(self, content_type):
|
||||
self.content_type = content_type
|
||||
|
||||
def get_content_type(self):
|
||||
return self.content_type
|
||||
|
||||
def __set_accept_type(self, accept_type):
|
||||
self.accept_type = accept_type
|
||||
|
||||
def get_accept_type(self):
|
||||
return self.accept_type
|
||||
|
||||
def __set_api_version(self, api_version):
|
||||
self.api_version = api_version
|
||||
|
||||
def get_api_version(self):
|
||||
return self.api_version
|
||||
|
||||
def __set_url(self, version=None, secure=None, host=None, endpoint=None):
|
||||
version = self.api_version if version is None else version
|
||||
secure = self.secure if secure is None else secure
|
||||
host = self.host if host is None else host
|
||||
endpoint = self.endpoint if endpoint is None else endpoint
|
||||
http_type = 'https' if secure else 'http'
|
||||
self.url = '%s://%s/api/%s%s' % (http_type, host, version, endpoint)
|
||||
|
||||
def get_url(self):
|
||||
return self.url
|
||||
|
||||
def __set_headers(self, content=None, accept=None):
|
||||
content_type = self.content_type if content is None else content
|
||||
accept_type = self.accept_type if accept is None else accept
|
||||
auth_cred = self.username + ":" + self.password
|
||||
auth = base64.b64encode(auth_cred)
|
||||
headers = {}
|
||||
headers['Authorization'] = "Basic %s" % auth
|
||||
headers['Content-Type'] = content_type
|
||||
headers['Accept'] = accept_type
|
||||
self.headers = headers
|
||||
|
||||
def get(self, endpoint=None, params=None):
|
||||
"""Basic query GET method for json API request."""
|
||||
self.__set_url(endpoint=endpoint)
|
||||
response = requests.get(self.url, headers=self.headers,
|
||||
verify=self.verify, params=params)
|
||||
return response
|
||||
|
||||
def delete(self, endpoint=None, params=None):
|
||||
"""Basic delete API method on endpoint."""
|
||||
self.__set_url(endpoint=endpoint)
|
||||
response = requests.delete(self.url, headers=self.headers,
|
||||
verify=self.verify, params=params)
|
||||
return response
|
||||
|
||||
def post(self, endpoint=None, body=None):
|
||||
"""Basic post API method on endpoint."""
|
||||
self.__set_url(endpoint=endpoint)
|
||||
response = requests.post(self.url, headers=self.headers,
|
||||
verify=self.verify,
|
||||
data=jsonutils.dumps(body))
|
||||
return response
|
||||
|
||||
def get_all_vdn_scopes(self):
|
||||
"""Retrieve existing network scopes"""
|
||||
self.__set_api_version('2.0')
|
||||
self.__set_endpoint("/vdn/scopes")
|
||||
response = self.get()
|
||||
return response.json()['allScopes']
|
||||
|
||||
# return the vdn_scope_id for the priamry Transport Zone
|
||||
def get_vdn_scope_id(self):
|
||||
"""Retrieve existing network scope id."""
|
||||
scopes = self.get_all_vdn_scopes()
|
||||
if len(scopes) == 0:
|
||||
return scopes[0]['objectId']
|
||||
return CONF.nsxv.vdn_scope_id
|
||||
|
||||
def get_vdn_scope_by_id(self, scope_id):
|
||||
"""Retrieve existing network scopes id"""
|
||||
self.__set_api_version('2.0')
|
||||
self.__set_endpoint("/vdn/scopes/%s" % scope_id)
|
||||
return self.get().json()
|
||||
|
||||
def get_vdn_scope_by_name(self, name):
|
||||
"""Retrieve network scope id of existing scope name:
|
||||
|
||||
nsxv_client.get_vdn_scope_id_by_name('TZ1')
|
||||
"""
|
||||
scopes = self.get_all_vdn_scopes()
|
||||
if name is None:
|
||||
for scope in scopes:
|
||||
if scope['objectId'] == CONF.nsxv.vdn_scope_id:
|
||||
return scope
|
||||
else:
|
||||
for scope in scopes:
|
||||
if scope['name'] == name:
|
||||
return scope
|
||||
return None
|
||||
|
||||
def get_all_logical_switches(self, vdn_scope_id=None):
|
||||
lswitches = []
|
||||
self.__set_api_version('2.0')
|
||||
vdn_scope_id = vdn_scope_id or self.get_vdn_scope_id()
|
||||
endpoint = "/vdn/scopes/%s/virtualwires" % (vdn_scope_id)
|
||||
self.__set_endpoint(endpoint)
|
||||
response = self.get()
|
||||
paging_info = response.json()['dataPage']['pagingInfo']
|
||||
page_size = int(paging_info['pageSize'])
|
||||
total_count = int(paging_info['totalCount'])
|
||||
msg = ("There are total %s logical switches and page size is %s"
|
||||
% (total_count, page_size))
|
||||
LOG.debug(msg)
|
||||
pages = utils.ceil(total_count, page_size)
|
||||
LOG.debug("Total pages: %s" % pages)
|
||||
for i in range(pages):
|
||||
start_index = page_size * i
|
||||
params = {'startindex': start_index}
|
||||
response = self.get(params=params)
|
||||
lswitches += response.json()['dataPage']['data']
|
||||
return lswitches
|
||||
|
||||
def get_logical_switch(self, name):
|
||||
"""Get the logical switch based on the name.
|
||||
|
||||
The uuid of the OpenStack L2 network. Return ls if found,
|
||||
otherwise return None.
|
||||
"""
|
||||
lswitches = self.get_all_logical_switches()
|
||||
lswitch = [ls for ls in lswitches if ls['name'] == name]
|
||||
if len(lswitch) == 0:
|
||||
LOG.debug('logical switch %s NOT found!' % name)
|
||||
lswitch = None
|
||||
else:
|
||||
ls = lswitch[0]
|
||||
LOG.debug('Found lswitch: %s' % ls)
|
||||
return ls
|
||||
|
||||
def delete_logical_switch(self, name):
|
||||
"""Delete logical switch based on name.
|
||||
|
||||
The name of the logical switch on NSX-v is the uuid
|
||||
of the openstack l2 network.
|
||||
"""
|
||||
ls = self.get_logical_switch(name)
|
||||
if ls is not None:
|
||||
endpoint = '/vdn/virtualwires/%s' % ls['objectId']
|
||||
response = self.delete(endpoint=endpoint)
|
||||
if response.status_code == 200:
|
||||
LOG.debug('Successfully deleted logical switch %s' % name)
|
||||
else:
|
||||
LOG.debug('ERROR @delete ls=%s failed with reponse code %s' %
|
||||
(name, response.status_code))
|
||||
|
||||
def get_all_edges(self):
|
||||
"""Get all edges on NSX-v backend."""
|
||||
self.__set_api_version('4.0')
|
||||
self.__set_endpoint('/edges')
|
||||
edges = []
|
||||
response = self.get()
|
||||
paging_info = response.json()['edgePage']['pagingInfo']
|
||||
page_size = int(paging_info['pageSize'])
|
||||
total_count = int(paging_info['totalCount'])
|
||||
msg = "There are total %s edges and page size is %s" % (total_count,
|
||||
page_size)
|
||||
LOG.debug(msg)
|
||||
pages = utils.ceil(total_count, page_size)
|
||||
for i in range(pages):
|
||||
start_index = page_size * i
|
||||
params = {'startindex': start_index}
|
||||
response = self.get(params=params)
|
||||
edges += response.json()['edgePage']['data']
|
||||
return edges
|
||||
|
||||
def get_edge(self, name):
|
||||
"""Get edge based on the name, which is OpenStack router.
|
||||
|
||||
Return edge if found, else return None.
|
||||
"""
|
||||
edges = self.get_all_edges()
|
||||
edge = [e for e in edges if e['name'] == name]
|
||||
if len(edge) == 0:
|
||||
LOG.debug('Edge %s NOT found!' % name)
|
||||
edge = None
|
||||
else:
|
||||
edge = edge[0]
|
||||
LOG.debug('Found edge: %s' % edge)
|
||||
return edge
|
||||
|
||||
def get_vsm_version(self):
|
||||
"""Get the VSM client version including major, minor, patch, & build#.
|
||||
|
||||
Build number, e.g. 6.2.0.2986609
|
||||
return: vsm version
|
||||
"""
|
||||
self.__set_api_version('1.0')
|
||||
self.__set_endpoint('/appliance-management/global/info')
|
||||
response = self.get()
|
||||
json_ver = response.json()['versionInfo']
|
||||
return '.'.join([json_ver['majorVersion'], json_ver['minorVersion'],
|
||||
json_ver['patchVersion'], json_ver['buildNumber']])
|
21
vmware_nsx_tempest/services/utils.py
Normal file
21
vmware_nsx_tempest/services/utils.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
def ceil(a, b):
|
||||
if b == 0:
|
||||
return 0
|
||||
div = a / b
|
||||
mod = 0 if a % b is 0 else 1
|
||||
return div + mod
|
0
vmware_nsx_tempest/tests/__init__.py
Normal file
0
vmware_nsx_tempest/tests/__init__.py
Normal file
23
vmware_nsx_tempest/tests/base.py
Normal file
23
vmware_nsx_tempest/tests/base.py
Normal file
@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2010-2011 OpenStack Foundation
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from oslotest import base
|
||||
|
||||
|
||||
class TestCase(base.BaseTestCase):
|
||||
|
||||
"""Test case base class for all unit tests."""
|
6
vmware_nsx_tempest/tests/nsxv/README.rst
Normal file
6
vmware_nsx_tempest/tests/nsxv/README.rst
Normal file
@ -0,0 +1,6 @@
|
||||
Placeholder for NSX-v plugin specific automated tests
|
||||
directory:
|
||||
nsxv/
|
||||
api/
|
||||
scenario/
|
||||
scale/
|
0
vmware_nsx_tempest/tests/nsxv/__init__.py
Normal file
0
vmware_nsx_tempest/tests/nsxv/__init__.py
Normal file
1
vmware_nsx_tempest/tests/nsxv/api/README.rst
Normal file
1
vmware_nsx_tempest/tests/nsxv/api/README.rst
Normal file
@ -0,0 +1 @@
|
||||
Placeholder for nsxv neutron plugin specific API tests.
|
0
vmware_nsx_tempest/tests/nsxv/api/__init__.py
Normal file
0
vmware_nsx_tempest/tests/nsxv/api/__init__.py
Normal file
179
vmware_nsx_tempest/tests/nsxv/api/base_provider.py
Normal file
179
vmware_nsx_tempest/tests/nsxv/api/base_provider.py
Normal file
@ -0,0 +1,179 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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 netaddr
|
||||
from tempest_lib.common.utils import data_utils
|
||||
from tempest_lib import exceptions
|
||||
|
||||
from tempest.api.network import base
|
||||
from tempest import config
|
||||
from tempest import test
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class BaseAdminNetworkTest(base.BaseAdminNetworkTest):
|
||||
# NOTE(akang): This class inherits from BaseAdminNetworkTest.
|
||||
# By default client is cls.client, but for provider network,
|
||||
# the client is admin_client. The test class should pass
|
||||
# client=self.admin_client, if it wants to create provider
|
||||
# network/subnet.
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(BaseAdminNetworkTest, cls).skip_checks()
|
||||
if not test.is_extension_enabled('provider', 'network'):
|
||||
msg = "Network Provider Extension not enabled."
|
||||
raise cls.skipException(msg)
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(BaseAdminNetworkTest, cls).resource_setup()
|
||||
cls.admin_netwk_info = []
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
if CONF.service_available.neutron:
|
||||
for netwk_info in cls.admin_netwk_info:
|
||||
net_client, network = netwk_info
|
||||
try:
|
||||
cls._try_delete_resource(net_client.delete_network,
|
||||
network['id'])
|
||||
except Exception:
|
||||
pass
|
||||
super(BaseAdminNetworkTest, cls).resource_cleanup()
|
||||
|
||||
@classmethod
|
||||
def create_network(cls, network_name=None, client=None,
|
||||
**kwargs):
|
||||
net_client = client if client else cls.admin_networks_client
|
||||
network_name = network_name or data_utils.rand_name('ADM-network-')
|
||||
post_body = {'name': network_name}
|
||||
post_body.update(kwargs)
|
||||
body = net_client.create_network(**post_body)
|
||||
network = body['network']
|
||||
cls.admin_netwk_info.append([net_client, network])
|
||||
return body
|
||||
|
||||
@classmethod
|
||||
def update_network(cls, network_id, client=None, **kwargs):
|
||||
net_client = client if client else cls.admin_networks_client
|
||||
return net_client.update_network(network_id, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def delete_network(cls, network_id, client=None):
|
||||
net_client = client if client else cls.admin_networks_client
|
||||
return net_client.delete_network(network_id)
|
||||
|
||||
@classmethod
|
||||
def show_network(cls, network_id, client=None, **kwargs):
|
||||
net_client = client if client else cls.admin_networks_client
|
||||
return net_client.show_network(network_id, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def list_networks(cls, client=None, **kwargs):
|
||||
net_client = client if client else cls.admin_networks_client
|
||||
return net_client.list_networks(**kwargs)
|
||||
|
||||
@classmethod
|
||||
def create_subnet(cls, network, client=None,
|
||||
gateway='', cidr=None, mask_bits=None,
|
||||
ip_version=None, cidr_offset=0, **kwargs):
|
||||
ip_version = (ip_version if ip_version is not None
|
||||
else cls._ip_version)
|
||||
net_client = client if client else cls.admin_subnets_client
|
||||
post_body = get_subnet_create_options(
|
||||
network['id'], ip_version,
|
||||
gateway=gateway, cidr=cidr, cidr_offset=cidr_offset,
|
||||
mask_bits=mask_bits, **kwargs)
|
||||
return net_client.create_subnet(**post_body)
|
||||
|
||||
@classmethod
|
||||
def update_subnet(cls, subnet_id, client=None, **kwargs):
|
||||
net_client = client if client else cls.admin_subnets_client
|
||||
return net_client.update_subnet(subnet_id, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def delete_subnet(cls, subnet_id, client=None):
|
||||
net_client = client if client else cls.admin_subnets_client
|
||||
return net_client.delete_subnet(subnet_id)
|
||||
|
||||
@classmethod
|
||||
def show_subnet(cls, subnet_id, client=None, **kwargs):
|
||||
net_client = client if client else cls.admin_subnets_client
|
||||
return net_client.show_subnet(subnet_id, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def list_subnets(cls, client=None, **kwargs):
|
||||
net_client = client if client else cls.admin_subnets_client
|
||||
return net_client.list_subnets(**kwargs)
|
||||
|
||||
# add other create methods, i.e. security-group, port, floatingip
|
||||
# if needed.
|
||||
|
||||
|
||||
def get_subnet_create_options(network_id, ip_version=4,
|
||||
gateway='', cidr=None, mask_bits=None,
|
||||
num_subnet=1, gateway_offset=1, cidr_offset=0,
|
||||
**kwargs):
|
||||
|
||||
"""When cidr_offset>0 it request only one subnet-options:
|
||||
|
||||
subnet = get_subnet_create_options('abcdefg', 4, num_subnet=4)[3]
|
||||
subnet = get_subnet_create_options('abcdefg', 4, cidr_offset=3)
|
||||
"""
|
||||
|
||||
gateway_not_set = (gateway == '')
|
||||
if ip_version == 4:
|
||||
cidr = cidr or netaddr.IPNetwork(CONF.network.tenant_network_cidr)
|
||||
mask_bits = mask_bits or CONF.network.tenant_network_mask_bits
|
||||
elif ip_version == 6:
|
||||
cidr = (
|
||||
cidr or netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr))
|
||||
mask_bits = mask_bits or CONF.network.tenant_network_v6_mask_bits
|
||||
# Find a cidr that is not in use yet and create a subnet with it
|
||||
subnet_list = []
|
||||
if cidr_offset > 0:
|
||||
num_subnet = cidr_offset + 1
|
||||
for subnet_cidr in cidr.subnet(mask_bits):
|
||||
if gateway_not_set:
|
||||
gateway_ip = gateway or (
|
||||
str(netaddr.IPAddress(subnet_cidr) + gateway_offset))
|
||||
else:
|
||||
gateway_ip = gateway
|
||||
try:
|
||||
subnet_body = dict(
|
||||
network_id=network_id,
|
||||
cidr=str(subnet_cidr),
|
||||
ip_version=ip_version,
|
||||
gateway_ip=gateway_ip,
|
||||
**kwargs)
|
||||
if num_subnet <= 1:
|
||||
return subnet_body
|
||||
subnet_list.append(subnet_body)
|
||||
if len(subnet_list) >= num_subnet:
|
||||
if cidr_offset > 0:
|
||||
# user request the 'cidr_offset'th of cidr
|
||||
return subnet_list[cidr_offset]
|
||||
# user request list of cidr
|
||||
return subnet_list
|
||||
except exceptions.BadRequest as e:
|
||||
is_overlapping_cidr = 'overlaps with another subnet' in str(e)
|
||||
if not is_overlapping_cidr:
|
||||
raise
|
||||
else:
|
||||
message = 'Available CIDR for subnet creation could not be found'
|
||||
raise exceptions.BuildErrorException(message)
|
||||
return {}
|
116
vmware_nsx_tempest/tests/nsxv/api/test_flat_network.py
Normal file
116
vmware_nsx_tempest/tests/nsxv/api/test_flat_network.py
Normal file
@ -0,0 +1,116 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
from tempest import test
|
||||
|
||||
from oslo_log import log as logging
|
||||
from tempest_lib.common.utils import data_utils
|
||||
import test_subnets as SNET
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FlatNetworksTestJSON(SNET.SubnetTestJSON):
|
||||
_interface = 'json'
|
||||
_provider_network_body = {
|
||||
'name': data_utils.rand_name('FLAT-network'),
|
||||
'provider:network_type': 'flat'}
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(FlatNetworksTestJSON, cls).resource_setup()
|
||||
|
||||
def _create_network(self, _auto_clean_up=True, network_name=None,
|
||||
**kwargs):
|
||||
network_name = network_name or data_utils.rand_name('flat-netwk')
|
||||
# self.create_network expect network_name
|
||||
# self.admin_client.create_network()
|
||||
# and self.client.create_network() expect name
|
||||
post_body = {'name': network_name,
|
||||
'provider:network_type': 'flat'}
|
||||
post_body.update(kwargs)
|
||||
LOG.debug("create FLAT network: %s", str(post_body))
|
||||
body = self.admin_networks_client.create_network(**post_body)
|
||||
network = body['network']
|
||||
if _auto_clean_up:
|
||||
self.addCleanup(self._try_delete_network, network['id'])
|
||||
return network
|
||||
|
||||
@test.idempotent_id('dc2f2f46-0577-4e2a-b35d-3c8c8bbce5bf')
|
||||
def test_create_network(self):
|
||||
# Create a network as an admin user specifying the
|
||||
# flat network type attribute
|
||||
network = self._create_network()
|
||||
# Verifies router:network_type parameter
|
||||
self.assertIsNotNone(network['id'])
|
||||
self.assertEqual(network.get('provider:network_type'), 'flat')
|
||||
|
||||
@test.idempotent_id('777fc335-b26c-42ea-9759-c71dff2ce1c6')
|
||||
def test_update_network(self):
|
||||
# Update flat network as an admin user specifying the
|
||||
# flat network attribute
|
||||
network = self._create_network(shared=True, _auto_clean_up=False)
|
||||
self.assertEqual(network.get('shared'), True)
|
||||
new_name = network['name'] + "-updated"
|
||||
update_body = {'shared': False, 'name': new_name}
|
||||
body = self.update_network(network['id'], **update_body)
|
||||
updated_network = body['network']
|
||||
# Verify that name and shared parameters were updated
|
||||
self.assertEqual(updated_network['shared'], False)
|
||||
self.assertEqual(updated_network['name'], new_name)
|
||||
# get flat network attributes and verify them
|
||||
body = self.show_network(network['id'])
|
||||
updated_network = body['network']
|
||||
# Verify that name and shared parameters were updated
|
||||
self.assertEqual(updated_network['shared'], False)
|
||||
self.assertEqual(updated_network['name'], new_name)
|
||||
self.assertEqual(updated_network['status'], network['status'])
|
||||
self.assertEqual(updated_network['subnets'], network['subnets'])
|
||||
self._delete_network(network['id'])
|
||||
|
||||
@test.idempotent_id('1dfc1c11-e838-464c-85b2-ed5e4c477c64')
|
||||
def test_list_networks(self):
|
||||
# Create flat network
|
||||
network = self._create_network(shared=True)
|
||||
# List networks as a normal user and confirm it is available
|
||||
body = self.list_networks(client=self.networks_client)
|
||||
network_list = [net['id'] for net in body['networks']]
|
||||
self.assertIn(network['id'], network_list)
|
||||
update_body = {'shared': False}
|
||||
body = self.update_network(network['id'], **update_body)
|
||||
# List networks as a normal user and confirm it is not available
|
||||
body = self.list_networks(client=self.networks_client)
|
||||
network_list = [net['id'] for net in body['networks']]
|
||||
self.assertNotIn(network['id'], network_list)
|
||||
|
||||
@test.idempotent_id('b5649fe2-a214-4105-8053-1825a877c45b')
|
||||
def test_show_network_attributes(self):
|
||||
# Create flat network
|
||||
network = self._create_network(shared=True)
|
||||
# Show a flat network as a normal user and confirm the
|
||||
# flat network attribute is returned.
|
||||
body = self.show_network(network['id'], client=self.networks_client)
|
||||
show_net = body['network']
|
||||
self.assertEqual(network['name'], show_net['name'])
|
||||
self.assertEqual(network['id'], show_net['id'])
|
||||
# provider attributes are for admin only
|
||||
body = self.show_network(network['id'])
|
||||
show_net = body['network']
|
||||
net_attr_list = show_net.keys()
|
||||
for attr in ('admin_state_up', 'port_security_enabled', 'shared',
|
||||
'status', 'subnets', 'tenant_id', 'router:external',
|
||||
'provider:network_type', 'provider:physical_network',
|
||||
'provider:segmentation_id'):
|
||||
self.assertIn(attr, net_attr_list)
|
186
vmware_nsx_tempest/tests/nsxv/api/test_l2_gateway.py
Normal file
186
vmware_nsx_tempest/tests/nsxv/api/test_l2_gateway.py
Normal file
@ -0,0 +1,186 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# Copyright 2015 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.
|
||||
|
||||
from tempest.api.network import base
|
||||
from tempest import config
|
||||
from tempest import test
|
||||
from tempest_lib.common.utils import data_utils
|
||||
from tempest_lib import decorators
|
||||
|
||||
from vmware_nsx_tempest.services import base_l2gw
|
||||
from vmware_nsx_tempest.services import l2_gateway_client as L2GW
|
||||
|
||||
CONF = config.CONF
|
||||
L2GW_RID = 'l2_gateway'
|
||||
L2GW_RIDs = 'l2_gateways'
|
||||
MSG_DIFF = "l2gw %s=%s is not the same as requested=%s"
|
||||
|
||||
|
||||
class L2GatewayTest(base.BaseAdminNetworkTest):
|
||||
"""Test l2-gateway operations:
|
||||
|
||||
l2-gateway-create
|
||||
l2-gateway-show
|
||||
l2-gateway-update
|
||||
l2-gateway-list
|
||||
l2-gateway-delete
|
||||
|
||||
over single device/interface/vlan
|
||||
over single device/interface/multiple-vlans
|
||||
over single device/multiple-interfaces/multiple-vlans
|
||||
over multiple-device/multiple-interfaces/multiple-vlans
|
||||
"""
|
||||
|
||||
credentials = ['primary', 'admin']
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(L2GatewayTest, cls).skip_checks()
|
||||
if not test.is_extension_enabled('l2-gateway', 'network'):
|
||||
msg = "l2-gateway extension not enabled."
|
||||
raise cls.skipException(msg)
|
||||
# if CONF attr device_on_vlan not defined, SKIP entire test suite
|
||||
cls.getattr_or_skip_test("device_one_vlan")
|
||||
|
||||
@classmethod
|
||||
def getattr_or_skip_test(cls, l2gw_attr_name):
|
||||
attr_value = getattr(CONF.l2gw, l2gw_attr_name, None)
|
||||
if attr_value:
|
||||
return attr_value
|
||||
msg = "CONF session:l2gw attr:%s is not defined." % (l2gw_attr_name)
|
||||
raise cls.skipException(msg)
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(L2GatewayTest, cls).setup_clients()
|
||||
cls.l2gw_created = {}
|
||||
l2gw_mgr = cls.os_adm
|
||||
cls.l2gw_client = L2GW.get_client(l2gw_mgr)
|
||||
cls.l2gw_list_0 = cls.l2gw_client.list_l2_gateways()[L2GW_RIDs]
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(L2GatewayTest, cls).resource_setup()
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
for _id in cls.l2gw_created.keys():
|
||||
try:
|
||||
cls.l2gw_client.delete_l2_gateway(_id)
|
||||
except Exception:
|
||||
# log it please
|
||||
pass
|
||||
|
||||
def get_segmentation_id(self, _l2gw, d_idx=0, i_idx=0):
|
||||
_dev = _l2gw['devices'][d_idx]
|
||||
_seg = _dev['interfaces'][i_idx].get('segmentation_id', [])
|
||||
return sorted(_seg)
|
||||
|
||||
def pop_segmentation_id(self, _l2gw, d_idx=0, i_idx=0):
|
||||
_dev = _l2gw['devices'][d_idx]
|
||||
_seg = _dev['interfaces'][i_idx].pop('segmentation_id', [])
|
||||
return sorted(_seg)
|
||||
|
||||
def get_interfaces(self, _l2gw, d_idx=0):
|
||||
_dev = _l2gw['devices'][d_idx]
|
||||
return sorted(_dev)
|
||||
|
||||
def do_csuld_single_device_interface_vlan(self, _name, _devices):
|
||||
_vlan_id_list = self.get_segmentation_id(_devices, 0, 0)
|
||||
_res_new = self.l2gw_client.create_l2_gateway(
|
||||
name=_name, **_devices)[L2GW_RID]
|
||||
self.l2gw_created[_res_new['id']] = _res_new
|
||||
self.assertEqual(_name, _res_new['name'],
|
||||
MSG_DIFF % ('name', _res_new['name'], _name))
|
||||
# w/wo vlan provided, need to check it is assigned/not-assigned
|
||||
_seg_list = self.get_segmentation_id(_res_new, 0, 0)
|
||||
self.assertEqual(0, cmp(_vlan_id_list, _seg_list),
|
||||
MSG_DIFF % ('vlan', _seg_list, _vlan_id_list))
|
||||
_res_show = self.l2gw_client.show_l2_gateway(
|
||||
_res_new['id'])[L2GW_RID]
|
||||
_if_created = _res_new['devices'][0]['interfaces']
|
||||
_if_shown = _res_show['devices'][0]['interfaces']
|
||||
self.assertEqual(0, cmp(_if_created, _if_shown),
|
||||
MSG_DIFF % ('interfaces', _if_created, _if_shown))
|
||||
_name2 = _name + "-day2"
|
||||
_res_upd = self.l2gw_client.update_l2_gateway(
|
||||
_res_new['id'], name=_name2)[L2GW_RID]
|
||||
_res_lst = self.l2gw_client.list_l2_gateways(
|
||||
name=_name2)[L2GW_RIDs][0]
|
||||
self.assertEqual(_name2 == _res_upd['name'],
|
||||
_name2 == _res_lst['name'],
|
||||
MSG_DIFF % ('name', _res_new['name'], _name2))
|
||||
self.l2gw_client.delete_l2_gateway(_res_new['id'])
|
||||
_res_lst = self.l2gw_client.list_l2_gateways(name=_name2)[L2GW_RIDs]
|
||||
self.l2gw_created.pop(_res_new['id'])
|
||||
self.assertEmpty(_res_lst,
|
||||
"l2gw name=%s, id=%s not deleted." %
|
||||
(_name2, _res_new['id']))
|
||||
|
||||
@test.idempotent_id('8b45a9a5-468b-4317-983d-7cceda367074')
|
||||
def test_csuld_single_device_interface_without_vlan(self):
|
||||
"""Single device/interface/vlan
|
||||
|
||||
Create l2gw with one and only one VLAN. In this case,
|
||||
l2-gateway-connnection does not need to specify VLAN.
|
||||
"""
|
||||
|
||||
dev_profile = self.getattr_or_skip_test("device_one_vlan")
|
||||
_name = data_utils.rand_name('l2gw-1v1')
|
||||
_devices = base_l2gw.get_l2gw_body(dev_profile)
|
||||
self.pop_segmentation_id(_devices, 0, 0)
|
||||
self.do_csuld_single_device_interface_vlan(_name, _devices)
|
||||
|
||||
@test.idempotent_id('af57cf56-a169-4d88-b32e-7f49365ce407')
|
||||
def test_csuld_single_device_interface_vlan(self):
|
||||
"""Single device/interface/vlan
|
||||
|
||||
Create l2gw without specifying LAN. In this case,
|
||||
l2-gateway-connnection need to specify VLAN.
|
||||
"""
|
||||
|
||||
dev_profile = self.getattr_or_skip_test("device_one_vlan")
|
||||
_name = data_utils.rand_name('l2gw-1v2')
|
||||
_devices = base_l2gw.get_l2gw_body(dev_profile)
|
||||
self.do_csuld_single_device_interface_vlan(_name, _devices)
|
||||
|
||||
@test.idempotent_id('cb59145e-3d2b-46b7-8f7b-f30f794a4d51')
|
||||
@decorators.skip_because(bug="1559913")
|
||||
def test_csuld_single_device_interface_mvlan(self):
|
||||
dev_profile = self.getattr_or_skip_test("device_multiple_vlans")
|
||||
_name = data_utils.rand_name('l2gw-2v1')
|
||||
_devices = base_l2gw.get_l2gw_body(dev_profile)
|
||||
self.do_csuld_single_device_interface_vlan(_name, _devices)
|
||||
|
||||
@decorators.skip_because(bug="1559913")
|
||||
@test.idempotent_id('5522bdfe-ebe8-4eea-81b4-f4075bb608cf')
|
||||
def test_csuld_single_device_minterface_mvlan_type1(self):
|
||||
# NSX-v does not support multiple interfaces
|
||||
dev_profile = self.getattr_or_skip_test(
|
||||
"multiple_interfaces_multiple_vlans")
|
||||
_name = data_utils.rand_name('l2gw-m2v1')
|
||||
_devices = base_l2gw.get_l2gw_body(dev_profile)
|
||||
self.do_csuld_single_device_interface_vlan(_name, _devices)
|
||||
|
||||
@decorators.skip_because(bug="1559913")
|
||||
@test.idempotent_id('5bec26e0-855f-4537-b31b-31663a820ddb')
|
||||
def test_csuld_single_device_minterface_mvlan_type2(self):
|
||||
# NSX-v does not support multiple interfaces
|
||||
dev_profile = self.getattr_or_skip_test(
|
||||
"multiple_interfaces_multiple_vlans")
|
||||
_name = data_utils.rand_name('l2gw-m2v2')
|
||||
_devices = base_l2gw.get_l2gw_body(dev_profile)
|
||||
self.do_csuld_single_device_interface_vlan(_name, _devices)
|
261
vmware_nsx_tempest/tests/nsxv/api/test_l2_gateway_connection.py
Normal file
261
vmware_nsx_tempest/tests/nsxv/api/test_l2_gateway_connection.py
Normal file
@ -0,0 +1,261 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# Copyright 2015 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 netaddr
|
||||
|
||||
from tempest.api.network import base
|
||||
from tempest import config
|
||||
from tempest import test
|
||||
|
||||
from tempest_lib.common.utils import data_utils
|
||||
from tempest_lib import decorators
|
||||
|
||||
from vmware_nsx_tempest.services import base_l2gw
|
||||
from vmware_nsx_tempest.services import l2_gateway_client as L2GW
|
||||
from vmware_nsx_tempest.services import \
|
||||
l2_gateway_connection_client as L2GWC
|
||||
|
||||
CONF = config.CONF
|
||||
L2GW_RID = 'l2_gateway'
|
||||
L2GW_RIDs = 'l2_gateways'
|
||||
L2GWC_RID = 'l2_gateway_connection'
|
||||
L2GWC_RIDs = 'l2_gateway_connections'
|
||||
MSG_DIFF = "l2gw %s=%s is not the same as requested=%s"
|
||||
|
||||
|
||||
class L2GatewayConnectionTest(base.BaseAdminNetworkTest):
|
||||
"""Test l2-gateway-connection operations:
|
||||
|
||||
l2-gateway-connection-create
|
||||
l2-gateway-connection-show
|
||||
l2-gateway-connection-update (no case)
|
||||
l2-gateway-connection-list
|
||||
l2-gateway-connection-delete
|
||||
|
||||
over single device/interface/vlan
|
||||
over single device/interface/multiple-vlans
|
||||
over single device/multiple-interfaces/multiple-vlans
|
||||
over multiple-device/multiple-interfaces/multiple-vlans
|
||||
"""
|
||||
|
||||
credentials = ['primary', 'admin']
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(L2GatewayConnectionTest, cls).skip_checks()
|
||||
if not test.is_extension_enabled('l2-gateway', 'network'):
|
||||
msg = "l2-gateway extension not enabled."
|
||||
raise cls.skipException(msg)
|
||||
if not test.is_extension_enabled('l2-gateway-connection',
|
||||
'network'):
|
||||
msg = "l2-gateway-connection extension is not enabled"
|
||||
raise cls.skipException(msg)
|
||||
# skip test if CONF session:l2gw does not have the following opts
|
||||
cls.getattr_or_skip_test("device_one_vlan")
|
||||
cls.getattr_or_skip_test("vlan_subnet_ipv4_dict")
|
||||
|
||||
@classmethod
|
||||
def getattr_or_skip_test(cls, l2gw_attr_name):
|
||||
attr_value = getattr(CONF.l2gw, l2gw_attr_name, None)
|
||||
if attr_value:
|
||||
return attr_value
|
||||
msg = "CONF session:l2gw attr:%s is not defined." % (l2gw_attr_name)
|
||||
raise cls.skipException(msg)
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(L2GatewayConnectionTest, cls).setup_clients()
|
||||
cls.l2gw_created = {}
|
||||
cls.l2gwc_created = {}
|
||||
l2gw_mgr = cls.os_adm
|
||||
cls.l2gw_client = L2GW.get_client(l2gw_mgr)
|
||||
cls.l2gwc_client = L2GWC.get_client(l2gw_mgr)
|
||||
cls.l2gw_list_0 = cls.l2gw_client.list_l2_gateways()[L2GW_RIDs]
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(L2GatewayConnectionTest, cls).resource_setup()
|
||||
# create primary tenant's VLAN network
|
||||
_subnet = cls.getattr_or_skip_test("vlan_subnet_ipv4_dict")
|
||||
for _x in ('mask_bits',):
|
||||
if _x in _subnet:
|
||||
_subnet[_x] = int(_subnet[_x])
|
||||
# cidr must be presented & in IPNetwork structure
|
||||
_subnet['cidr'] = netaddr.IPNetwork(_subnet['cidr'])
|
||||
_start = _subnet.pop('start', None)
|
||||
_end = _subnet.pop('end', None)
|
||||
if _start and _end:
|
||||
_subnet['allocation_pools'] = [{'start': _start, 'end': _end}]
|
||||
cls.network = cls.create_network()
|
||||
# baseAdminNetworkTest does not derive ip_version, mask_bits from cidr
|
||||
_subnet['ip_version'] = 4
|
||||
if 'mask_bits' not in _subnet:
|
||||
_subnet['mask_bits'] = _subnet['cidr'].prefixlen
|
||||
cls.subnet = cls.create_subnet(cls.network, **_subnet)
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
for _id in cls.l2gwc_created.keys():
|
||||
try:
|
||||
cls.l2gwc_client.delete_l2_gateway_connection(_id)
|
||||
except Exception:
|
||||
# log it please
|
||||
pass
|
||||
for _id in cls.l2gw_created.keys():
|
||||
try:
|
||||
cls.l2gw_client.delete_l2_gateway(_id)
|
||||
except Exception:
|
||||
# log it please
|
||||
pass
|
||||
if hasattr(cls, 'network'):
|
||||
cls.networks_client.delete_network(cls.network['id'])
|
||||
|
||||
@classmethod
|
||||
def get_ipaddress_from_tempest_conf(cls, ip_version=4):
|
||||
"""Return first subnet gateway for configured CIDR."""
|
||||
if ip_version == 4:
|
||||
cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
|
||||
elif ip_version == 6:
|
||||
cidr = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
|
||||
return netaddr.IPAddress(cidr)
|
||||
|
||||
def get_segmentation_id(self, _l2gw, d_idx=0, i_idx=0):
|
||||
_dev = _l2gw['devices'][d_idx]
|
||||
_seg = _dev['interfaces'][i_idx].get('segmentation_id', [])
|
||||
return sorted(_seg)
|
||||
|
||||
def get_interfaces(self, _l2gw, d_idx=0):
|
||||
_dev = _l2gw['devices'][d_idx]
|
||||
return sorted(_dev)
|
||||
|
||||
def pop_segmentation_id(self, _l2gw, d_idx=0, i_idx=0):
|
||||
_dev = _l2gw['devices'][d_idx]
|
||||
_seg = _dev['interfaces'][i_idx].pop('segmentation_id', [])
|
||||
return sorted(_seg)
|
||||
|
||||
def create_l2gw_switch(self, _name, _devices):
|
||||
_vlan_id_list = self.get_segmentation_id(_devices)
|
||||
_res_new = self.l2gw_client.create_l2_gateway(
|
||||
name=_name, **_devices)[L2GW_RID]
|
||||
self.l2gw_created[_res_new['id']] = _res_new
|
||||
_res_show = self.l2gw_client.show_l2_gateway(
|
||||
_res_new['id'])[L2GW_RID]
|
||||
return (_res_show, _vlan_id_list)
|
||||
|
||||
def create_l2gw_connection(self, _l2gw, network_id=None, **kwargs):
|
||||
network_id = network_id or self.network['id']
|
||||
_seg_id = kwargs.pop('default_segmentation_id',
|
||||
kwargs.pop('segmentation_id', None))
|
||||
cr_body = {'l2_gateway_id': _l2gw['id'], 'network_id': network_id}
|
||||
if _seg_id:
|
||||
cr_body['segmentation_id'] = _seg_id
|
||||
_res_new = self.l2gwc_client.create_l2_gateway_connection(
|
||||
**cr_body)[L2GWC_RID]
|
||||
self.l2gwc_created[_res_new['id']] = _res_new
|
||||
_res_show = self.l2gwc_client.show_l2_gateway_connection(
|
||||
_res_new['id'])[L2GWC_RID]
|
||||
return (_res_show, _seg_id)
|
||||
|
||||
def do_suld_l2gw_connection(self, _res_new):
|
||||
_res_show = self.l2gwc_client.show_l2_gateway_connection(
|
||||
_res_new['id'])[L2GWC_RID]
|
||||
for _k in ('l2_gateway_id', 'network_id'):
|
||||
self.assertEqual(_res_show[_k], _res_new[_k])
|
||||
_res_lst = self.l2gwc_client.list_l2_gateway_connections(
|
||||
l2_gateway_id=_res_new['l2_gateway_id'],
|
||||
network_id=_res_new['network_id'])[L2GWC_RIDs][0]
|
||||
self.assertEqual(_res_show['l2_gateway_id'], _res_lst['l2_gateway_id'])
|
||||
self.l2gwc_client.delete_l2_gateway_connection(_res_new['id'])
|
||||
_res_lst = self.l2gwc_client.list_l2_gateway_connections(
|
||||
l2_gateway_id=_res_new['l2_gateway_id'],
|
||||
network_id=_res_new['network_id'])[L2GWC_RIDs]
|
||||
self.l2gwc_created.pop(_res_new['id'])
|
||||
self.assertEmpty(_res_lst,
|
||||
"l2gwc id=%s not deleted." % (_res_new['id']))
|
||||
|
||||
@test.idempotent_id('6628c662-b997-46cd-8266-77f329bda062')
|
||||
def test_csuld_single_device_interface_without_vlan(self):
|
||||
"""Single device/interface/vlan
|
||||
|
||||
Create l2gw with one and only one VLAN. In this case,
|
||||
l2-gateway-connnection does not need to specify VLAN.
|
||||
"""
|
||||
|
||||
dev_profile = self.getattr_or_skip_test("device_one_vlan")
|
||||
_name = data_utils.rand_name('l2gwc-1v1')
|
||||
_devices = base_l2gw.get_l2gw_body(dev_profile)
|
||||
_vlan_id_list = self.pop_segmentation_id(_devices)
|
||||
(_gw, _seg_list) = self.create_l2gw_switch(_name, _devices)
|
||||
(_res_new, _seg_id) = self.create_l2gw_connection(
|
||||
_gw, segmentation_id=_vlan_id_list[0])
|
||||
_seg_new = str(_res_new.get('segmentation_id'))
|
||||
self.assertEqual(_seg_new, str(_seg_id))
|
||||
self.do_suld_l2gw_connection(_res_new)
|
||||
|
||||
@test.idempotent_id('222104e3-1260-42c1-bdf6-536c1141387c')
|
||||
def test_csuld_single_device_interface_vlan(self):
|
||||
"""Single device/interface/vlan
|
||||
|
||||
Create l2gw without specifying LAN. In this case,
|
||||
l2-gateway-connnection need to specify VLAN.
|
||||
"""
|
||||
|
||||
dev_profile = self.getattr_or_skip_test("device_one_vlan")
|
||||
_name = data_utils.rand_name('l2gwc-1v2')
|
||||
_devices = base_l2gw.get_l2gw_body(dev_profile)
|
||||
(_gw, _seg_list) = self.create_l2gw_switch(_name, _devices)
|
||||
(_res_new, _seg_id) = self.create_l2gw_connection(_gw)
|
||||
_seg_new = _res_new.get('segmentation_id', None)
|
||||
# vlan specified @l2-gateway, so it is empty @l2-gateway-connection
|
||||
self.assertEmpty(_seg_new)
|
||||
self.do_suld_l2gw_connection(_res_new)
|
||||
|
||||
@decorators.skip_because(bug="1559913")
|
||||
@test.idempotent_id('1875eca7-fde9-49ba-be21-47a8cc41f2e5')
|
||||
def test_csuld_single_device_interface_mvlan_type2(self):
|
||||
dev_profile = self.getattr_or_skip_test("device_multiple_vlans")
|
||||
_name = data_utils.rand_name('l2gwc-2v1')
|
||||
_devices = base_l2gw.get_l2gw_body(dev_profile)
|
||||
_vlan_id_list = self.get_segmentation_id(_devices)
|
||||
(_gw, _seg_list) = self.create_l2gw_switch(_name, _devices)
|
||||
(_res_new, _seg_id_list) = self.create_l2gw_connection(_gw)
|
||||
_seg_id_list = _res_new.get('segmentation_id')
|
||||
self.assertEqaul(0, cmp(_vlan_id_list, _seg_id_list),
|
||||
MSG_DIFF % ('vlan', _vlan_id_list, _seg_id_list))
|
||||
self.do_suld_l2gw_connection(_res_new)
|
||||
|
||||
@decorators.skip_because(bug="1559913")
|
||||
@test.idempotent_id('53755cb0-fdca-4ee7-8e43-a9b8a9d6d90a')
|
||||
def test_csuld_single_device_minterface_mvlan_type1(self):
|
||||
# NSX-v does not support multiple interfaces
|
||||
dev_profile = self.getattr_or_skip_test(
|
||||
"multiple_interfaces_multiple_vlans")
|
||||
_name = data_utils.rand_name('l2gwc-m2v1')
|
||||
_devices = base_l2gw.get_l2gw_body(dev_profile)
|
||||
_gw = self.create_l2gw_switch(_name, _devices)
|
||||
(_res_new, _seg_id) = self.create_l2gw_connection(_gw)
|
||||
self.do_suld_l2gw_connection(_res_new)
|
||||
|
||||
@decorators.skip_because(bug="1559913")
|
||||
@test.idempotent_id('723b0b78-35d7-4774-89c1-ec73797a1fe3')
|
||||
def test_csuld_single_device_minterface_mvlan_type2(self):
|
||||
dev_profile = self.getattr_or_skip_test(
|
||||
"multiple_interfaces_multiple_vlans")
|
||||
_name = data_utils.rand_name('l2gwc-m2v2')
|
||||
_devices = base_l2gw.get_l2gw_body(dev_profile)
|
||||
_gw = self.create_l2gw_switch(_name, _devices)
|
||||
(_res_new, _seg_id) = self.create_l2gw_connection(_gw)
|
||||
self.do_suld_l2gw_connection(_res_new)
|
198
vmware_nsx_tempest/tests/nsxv/api/test_router_types.py
Normal file
198
vmware_nsx_tempest/tests/nsxv/api/test_router_types.py
Normal file
@ -0,0 +1,198 @@
|
||||
# Copyright 2015 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 re
|
||||
import time
|
||||
|
||||
from tempest_lib.common.utils import data_utils
|
||||
|
||||
from tempest.api.network import base_routers as base
|
||||
from tempest import config
|
||||
from tempest import test
|
||||
from vmware_nsx_tempest.services import nsxv_client
|
||||
|
||||
CONF = config.CONF
|
||||
ROUTER_SIZE = ('compact', 'large', 'xlarge', 'quadlarge')
|
||||
|
||||
|
||||
class ExcRouterTest(base.BaseRouterTest):
|
||||
"""
|
||||
Test class for exclusive router type, which is 1:1 mapping of
|
||||
NSX-v service edge. Tests will sipped if the router-type
|
||||
extension is not enabled.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(ExcRouterTest, cls).skip_checks()
|
||||
if not test.is_extension_enabled('nsxv-router-type', 'network'):
|
||||
msg = "router-type extension is not enabled"
|
||||
raise cls.skipException(msg)
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(ExcRouterTest, cls).setup_clients()
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(ExcRouterTest, cls).resource_setup()
|
||||
cls.tenant_cidr = (CONF.network.tenant_network_cidr
|
||||
if cls._ip_version == 4 else
|
||||
CONF.network.tenant_network_v6_cidr)
|
||||
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)
|
||||
|
||||
@test.attr(type='nsxv')
|
||||
@test.idempotent_id('ac1639a0-2a8d-4c68-bccd-54849fd45f86')
|
||||
def test_create_exc_router(self):
|
||||
"""
|
||||
Test create an exclusive router. After creation, check nsx_v
|
||||
backend create service for the exclusive router.
|
||||
"""
|
||||
name = data_utils.rand_name('router-')
|
||||
router = self.client.create_router(
|
||||
name, external_gateway_info={
|
||||
"network_id": CONF.network.public_network_id},
|
||||
admin_state_up=False, router_type='exclusive')
|
||||
self.addCleanup(self._delete_router, router['router']['id'])
|
||||
router_nsxv_name = '%s-%s' % (router['router']['name'],
|
||||
router['router']['id'])
|
||||
self.assertEqual(router['router']['name'], name)
|
||||
exc_edge = self.vsm.get_edge(router_nsxv_name)
|
||||
self.assertTrue(exc_edge is not None)
|
||||
self.assertEqual(exc_edge['edgeType'], 'gatewayServices')
|
||||
|
||||
@test.attr(type='nsxv')
|
||||
@test.idempotent_id('c4b94988-0bc7-11e5-9203-0050568833db')
|
||||
def test_update_exc_router(self):
|
||||
"""
|
||||
Test update an exclusive router
|
||||
"""
|
||||
name = data_utils.rand_name('router-')
|
||||
router = self.client.create_router(
|
||||
name, external_gateway_info={
|
||||
"network_id": CONF.network.public_network_id},
|
||||
admin_state_up=False, router_type='exclusive')
|
||||
self.addCleanup(self._delete_router, router['router']['id'])
|
||||
self.assertEqual(router['router']['name'], name)
|
||||
updated_name = 'updated' + name
|
||||
update_body = self.client.update_router(router['router']['id'],
|
||||
name=updated_name)
|
||||
self.assertEqual(update_body['router']['name'], updated_name)
|
||||
|
||||
@test.attr(type='nsxv')
|
||||
@test.idempotent_id('a0ff5afa-0bcc-11e5-9203-0050568833db')
|
||||
def test_list_show_exc_router(self):
|
||||
"""
|
||||
Test list and show exclusive router.
|
||||
"""
|
||||
name = data_utils.rand_name('router-')
|
||||
router = self.client.create_router(
|
||||
name, external_gateway_info={
|
||||
"network_id": CONF.network.public_network_id},
|
||||
admin_state_up=False, router_type='exclusive')
|
||||
self.addCleanup(self._delete_router, router['router']['id'])
|
||||
self.assertEqual(router['router']['name'], name)
|
||||
# Show details of exclusive router
|
||||
show_body = self.client.show_router(router['router']['id'])
|
||||
self.assertEqual(show_body['router']['name'], name)
|
||||
self.assertEqual(show_body['router']['admin_state_up'], False)
|
||||
# List routers and verify if created router in list
|
||||
list_body = self.client.list_routers()
|
||||
routers_list = [r['id'] for r in list_body['routers']]
|
||||
self.assertIn(router['router']['id'], routers_list)
|
||||
|
||||
@test.attr(type='nsxv')
|
||||
@test.idempotent_id('adef8d1e-0bce-11e5-9203-0050568833db')
|
||||
def test_delete_exc_router(self):
|
||||
"""
|
||||
Test create, update, and delete an exclusive router
|
||||
"""
|
||||
name = data_utils.rand_name('router-')
|
||||
router = self.client.create_router(
|
||||
name, external_gateway_info={
|
||||
"network_id": CONF.network.public_network_id},
|
||||
admin_state_up=False, router_type='exclusive')
|
||||
self.assertEqual(router['router']['name'], name)
|
||||
# Update the name of the exclusive router
|
||||
updated_name = 'updated' + name
|
||||
update_body = self.client.update_router(router['router']['id'],
|
||||
name=updated_name)
|
||||
self.assertEqual(update_body['router']['name'], updated_name)
|
||||
# Delete the exclusive router and verify it has been deleted
|
||||
# from nsxv backend
|
||||
self.client.delete_router(router['router']['id'])
|
||||
list_body = self.client.list_routers()
|
||||
routers_list = [r['id'] for r in list_body['routers']]
|
||||
self.assertNotIn(router['router']['id'], routers_list)
|
||||
nsxv_edge_name = "%s-%s" % (name, router['router']['id'])
|
||||
self.assertEqual(self.vsm.get_edge(nsxv_edge_name), None)
|
||||
|
||||
@test.attr(type='nsxv')
|
||||
@test.idempotent_id('d75fbcd5-c8cb-49ea-a868-ada12fd8c87f')
|
||||
def test_create_update_delete_compact_router(self):
|
||||
self.do_create_update_delete_router_with_size('compact')
|
||||
|
||||
@test.attr(type='nsxv')
|
||||
@test.idempotent_id('da00c74f-81e6-4ef9-8aca-8e0345b376e9')
|
||||
def test_create_update_delete_large_router(self):
|
||||
self.do_create_update_delete_router_with_size('large', 20.0)
|
||||
|
||||
@test.attr(type='nsxv')
|
||||
@test.idempotent_id('091dad07-6044-4ca3-b16c-54a3ef92254b')
|
||||
def test_create_update_delete_xlarge_router(self):
|
||||
self.do_create_update_delete_router_with_size('xlarge', 20.0)
|
||||
|
||||
@test.attr(type='nsxv')
|
||||
@test.idempotent_id('0f69bf8a-4b06-47ac-a3f7-eedba95fd395')
|
||||
def test_create_update_delete_quadlarge_router(self):
|
||||
self.do_create_update_delete_router_with_size('quadlarge', 30.0)
|
||||
|
||||
def do_create_update_delete_router_with_size(self,
|
||||
router_size,
|
||||
del_waitfor=10.0,
|
||||
del_interval=1.5):
|
||||
name = data_utils.rand_name('rtr-%s' % router_size)
|
||||
router = self.client.create_router(
|
||||
name, external_gateway_info={
|
||||
"network_id": CONF.network.public_network_id},
|
||||
admin_state_up=False, router_type='exclusive',
|
||||
router_size=router_size)
|
||||
self.assertEqual(router['router']['name'], name)
|
||||
# Update the name of the exclusive router
|
||||
updated_name = 'updated' + name
|
||||
update_body = self.client.update_router(router['router']['id'],
|
||||
name=updated_name)
|
||||
self.assertEqual(update_body['router']['name'], updated_name)
|
||||
# Delete the exclusive router and verify it has been deleted
|
||||
# from nsxv backend
|
||||
self.client.delete_router(router['router']['id'])
|
||||
list_body = self.client.list_routers()
|
||||
routers_list = [r['id'] for r in list_body['routers']]
|
||||
self.assertNotIn(router['router']['id'], routers_list)
|
||||
nsxv_edge_name = "%s-%s" % (name, router['router']['id'])
|
||||
wait_till = time.time() + del_waitfor
|
||||
while (time.time() < wait_till):
|
||||
try:
|
||||
self.assertEqual(self.vsm.get_edge(nsxv_edge_name), None)
|
||||
return
|
||||
except Exception:
|
||||
time.sleep(del_interval)
|
||||
# last try. Fail if nesx_edge still exists
|
||||
fail_msg = ("%s router nsxv_edge[%s] still exists after %s seconds." %
|
||||
(router_size, nsxv_edge_name, del_waitfor))
|
||||
self.assertEqual(self.vsm.get_edge(nsxv_edge_name), None, fail_msg)
|
494
vmware_nsx_tempest/tests/nsxv/api/test_subnets.py
Normal file
494
vmware_nsx_tempest/tests/nsxv/api/test_subnets.py
Normal file
@ -0,0 +1,494 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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 base_provider as base
|
||||
from tempest.common import custom_matchers
|
||||
from tempest import config
|
||||
from tempest import test
|
||||
|
||||
import netaddr
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
from tempest_lib.common.utils import data_utils
|
||||
from tempest_lib import decorators
|
||||
from tempest_lib import exceptions
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SubnetTestJSON(base.BaseAdminNetworkTest):
|
||||
_provider_network_body = {}
|
||||
|
||||
"""
|
||||
[NOTE: This module copied/modified from api/network/test_networks.py
|
||||
to create provider networks/subnets tests]
|
||||
|
||||
Tests the following operations in the Neutron API using the REST client for
|
||||
Neutron:
|
||||
|
||||
create a network for a tenant
|
||||
list tenant's networks
|
||||
show a tenant network details
|
||||
create a subnet for a tenant
|
||||
list tenant's subnets
|
||||
show a tenant subnet details
|
||||
network update
|
||||
subnet update
|
||||
delete a network also deletes its subnets
|
||||
|
||||
All subnet tests are run once with ipv4 and once with ipv6.
|
||||
|
||||
v2.0 of the Neutron API is assumed. It is also assumed that the following
|
||||
options are defined in the [network] section of etc/tempest.conf:
|
||||
|
||||
tenant_network_cidr with a block of cidr's from which smaller blocks
|
||||
can be allocated for tenant ipv4 subnets
|
||||
|
||||
tenant_network_v6_cidr is the equivalent for ipv6 subnets
|
||||
|
||||
tenant_network_mask_bits with the mask bits to be used to partition the
|
||||
block defined by tenant_network_cidr
|
||||
|
||||
tenant_network_v6_mask_bits is the equivalent for ipv6 subnets
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(SubnetTestJSON, cls).resource_setup()
|
||||
for k, v in cls._provider_network_body.items():
|
||||
if not v:
|
||||
cls._provider_network_body.pop(k)
|
||||
body = cls.create_network(client=cls.admin_networks_client,
|
||||
**cls._provider_network_body)
|
||||
cls.network = body['network']
|
||||
cls.name = cls.network['name']
|
||||
cls.subnet = cls._create_subnet_with_last_subnet_block(cls.network)
|
||||
cls.cidr = cls.subnet['cidr']
|
||||
cls._subnet_data = {6: {'gateway':
|
||||
str(cls._get_gateway_from_tempest_conf(6)),
|
||||
'allocation_pools':
|
||||
cls._get_allocation_pools_from_gateway(6),
|
||||
'dns_nameservers': ['2001:4860:4860::8844',
|
||||
'2001:4860:4860::8888'],
|
||||
'host_routes': [{'destination': '2001::/64',
|
||||
'nexthop': '2003::1'}],
|
||||
'new_host_routes': [{'destination':
|
||||
'2001::/64',
|
||||
'nexthop': '2005::1'}],
|
||||
'new_dns_nameservers':
|
||||
['2001:4860:4860::7744',
|
||||
'2001:4860:4860::7888']},
|
||||
4: {'gateway':
|
||||
str(cls._get_gateway_from_tempest_conf(4)),
|
||||
'allocation_pools':
|
||||
cls._get_allocation_pools_from_gateway(4),
|
||||
'dns_nameservers': ['8.8.4.4', '8.8.8.8'],
|
||||
'host_routes': [{'destination': '10.20.0.0/32',
|
||||
'nexthop': '10.100.1.1'}],
|
||||
'new_host_routes': [{'destination':
|
||||
'10.20.0.0/32',
|
||||
'nexthop':
|
||||
'10.100.1.2'}],
|
||||
'new_dns_nameservers': ['7.8.8.8', '7.8.4.4']}}
|
||||
|
||||
@classmethod
|
||||
def _create_subnet_with_last_subnet_block(cls, network, ip_version=4):
|
||||
"""Derive last subnet CIDR block from tenant CIDR and
|
||||
create the subnet with that derived CIDR
|
||||
"""
|
||||
if ip_version == 4:
|
||||
cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
|
||||
mask_bits = CONF.network.tenant_network_mask_bits
|
||||
elif ip_version == 6:
|
||||
cidr = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
|
||||
mask_bits = CONF.network.tenant_network_v6_mask_bits
|
||||
|
||||
subnet_cidr = list(cidr.subnet(mask_bits))[-1]
|
||||
gateway_ip = str(netaddr.IPAddress(subnet_cidr) + 1)
|
||||
body = cls.create_subnet(network, gateway=gateway_ip,
|
||||
cidr=subnet_cidr, mask_bits=mask_bits)
|
||||
return body['subnet']
|
||||
|
||||
@classmethod
|
||||
def _get_gateway_from_tempest_conf(cls, ip_version):
|
||||
"""Return first subnet gateway for configured CIDR."""
|
||||
if ip_version == 4:
|
||||
cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
|
||||
mask_bits = CONF.network.tenant_network_mask_bits
|
||||
elif ip_version == 6:
|
||||
cidr = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
|
||||
mask_bits = CONF.network.tenant_network_v6_mask_bits
|
||||
|
||||
if mask_bits >= cidr.prefixlen:
|
||||
return netaddr.IPAddress(cidr) + 1
|
||||
else:
|
||||
for subnet in cidr.subnet(mask_bits):
|
||||
return netaddr.IPAddress(subnet) + 1
|
||||
|
||||
@classmethod
|
||||
def _get_allocation_pools_from_gateway(cls, ip_version):
|
||||
"""Return allocation range for subnet of given gateway."""
|
||||
gateway = cls._get_gateway_from_tempest_conf(ip_version)
|
||||
return [{'start': str(gateway + 2), 'end': str(gateway + 3)}]
|
||||
|
||||
def subnet_dict(self, include_keys):
|
||||
"""Return a subnet dict which has include_keys and their corresponding
|
||||
value from self._subnet_data
|
||||
"""
|
||||
return dict((key, self._subnet_data[self._ip_version][key])
|
||||
for key in include_keys)
|
||||
|
||||
def _create_network(self, _auto_clean_up=True, network_name=None,
|
||||
**kwargs):
|
||||
network_name = network_name or data_utils.rand_name('adm-netwk')
|
||||
post_body = {'name': network_name}
|
||||
post_body.update(kwargs)
|
||||
LOG.debug("create ADM network: %s", str(post_body))
|
||||
body = self.create_network(client=self.admin_networks_client,
|
||||
**post_body)
|
||||
network = body['network']
|
||||
if _auto_clean_up:
|
||||
self.addCleanup(self._try_delete_network, network['id'])
|
||||
return network
|
||||
|
||||
# when you call _delete_network() you mean it is part of test,
|
||||
# so we will not pass exception
|
||||
def _delete_network(self, net_id):
|
||||
self._remove_network_from_book(net_id)
|
||||
return self.delete_network(net_id)
|
||||
|
||||
def _remove_network_from_book(self, net_id):
|
||||
for idx, netwk_info in zip(range(0, len(self.admin_netwk_info)),
|
||||
self.admin_netwk_info):
|
||||
net_client, network = netwk_info
|
||||
if network['id'] == net_id:
|
||||
self.admin_netwk_info.pop(idx)
|
||||
return
|
||||
|
||||
# call _try_delete_network() for teardown purpose, so pass exception
|
||||
def _try_delete_network(self, net_id):
|
||||
# delete network, if it exists
|
||||
self._remove_network_from_book(net_id)
|
||||
try:
|
||||
self.delete_network(net_id)
|
||||
# if network is not found, this means it was deleted in the test
|
||||
except exceptions.NotFound:
|
||||
pass
|
||||
|
||||
# by default, subnet will be deleted when its network is deleted
|
||||
def _create_subnet(self, network, gateway='', cidr=None, mask_bits=None,
|
||||
ip_version=None, cidr_offset=0,
|
||||
_auto_clean_up=False, **kwargs):
|
||||
body = self.create_subnet(network,
|
||||
gateway=gateway,
|
||||
cidr=cidr,
|
||||
mask_bits=mask_bits,
|
||||
ip_version=ip_version,
|
||||
cidr_offset=cidr_offset,
|
||||
**kwargs)
|
||||
subnet = body['subnet']
|
||||
if _auto_clean_up:
|
||||
self.addCleanup(self._try_delete_subnet, subnet['id'])
|
||||
return subnet
|
||||
|
||||
def _try_delete_subnet(self, net_id):
|
||||
# delete subnet, if it exists
|
||||
try:
|
||||
self.delete_subnet(net_id)
|
||||
# if network is not found, this means it was deleted in the test
|
||||
except exceptions.NotFound:
|
||||
pass
|
||||
|
||||
def _compare_resource_attrs(self, actual, expected):
|
||||
exclude_keys = set(actual).symmetric_difference(expected)
|
||||
self.assertThat(actual, custom_matchers.MatchesDictExceptForKeys(
|
||||
expected, exclude_keys))
|
||||
|
||||
def _create_verify_delete_subnet(self, cidr=None, mask_bits=None,
|
||||
**kwargs):
|
||||
network = self._create_network(_auto_clean_up=True)
|
||||
net_id = network['id']
|
||||
gateway = kwargs.pop('gateway', None)
|
||||
subnet = self._create_subnet(network, gateway, cidr, mask_bits,
|
||||
**kwargs)
|
||||
compare_args_full = dict(gateway_ip=gateway, cidr=cidr,
|
||||
mask_bits=mask_bits, **kwargs)
|
||||
compare_args = (dict((k, v)
|
||||
for k, v in six.iteritems(compare_args_full)
|
||||
if v is not None))
|
||||
|
||||
if 'dns_nameservers' in set(subnet).intersection(compare_args):
|
||||
self.assertEqual(sorted(compare_args['dns_nameservers']),
|
||||
sorted(subnet['dns_nameservers']))
|
||||
del subnet['dns_nameservers'], compare_args['dns_nameservers']
|
||||
|
||||
self._compare_resource_attrs(subnet, compare_args)
|
||||
self._delete_network(net_id)
|
||||
|
||||
@test.idempotent_id('2ecbc3ab-93dd-44bf-a827-95beeb008e9a')
|
||||
def test_create_update_delete_network_subnet(self):
|
||||
# Create a network
|
||||
network = self._create_network(_auto_clean_up=True)
|
||||
net_id = network['id']
|
||||
self.assertEqual('ACTIVE', network['status'])
|
||||
# Verify network update
|
||||
new_name = data_utils.rand_name('new-adm-netwk')
|
||||
body = self.update_network(net_id, name=new_name)
|
||||
updated_net = body['network']
|
||||
self.assertEqual(updated_net['name'], new_name)
|
||||
# Find a cidr that is not in use yet and create a subnet with it
|
||||
subnet = self._create_subnet(network)
|
||||
subnet_id = subnet['id']
|
||||
# Verify subnet update
|
||||
new_name = data_utils.rand_name('new-subnet')
|
||||
body = self.update_subnet(subnet_id, name=new_name)
|
||||
updated_subnet = body['subnet']
|
||||
self.assertEqual(updated_subnet['name'], new_name)
|
||||
self._delete_network(net_id)
|
||||
|
||||
@test.idempotent_id('a2cf6398-aece-4256-88a6-0dfe8aa44975')
|
||||
def test_show_network(self):
|
||||
# Verify the details of a network
|
||||
body = self.show_network(self.network['id'])
|
||||
network = body['network']
|
||||
for key in ['id', 'name']:
|
||||
self.assertEqual(network[key], self.network[key])
|
||||
|
||||
@test.idempotent_id('5b42067d-4b9d-4f04-bb6a-adb9756ebe0c')
|
||||
def test_show_network_fields(self):
|
||||
# Verify specific fields of a network
|
||||
fields = ['id', 'name']
|
||||
body = self.show_network(self.network['id'], fields=fields)
|
||||
network = body['network']
|
||||
self.assertEqual(sorted(network.keys()), sorted(fields))
|
||||
for field_name in fields:
|
||||
self.assertEqual(network[field_name], self.network[field_name])
|
||||
|
||||
@test.idempotent_id('324be3c2-457d-4e21-b0b3-5106bbbf1a28')
|
||||
def test_list_networks(self):
|
||||
# Verify the network exists in the list of all networks
|
||||
body = self.list_networks()
|
||||
networks = [network['id'] for network in body['networks']
|
||||
if network['id'] == self.network['id']]
|
||||
self.assertNotEmpty(networks, "Created network not found in the list")
|
||||
|
||||
@test.idempotent_id('3a934a8d-6b52-427e-af49-3dfdd224fdeb')
|
||||
def test_list_networks_fields(self):
|
||||
# Verify specific fields of the networks
|
||||
fields = ['id', 'name']
|
||||
body = self.list_networks(fields=fields)
|
||||
networks = body['networks']
|
||||
self.assertNotEmpty(networks, "Network list returned is empty")
|
||||
for network in networks:
|
||||
self.assertEqual(sorted(network.keys()), sorted(fields))
|
||||
|
||||
@test.idempotent_id('5f6616c4-bfa7-4308-8eab-f45d75c94c6d')
|
||||
def test_show_subnet(self):
|
||||
# Verify the details of a subnet
|
||||
body = self.show_subnet(self.subnet['id'])
|
||||
subnet = body['subnet']
|
||||
self.assertNotEmpty(subnet, "Subnet returned has no fields")
|
||||
for key in ['id', 'cidr']:
|
||||
self.assertIn(key, subnet)
|
||||
self.assertEqual(subnet[key], self.subnet[key])
|
||||
|
||||
@test.idempotent_id('2f326955-551e-4e9e-a4f6-e5db77c34c8d')
|
||||
def test_show_subnet_fields(self):
|
||||
# Verify specific fields of a subnet
|
||||
fields = ['id', 'network_id']
|
||||
body = self.show_subnet(self.subnet['id'], fields=fields)
|
||||
subnet = body['subnet']
|
||||
self.assertEqual(sorted(subnet.keys()), sorted(fields))
|
||||
for field_name in fields:
|
||||
self.assertEqual(subnet[field_name], self.subnet[field_name])
|
||||
|
||||
@test.idempotent_id('66631557-2466-4827-bba6-d961b0242be3')
|
||||
def test_list_subnets(self):
|
||||
# Verify the subnet exists in the list of all subnets
|
||||
body = self.list_subnets()
|
||||
subnets = [subnet['id'] for subnet in body['subnets']
|
||||
if subnet['id'] == self.subnet['id']]
|
||||
self.assertNotEmpty(subnets, "Created subnet not found in the list")
|
||||
|
||||
@test.idempotent_id('3d5ea69b-f122-43e7-b7f4-c78586629eb8')
|
||||
def test_list_subnets_fields(self):
|
||||
# Verify specific fields of subnets
|
||||
fields = ['id', 'network_id']
|
||||
body = self.list_subnets(fields=fields)
|
||||
subnets = body['subnets']
|
||||
self.assertNotEmpty(subnets, "Subnet list returned is empty")
|
||||
for subnet in subnets:
|
||||
self.assertEqual(sorted(subnet.keys()), sorted(fields))
|
||||
|
||||
@test.idempotent_id('e966bb2f-402c-49b7-8147-b275cee584c4')
|
||||
def test_delete_network_with_subnet(self):
|
||||
# Creates a network
|
||||
network = self._create_network(_auto_clean_up=True)
|
||||
net_id = network['id']
|
||||
|
||||
# Find a cidr that is not in use yet and create a subnet with it
|
||||
subnet = self._create_subnet(network)
|
||||
subnet_id = subnet['id']
|
||||
|
||||
# Delete network while the subnet still exists
|
||||
self._delete_network(net_id)
|
||||
|
||||
# Verify that the subnet got automatically deleted.
|
||||
self.assertRaises(exceptions.NotFound,
|
||||
self.show_subnet, subnet_id)
|
||||
|
||||
@test.idempotent_id('8aba0e1b-4b70-4181-a8a4-792c08db699d')
|
||||
def test_create_delete_subnet_without_gateway(self):
|
||||
self._create_verify_delete_subnet()
|
||||
|
||||
@test.idempotent_id('67364a4b-6725-4dbe-84cf-504bdb20ac06')
|
||||
def test_create_delete_subnet_with_gw(self):
|
||||
self._create_verify_delete_subnet(
|
||||
**self.subnet_dict(['gateway']))
|
||||
|
||||
@test.idempotent_id('f8f43e65-5090-4902-b5d2-2b610505cca6')
|
||||
def test_create_delete_subnet_with_allocation_pools(self):
|
||||
self._create_verify_delete_subnet(
|
||||
**self.subnet_dict(['allocation_pools']))
|
||||
|
||||
@test.idempotent_id('5b085669-97e6-48e0-b99e-315a9b4d8482')
|
||||
def test_create_delete_subnet_with_gw_and_allocation_pools(self):
|
||||
self._create_verify_delete_subnet(**self.subnet_dict(
|
||||
['gateway', 'allocation_pools']))
|
||||
|
||||
@decorators.skip_because(bug="1501827")
|
||||
def test_create_delete_subnet_with_host_routes_and_dns_nameservers(self):
|
||||
self._create_verify_delete_subnet(
|
||||
**self.subnet_dict(['host_routes', 'dns_nameservers']))
|
||||
|
||||
@test.idempotent_id('df518c87-b817-48b5-9365-bd1daaf68955')
|
||||
def test_create_delete_subnet_with_dns_nameservers(self):
|
||||
self._create_verify_delete_subnet(
|
||||
**self.subnet_dict(['dns_nameservers']))
|
||||
|
||||
@test.idempotent_id('b6822feb-6760-4052-b550-f0fe8bac7451')
|
||||
def test_create_delete_subnet_with_dhcp_enabled(self):
|
||||
self._create_verify_delete_subnet(enable_dhcp=True)
|
||||
|
||||
@decorators.skip_because(bug="1501827")
|
||||
def test_update_subnet_gw_dns_host_routes_dhcp(self):
|
||||
network = self._create_network(_auto_clean_up=True)
|
||||
subnet_attrs = ['gateway', 'host_routes',
|
||||
'dns_nameservers', 'allocation_pools']
|
||||
subnet_dict = self.subnet_dict(subnet_attrs)
|
||||
subnet = self._create_subnet(network, **subnet_dict)
|
||||
subnet_id = subnet['id']
|
||||
new_gateway = str(netaddr.IPAddress(
|
||||
self._subnet_data[self._ip_version]['gateway']) + 1)
|
||||
# Verify subnet update
|
||||
new_host_routes = self._subnet_data[self._ip_version][
|
||||
'new_host_routes']
|
||||
|
||||
new_dns_nameservers = self._subnet_data[self._ip_version][
|
||||
'new_dns_nameservers']
|
||||
kwargs = {'host_routes': new_host_routes,
|
||||
'dns_nameservers': new_dns_nameservers,
|
||||
'gateway_ip': new_gateway, 'enable_dhcp': True}
|
||||
|
||||
new_name = "New_subnet"
|
||||
body = self.update_subnet(subnet_id, name=new_name, **kwargs)
|
||||
updated_subnet = body['subnet']
|
||||
kwargs['name'] = new_name
|
||||
self.assertEqual(sorted(updated_subnet['dns_nameservers']),
|
||||
sorted(kwargs['dns_nameservers']))
|
||||
del subnet['dns_nameservers'], kwargs['dns_nameservers']
|
||||
|
||||
self._compare_resource_attrs(updated_subnet, kwargs)
|
||||
self._delete_network(network['id'])
|
||||
|
||||
@test.idempotent_id('a5caa7d9-ab71-4278-a57c-d6631b7474f8')
|
||||
def test_update_subnet_gw_dns_dhcp(self):
|
||||
network = self._create_network(_auto_clean_up=True)
|
||||
subnet_attrs = ['gateway',
|
||||
'dns_nameservers', 'allocation_pools']
|
||||
subnet_dict = self.subnet_dict(subnet_attrs)
|
||||
subnet = self._create_subnet(network, **subnet_dict)
|
||||
subnet_id = subnet['id']
|
||||
new_gateway = str(netaddr.IPAddress(
|
||||
self._subnet_data[self._ip_version]['gateway']) + 1)
|
||||
# Verify subnet update
|
||||
new_dns_nameservers = self._subnet_data[self._ip_version][
|
||||
'new_dns_nameservers']
|
||||
kwargs = {'dns_nameservers': new_dns_nameservers,
|
||||
'gateway_ip': new_gateway, 'enable_dhcp': True}
|
||||
|
||||
new_name = "New_subnet"
|
||||
body = self.update_subnet(subnet_id, name=new_name, **kwargs)
|
||||
updated_subnet = body['subnet']
|
||||
kwargs['name'] = new_name
|
||||
self.assertEqual(sorted(updated_subnet['dns_nameservers']),
|
||||
sorted(kwargs['dns_nameservers']))
|
||||
del subnet['dns_nameservers'], kwargs['dns_nameservers']
|
||||
|
||||
self._compare_resource_attrs(updated_subnet, kwargs)
|
||||
self._delete_network(network['id'])
|
||||
|
||||
@decorators.skip_because(bug="1501827")
|
||||
def test_create_delete_subnet_all_attributes(self):
|
||||
self._create_verify_delete_subnet(
|
||||
enable_dhcp=True,
|
||||
**self.subnet_dict(['gateway',
|
||||
'host_routes',
|
||||
'dns_nameservers']))
|
||||
|
||||
@test.idempotent_id('969f20b2-7eb5-44f5-98cd-381545b7c7e7')
|
||||
def test_create_delete_subnet_with_gw_dns(self):
|
||||
self._create_verify_delete_subnet(
|
||||
enable_dhcp=True,
|
||||
**self.subnet_dict(['gateway',
|
||||
'dns_nameservers']))
|
||||
|
||||
@test.idempotent_id('3c4c36a1-684b-4e89-8e71-d528f19324a0')
|
||||
def test_add_upd_del_multiple_overlapping_networks_subnet(self):
|
||||
r0, R1 = 0, 3 # (todo) get from CONF
|
||||
return self._add_upd_del_multiple_networks_subnet(
|
||||
r0, R1, "ovla-netwk")
|
||||
|
||||
@test.idempotent_id('5267bf9d-de82-4af9-914a-8320e9f4c38c')
|
||||
def test_add_upd_del_multiple_nonoverlapping_networks_subnet(self):
|
||||
r0, R1 = 1, 4 # (todo) get from CONF
|
||||
return self._add_upd_del_multiple_networks_subnet(
|
||||
r0, R1, "noov-netwk", _step_cidr=2)
|
||||
|
||||
def _add_upd_del_multiple_networks_subnet(self, r0, R1,
|
||||
name_prefix="m-network",
|
||||
_step_cidr=0):
|
||||
m_name = data_utils.rand_name(name_prefix)
|
||||
netwk = []
|
||||
for x in range(r0, R1):
|
||||
network = self._create_network(_auto_clean_up=True)
|
||||
net_id = network['id']
|
||||
self.assertEqual('ACTIVE', network['status'])
|
||||
new_name = m_name + "-%02d" % x
|
||||
body = self.update_network(net_id, name=new_name)
|
||||
network = body['network']
|
||||
cidr_offset = (x * _step_cidr) if _step_cidr > 0 else 0
|
||||
subnet = self._create_subnet(network, cidr_offset=cidr_offset)
|
||||
subnet_id = subnet['id']
|
||||
netwk.append([x, net_id, subnet_id])
|
||||
for x, net_id, subnet_id in netwk:
|
||||
# make sure subnet is updatable after creation
|
||||
new_name = m_name + "-%02d-snet" % x
|
||||
body = self.update_subnet(subnet_id, name=new_name)
|
||||
updated_subnet = body['subnet']
|
||||
self.assertEqual(updated_subnet['name'], new_name)
|
||||
self._delete_network(net_id)
|
492
vmware_nsx_tempest/tests/nsxv/api/test_v1_lbaas.py
Normal file
492
vmware_nsx_tempest/tests/nsxv/api/test_v1_lbaas.py
Normal file
@ -0,0 +1,492 @@
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
# Copyright 2015 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 six
|
||||
|
||||
from tempest_lib.common.utils import data_utils
|
||||
from tempest_lib import decorators
|
||||
|
||||
from tempest.api.network import base
|
||||
from tempest import config
|
||||
from tempest import test
|
||||
|
||||
from vmware_nsx_tempest.services import load_balancer_v1_client as LBV1C
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class LoadBalancerTestJSON(base.BaseNetworkTest):
|
||||
"""
|
||||
Tests the following operations in the Neutron API using the REST client
|
||||
for
|
||||
Neutron:
|
||||
|
||||
create vIP, and Pool
|
||||
show vIP
|
||||
list vIP
|
||||
update vIP
|
||||
delete vIP
|
||||
update pool
|
||||
delete pool
|
||||
show pool
|
||||
list pool
|
||||
health monitoring operations
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(LoadBalancerTestJSON, cls).skip_checks()
|
||||
if not test.is_extension_enabled('lbaas', 'network'):
|
||||
msg = "lbaas extension not enabled."
|
||||
raise cls.skipException(msg)
|
||||
if not test.is_extension_enabled('nsxv-router-type', 'network'):
|
||||
msg = "nsxv-router-type extension is not enabled"
|
||||
raise cls.skipException(msg)
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(LoadBalancerTestJSON, cls).resource_setup()
|
||||
_params = cls.manager.default_params_with_timeout_values.copy()
|
||||
for p in _params.keys():
|
||||
if p in ['service', 'region', 'endpoint_type']:
|
||||
_params.pop(p)
|
||||
cls.lbv1_client = LBV1C.get_client(cls.manager)
|
||||
cls.network = cls.create_network()
|
||||
cls.name = cls.network['name']
|
||||
cls.subnet = cls.create_subnet(cls.network)
|
||||
cls.ext_net_id = CONF.network.public_network_id
|
||||
cls.router = cls.create_router(data_utils.rand_name('router-'),
|
||||
admin_state_up=True,
|
||||
external_network_id=cls.ext_net_id,
|
||||
router_type='exclusive')
|
||||
cls.create_router_interface(cls.router['id'], cls.subnet['id'])
|
||||
pool_name = data_utils.rand_name('pool-')
|
||||
vip_name = data_utils.rand_name('vip-')
|
||||
cls.pool = cls.lbv1_client.create_pool(
|
||||
pool_name, "ROUND_ROBIN", "HTTP", cls.subnet['id'])['pool']
|
||||
cls.vip = cls.lbv1_client.create_vip(cls.pool['id'],
|
||||
subnet_id=cls.subnet['id'],
|
||||
name=vip_name,
|
||||
protocol="HTTP",
|
||||
protocol_port=80)['vip']
|
||||
cls.member = cls.lbv1_client.create_member(
|
||||
80, cls.pool['id'], cls._ip_version)['member']
|
||||
cls.member_address = ("10.0.9.47" if cls._ip_version == 4
|
||||
else "2015::beef")
|
||||
cls.health_monitor = cls.lbv1_client.create_health_monitor(
|
||||
delay=4, max_retries=3, type="TCP", timeout=1)['health_monitor']
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
"""
|
||||
Cleanup the lb resources first and then call resource_cleanup
|
||||
in BaseNetworkTest to cleanup other network resources. NSX-v
|
||||
plugin requires the lb resources to be deleted before we can
|
||||
delete subnet or remove interface from router.
|
||||
"""
|
||||
# Cleanup lb health monitors
|
||||
if cls.health_monitor:
|
||||
cls._try_delete_resource(cls.lbv1_client.delete_health_monitor,
|
||||
cls.health_monitor['id'])
|
||||
cls.health_monitor = None
|
||||
|
||||
# Cleanup members
|
||||
if cls.member:
|
||||
cls._try_delete_resource(cls.lbv1_client.delete_member,
|
||||
cls.member['id'])
|
||||
cls.member = None
|
||||
|
||||
# Cleanup vips
|
||||
if cls.vip:
|
||||
cls._try_delete_resource(cls.lbv1_client.delete_vip,
|
||||
cls.vip['id'])
|
||||
cls.vip = None
|
||||
|
||||
# Cleanup pool
|
||||
if cls.pool:
|
||||
cls._try_delete_resource(cls.lbv1_client.delete_pool,
|
||||
cls.pool['id'])
|
||||
cls.pool = None
|
||||
|
||||
super(LoadBalancerTestJSON, cls).resource_cleanup()
|
||||
|
||||
def _check_list_with_filter(self, obj_name, attr_exceptions, **kwargs):
|
||||
create_obj = getattr(self.lbv1_client, 'create_' + obj_name)
|
||||
delete_obj = getattr(self.lbv1_client, 'delete_' + obj_name)
|
||||
list_objs = getattr(self.lbv1_client, 'list_' + obj_name + 's')
|
||||
|
||||
body = create_obj(**kwargs)
|
||||
obj = body[obj_name]
|
||||
self.addCleanup(delete_obj, obj['id'])
|
||||
for key, value in six.iteritems(obj):
|
||||
# It is not relevant to filter by all arguments. That is why
|
||||
# there is a list of attr to except
|
||||
if key not in attr_exceptions:
|
||||
body = list_objs(**{key: value})
|
||||
objs = [v[key] for v in body[obj_name + 's']]
|
||||
self.assertIn(value, objs)
|
||||
|
||||
@test.idempotent_id('1c959a37-feb3-4d58-b5fc-58ba653de065')
|
||||
def test_list_vips(self):
|
||||
# Verify the vIP exists in the list of all vIPs
|
||||
body = self.lbv1_client.list_vips()
|
||||
vips = body['vips']
|
||||
self.assertIn(self.vip['id'], [v['id'] for v in vips])
|
||||
|
||||
@test.idempotent_id('687b7fd1-fd15-4ffd-8166-f376407a6081')
|
||||
def test_list_vips_with_filter(self):
|
||||
pool_name = data_utils.rand_name("pool-")
|
||||
vip_name = data_utils.rand_name('vip-')
|
||||
body = self.lbv1_client.create_pool(pool_name,
|
||||
lb_method="ROUND_ROBIN",
|
||||
protocol="HTTPS",
|
||||
subnet_id=self.subnet['id'])
|
||||
pool = body['pool']
|
||||
self.addCleanup(self.lbv1_client.delete_pool, pool['id'])
|
||||
attr_exceptions = ['status', 'session_persistence',
|
||||
'status_description']
|
||||
self._check_list_with_filter(
|
||||
'vip', attr_exceptions, name=vip_name, protocol="HTTPS",
|
||||
protocol_port=81, subnet_id=self.subnet['id'], pool_id=pool['id'],
|
||||
description=data_utils.rand_name('description-'),
|
||||
admin_state_up=False)
|
||||
|
||||
@test.idempotent_id('73dfc119-b64b-4e56-90d2-df61d7181098')
|
||||
def test_create_update_delete_pool_vip(self):
|
||||
# Creates a vip
|
||||
pool_name = data_utils.rand_name("pool-")
|
||||
vip_name = data_utils.rand_name('vip-')
|
||||
address = self.subnet['allocation_pools'][0]['end']
|
||||
body = self.lbv1_client.create_pool(
|
||||
pool_name,
|
||||
lb_method='ROUND_ROBIN',
|
||||
protocol='HTTP',
|
||||
subnet_id=self.subnet['id'])
|
||||
pool = body['pool']
|
||||
body = self.lbv1_client.create_vip(pool['id'],
|
||||
name=vip_name,
|
||||
protocol="HTTP",
|
||||
protocol_port=80,
|
||||
subnet_id=self.subnet['id'],
|
||||
address=address)
|
||||
vip = body['vip']
|
||||
vip_id = vip['id']
|
||||
# Confirm VIP's address correctness with a show
|
||||
body = self.lbv1_client.show_vip(vip_id)
|
||||
vip = body['vip']
|
||||
self.assertEqual(address, vip['address'])
|
||||
# Verification of vip update
|
||||
new_name = "New_vip"
|
||||
new_description = "New description"
|
||||
persistence_type = "HTTP_COOKIE"
|
||||
update_data = {"session_persistence": {
|
||||
"type": persistence_type}}
|
||||
body = self.lbv1_client.update_vip(vip_id,
|
||||
name=new_name,
|
||||
description=new_description,
|
||||
connection_limit=10,
|
||||
admin_state_up=False,
|
||||
**update_data)
|
||||
updated_vip = body['vip']
|
||||
self.assertEqual(new_name, updated_vip['name'])
|
||||
self.assertEqual(new_description, updated_vip['description'])
|
||||
self.assertEqual(10, updated_vip['connection_limit'])
|
||||
self.assertFalse(updated_vip['admin_state_up'])
|
||||
self.assertEqual(persistence_type,
|
||||
updated_vip['session_persistence']['type'])
|
||||
self.lbv1_client.delete_vip(vip['id'])
|
||||
self.lbv1_client.wait_for_resource_deletion('vip', vip['id'])
|
||||
# Verification of pool update
|
||||
new_name = "New_pool"
|
||||
body = self.lbv1_client.update_pool(pool['id'],
|
||||
name=new_name,
|
||||
description="new_description",
|
||||
lb_method='LEAST_CONNECTIONS')
|
||||
updated_pool = body['pool']
|
||||
self.assertEqual(new_name, updated_pool['name'])
|
||||
self.assertEqual('new_description', updated_pool['description'])
|
||||
self.assertEqual('LEAST_CONNECTIONS', updated_pool['lb_method'])
|
||||
self.lbv1_client.delete_pool(pool['id'])
|
||||
|
||||
@test.idempotent_id('277a99ce-4b3e-451d-a18a-d26c0376d176')
|
||||
def test_show_vip(self):
|
||||
# Verifies the details of a vip
|
||||
body = self.lbv1_client.show_vip(self.vip['id'])
|
||||
vip = body['vip']
|
||||
for key, value in six.iteritems(vip):
|
||||
# 'status' should not be confirmed in api tests
|
||||
if key != 'status':
|
||||
self.assertEqual(self.vip[key], value)
|
||||
|
||||
@test.idempotent_id('432470dd-836b-4555-8388-af95a1c74d32')
|
||||
def test_show_pool(self):
|
||||
# Here we need to new pool without any dependence with vips
|
||||
pool_name = data_utils.rand_name("pool-")
|
||||
body = self.lbv1_client.create_pool(pool_name,
|
||||
lb_method='ROUND_ROBIN',
|
||||
protocol='HTTP',
|
||||
subnet_id=self.subnet['id'])
|
||||
pool = body['pool']
|
||||
self.addCleanup(self.lbv1_client.delete_pool, pool['id'])
|
||||
# Verifies the details of a pool
|
||||
body = self.lbv1_client.show_pool(pool['id'])
|
||||
shown_pool = body['pool']
|
||||
for key, value in six.iteritems(pool):
|
||||
# 'status' should not be confirmed in api tests
|
||||
if key != 'status':
|
||||
self.assertEqual(value, shown_pool[key])
|
||||
|
||||
@test.idempotent_id('c9951820-7b24-4e67-8c0c-41065ec66071')
|
||||
def test_list_pools(self):
|
||||
# Verify the pool exists in the list of all pools
|
||||
body = self.lbv1_client.list_pools()
|
||||
pools = body['pools']
|
||||
self.assertIn(self.pool['id'], [p['id'] for p in pools])
|
||||
|
||||
@test.idempotent_id('55a1fb8e-e88e-4042-a46a-13a0282e4990')
|
||||
def test_list_pools_with_filters(self):
|
||||
attr_exceptions = ['status', 'vip_id', 'members', 'provider',
|
||||
'status_description']
|
||||
self._check_list_with_filter(
|
||||
'pool', attr_exceptions, name=data_utils.rand_name("pool-"),
|
||||
lb_method="ROUND_ROBIN", protocol="HTTPS",
|
||||
subnet_id=self.subnet['id'],
|
||||
description=data_utils.rand_name('description-'),
|
||||
admin_state_up=False)
|
||||
|
||||
@test.idempotent_id('dd441433-de8f-4992-a721-0755dec737ff')
|
||||
def test_list_members(self):
|
||||
# Verify the member exists in the list of all members
|
||||
body = self.lbv1_client.list_members()
|
||||
members = body['members']
|
||||
self.assertIn(self.member['id'], [m['id'] for m in members])
|
||||
|
||||
@test.idempotent_id('ccebe68a-f096-478d-b495-f17d5c0eac7b')
|
||||
def test_list_members_with_filters(self):
|
||||
attr_exceptions = ['status', 'status_description']
|
||||
self._check_list_with_filter('member', attr_exceptions,
|
||||
address=self.member_address,
|
||||
protocol_port=80,
|
||||
pool_id=self.pool['id'])
|
||||
|
||||
@test.idempotent_id('b4efe862-0439-4260-828c-cc09ff7e12a6')
|
||||
def test_create_update_delete_member(self):
|
||||
# Creates a member
|
||||
body = self.lbv1_client.create_member(address=self.member_address,
|
||||
protocol_port=80,
|
||||
pool_id=self.pool['id'])
|
||||
member = body['member']
|
||||
# Verification of member update
|
||||
body = self.lbv1_client.update_member(member['id'],
|
||||
admin_state_up=False)
|
||||
updated_member = body['member']
|
||||
self.assertFalse(updated_member['admin_state_up'])
|
||||
# Verification of member delete
|
||||
self.lbv1_client.delete_member(member['id'])
|
||||
|
||||
@test.idempotent_id('4806ca47-b3a0-4280-9962-6631c6815e93')
|
||||
def test_show_member(self):
|
||||
# Verifies the details of a member
|
||||
body = self.lbv1_client.show_member(self.member['id'])
|
||||
member = body['member']
|
||||
for key, value in six.iteritems(member):
|
||||
# 'status' should not be confirmed in api tests
|
||||
if key != 'status':
|
||||
self.assertEqual(self.member[key], value)
|
||||
|
||||
@test.idempotent_id('65c4d817-d8d2-44df-9c15-86fc7b910044')
|
||||
def test_list_health_monitors(self):
|
||||
# Verify the health monitor exists in the list of all health monitors
|
||||
body = self.lbv1_client.list_health_monitors()
|
||||
health_monitors = body['health_monitors']
|
||||
self.assertIn(self.health_monitor['id'],
|
||||
[h['id'] for h in health_monitors])
|
||||
|
||||
@test.idempotent_id('a2c749a0-4eac-4acc-b729-6b469c3c616a')
|
||||
def test_list_health_monitors_with_filters(self):
|
||||
attr_exceptions = ['status', 'status_description', 'pools']
|
||||
self._check_list_with_filter('health_monitor', attr_exceptions,
|
||||
delay=5, max_retries=4, type="TCP",
|
||||
timeout=2)
|
||||
|
||||
@test.idempotent_id('94f1e066-de6e-4cd8-b352-533d216956b7')
|
||||
def test_create_update_delete_health_monitor(self):
|
||||
# Creates a health_monitor
|
||||
body = self.lbv1_client.create_health_monitor(delay=4,
|
||||
max_retries=3,
|
||||
type="TCP",
|
||||
timeout=1)
|
||||
health_monitor = body['health_monitor']
|
||||
# Verification of health_monitor update
|
||||
body = (self.lbv1_client.update_health_monitor
|
||||
(health_monitor['id'],
|
||||
admin_state_up=False))
|
||||
updated_health_monitor = body['health_monitor']
|
||||
self.assertFalse(updated_health_monitor['admin_state_up'])
|
||||
# Verification of health_monitor delete
|
||||
body = self.lbv1_client.delete_health_monitor(health_monitor['id'])
|
||||
|
||||
@test.idempotent_id('82943dcf-d424-43f0-890f-4b796f5043dc')
|
||||
def test_create_health_monitor_http_type(self):
|
||||
hm_type = "HTTP"
|
||||
body = self.lbv1_client.create_health_monitor(delay=4,
|
||||
max_retries=3,
|
||||
type=hm_type,
|
||||
timeout=1)
|
||||
health_monitor = body['health_monitor']
|
||||
self.addCleanup(self.lbv1_client.delete_health_monitor,
|
||||
health_monitor['id'])
|
||||
self.assertEqual(hm_type, health_monitor['type'])
|
||||
|
||||
@test.idempotent_id('b1279c46-822a-4406-bb16-6a6ce7bf4e4e')
|
||||
def test_update_health_monitor_http_method(self):
|
||||
body = self.lbv1_client.create_health_monitor(delay=4,
|
||||
max_retries=3,
|
||||
type="HTTP",
|
||||
timeout=1)
|
||||
health_monitor = body['health_monitor']
|
||||
self.addCleanup(self.lbv1_client.delete_health_monitor,
|
||||
health_monitor['id'])
|
||||
body = (self.lbv1_client.update_health_monitor
|
||||
(health_monitor['id'],
|
||||
http_method="POST",
|
||||
url_path="/home/user",
|
||||
expected_codes="290"))
|
||||
updated_health_monitor = body['health_monitor']
|
||||
self.assertEqual("POST", updated_health_monitor['http_method'])
|
||||
self.assertEqual("/home/user", updated_health_monitor['url_path'])
|
||||
self.assertEqual("290", updated_health_monitor['expected_codes'])
|
||||
|
||||
@test.idempotent_id('7beabd44-0200-4cc4-b18d-5fb1f44cf36c')
|
||||
def test_show_health_monitor(self):
|
||||
# Verifies the details of a health_monitor
|
||||
body = self.lbv1_client.show_health_monitor(self.health_monitor['id'])
|
||||
health_monitor = body['health_monitor']
|
||||
for key, value in six.iteritems(health_monitor):
|
||||
# 'status' should not be confirmed in api tests
|
||||
if key != 'status':
|
||||
self.assertEqual(self.health_monitor[key], value)
|
||||
|
||||
@test.idempotent_id('5386d600-1372-4f99-b0f2-316401718ac4')
|
||||
def test_associate_disassociate_health_monitor_with_pool(self):
|
||||
# Verify that a health monitor can be associated with a pool
|
||||
self.lbv1_client.associate_health_monitor_with_pool(
|
||||
self.health_monitor['id'], self.pool['id'])
|
||||
body = self.lbv1_client.show_health_monitor(
|
||||
self.health_monitor['id'])
|
||||
health_monitor = body['health_monitor']
|
||||
body = self.lbv1_client.show_pool(self.pool['id'])
|
||||
pool = body['pool']
|
||||
self.assertIn(pool['id'],
|
||||
[p['pool_id'] for p in health_monitor['pools']])
|
||||
self.assertIn(health_monitor['id'], pool['health_monitors'])
|
||||
# Verify that a health monitor can be disassociated from a pool
|
||||
(self.lbv1_client.disassociate_health_monitor_with_pool
|
||||
(self.health_monitor['id'], self.pool['id']))
|
||||
body = self.lbv1_client.show_pool(self.pool['id'])
|
||||
pool = body['pool']
|
||||
body = self.lbv1_client.show_health_monitor(
|
||||
self.health_monitor['id'])
|
||||
health_monitor = body['health_monitor']
|
||||
self.assertNotIn(health_monitor['id'], pool['health_monitors'])
|
||||
self.assertNotIn(pool['id'],
|
||||
[p['pool_id'] for p in health_monitor['pools']])
|
||||
|
||||
@test.idempotent_id('17a6b730-0780-46c9-bca0-cec67387e469')
|
||||
def test_get_lb_pool_stats(self):
|
||||
# Verify the details of pool stats
|
||||
body = self.lbv1_client.list_lb_pool_stats(self.pool['id'])
|
||||
stats = body['stats']
|
||||
self.assertIn("bytes_in", stats)
|
||||
self.assertIn("total_connections", stats)
|
||||
self.assertIn("active_connections", stats)
|
||||
self.assertIn("bytes_out", stats)
|
||||
|
||||
@test.idempotent_id('a113c740-6194-4622-a187-8343ad3e5208')
|
||||
def test_update_list_of_health_monitors_associated_with_pool(self):
|
||||
(self.lbv1_client.associate_health_monitor_with_pool
|
||||
(self.health_monitor['id'], self.pool['id']))
|
||||
self.lbv1_client.update_health_monitor(
|
||||
self.health_monitor['id'], admin_state_up=False)
|
||||
body = self.lbv1_client.show_pool(self.pool['id'])
|
||||
health_monitors = body['pool']['health_monitors']
|
||||
for health_monitor_id in health_monitors:
|
||||
body = self.lbv1_client.show_health_monitor(health_monitor_id)
|
||||
self.assertFalse(body['health_monitor']['admin_state_up'])
|
||||
(self.lbv1_client.disassociate_health_monitor_with_pool
|
||||
(self.health_monitor['id'], self.pool['id']))
|
||||
|
||||
@test.idempotent_id('a2843ec6-80d8-4617-b985-8c8565daac8d')
|
||||
def test_update_admin_state_up_of_pool(self):
|
||||
self.lbv1_client.update_pool(self.pool['id'],
|
||||
admin_state_up=False)
|
||||
body = self.lbv1_client.show_pool(self.pool['id'])
|
||||
pool = body['pool']
|
||||
self.assertFalse(pool['admin_state_up'])
|
||||
|
||||
@test.idempotent_id('fd45c684-b847-472f-a7e8-a3f70e8e08e0')
|
||||
def test_show_vip_associated_with_pool(self):
|
||||
body = self.lbv1_client.show_pool(self.pool['id'])
|
||||
pool = body['pool']
|
||||
body = self.lbv1_client.show_vip(pool['vip_id'])
|
||||
vip = body['vip']
|
||||
self.assertEqual(self.vip['name'], vip['name'])
|
||||
self.assertEqual(self.vip['id'], vip['id'])
|
||||
|
||||
@test.idempotent_id('1ac0ca5f-7d6a-4ac4-b286-d68c92a98405')
|
||||
def test_show_members_associated_with_pool(self):
|
||||
body = self.lbv1_client.show_pool(self.pool['id'])
|
||||
members = body['pool']['members']
|
||||
for member_id in members:
|
||||
body = self.lbv1_client.show_member(member_id)
|
||||
self.assertIsNotNone(body['member']['status'])
|
||||
self.assertEqual(member_id, body['member']['id'])
|
||||
self.assertIsNotNone(body['member']['admin_state_up'])
|
||||
|
||||
@test.idempotent_id('4fa308fa-ac2b-4acf-87db-adfe2ee4739c')
|
||||
def test_update_pool_related_to_member(self):
|
||||
# Create new pool
|
||||
pool_name = data_utils.rand_name("pool-")
|
||||
body = self.lbv1_client.create_pool(
|
||||
pool_name,
|
||||
lb_method='ROUND_ROBIN',
|
||||
protocol='HTTP',
|
||||
subnet_id=self.subnet['id'])
|
||||
new_pool = body['pool']
|
||||
self.addCleanup(self.lbv1_client.delete_pool, new_pool['id'])
|
||||
# Update member with new pool's id
|
||||
body = self.lbv1_client.update_member(self.member['id'],
|
||||
pool_id=new_pool['id'])
|
||||
# Confirm with show that pool_id change
|
||||
body = self.lbv1_client.show_member(self.member['id'])
|
||||
member = body['member']
|
||||
self.assertEqual(member['pool_id'], new_pool['id'])
|
||||
# Update member with old pool id, this is needed for clean up
|
||||
body = self.lbv1_client.update_member(self.member['id'],
|
||||
pool_id=self.pool['id'])
|
||||
|
||||
@test.idempotent_id('0af2ff6b-a896-433d-8107-3c76262a9dfa')
|
||||
def test_update_member_weight(self):
|
||||
self.lbv1_client.update_member(self.member['id'],
|
||||
weight=2)
|
||||
body = self.lbv1_client.show_member(self.member['id'])
|
||||
member = body['member']
|
||||
self.assertEqual(2, member['weight'])
|
||||
|
||||
|
||||
@decorators.skip_because(bug="1402007")
|
||||
class LoadBalancerIpV6TestJSON(LoadBalancerTestJSON):
|
||||
_ip_version = 6
|
169
vmware_nsx_tempest/tests/nsxv/api/test_vlan_network.py
Normal file
169
vmware_nsx_tempest/tests/nsxv/api/test_vlan_network.py
Normal file
@ -0,0 +1,169 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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 random
|
||||
|
||||
from tempest import config
|
||||
from tempest import test
|
||||
|
||||
from oslo_log import log as logging
|
||||
from tempest_lib.common.utils import data_utils
|
||||
import test_subnets as SNET
|
||||
|
||||
CONF = config.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
VLAN_PHYSICAL_NETWORK = CONF.nsxv.vlan_physical_network or None
|
||||
VLAN_ID_PROVIDER = CONF.nsxv.provider_vlan_id
|
||||
|
||||
|
||||
class VlanNetworksTestJSON(SNET.SubnetTestJSON):
|
||||
_interface = 'json'
|
||||
_vlanid = int(VLAN_ID_PROVIDER)
|
||||
_provider_network_body = {
|
||||
'name': data_utils.rand_name('VLAN-%04d-network' % _vlanid),
|
||||
'provider:network_type': 'vlan',
|
||||
'provider:physical_network': VLAN_PHYSICAL_NETWORK,
|
||||
'provider:segmentation_id': _vlanid}
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
cls.vlan_range = (2001, 2999)
|
||||
cls.vlan_assigned = []
|
||||
super(VlanNetworksTestJSON, cls).resource_setup()
|
||||
|
||||
def get_next_vlan(self):
|
||||
next_vlan = self.next_vlan
|
||||
self.next_vlan += 1
|
||||
if self.next_vlan > self.vlan_range[1]:
|
||||
self.next_vlan = self.vlan_range[0]
|
||||
return next_vlan
|
||||
|
||||
def get_vlan(self):
|
||||
for x in range(0, 10):
|
||||
next_vlan = random.randint(*self.vlan_range)
|
||||
if next_vlan in self.vlan_assigned:
|
||||
continue
|
||||
else:
|
||||
self.vlan_assigned.append(next_vlan)
|
||||
return next_vlan
|
||||
return 3000
|
||||
|
||||
def _create_network(self, _auto_clean_up=True, network_name=None,
|
||||
**kwargs):
|
||||
segmentation_id = kwargs.pop('provider:segmentation_id', None)
|
||||
if not segmentation_id:
|
||||
segmentation_id = self.get_vlan()
|
||||
network_name = (network_name or
|
||||
data_utils.rand_name(
|
||||
'vlan-' + str(segmentation_id) + '-netwk'))
|
||||
post_body = {'name': network_name,
|
||||
'provider:network_type': 'vlan',
|
||||
'provider:physical_network': VLAN_PHYSICAL_NETWORK,
|
||||
'provider:segmentation_id': segmentation_id}
|
||||
post_body.update(kwargs)
|
||||
for k, v in post_body.items():
|
||||
if not v:
|
||||
post_body.pop(k)
|
||||
LOG.debug("create VLAN network: %s", str(post_body))
|
||||
body = self.create_network(**post_body)
|
||||
network = body['network']
|
||||
if _auto_clean_up:
|
||||
self.addCleanup(self._try_delete_network, network['id'])
|
||||
return network
|
||||
|
||||
@test.idempotent_id('c5f98016-dee3-42f1-8c23-b9cd1e625561')
|
||||
def test_create_network(self):
|
||||
# Create a network as an admin user specifying the
|
||||
# vlan network type attribute
|
||||
provider_attrs = {
|
||||
'provider:network_type': 'vlan',
|
||||
'provider:physical_network': VLAN_PHYSICAL_NETWORK,
|
||||
'provider:segmentation_id': 1002}
|
||||
network = self._create_network(_auto_clean_up=False, **provider_attrs)
|
||||
# Verifies parameters
|
||||
self.assertIsNotNone(network['id'])
|
||||
self.assertEqual(network.get('provider:network_type'), 'vlan')
|
||||
if VLAN_PHYSICAL_NETWORK:
|
||||
self.assertEqual(network.get('provider:physical_network'),
|
||||
VLAN_PHYSICAL_NETWORK)
|
||||
self.assertEqual(network.get('provider:segmentation_id'), 1002)
|
||||
self._delete_network(network['id'])
|
||||
|
||||
@test.idempotent_id('714e69eb-bb31-4cfc-9804-8e988f04ca65')
|
||||
def test_update_network(self):
|
||||
# Update flat network as an admin user specifying the
|
||||
# flat network attribute
|
||||
net_profile = {'shared': True, '_auto_clean_up': False,
|
||||
'provider:segmentation_id': 1003}
|
||||
network = self._create_network(**net_profile)
|
||||
self.assertEqual(network.get('shared'), True)
|
||||
new_name = network['name'] + "-updated"
|
||||
update_body = {'shared': False, 'name': new_name}
|
||||
body = self.update_network(network['id'], **update_body)
|
||||
updated_network = body['network']
|
||||
# Verify that name and shared parameters were updated
|
||||
self.assertEqual(updated_network['shared'], False)
|
||||
self.assertEqual(updated_network['name'], new_name)
|
||||
# get flat network attributes and verify them
|
||||
body = self.show_network(network['id'])
|
||||
updated_network = body['network']
|
||||
# Verify that name and shared parameters were updated
|
||||
self.assertEqual(updated_network['shared'], False)
|
||||
self.assertEqual(updated_network['name'], new_name)
|
||||
self.assertEqual(updated_network['status'], network['status'])
|
||||
self.assertEqual(updated_network['subnets'], network['subnets'])
|
||||
self._delete_network(network['id'])
|
||||
|
||||
@test.idempotent_id('8a8b9f2c-37f8-4c53-b8e3-0c9c0910380f')
|
||||
def test_list_networks(self):
|
||||
# Create flat network
|
||||
net_profile = {'shared': True, '_auto_clean_up': False,
|
||||
'provider:segmentation_id': 1004}
|
||||
network = self._create_network(**net_profile)
|
||||
# List networks as a normal user and confirm it is available
|
||||
body = self.list_networks(client=self.networks_client)
|
||||
networks_list = [net['id'] for net in body['networks']]
|
||||
self.assertIn(network['id'], networks_list)
|
||||
update_body = {'shared': False}
|
||||
body = self.update_network(network['id'], **update_body)
|
||||
# List networks as a normal user and confirm it is not available
|
||||
body = self.list_networks(client=self.networks_client)
|
||||
networks_list = [net['id'] for net in body['networks']]
|
||||
self.assertNotIn(network['id'], networks_list)
|
||||
self._delete_network(network['id'])
|
||||
|
||||
@test.idempotent_id('5807958d-9ee2-48a5-937e-ddde092956a6')
|
||||
def test_show_network_attributes(self):
|
||||
# Create flat network
|
||||
net_profile = {'shared': True, '_auto_clean_up': False,
|
||||
'provider:segmentation_id': 1005}
|
||||
network = self._create_network(**net_profile)
|
||||
# Show a flat network as a normal user and confirm the
|
||||
# flat network attribute is returned.
|
||||
body = self.show_network(network['id'], client=self.networks_client)
|
||||
show_net = body['network']
|
||||
self.assertEqual(network['name'], show_net['name'])
|
||||
self.assertEqual(network['id'], show_net['id'])
|
||||
# provider attributes are for admin only
|
||||
body = self.show_network(network['id'])
|
||||
show_net = body['network']
|
||||
net_attr_list = show_net.keys()
|
||||
for attr in ('admin_state_up', 'port_security_enabled', 'shared',
|
||||
'status', 'subnets', 'tenant_id', 'router:external',
|
||||
'provider:network_type', 'provider:physical_network',
|
||||
'provider:segmentation_id'):
|
||||
self.assertIn(attr, net_attr_list)
|
||||
self._delete_network(network['id'])
|
0
vmware_nsx_tempest/tests/nsxv/scenario/__init__.py
Normal file
0
vmware_nsx_tempest/tests/nsxv/scenario/__init__.py
Normal file
@ -0,0 +1,701 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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 collections
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
import traceback
|
||||
|
||||
import net_resources
|
||||
|
||||
from tempest.common.utils.linux import remote_client
|
||||
from tempest.common import waiters
|
||||
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
|
||||
|
||||
CONF = config.CONF
|
||||
LOG = manager.log.getLogger(__name__)
|
||||
|
||||
Floating_IP_tuple = collections.namedtuple(
|
||||
'Floating_IP_tuple', ['floating_ip', 'server'])
|
||||
|
||||
Z_VM2_DEST = "VM[%(h_ipaddr)s] %(msg)s [%(helper)s %(d_ipaddr)s]"
|
||||
|
||||
# Before checking for floatingIP and server connectivity, we need to wait
|
||||
# x seconds for the control-plane to push configuration to data-plane
|
||||
# prior to process add/update/delete requests.
|
||||
WAITTIME_AFTER_DISASSOC_FLOATINGIP = CONF.scenario.waitfor_disassoc
|
||||
WAITTIME_AFTER_ASSOC_FLOATINGIP = CONF.scenario.waitfor_assoc
|
||||
WAITTIME_FOR_CONNECTIVITY = CONF.scenario.waitfor_connectivity
|
||||
DNS_SERVERS_IPV4 = CONF.network.dns_servers
|
||||
OUTSIDE_WORLD_SERVERS = CONF.scenario.outside_world_servers
|
||||
# iptype
|
||||
IPTYPE_FLOATING = 'floating-ip'
|
||||
IPTYPE_FIXED = 'fixed-ip'
|
||||
IPTYPE_OUTSIDE_SERVER = 'outside-server'
|
||||
|
||||
|
||||
class TopoDeployScenarioManager(manager.NetworkScenarioTest):
|
||||
"""Purposes for TopoDeployScenarionManager:
|
||||
|
||||
1. Each deployment scenarion create its network resources, so
|
||||
call set_network_resource at setup_credentials() to overwrite it.
|
||||
2. setUp() is for test framework. Test case topology is part of
|
||||
test and is configured during test() cycle.
|
||||
3. net_resources.py overwrite resourses.py so the method to add
|
||||
interfaces to routers are inline with CLI, and support router
|
||||
owned by admin, but subnets are primary/alt clients.
|
||||
4. Ping is used for Data-plane testing. OUTSIDE_WORLD_SERVERS ping
|
||||
test make sense when tenant's DNS is pirvate to provider.
|
||||
5. Teardown is high cost, each test should perform its un-config to
|
||||
complete the whole tenant life-cycle.
|
||||
WARNING: you need to increase your quota to run in parallel as
|
||||
you might run out of quota when things went wrong.
|
||||
"""
|
||||
|
||||
# defined at test.py; used to create client managers
|
||||
credentials = ['admin', 'primary', 'alt']
|
||||
# router attributes used to create the tenant's router
|
||||
tenant_router_attrs = {}
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(TopoDeployScenarioManager, cls).skip_checks()
|
||||
for ext in ['router', 'security-group']:
|
||||
if not test.is_extension_enabled(ext, 'network'):
|
||||
msg = "%s extension not enabled." % ext
|
||||
raise cls.skipException(msg)
|
||||
|
||||
@classmethod
|
||||
def check_preconditions(cls):
|
||||
super(TopoDeployScenarioManager, cls).check_preconditions()
|
||||
if not (CONF.network.tenant_networks_reachable
|
||||
or CONF.network.public_network_id):
|
||||
msg = ('Either tenant_networks_reachable must be "true", or '
|
||||
'public_network_id must be defined.')
|
||||
cls.enabled = False
|
||||
raise cls.skipException(msg)
|
||||
|
||||
@classmethod
|
||||
def setup_credentials(cls):
|
||||
# Each client's network is created when client manager is created,
|
||||
# and client manager is created at setup_credentials.
|
||||
# topo-deploy scenarion manager asks not to create network resources.
|
||||
cls.set_network_resources(False, False, False, False)
|
||||
super(TopoDeployScenarioManager, cls).setup_credentials()
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TopoDeployScenarioManager, cls).resource_setup()
|
||||
cls.namestart = 'topo-deploy-tenant'
|
||||
cls.public_network_id = CONF.network.public_network_id
|
||||
# The creation of the 2nd tenant is defined by class.credentials
|
||||
# cls.alt_manager = clients.Manager(credentials=cls.alt_credentials())
|
||||
cls.alt_tenant_id = cls.alt_manager.identity_client.tenant_id
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(TopoDeployScenarioManager, cls).resource_cleanup()
|
||||
|
||||
def setUp(self):
|
||||
super(TopoDeployScenarioManager, self).setUp()
|
||||
self.servers_on_net = {}
|
||||
|
||||
def tearDown(self):
|
||||
super(TopoDeployScenarioManager, self).tearDown()
|
||||
|
||||
# bypass pareant _create_router() to use the net_resources module.
|
||||
# Scenario: routers belong to admin, subnets belon to tenent
|
||||
def _create_router(self, client_mgr=None, tenant_id=None,
|
||||
namestart='topo-deploy', **kwargs):
|
||||
client_mgr = client_mgr or self.manager
|
||||
router_client = client_mgr.network_client
|
||||
|
||||
if not tenant_id:
|
||||
tenant_id = router_client.tenant_id
|
||||
distributed = kwargs.pop('distributed', None)
|
||||
router_type = kwargs.pop('router_type', None)
|
||||
if distributed in (True, False):
|
||||
kwargs['distributed'] = distributed
|
||||
elif router_type in ('shared', 'exclusive'):
|
||||
kwargs['router_type'] = router_type
|
||||
name = data_utils.rand_name(namestart)
|
||||
result = router_client.create_router(name=name,
|
||||
admin_state_up=True,
|
||||
tenant_id=tenant_id,
|
||||
**kwargs)
|
||||
router = net_resources.DeletableRouter(client=router_client,
|
||||
**result['router'])
|
||||
self.assertEqual(router.name, name)
|
||||
self.addCleanup(self.delete_wrapper, router.delete)
|
||||
return router
|
||||
|
||||
def create_server_on_network(self, networks, security_groups=None,
|
||||
name=None, image=None, wait_on_boot=True,
|
||||
flavor=None, servers_client=None):
|
||||
name = name or data_utils.rand_name('topo-deploy-vm')
|
||||
if security_groups is None:
|
||||
security_groups = [{'name': 'default'}]
|
||||
if type(networks) in (list, tuple):
|
||||
network_ifs = [{'uuid': nw.id} for nw in networks]
|
||||
else:
|
||||
network_ifs = [{'uuid': networks.id}]
|
||||
create_kwargs = {
|
||||
'networks': network_ifs,
|
||||
'security_groups': security_groups,
|
||||
}
|
||||
LOG.debug("TopoDeploy Create server name=%(name)s"
|
||||
", create_kwargs=%(create_kwargs)s",
|
||||
{'name': name, 'create_kwargs': str(create_kwargs)})
|
||||
server = self.create_server(
|
||||
name=name, image=image, wait_on_boot=wait_on_boot,
|
||||
servers_client=servers_client, flavor=flavor,
|
||||
create_kwargs=create_kwargs)
|
||||
return server
|
||||
|
||||
# overwrite parent classes; add servers_client
|
||||
# BUG https://bugs.launchpad.net/tempest/+bug/1416175
|
||||
def create_server(self, name=None, image=None, flavor=None,
|
||||
wait_on_boot=True, wait_on_delete=True,
|
||||
servers_client=None, tenant_id=None,
|
||||
create_kwargs=None):
|
||||
"""Creates VM instance.
|
||||
|
||||
@param image: image from which to create the instance
|
||||
@param wait_on_boot: wait for status ACTIVE before continue
|
||||
@param wait_on_delete: force synchronous delete on cleanup
|
||||
@param servers_client: the servers_client to create VM
|
||||
@param create_kwargs: additional details for instance creation
|
||||
@return: server dict
|
||||
"""
|
||||
name = name or data_utils.rand_name('topo-deploy-vm')
|
||||
image = image or CONF.compute.image_ref
|
||||
flavor = flavor or CONF.compute.flavor_ref
|
||||
servers_client = servers_client or self.servers_client
|
||||
create_kwargs = create_kwargs or {}
|
||||
if type(tenant_id) in (str, unicode):
|
||||
create_kwargs['tenant_id'] = tenant_id
|
||||
|
||||
xmsg = ("Creating a server name=%(name)s, image=%(image)s"
|
||||
", flavor=%(flavor)s, create_kwargs=%(create_kwargs)s" %
|
||||
{'name': name, 'image': image, 'flavor': flavor,
|
||||
'create_kwargs': str(create_kwargs)})
|
||||
LOG.debug(xmsg)
|
||||
server_resp = servers_client.create_server(
|
||||
name=name, imageRef=image, flavorRef=flavor, **create_kwargs)
|
||||
server = server_resp['server']
|
||||
if wait_on_delete:
|
||||
self.addCleanup(
|
||||
waiters.wait_for_server_termination,
|
||||
servers_client, server['id'])
|
||||
self.addCleanup_with_wait(
|
||||
waiter_callable=waiters.wait_for_server_termination,
|
||||
thing_id=server['id'], thing_id_param='server_id',
|
||||
waiter_client=servers_client,
|
||||
cleanup_callable=self.delete_wrapper,
|
||||
cleanup_args=[servers_client.delete_server, server['id']])
|
||||
if wait_on_boot:
|
||||
waiters.wait_for_server_status(
|
||||
client=servers_client,
|
||||
server_id=server['id'], status='ACTIVE')
|
||||
# The instance retrieved on creation is missing network
|
||||
# details, necessitating retrieval after it becomes active to
|
||||
# ensure correct details.
|
||||
server_resp = servers_client.show_server(server['id'])
|
||||
server = server_resp['server']
|
||||
self.assertEqual(server['name'], name)
|
||||
self.servers_on_net[server['id']] = server
|
||||
return server
|
||||
|
||||
def create_provider_network(self, client_mgr=None, create_body=None):
|
||||
name = create_body.get('name', None) or data_utils.rand_name('P-net')
|
||||
create_body['name'] = name
|
||||
client_mgr = client_mgr or self.admin_manager
|
||||
networks_client = client_mgr.networks_client
|
||||
body = networks_client.create_network(**create_body)
|
||||
net_network = net_resources.DeletableNetwork(
|
||||
networks_client=networks_client, **body['network'])
|
||||
self.assertEqual(net_network.name, name)
|
||||
self.addCleanup(self.delete_wrapper, net_network.delete)
|
||||
return net_network
|
||||
|
||||
def create_provider_subnet(self, client_mgr=None, create_body=None):
|
||||
client_mgr = client_mgr or self.admin_manager
|
||||
subnets_client = client_mgr.subnets_client
|
||||
body = subnets_client.create_subnet(**create_body)
|
||||
net_subnet = net_resources.DeletableSubnet(
|
||||
subnets_client=subnets_client, **body['subnet'])
|
||||
self.addCleanup(self.delete_wrapper, net_subnet.delete)
|
||||
return net_subnet
|
||||
|
||||
def setup_tenant_network(self, external_network_id,
|
||||
client_mgr=None,
|
||||
namestart=None, client=None,
|
||||
tenant_id=None, cidr_offset=0):
|
||||
"""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.
|
||||
"""
|
||||
# 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.
|
||||
net_router = self._create_router(
|
||||
client_mgr=client_mgr, tenant_id=tenant_id,
|
||||
namestart=name,
|
||||
distributed=distributed, router_type=router_type)
|
||||
net_router.set_gateway(external_network_id)
|
||||
net_network, net_subnet = self.create_network_subnet(
|
||||
client_mgr=client_mgr,
|
||||
tenant_id=tenant_id, name=net_router.name,
|
||||
cidr_offset=cidr_offset)
|
||||
# different from the resources.py
|
||||
net_router.add_interface(net_subnet)
|
||||
return net_network, net_subnet, net_router
|
||||
|
||||
def create_network_subnet(self, client_mgr=None,
|
||||
tenant_id=None, name=None, cidr_offset=0):
|
||||
client_mgr = client_mgr or self.manager
|
||||
tenant_id = tenant_id or _g_tenant_id(client_mgr.networks_client)
|
||||
name = name or data_utils.rand_name('topo-deploy-network')
|
||||
net_network = self.create_network(
|
||||
client=client_mgr.networks_client,
|
||||
tenant_id=tenant_id, name=name)
|
||||
net_subnet = self.create_subnet(
|
||||
client=client_mgr.subnets_client,
|
||||
network=net_network,
|
||||
cidr_offset=cidr_offset, name=net_network['name'])
|
||||
return net_network, net_subnet
|
||||
|
||||
# cloned from _create_network@manager.py. Allow name parameter
|
||||
def create_network(self, client=None, tenant_id=None, name=None,
|
||||
**kwargs):
|
||||
client = client or self.networks_client
|
||||
tenant_id = tenant_id or _g_tenant_id(client)
|
||||
name = name or data_utils.rand_name('topo-deploy-network')
|
||||
result = client.create_network(name=name, tenant_id=tenant_id,
|
||||
**kwargs)
|
||||
net_network = net_resources.DeletableNetwork(
|
||||
client=client, networks_client=client,
|
||||
**result['network'])
|
||||
self.assertEqual(net_network.name, name)
|
||||
self.addCleanup(self.delete_wrapper, net_network.delete)
|
||||
return net_network
|
||||
|
||||
def create_subnet(self, network, client=None,
|
||||
gateway='', cidr=None, mask_bits=None,
|
||||
ip_version=None, cidr_offset=0,
|
||||
allocation_pools=None, dns_nameservers=None,
|
||||
**kwargs):
|
||||
client = client or self.subnets_client
|
||||
ip_version = ip_version or 4
|
||||
post_body = get_subnet_create_options(
|
||||
network['id'], ip_version,
|
||||
gateway=gateway, cidr=cidr, cidr_offset=cidr_offset,
|
||||
mask_bits=mask_bits, **kwargs)
|
||||
if allocation_pools:
|
||||
post_body['allocation_pools'] = allocation_pools
|
||||
if dns_nameservers:
|
||||
post_body['dns_nameservers'] = dns_nameservers
|
||||
LOG.debug("create_subnet args: %s", post_body)
|
||||
body = client.create_subnet(**post_body)
|
||||
net_subnet = net_resources.DeletableSubnet(
|
||||
client=client, subnets_client=client,
|
||||
**body['subnet'])
|
||||
self.addCleanup(self.delete_wrapper, net_subnet.delete)
|
||||
return net_subnet
|
||||
|
||||
def create_floatingip_for_server(self, server, external_network_id=None,
|
||||
port_id=None, client_mgr=None):
|
||||
client_mgr = client_mgr or self.manager
|
||||
net_floatingip = self.create_floating_ip(
|
||||
server,
|
||||
external_network_id=external_network_id,
|
||||
port_id=port_id,
|
||||
client=client_mgr.floating_ips_client)
|
||||
server_pingable = self._waitfor_associated_floatingip(net_floatingip)
|
||||
self.assertTrue(
|
||||
server_pingable,
|
||||
msg="Expect server to be reachable after floatingip assigned.")
|
||||
return net_floatingip
|
||||
|
||||
def _waitfor_associated_floatingip(self, net_floatingip):
|
||||
host_ip = net_floatingip['floating_ip_address']
|
||||
return self.waitfor_host_connected(host_ip)
|
||||
|
||||
def waitfor_host_connected(self, host_ip, ping_timeout=5, msg=None):
|
||||
PING_START = 'ping-progress-start'
|
||||
PING_INSESSION = 'ping-progress-in-session'
|
||||
PING_DONE = 'ping-progress-completed'
|
||||
PING_TIMEOUT = 'ping-progress-timeout'
|
||||
if msg and type(msg) in (str, unicode):
|
||||
xmsg = ("waitfor_host_connected ip=%(ip)s! %(msg)s" %
|
||||
{'ip': host_ip, 'msg': msg})
|
||||
LOG.debug(xmsg)
|
||||
t0 = time.time()
|
||||
t1 = time.time() + WAITTIME_FOR_CONNECTIVITY
|
||||
LOG.debug("VM-IP[%(ip)s] %(msg)s: %(t1)s.",
|
||||
{'ip': host_ip, 'msg': PING_START, 't1': t0})
|
||||
while (time.time() < t1):
|
||||
# waitfor backend to create floatingip & linkages
|
||||
time.sleep(WAITTIME_AFTER_ASSOC_FLOATINGIP)
|
||||
server_pingable = self.ping_ip_address(
|
||||
host_ip, ping_timeout=ping_timeout)
|
||||
if server_pingable:
|
||||
xmsg = ("VM-IP[%(ip)s] %(msg)s: %(t1)s (%(t2)s)." %
|
||||
{'ip': host_ip, 'msg': PING_DONE,
|
||||
't1': time.time(), 't2': (time.time() - t0)})
|
||||
LOG.debug(xmsg)
|
||||
break
|
||||
xmsg = ("VM-IP[%(ip)s] %(msg)s, redo after %(t1)s seconds." %
|
||||
{'ip': host_ip, 'msg': PING_INSESSION,
|
||||
't1': WAITTIME_AFTER_ASSOC_FLOATINGIP})
|
||||
LOG.debug(xmsg)
|
||||
if not server_pingable:
|
||||
xmsg = ("VM-IP[%(ip)s] %(msg)s: %(t1)s (%(t2)s)." %
|
||||
{'ip': host_ip, 'msg': PING_TIMEOUT,
|
||||
't1': time.time(), 't2': (time.time() - t0)})
|
||||
LOG.debug(xmsg)
|
||||
return server_pingable
|
||||
|
||||
def disassociate_floatingip(self, net_floatingip, and_delete=False):
|
||||
self._disassociate_floating_ip(net_floatingip)
|
||||
if and_delete:
|
||||
net_floatingip.delete()
|
||||
|
||||
def associate_floatingip(self, net_floatingip, to_server):
|
||||
self._associate_floating_ip(net_floatingip, to_server)
|
||||
|
||||
def check_networks(self, net_network, net_subnet=None, net_router=None):
|
||||
seen_nets = self._list_networks()
|
||||
seen_names = [n['name'] for n in seen_nets]
|
||||
seen_ids = [n['id'] for n in seen_nets]
|
||||
self.assertIn(net_network.name, seen_names)
|
||||
self.assertIn(net_network.id, seen_ids)
|
||||
|
||||
if net_subnet:
|
||||
seen_subnets = self._list_subnets()
|
||||
seen_net_ids = [n['network_id'] for n in seen_subnets]
|
||||
seen_subnet_ids = [n['id'] for n in seen_subnets]
|
||||
self.assertIn(net_network.id, seen_net_ids)
|
||||
self.assertIn(net_subnet.id, seen_subnet_ids)
|
||||
|
||||
if net_router:
|
||||
seen_routers = self._list_routers()
|
||||
seen_router_ids = [n['id'] for n in seen_routers]
|
||||
seen_router_names = [n['name'] for n in seen_routers]
|
||||
self.assertIn(net_router.name, seen_router_names)
|
||||
self.assertIn(net_router.id, seen_router_ids)
|
||||
|
||||
# use this carefully, as it expect existence of floating_ip_tuple
|
||||
def check_public_network_connectivity(self, should_connect=True,
|
||||
msg=None, ping_timeout=30):
|
||||
"""Verifies connectivty
|
||||
|
||||
To a VM via public network and floating IP, and verifies
|
||||
floating IP has resource status is correct.
|
||||
|
||||
@param should_connect: bool. determines if connectivity check is
|
||||
negative or positive.
|
||||
@param msg: Failure message to add to Error message. Should describe
|
||||
the place in the test scenario where the method was called,
|
||||
to indicate the context of the failure
|
||||
"""
|
||||
floating_ip, server = self.floating_ip_tuple
|
||||
return self._check_floatingip_connectivity(
|
||||
floating_ip, server, should_connect, msg, ping_timeout)
|
||||
|
||||
def _check_floatingip_connectivity(self, floating_ip, server,
|
||||
should_connect=True,
|
||||
msg=None, ping_timeout=30):
|
||||
ip_address = floating_ip.floating_ip_address
|
||||
floatingip_status = 'ACTIVE' if should_connect else 'DOWN'
|
||||
is_pingable = self.ping_ip_address(ip_address,
|
||||
ping_timeout=ping_timeout)
|
||||
msg = msg if msg else (
|
||||
"Timeout out waiting for %s to become reachable" % ip_address)
|
||||
if should_connect:
|
||||
self.assertTrue(is_pingable, msg=msg)
|
||||
else:
|
||||
self.assertFalse(is_pingable, msg=msg)
|
||||
self.check_floating_ip_status(floating_ip, floatingip_status)
|
||||
|
||||
def get_image_userpass(self):
|
||||
return (CONF.validation.image_ssh_user,
|
||||
CONF.validation.image_ssh_password)
|
||||
|
||||
def get_server_image(self):
|
||||
return CONF.compute.image_ref
|
||||
|
||||
def get_server_flavor(self):
|
||||
return CONF.compute.flavor_ref
|
||||
|
||||
|
||||
# common utilities
|
||||
def make_node_info(net_floatingip, username, password,
|
||||
include_outside_servers=False):
|
||||
node = dict(ipaddr=net_floatingip.floating_ip_address,
|
||||
username=username, password=password)
|
||||
node['dest'] = [dict(ipaddr=net_floatingip.floating_ip_address,
|
||||
reachable=None, helper=IPTYPE_FLOATING),
|
||||
dict(ipaddr=net_floatingip.fixed_ip_address,
|
||||
reachable=None, helper=IPTYPE_FIXED)]
|
||||
if include_outside_servers:
|
||||
outside_servers = dict(ipaddr=OUTSIDE_WORLD_SERVERS[0],
|
||||
reachable=None, helper=IPTYPE_OUTSIDE_SERVER)
|
||||
node['dest'].append(outside_servers)
|
||||
|
||||
return node
|
||||
|
||||
|
||||
# we want to check the dest[iptype] is not reachable for
|
||||
# at least (x_contd=2+=1 to make it is not really reachable.
|
||||
def check_host_not_reachable(host, dest_list, iptype_list,
|
||||
time_out=10, repeat_cnt=12,
|
||||
x_contd=2):
|
||||
not_connected = 0
|
||||
for x in range(0, 12):
|
||||
not_reachable = check_host_is_reachable(
|
||||
host, dest_list, iptype_list, time_out=time_out)
|
||||
if not_reachable:
|
||||
not_connected += 1
|
||||
else:
|
||||
not_connected = 0
|
||||
if not_connected > x_contd:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# check_hosts_connectivity
|
||||
def check_host_is_reachable(host, dest_list, iptype_list, time_out=120):
|
||||
rm_sshkey(host['ipaddr'])
|
||||
ssh_client = get_remote_client_by_password(host['ipaddr'],
|
||||
host['username'],
|
||||
host['password'])
|
||||
n_not_reachable = 0
|
||||
for dest in dest_list:
|
||||
for iptype in iptype_list:
|
||||
if not dest_has_iptype(dest, iptype):
|
||||
dest['reachable'] = None
|
||||
continue
|
||||
dest['reachable'] = is_reachable(
|
||||
ssh_client, dest['ipaddr'], time_out=time_out)
|
||||
if not dest['reachable']:
|
||||
n_not_reachable += 1
|
||||
xmsg = {'h_ipaddr': host['ipaddr'],
|
||||
'msg': "can-not-reach-dest",
|
||||
'helper': dest['helper'],
|
||||
'd_ipaddr': dest['ipaddr']}
|
||||
LOG.debug(Z_VM2_DEST, xmsg)
|
||||
else:
|
||||
xmsg = {'h_ipaddr': host['ipaddr'],
|
||||
'msg': "can-not-dest",
|
||||
'helper': dest['helper'],
|
||||
'd_ipaddr': dest['ipaddr']}
|
||||
LOG.debug(Z_VM2_DEST, xmsg)
|
||||
return (False if n_not_reachable else True)
|
||||
|
||||
|
||||
def dest_has_iptype(dest, iptype):
|
||||
if ('helper' in dest and
|
||||
re.search(iptype, dest['helper'], re.I)):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def check_hosts_connectivity(host, dest_list, ignore_helper=None,
|
||||
time_out=120):
|
||||
rm_sshkey(host['ipaddr'])
|
||||
ssh_client = get_remote_client_by_password(host['ipaddr'],
|
||||
host['username'],
|
||||
host['password'])
|
||||
n_not_reachable = 0
|
||||
for dest in dest_list:
|
||||
# caller can say to ignore dest ipaddr
|
||||
if ('helper' in dest and type(ignore_helper) in (str, unicode) and
|
||||
re.search(ignore_helper, dest['helper'], re.I)):
|
||||
dest['reachable'] = None
|
||||
continue
|
||||
dest['reachable'] = is_reachable(ssh_client, dest['ipaddr'],
|
||||
time_out=time_out)
|
||||
if not dest['reachable']:
|
||||
n_not_reachable += 1
|
||||
xmsg = {'h_ipaddr': host['ipaddr'],
|
||||
'msg': "can-not-reach-dest",
|
||||
'helper': dest['helper'],
|
||||
'd_ipaddr': dest['ipaddr']}
|
||||
LOG.debug(Z_VM2_DEST, xmsg)
|
||||
else:
|
||||
xmsg = {'h_ipaddr': host['ipaddr'],
|
||||
'msg': "can-reach-dest",
|
||||
'helper': dest['helper'],
|
||||
'd_ipaddr': dest['ipaddr']}
|
||||
LOG.debug(Z_VM2_DEST, xmsg)
|
||||
|
||||
return n_not_reachable
|
||||
|
||||
|
||||
def rm_sshkey(ip_addr):
|
||||
# ssh-keygen -f "/home/stack/.ssh/known_hosts" -R 10.34.57.3
|
||||
kh_file = os.path.join(os.environ.get('HOME', '/home/stack'),
|
||||
'.ssh/known_hosts')
|
||||
cmd = ['ssh-keygen', '-f', kh_file, '-R', ip_addr]
|
||||
|
||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
proc.communicate()
|
||||
return proc.returncode
|
||||
|
||||
|
||||
def is_reachable(ssh_client, dest_ip, time_out=60.0, ping_timeout=5.0):
|
||||
for now in run_till_timeout(time_out, ping_timeout):
|
||||
reachable = dest_is_reachable(ssh_client, dest_ip)
|
||||
if reachable:
|
||||
return True
|
||||
LOG.debug("DEST[%(ip)s] NOT-REACHABLE, retry in %(t1)s seconds.",
|
||||
{'ip': dest_ip, 't1': time_out})
|
||||
return False
|
||||
|
||||
|
||||
def isnot_reachable(ssh_client, dest_ip, time_out=60.0, ping_timeout=5.0,
|
||||
idle_time=2.0):
|
||||
if idle_time > 0.0:
|
||||
time.sleep(idle_time)
|
||||
for now in run_till_timeout(time_out, ping_timeout):
|
||||
reachable = dest_is_reachable(ssh_client, dest_ip)
|
||||
if not reachable:
|
||||
return True
|
||||
LOG.debug("DEST[%(ip)s] IS-REACHABLE, retry in %(t1)s seconds.",
|
||||
{'ip': dest_ip, 't1': time_out})
|
||||
return False
|
||||
|
||||
|
||||
def dest_is_reachable(ssh_client, dest_ip):
|
||||
XPTN = r"(\d+).*transmit.*(\d+).*receive.*(\d+).*loss"
|
||||
try:
|
||||
result = ssh_client.ping_host(dest_ip)
|
||||
m = re.search(XPTN, result, (re.I | re.M))
|
||||
if m and int(m.group(1)) > 0 and int(m.group(3)) == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except Exception:
|
||||
tb_str = traceback.format_exc()
|
||||
mesg = ("ERROR on testing dest_ip[%s] is reachable:\n%s" %
|
||||
(dest_ip, tb_str))
|
||||
LOG.debug(mesg)
|
||||
return False
|
||||
|
||||
|
||||
def run_till_timeout(seconds_to_try, interval=5.0):
|
||||
now, end_time = time.time(), time.time() + seconds_to_try
|
||||
while now < end_time:
|
||||
yield now
|
||||
time.sleep(interval)
|
||||
now = time.time()
|
||||
|
||||
|
||||
def _g_tenant_id(os_client):
|
||||
try:
|
||||
return os_client.tenant_id
|
||||
except Exception:
|
||||
return os_client.rest_client.tenant_id
|
||||
|
||||
|
||||
def get_subnet_create_options(network_id, ip_version=4,
|
||||
gateway='', cidr=None, mask_bits=None,
|
||||
num_subnet=1, gateway_offset=1, cidr_offset=0,
|
||||
**kwargs):
|
||||
"""When cidr_offset>0 it request only one subnet-options:
|
||||
|
||||
subnet = get_subnet_create_options('abcdefg', 4, num_subnet=4)[3]
|
||||
subnet = get_subnet_create_options('abcdefg', 4, cidr_offset=3)
|
||||
"""
|
||||
|
||||
gateway_not_set = gateway == ''
|
||||
if ip_version == 4:
|
||||
cidr = cidr or netaddr.IPNetwork(CONF.network.tenant_network_cidr)
|
||||
mask_bits = mask_bits or CONF.network.tenant_network_mask_bits
|
||||
elif ip_version == 6:
|
||||
cidr = (
|
||||
cidr or netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr))
|
||||
mask_bits = mask_bits or CONF.network.tenant_network_v6_mask_bits
|
||||
# Find a cidr that is not in use yet and create a subnet with it
|
||||
subnet_list = []
|
||||
if cidr_offset > 0:
|
||||
num_subnet = cidr_offset + 1
|
||||
for subnet_cidr in cidr.subnet(mask_bits):
|
||||
if gateway_not_set:
|
||||
gateway_ip = gateway or (
|
||||
str(netaddr.IPAddress(subnet_cidr) + gateway_offset))
|
||||
else:
|
||||
gateway_ip = gateway
|
||||
try:
|
||||
subnet_body = dict(network_id=network_id,
|
||||
cidr=str(subnet_cidr),
|
||||
ip_version=ip_version,
|
||||
gateway_ip=gateway_ip,
|
||||
**kwargs)
|
||||
if num_subnet <= 1:
|
||||
return subnet_body
|
||||
subnet_list.append(subnet_body)
|
||||
if len(subnet_list) >= num_subnet:
|
||||
if cidr_offset > 0:
|
||||
# user request the 'cidr_offset'th of cidr
|
||||
return subnet_list[cidr_offset]
|
||||
# user request list of cidr
|
||||
return subnet_list
|
||||
except exceptions.BadRequest as e:
|
||||
is_overlapping_cidr = 'overlaps with another subnet' in str(e)
|
||||
if not is_overlapping_cidr:
|
||||
raise
|
||||
else:
|
||||
message = 'Available CIDR for subnet creation could not be found'
|
||||
raise exceptions.BuildErrorException(message)
|
||||
return {}
|
||||
|
||||
|
||||
def get_remote_client_by_password(client_ip, username, password):
|
||||
ssh_client = remote_client.RemoteClient(client_ip, username, password)
|
||||
return ssh_client
|
||||
|
||||
|
||||
def delete_all_servers(tenant_servers_client):
|
||||
for s in tenant_servers_client.list_servers()['servers']:
|
||||
tenant_servers_client.delete_server(s['id'])
|
||||
waitfor_servers_terminated(tenant_servers_client)
|
||||
|
||||
|
||||
def waitfor_servers_terminated(tenant_servers_client, pause=2.0):
|
||||
while (True):
|
||||
s_list = tenant_servers_client.list_servers()['servers']
|
||||
if len(s_list) < 1:
|
||||
return
|
||||
time.sleep(pause)
|
125
vmware_nsx_tempest/tests/nsxv/scenario/net_resources.py
Normal file
125
vmware_nsx_tempest/tests/nsxv/scenario/net_resources.py
Normal file
@ -0,0 +1,125 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# This module inherents from resources module and enhances router functions
|
||||
# and block subnet's add_to/delete_from_router so it is more similar to CLI.
|
||||
|
||||
from tempest.services.network import resources as n_resources
|
||||
|
||||
|
||||
DELETABLE_CLASS_DEF = """class %(cls_name)s(n_resources.%(cls_name)s):
|
||||
pass
|
||||
"""
|
||||
IGNORE_LIST = ['DeletableSubnet', 'DeletableRouter']
|
||||
|
||||
|
||||
# inhere Deletable<Class> from parent module
|
||||
for cls_name in [x for x in dir(n_resources)
|
||||
if x.startswith('Deletable') and x not in IGNORE_LIST]:
|
||||
class_def = DELETABLE_CLASS_DEF % dict(cls_name=cls_name)
|
||||
exec class_def
|
||||
|
||||
|
||||
# Add/mod methods so we can use it while sustain original functions.
|
||||
MSG_BLOCK_BY_ADMIN = "Block %s as router might be owned by ADMIN. " \
|
||||
"Use DeletableRouter instead."
|
||||
|
||||
|
||||
class DeletableSubnet(n_resources.DeletableSubnet):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DeletableSubnet, self).__init__(*args, **kwargs)
|
||||
|
||||
def add_to_router(self, router_id):
|
||||
raise Exception(MSG_BLOCK_BY_ADMIN % "add_to_router()")
|
||||
|
||||
def delete_from_router(self, router_id):
|
||||
raise Exception(MSG_BLOCK_BY_ADMIN % "delete_from_router()")
|
||||
|
||||
|
||||
# DeletableSubnet should not deal with router which when owned by ADMIN
|
||||
# will raise privilege issue. Always let the router deals with interfaces.
|
||||
class DeletableRouter(n_resources.DeletableRouter):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DeletableRouter, self).__init__(*args, **kwargs)
|
||||
self._subnets = set()
|
||||
|
||||
def set_gateway(self, network_id):
|
||||
return self.client.update_router(
|
||||
self.id,
|
||||
external_gateway_info=dict(network_id=network_id))
|
||||
|
||||
def unset_gateway(self):
|
||||
return self.client.update_router(
|
||||
self.id,
|
||||
external_gateway_info=dict())
|
||||
|
||||
def add_subnet(self, subnet):
|
||||
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.
|
||||
"""
|
||||
self.client.add_router_interface_with_subnbet_id(
|
||||
self.id, subnet_id=subnet.id)
|
||||
"""
|
||||
x_method(self.client, 'add_router_interface_with_subnet_id',
|
||||
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):
|
||||
"""
|
||||
self.client.remove_router_interface_with_subnet_id(
|
||||
self.id, subnet_id=subnet.id)
|
||||
"""
|
||||
x_method(self.client, 'remove_router_interface_with_subnet_id',
|
||||
self.id, subnet_id=subnet.id)
|
||||
try:
|
||||
self._subnets.remove(subnet)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def update_extra_routes(self, nexthop, destination):
|
||||
return self.client.update_extra_routes(self.id, nexthop, destination)
|
||||
|
||||
# to-be-fixed by https://bugs.launchpad.net/tempest/+bug/1468600
|
||||
def update_extra_routes_future(self, routes):
|
||||
return self.client.update_extra_routes(self.id, routes)
|
||||
|
||||
def delete_extra_routes(self):
|
||||
return self.client.delete_extra_routes(self.id)
|
||||
|
||||
def delete(self):
|
||||
try:
|
||||
self.delete_extra_routes()
|
||||
except Exception:
|
||||
pass
|
||||
self.unset_gateway()
|
||||
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
|
565
vmware_nsx_tempest/tests/nsxv/scenario/test_deployments.py
Normal file
565
vmware_nsx_tempest/tests/nsxv/scenario/test_deployments.py
Normal file
@ -0,0 +1,565 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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 import config
|
||||
from tempest import test
|
||||
|
||||
import manager_topo_deployment as dmgr
|
||||
from tempest_lib.common.utils import data_utils
|
||||
|
||||
CONF = config.CONF
|
||||
LOG = dmgr.manager.log.getLogger(__name__)
|
||||
|
||||
FLAT_ALLOC_DICT = CONF.scenario.flat_alloc_pool_dict
|
||||
Z_DEPLOY_TOPO = "tc[%s] deploying"
|
||||
Z_DEPLOY_DELETE_SERVER = "tc[%s] deploy delete-server"
|
||||
Z_DEPLOY_COMPLETED = "tc[%s] deploy test-completed."
|
||||
|
||||
|
||||
class TestSimpleFlatNetwork(dmgr.TopoDeployScenarioManager):
|
||||
|
||||
"""TestSimpleFlatNetwork: with 1 flat network/subnet
|
||||
|
||||
1. client:admin create FLAT network.
|
||||
2. client:primary boot a server (icmp/ssh security rules enabled)
|
||||
on the flat network.
|
||||
3. check the server is reachable.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(TestSimpleFlatNetwork, cls).skip_checks()
|
||||
if not FLAT_ALLOC_DICT:
|
||||
msg = "FLAT network allocation pool not defined."
|
||||
raise cls.skipException(msg)
|
||||
|
||||
def setUp(self):
|
||||
super(TestSimpleFlatNetwork, self).setUp()
|
||||
self.admin_client = self.admin_manager.network_client
|
||||
self.info_flat1 = FLAT_ALLOC_DICT
|
||||
|
||||
def tearDown(self):
|
||||
super(TestSimpleFlatNetwork, self).tearDown()
|
||||
|
||||
def create_network(self, name=None, shared=True):
|
||||
name = name or data_utils.rand_name('FLAT-net')
|
||||
post_body = {'name': name,
|
||||
'provider:network_type': 'flat',
|
||||
'shared': shared}
|
||||
net_flat = self.create_provider_network(create_body=post_body)
|
||||
return net_flat
|
||||
|
||||
def create_subnet(self, net_network, info_flat):
|
||||
alloc_pool = [{'start': info_flat['start'],
|
||||
'end': info_flat['end']}]
|
||||
post_body = {'name': net_network.name,
|
||||
'network_id': net_network.id,
|
||||
'ip_version': 4,
|
||||
'gateway_ip': info_flat['gateway'],
|
||||
'cidr': info_flat['cidr'],
|
||||
'allocation_pools': alloc_pool,
|
||||
'dns_nameservers': CONF.network.dns_servers}
|
||||
net_subnet = self.create_provider_subnet(create_body=post_body)
|
||||
return net_subnet
|
||||
|
||||
def check_server_connected(self, serv):
|
||||
serv_net = serv['addresses'].keys()[0]
|
||||
serv_addr = serv['addresses'][serv_net][0]
|
||||
host_ip = serv_addr['addr']
|
||||
# host_mac = serv_addr['OS-EXT-IPS-MAC:mac_addr']
|
||||
# host_ver = serv_addr['version']
|
||||
self.waitfor_host_connected(host_ip)
|
||||
|
||||
@test.idempotent_id('bc081b8d-49eb-4710-9442-c6b225ef16f0')
|
||||
@test.services('compute', 'network')
|
||||
def test_simple_flat_network(self):
|
||||
# provider actions
|
||||
self.net_network = self.create_network()
|
||||
self.net_subnet = self.create_subnet(self.net_network, self.info_flat1)
|
||||
# tenant actions
|
||||
self.security_group = self._create_security_group(
|
||||
client=self.network_client, namestart='FLAT-tenant')
|
||||
security_groups = [{'name': self.security_group['name']}]
|
||||
self.serv1 = self.create_server_on_network(
|
||||
self.net_network, security_groups,
|
||||
image=self.get_server_image(),
|
||||
flavor=self.get_server_flavor(),
|
||||
name=self.net_network['name'])
|
||||
self.check_server_connected(self.serv1)
|
||||
LOG.debug(Z_DEPLOY_DELETE_SERVER, "flat-network")
|
||||
self.servers_client.delete_server(self.serv1['id'])
|
||||
LOG.debug(Z_DEPLOY_COMPLETED, "flat-network")
|
||||
|
||||
|
||||
class TestTenantConnectivity(dmgr.TopoDeployScenarioManager):
|
||||
|
||||
"""TestTenantConnectivity: router attached with one network/subnet
|
||||
|
||||
1. boot server #1 with icmp/ssh security rules enabled.
|
||||
2. create/associate floatingip associate to server #1
|
||||
3. disassociate floatingip from server #1
|
||||
4. check server #1 is not reachable.
|
||||
5. boot server #2, and associated with the last floatingip.
|
||||
6. check the 2nd and outside-world-server are reachable.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestTenantConnectivity, self).setUp()
|
||||
self.servers = []
|
||||
|
||||
def tearDown(self):
|
||||
# do mini teardown if test failed already
|
||||
try:
|
||||
self.disassociate_floatingip(self.fip1)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
self.router.unset_gateway()
|
||||
self.router.delete()
|
||||
except Exception:
|
||||
pass
|
||||
super(TestTenantConnectivity, self).tearDown()
|
||||
|
||||
@test.idempotent_id('3c6cd4fe-de25-47ef-b638-a6bbb312da09')
|
||||
@test.services('compute', 'network')
|
||||
def test_tenant_connectivity(self):
|
||||
LOG.debug(Z_DEPLOY_TOPO, "tenant connectivity")
|
||||
client_mgr = self.manager
|
||||
username, password = self.get_image_userpass()
|
||||
# create security_group with loginable rules
|
||||
self.security_group = self._create_security_group(
|
||||
security_groups_client=client_mgr.security_groups_client,
|
||||
client=client_mgr.network_client, namestart='deploy-connect')
|
||||
self.network, self.subnet, self.router = self.setup_tenant_network(
|
||||
self.public_network_id, namestart='deploy-connect')
|
||||
self.check_networks(self.network, self.subnet, self.router)
|
||||
security_groups = [{'name': self.security_group['name']}]
|
||||
self.serv1 = self.create_server_on_network(
|
||||
self.network, security_groups,
|
||||
image=self.get_server_image(),
|
||||
flavor=self.get_server_flavor(),
|
||||
name=self.network['name'])
|
||||
self.fip1 = self.create_floatingip_for_server(
|
||||
self.serv1, client_mgr=client_mgr)
|
||||
msg = "Associate floatingip[%s] sever#1" % self.fip1
|
||||
self._check_floatingip_connectivity(
|
||||
self.fip1, self.serv1, should_connect=True, msg=msg)
|
||||
# VM is reachable from public; check VM can reach outside world
|
||||
node1 = dmgr.make_node_info(self.fip1, username, password, True)
|
||||
is_reachable = dmgr.check_host_is_reachable(
|
||||
node1, node1['dest'], ['outside'])
|
||||
self.assertTrue(
|
||||
is_reachable,
|
||||
"VM=%s CAN-NOT-REACH-OUTSIDE-WORLD" % (node1['ipaddr']))
|
||||
LOG.debug('tenant[%s] CAN-REACH-OUTSIDE-WORLD',
|
||||
node1['ipaddr'])
|
||||
self.disassociate_floatingip(self.fip1)
|
||||
time.sleep(dmgr.WAITTIME_AFTER_DISASSOC_FLOATINGIP)
|
||||
msg = "after disassociate floatingip[%s] from server#1" % self.fip1
|
||||
self._check_floatingip_connectivity(
|
||||
self.fip1, self.serv1, should_connect=False, msg=msg)
|
||||
self.serv2 = self.create_server_on_network(
|
||||
self.network, security_groups,
|
||||
image=self.get_server_image(),
|
||||
flavor=self.get_server_flavor(),
|
||||
name=self.network['name'])
|
||||
self.associate_floatingip(self.fip1, self.serv2)
|
||||
server_pingable = self._waitfor_associated_floatingip(self.fip1)
|
||||
self.assertTrue(
|
||||
server_pingable,
|
||||
msg="Expect server#2 to be reachable after floatingip assigned.")
|
||||
self.disassociate_floatingip(self.fip1)
|
||||
LOG.debug(Z_DEPLOY_DELETE_SERVER, "tenant connectivity")
|
||||
self.servers_client.delete_server(self.serv1['id'])
|
||||
self.servers_client.delete_server(self.serv2['id'])
|
||||
self.router.unset_gateway()
|
||||
self.router.delete()
|
||||
LOG.debug(Z_DEPLOY_COMPLETED, "tenant connectivity")
|
||||
|
||||
|
||||
class TestMultiTenantsNetwork(dmgr.TopoDeployScenarioManager):
|
||||
|
||||
"""TestMultiTenantsNetwork: with router, attached with 1 network/subnet
|
||||
|
||||
1. boot 2 servers (icmp/ssh rules enabled) on primary(green) network.
|
||||
2. create/associate floatingip to each server.
|
||||
3. check VM-A can reach VM-B's fixed IP
|
||||
4. chekc VM-B can reach VM-A's fixed IP
|
||||
5. repeat 1-4 with alt-tenant (red), however its cidr is different
|
||||
from the primary network for negative test. We don't want to ping
|
||||
fixed-ip that being assigned to both tenents.
|
||||
6. check VM@primary can not access VM@alt with fixed-ip
|
||||
7. check VM@primary can access floatingip of VM@alt
|
||||
"""
|
||||
|
||||
def tearDown(self):
|
||||
# do mini teardown if test failed already
|
||||
try:
|
||||
self.remove_tenant_network(False)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
super(TestMultiTenantsNetwork, self).tearDown()
|
||||
|
||||
def remove_tenant_network(self, from_test=True):
|
||||
for tn in ['green', 'red']:
|
||||
tenant = getattr(self, tn, None)
|
||||
if tenant and 'fip1' in tenant:
|
||||
servers_client = tenant['client_mgr'].servers_client
|
||||
dmgr.delete_all_servers(servers_client)
|
||||
self.disassociate_floatingip(tenant['fip1'])
|
||||
self.disassociate_floatingip(tenant['fip2'])
|
||||
if from_test:
|
||||
time.sleep(dmgr.WAITTIME_AFTER_DISASSOC_FLOATINGIP)
|
||||
fip_client = tenant['client_mgr'].floating_ips_client
|
||||
fip_client.delete_floatingip(tenant['fip1'].id)
|
||||
fip_client.delete_floatingip(tenant['fip2'].id)
|
||||
tenant.pop('fip1')
|
||||
tenant['router'].delete()
|
||||
if from_test:
|
||||
time.sleep(dmgr.WAITTIME_AFTER_ASSOC_FLOATINGIP)
|
||||
tenant['network'].delete()
|
||||
|
||||
def create_tenant_network_env(self, client_mgr, t_id,
|
||||
check_outside_world=True,
|
||||
cidr_offset=0):
|
||||
username, password = self.get_image_userpass()
|
||||
t_security_group = self._create_security_group(
|
||||
client=client_mgr.network_client,
|
||||
security_groups_client=client_mgr.security_groups_client,
|
||||
namestart="deploy-multi-tenant")
|
||||
t_network, t_subnet, t_router = self.setup_tenant_network(
|
||||
self.public_network_id, client_mgr,
|
||||
namestart=("deploy-%s-tenant" % t_id),
|
||||
cidr_offset=cidr_offset)
|
||||
self.check_networks(t_network, t_subnet, t_router)
|
||||
name1 = t_network['name'] + "-A"
|
||||
name2 = t_network['name'] + "-B"
|
||||
security_groups = [{'name': t_security_group['name']}]
|
||||
servers_client = client_mgr.servers_client
|
||||
t_serv1 = self.create_server_on_network(
|
||||
t_network, security_groups,
|
||||
image=self.get_server_image(),
|
||||
flavor=self.get_server_flavor(),
|
||||
name=name1,
|
||||
servers_client=servers_client, wait_on_boot=False)
|
||||
t_serv2 = self.create_server_on_network(
|
||||
t_network, security_groups,
|
||||
image=self.get_server_image(),
|
||||
flavor=self.get_server_flavor(),
|
||||
servers_client=servers_client, name=name2)
|
||||
t_fip1 = self.create_floatingip_for_server(
|
||||
t_serv1, client_mgr=client_mgr)
|
||||
t_fip2 = self.create_floatingip_for_server(
|
||||
t_serv2, client_mgr=client_mgr)
|
||||
node1 = dmgr.make_node_info(t_fip1, username, password,
|
||||
check_outside_world)
|
||||
node2 = dmgr.make_node_info(t_fip2, username, password,
|
||||
check_outside_world)
|
||||
T = dict(security_group=t_security_group,
|
||||
network=t_network, subnet=t_subnet,
|
||||
router=t_router, client_mgr=client_mgr,
|
||||
serv1=t_serv1, fip1=t_fip1, node1=node1,
|
||||
serv2=t_serv2, fip2=t_fip2, node2=node2)
|
||||
is_reachable = dmgr.check_host_is_reachable(
|
||||
node1, node2['dest'], [dmgr.IPTYPE_FIXED])
|
||||
self.assertTrue(
|
||||
is_reachable,
|
||||
("VM-A-%s=%s CANNOT-REACH VM-B-%s=%s" %
|
||||
(t_id, str(node1), t_id, str(node2))))
|
||||
is_reachable = dmgr.check_host_is_reachable(
|
||||
node2, node1['dest'], [dmgr.IPTYPE_FIXED])
|
||||
self.assertTrue(
|
||||
True,
|
||||
("VM-B-%s=%s CANNOT-REACH VM-A-%s=%s" %
|
||||
(t_id, str(node2), t_id, str(node1))))
|
||||
return T
|
||||
|
||||
@test.idempotent_id('19d19cd0-9686-49c9-acea-a9db28f7458c')
|
||||
@test.services('compute', 'network')
|
||||
def test_multi_tenants_network(self):
|
||||
LOG.debug(Z_DEPLOY_TOPO, "multi tenant network")
|
||||
self.green = self.create_tenant_network_env(
|
||||
self.manager, 'green', True)
|
||||
# in multiple tenant environment, ip overlay could happen
|
||||
# for the 2nd tenent give it a different ip-range to
|
||||
# make sure private-ip at tenat-1 is not the same being
|
||||
# assigned to tenant-2
|
||||
self.red = self.create_tenant_network_env(
|
||||
self.alt_manager, 'red', False, cidr_offset=3)
|
||||
# t1 can reach t2's public interface
|
||||
is_rechable = dmgr.check_host_is_reachable(
|
||||
self.green['node1'], self.red['node2']['dest'],
|
||||
[dmgr.IPTYPE_FLOATING])
|
||||
self.assertTrue(
|
||||
is_rechable,
|
||||
("t1:VM-A=%s CANNOT-REACH t2:VM-A=[floating-ip %s]" %
|
||||
(str(self.green['node1']), str(self.red['node2']))))
|
||||
# Do the reachable first, then check other VM's fixed-ip
|
||||
# is not reachable - again tenants should not have overlay IPs.
|
||||
not_reachable = dmgr.check_host_not_reachable(
|
||||
self.green['node1'], self.red['node2']['dest'],
|
||||
[dmgr.IPTYPE_FIXED], 10, 20, 2)
|
||||
self.assertFalse(
|
||||
not_reachable,
|
||||
("t1:VM-A=%s SHOULD-NOT-REACH t2:VM-B=[fixed-ip %s]" %
|
||||
(str(self.green['node1']), str(self.red['node2']))))
|
||||
self.remove_tenant_network()
|
||||
LOG.debug(Z_DEPLOY_COMPLETED, "multi tenant network")
|
||||
|
||||
|
||||
class TestProviderRouterTenantNetwork(dmgr.TopoDeployScenarioManager):
|
||||
|
||||
"""TestProviderRouterTenantNetwork:
|
||||
|
||||
1. admin client create a router, gw to external network
|
||||
2. primary client (yellow) create a network
|
||||
3. alt client (blue) create a network
|
||||
4. admin client add primary network and alt network to router
|
||||
5. primary client boot a server, icmp/ssh enabled, to its network
|
||||
6. alt client boot a server, icmp/ssh enabled, to its network
|
||||
7. primary client create floatingip to its server
|
||||
8. alt client create floatingip to its server
|
||||
9. check primary server can reach fixed-ip & floating-ip of alt server
|
||||
10. check alt server can reach fixed-ip & floating-ip of primary server
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestProviderRouterTenantNetwork, self).setUp()
|
||||
self.admin_client = self.admin_manager.network_client
|
||||
|
||||
def tearDown(self):
|
||||
# do mini teardown if test failed already
|
||||
try:
|
||||
self.remove_tenant_network(False)
|
||||
except Exception:
|
||||
pass
|
||||
super(TestProviderRouterTenantNetwork, self).tearDown()
|
||||
|
||||
def remove_tenant_network(self, from_test=True):
|
||||
for tn in ['yellow', 'blue']:
|
||||
tenant = getattr(self, tn, None)
|
||||
if tenant and 'fip' in tenant:
|
||||
servers_client = tenant['client_mgr'].servers_client
|
||||
dmgr.delete_all_servers(servers_client)
|
||||
self.disassociate_floatingip(tenant['fip'])
|
||||
if from_test:
|
||||
time.sleep(dmgr.WAITTIME_AFTER_DISASSOC_FLOATINGIP)
|
||||
fip_client = tenant['client_mgr'].floating_ips_client
|
||||
fip_client.delete_floatingip(tenant['fip'].id)
|
||||
tenant.pop('fip')
|
||||
tenant['router'].delete_subnet(tenant['subnet'])
|
||||
tenant['network'].delete()
|
||||
self.p_router.unset_gateway()
|
||||
self.p_router.delete()
|
||||
|
||||
def create_tenant_network_env(self, to_router, t_id, client_mgr=None,
|
||||
cidr_offset=0, **kwargs):
|
||||
namestart = "deploy-%s-tenant" % t_id
|
||||
name = data_utils.rand_name(namestart)
|
||||
client_mgr = client_mgr or self.manager
|
||||
servers_client = client_mgr.servers_client
|
||||
t_network, t_subnet = self.create_network_subnet(
|
||||
client_mgr, name=name,
|
||||
cidr_offset=cidr_offset,)
|
||||
to_router.add_subnet(t_subnet)
|
||||
t_security_group = self._create_security_group(
|
||||
security_groups_client=client_mgr.security_groups_client,
|
||||
client=client_mgr.network_client, namestart=namestart)
|
||||
security_groups = [{'name': t_security_group['name']}]
|
||||
t_serv = self.create_server_on_network(
|
||||
t_network, security_groups,
|
||||
name=t_network['name'],
|
||||
image=self.get_server_image(),
|
||||
flavor=self.get_server_flavor(),
|
||||
servers_client=servers_client)
|
||||
t_fip = self.create_floatingip_for_server(
|
||||
t_serv, client_mgr=client_mgr)
|
||||
|
||||
return dict(network=t_network, subnet=t_subnet,
|
||||
router=to_router,
|
||||
client_mgr=client_mgr,
|
||||
secuirty_group=t_security_group,
|
||||
serv=t_serv, fip=t_fip)
|
||||
|
||||
@test.idempotent_id('a31712de-33ad-4dc2-9755-1a0631a4f66a')
|
||||
@test.services('compute', 'network')
|
||||
def test_provider_router_tenant_network(self):
|
||||
# provider router owned by admin_client
|
||||
self.p_router = self._create_router(
|
||||
client_mgr=self.admin_manager, namestart="deploy-provider-router",
|
||||
distributed=self.tenant_router_attrs.get('distributed'),
|
||||
router_type=self.tenant_router_attrs.get('router_type'))
|
||||
self.p_router.set_gateway(self.public_network_id)
|
||||
self.yellow = self.create_tenant_network_env(
|
||||
self.p_router, 'yellow', self.manager, 0)
|
||||
self.blue = self.create_tenant_network_env(
|
||||
self.p_router, 'blue', self.alt_manager, 2)
|
||||
username, password = self.get_image_userpass()
|
||||
yellow = dmgr.make_node_info(self.yellow['fip'], username, password)
|
||||
blue = dmgr.make_node_info(self.blue['fip'], username, password)
|
||||
is_reachable = dmgr.check_host_is_reachable(
|
||||
yellow, blue['dest'], [dmgr.IPTYPE_FLOATING])
|
||||
self.assertTrue(
|
||||
is_reachable,
|
||||
"VM-yello=%s CANNOT-REACH VM-blue=%s" % (str(yellow), str(blue)))
|
||||
is_reachable = dmgr.check_host_is_reachable(
|
||||
blue, yellow['dest'], [dmgr.IPTYPE_FLOATING])
|
||||
self.assertTrue(
|
||||
is_reachable,
|
||||
"VM-blue=%s CANNOT-REACH VM-yellow=%s" % (str(blue), str(yellow)))
|
||||
self.remove_tenant_network()
|
||||
|
||||
|
||||
# exclusive router
|
||||
class TestTenantConnectivityWithExclusiveRouter(
|
||||
TestTenantConnectivity):
|
||||
|
||||
"""TestTenantConnectivityWithExclusiveRouter:
|
||||
|
||||
samet as TestTenantConnectivity, except router is exclusive.
|
||||
"""
|
||||
|
||||
# router attributes used to create the tenant's router
|
||||
tenant_router_attrs = {'router_type': 'exclusive'}
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(TestTenantConnectivityWithExclusiveRouter,
|
||||
cls).skip_checks()
|
||||
for ext in ['nsxv-router-type']:
|
||||
if not test.is_extension_enabled(ext, 'network'):
|
||||
msg = "%s extension not enabled." % ext
|
||||
raise cls.skipException(msg)
|
||||
|
||||
|
||||
class TestMultiTenantsNetworkWithExclusiveRouter(
|
||||
TestMultiTenantsNetwork):
|
||||
|
||||
"""TestMultiTenantsNetworkWithExclusiveRouter:
|
||||
|
||||
samet as TenantNetwork , except router is exclusive.
|
||||
"""
|
||||
|
||||
tenant_router_attrs = {'router_type': 'exclusive'}
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(TestMultiTenantsNetworkWithExclusiveRouter,
|
||||
cls).skip_checks()
|
||||
for ext in ['nsxv-router-type']:
|
||||
if not test.is_extension_enabled(ext, 'network'):
|
||||
msg = "%s extension not enabled." % ext
|
||||
raise cls.skipException(msg)
|
||||
|
||||
|
||||
class TestProviderExclusiveRouterTenantNetwork(
|
||||
TestProviderRouterTenantNetwork):
|
||||
|
||||
"""TestProviderExclusiveRouterTenantNetwork:
|
||||
|
||||
same as TestProviderRouterTenantNework, except router is exclusive.
|
||||
"""
|
||||
|
||||
tenant_router_attrs = {'router_type': 'exclusive'}
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(TestProviderExclusiveRouterTenantNetwork,
|
||||
cls).skip_checks()
|
||||
for ext in ['nsxv-router-type']:
|
||||
if not test.is_extension_enabled(ext, 'network'):
|
||||
msg = "%s extension not enabled." % ext
|
||||
raise cls.skipException(msg)
|
||||
|
||||
|
||||
# distributed router
|
||||
class TestTenantConnectivityWithDistributedRouter(
|
||||
TestTenantConnectivity):
|
||||
|
||||
"""TestTenantConnectivityWithDistributedRouter:
|
||||
|
||||
same as TestTenantConnectivity, except router is distributed.
|
||||
"""
|
||||
|
||||
# router attributes used to create the tenant's router
|
||||
tenant_router_attrs = {'distributed': True}
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(TestTenantConnectivityWithDistributedRouter,
|
||||
cls).skip_checks()
|
||||
for ext in ['dvr', 'nsxv-router-type']:
|
||||
if not test.is_extension_enabled(ext, 'network'):
|
||||
msg = "%s extension not enabled." % ext
|
||||
raise cls.skipException(msg)
|
||||
|
||||
|
||||
class TestMultiTenantsNetworkWithDistributedRouter(
|
||||
TestMultiTenantsNetwork):
|
||||
|
||||
"""TestMultiTenantsNetworkWithDistributedRouter:
|
||||
|
||||
same as TestMultiTenantsNetwork, except router is distributed.
|
||||
"""
|
||||
|
||||
tenant_router_attrs = {'distributed': True}
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(TestMultiTenantsNetworkWithDistributedRouter,
|
||||
cls).skip_checks()
|
||||
for ext in ['dvr', 'nsxv-router-type']:
|
||||
if not test.is_extension_enabled(ext, 'network'):
|
||||
msg = "%s extension not enabled." % ext
|
||||
raise cls.skipException(msg)
|
||||
|
||||
|
||||
class TestProviderDistributedRouterTenantNetwork(
|
||||
TestProviderRouterTenantNetwork):
|
||||
|
||||
"""TestProviderDistributedRouterTenantNetwork:
|
||||
|
||||
same as TestProviderRouterTenantNework, except router is distributed.
|
||||
"""
|
||||
|
||||
tenant_router_attrs = {'distributed': True}
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(TestProviderDistributedRouterTenantNetwork,
|
||||
cls).skip_checks()
|
||||
for ext in ['dvr', 'nsxv-router-type']:
|
||||
if not test.is_extension_enabled(ext, 'network'):
|
||||
msg = "%s extension not enabled." % ext
|
||||
raise cls.skipException(msg)
|
||||
|
||||
|
||||
def _g_service_client(req_mgr, client_name):
|
||||
s_client = getattr(req_mgr, client_name, None)
|
||||
if s_client:
|
||||
return s_client
|
||||
return req_mgr.network_client
|
||||
|
||||
|
||||
# self vs req: there are possible 3 client managers (admin, pri, 2nd)
|
||||
# in each class, but the default is the primary, other clients need aslo
|
||||
# to create resources, so you should call this to get proper client.
|
||||
def _g_neutron_service_client(self_mgr, req_mgr, client_name):
|
||||
if req_mgr:
|
||||
return _g_service_client(req_mgr, client_name)
|
||||
return _g_service_client(self_mgr, client_name)
|
539
vmware_nsx_tempest/tests/nsxv/scenario/test_dvr_basic_ops.py
Normal file
539
vmware_nsx_tempest/tests/nsxv/scenario/test_dvr_basic_ops.py
Normal file
@ -0,0 +1,539 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# Copyright 2015 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 collections
|
||||
import re
|
||||
import time
|
||||
|
||||
from oslo_log import log as logging
|
||||
from tempest_lib.common.utils import data_utils
|
||||
import testtools
|
||||
|
||||
from tempest import config
|
||||
from tempest import exceptions
|
||||
from tempest.scenario import manager
|
||||
from tempest.services.network import resources as net_resources
|
||||
from tempest import test
|
||||
|
||||
CONF = config.CONF
|
||||
FIP_OPS_TIMEOUT = 10
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
Floating_IP_tuple = collections.namedtuple('Floating_IP_tuple',
|
||||
['floating_ip', 'server'])
|
||||
|
||||
|
||||
class TestDvrBasicOps(manager.NetworkScenarioTest):
|
||||
|
||||
"""
|
||||
This smoke test suite assumes that Nova has been configured to
|
||||
boot VM's with Neutron-managed networking, and attempts to
|
||||
verify network connectivity as follows:
|
||||
|
||||
There are presumed to be two types of networks: tenant and
|
||||
public. A tenant network may or may not be reachable from the
|
||||
Tempest host. A public network is assumed to be reachable from
|
||||
the Tempest host, and it should be possible to associate a public
|
||||
('floating') IP address with a tenant ('fixed') IP address to
|
||||
facilitate external connectivity to a potentially unroutable
|
||||
tenant IP address.
|
||||
|
||||
This test suite can be configured to test network connectivity to
|
||||
a VM via a tenant network, a public network, or both. If both
|
||||
networking types are to be evaluated, tests that need to be
|
||||
executed remotely on the VM (via ssh) will only be run against
|
||||
one of the networks (to minimize test execution time).
|
||||
|
||||
Determine which types of networks to test as follows:
|
||||
|
||||
* Configure tenant network checks (via the
|
||||
'tenant_networks_reachable' key) if the Tempest host should
|
||||
have direct connectivity to tenant networks. This is likely to
|
||||
be the case if Tempest is running on the same host as a
|
||||
single-node devstack installation with IP namespaces disabled.
|
||||
|
||||
* Configure checks for a public network if a public network has
|
||||
been configured prior to the test suite being run and if the
|
||||
Tempest host should have connectivity to that public network.
|
||||
Checking connectivity for a public network requires that a
|
||||
value be provided for 'public_network_id'. A value can
|
||||
optionally be provided for 'public_router_id' if tenants will
|
||||
use a shared router to access a public network (as is likely to
|
||||
be the case when IP namespaces are not enabled). If a value is
|
||||
not provided for 'public_router_id', a router will be created
|
||||
for each tenant and use the network identified by
|
||||
'public_network_id' as its gateway.
|
||||
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(TestDvrBasicOps, cls).skip_checks()
|
||||
if not (CONF.network.tenant_networks_reachable
|
||||
or CONF.network.public_network_id):
|
||||
msg = ('Either tenant_networks_reachable must be "true", or '
|
||||
'public_network_id must be defined.')
|
||||
raise cls.skipException(msg)
|
||||
for ext in ['router', 'security-group', 'dvr']:
|
||||
if not test.is_extension_enabled(ext, 'network'):
|
||||
msg = "%s extension not enabled." % ext
|
||||
raise cls.skipException(msg)
|
||||
|
||||
@classmethod
|
||||
def setup_credentials(cls):
|
||||
# Ask framework to not create network resources for these tests.
|
||||
cls.set_network_resources()
|
||||
super(TestDvrBasicOps, cls).setup_credentials()
|
||||
|
||||
def setUp(self):
|
||||
super(TestDvrBasicOps, self).setUp()
|
||||
self.keypairs = {}
|
||||
self.servers = []
|
||||
|
||||
def _setup_network_and_servers(self, **kwargs):
|
||||
boot_with_port = kwargs.pop('boot_with_port', False)
|
||||
self.security_group = \
|
||||
self._create_security_group(tenant_id=self.tenant_id)
|
||||
self.network, self.subnet, self.router = self.create_networks(**kwargs)
|
||||
self.check_networks()
|
||||
|
||||
self.port_id = None
|
||||
if boot_with_port:
|
||||
# create a port on the network and boot with that
|
||||
self.port_id = self._create_port(self.network['id']).id
|
||||
|
||||
name = data_utils.rand_name('server-smoke')
|
||||
server = self._create_server(name, self.network, self.port_id)
|
||||
self._check_tenant_network_connectivity()
|
||||
|
||||
floating_ip = self.create_floating_ip(server)
|
||||
self.floating_ip_tuple = Floating_IP_tuple(floating_ip, server)
|
||||
|
||||
# overwrite super class who does not accept router attributes
|
||||
def create_networks(self, dns_nameservers=None, **kwargs):
|
||||
client = self.network_client
|
||||
networks_client = self.networks_client
|
||||
subnets_client = self.subnets_client
|
||||
network = self._create_network(
|
||||
client=client, networks_client=networks_client,
|
||||
tenant_id=self.tenant_id)
|
||||
|
||||
router_kwargs = {}
|
||||
for k in kwargs.keys():
|
||||
if k in ('distributed', 'router_type', 'router_size'):
|
||||
router_kwargs[k] = kwargs.pop(k)
|
||||
router = self._create_router(**router_kwargs)
|
||||
router.set_gateway(CONF.network.public_network_id)
|
||||
|
||||
subnet_kwargs = dict(network=network, client=client,
|
||||
subnets_client=subnets_client)
|
||||
# use explicit check because empty list is a valid option
|
||||
if dns_nameservers is not None:
|
||||
subnet_kwargs['dns_nameservers'] = dns_nameservers
|
||||
subnet = self._create_subnet(**subnet_kwargs)
|
||||
subnet.add_to_router(router.id)
|
||||
return network, subnet, router
|
||||
|
||||
# overwrite super class
|
||||
def _create_router(self, client=None, tenant_id=None,
|
||||
namestart='router-smoke', **kwargs):
|
||||
if not client:
|
||||
client = self.network_client
|
||||
if not tenant_id:
|
||||
tenant_id = client.tenant_id
|
||||
name = data_utils.rand_name(namestart)
|
||||
result = client.create_router(name=name,
|
||||
admin_state_up=True,
|
||||
tenant_id=tenant_id,
|
||||
**kwargs)
|
||||
router = net_resources.DeletableRouter(client=client,
|
||||
**result['router'])
|
||||
self.assertEqual(router.name, name)
|
||||
self.addCleanup(self.delete_wrapper, router.delete)
|
||||
return router
|
||||
|
||||
def check_networks(self):
|
||||
"""
|
||||
Checks that we see the newly created network/subnet/router via
|
||||
checking the result of list_[networks,routers,subnets]
|
||||
"""
|
||||
|
||||
seen_nets = self._list_networks()
|
||||
seen_names = [n['name'] for n in seen_nets]
|
||||
seen_ids = [n['id'] for n in seen_nets]
|
||||
self.assertIn(self.network.name, seen_names)
|
||||
self.assertIn(self.network.id, seen_ids)
|
||||
|
||||
if self.subnet:
|
||||
seen_subnets = self._list_subnets()
|
||||
seen_net_ids = [n['network_id'] for n in seen_subnets]
|
||||
seen_subnet_ids = [n['id'] for n in seen_subnets]
|
||||
self.assertIn(self.network.id, seen_net_ids)
|
||||
self.assertIn(self.subnet.id, seen_subnet_ids)
|
||||
|
||||
if self.router:
|
||||
seen_routers = self._list_routers()
|
||||
seen_router_ids = [n['id'] for n in seen_routers]
|
||||
seen_router_names = [n['name'] for n in seen_routers]
|
||||
self.assertIn(self.router.name,
|
||||
seen_router_names)
|
||||
self.assertIn(self.router.id,
|
||||
seen_router_ids)
|
||||
|
||||
def _create_server(self, name, network, port_id=None):
|
||||
keypair = self.create_keypair()
|
||||
self.keypairs[keypair['name']] = keypair
|
||||
security_groups = [{'name': self.security_group['name']}]
|
||||
create_kwargs = {
|
||||
'networks': [
|
||||
{'uuid': network.id},
|
||||
],
|
||||
'key_name': keypair['name'],
|
||||
'security_groups': security_groups,
|
||||
'wait_until': 'ACTIVE',
|
||||
}
|
||||
if port_id is not None:
|
||||
create_kwargs['networks'][0]['port'] = port_id
|
||||
server = self.create_server(name=name, **create_kwargs)
|
||||
self.servers.append(server)
|
||||
return server
|
||||
|
||||
def _get_server_key(self, server):
|
||||
return self.keypairs[server['key_name']]['private_key']
|
||||
|
||||
def _check_tenant_network_connectivity(self):
|
||||
ssh_login = CONF.validation.image_ssh_user
|
||||
for server in self.servers:
|
||||
# call the common method in the parent class
|
||||
super(TestDvrBasicOps, self).\
|
||||
_check_tenant_network_connectivity(
|
||||
server, ssh_login, self._get_server_key(server),
|
||||
servers_for_debug=self.servers)
|
||||
|
||||
def check_public_network_connectivity(
|
||||
self, should_connect=True, msg=None,
|
||||
should_check_floating_ip_status=True):
|
||||
"""Verifies connectivty to a VM via public network and floating IP,
|
||||
and verifies floating IP has resource status is correct.
|
||||
|
||||
:param should_connect: bool. determines if connectivity check is
|
||||
negative or positive.
|
||||
:param msg: Failure message to add to Error message. Should describe
|
||||
the place in the test scenario where the method was called,
|
||||
to indicate the context of the failure
|
||||
:param should_check_floating_ip_status: bool. should status of
|
||||
floating_ip be checked or not
|
||||
"""
|
||||
ssh_login = CONF.validation.image_ssh_user
|
||||
floating_ip, server = self.floating_ip_tuple
|
||||
ip_address = floating_ip.floating_ip_address
|
||||
private_key = None
|
||||
floatingip_status = 'DOWN'
|
||||
if should_connect:
|
||||
private_key = self._get_server_key(server)
|
||||
floatingip_status = 'ACTIVE'
|
||||
# Check FloatingIP Status before initiating a connection
|
||||
if should_check_floating_ip_status:
|
||||
self.check_floating_ip_status(floating_ip, floatingip_status)
|
||||
# call the common method in the parent class
|
||||
super(TestDvrBasicOps, self).check_public_network_connectivity(
|
||||
ip_address, ssh_login, private_key, should_connect, msg,
|
||||
self.servers)
|
||||
|
||||
def _disassociate_floating_ips(self):
|
||||
floating_ip, server = self.floating_ip_tuple
|
||||
self._disassociate_floating_ip(floating_ip)
|
||||
self.floating_ip_tuple = Floating_IP_tuple(
|
||||
floating_ip, None)
|
||||
|
||||
def _reassociate_floating_ips(self):
|
||||
floating_ip, server = self.floating_ip_tuple
|
||||
name = data_utils.rand_name('new_server-smoke')
|
||||
# create a new server for the floating ip
|
||||
server = self._create_server(name, self.network)
|
||||
self._associate_floating_ip(floating_ip, server)
|
||||
self.floating_ip_tuple = Floating_IP_tuple(
|
||||
floating_ip, server)
|
||||
|
||||
def _create_new_network(self, create_gateway=False):
|
||||
self.new_net = self._create_network(tenant_id=self.tenant_id)
|
||||
if create_gateway:
|
||||
self.new_subnet = self._create_subnet(
|
||||
network=self.new_net)
|
||||
else:
|
||||
self.new_subnet = self._create_subnet(
|
||||
network=self.new_net,
|
||||
gateway_ip=None)
|
||||
|
||||
def _hotplug_server(self):
|
||||
old_floating_ip, server = self.floating_ip_tuple
|
||||
ip_address = old_floating_ip.floating_ip_address
|
||||
private_key = self._get_server_key(server)
|
||||
ssh_client = self.get_remote_client(ip_address,
|
||||
private_key=private_key)
|
||||
old_nic_list = self._get_server_nics(ssh_client)
|
||||
# get a port from a list of one item
|
||||
port_list = self._list_ports(device_id=server['id'])
|
||||
self.assertEqual(1, len(port_list))
|
||||
old_port = port_list[0]
|
||||
interface = self.interface_client.create_interface(
|
||||
server=server['id'],
|
||||
network_id=self.new_net.id)
|
||||
self.addCleanup(self.network_client.wait_for_resource_deletion,
|
||||
'port',
|
||||
interface['port_id'])
|
||||
self.addCleanup(self.delete_wrapper,
|
||||
self.interface_client.delete_interface,
|
||||
server['id'], interface['port_id'])
|
||||
|
||||
def check_ports():
|
||||
self.new_port_list = [port for port in
|
||||
self._list_ports(device_id=server['id'])
|
||||
if port['id'] != old_port['id']]
|
||||
return len(self.new_port_list) == 1
|
||||
|
||||
if not test.call_until_true(check_ports, CONF.network.build_timeout,
|
||||
CONF.network.build_interval):
|
||||
raise exceptions.TimeoutException(
|
||||
"No new port attached to the server in time (%s sec)! "
|
||||
"Old port: %s. Number of new ports: %d" % (
|
||||
CONF.network.build_timeout, old_port,
|
||||
len(self.new_port_list)))
|
||||
new_port = net_resources.DeletablePort(client=self.network_client,
|
||||
**self.new_port_list[0])
|
||||
|
||||
def check_new_nic():
|
||||
new_nic_list = self._get_server_nics(ssh_client)
|
||||
self.diff_list = [n for n in new_nic_list if n not in old_nic_list]
|
||||
return len(self.diff_list) == 1
|
||||
|
||||
if not test.call_until_true(check_new_nic, CONF.network.build_timeout,
|
||||
CONF.network.build_interval):
|
||||
raise exceptions.TimeoutException("Interface not visible on the "
|
||||
"guest after %s sec"
|
||||
% CONF.network.build_timeout)
|
||||
|
||||
num, new_nic = self.diff_list[0]
|
||||
ssh_client.assign_static_ip(nic=new_nic,
|
||||
addr=new_port.fixed_ips[0]['ip_address'])
|
||||
ssh_client.turn_nic_on(nic=new_nic)
|
||||
|
||||
def _get_server_nics(self, ssh_client):
|
||||
reg = re.compile(r'(?P<num>\d+): (?P<nic_name>\w+):')
|
||||
ipatxt = ssh_client.get_ip_list()
|
||||
return reg.findall(ipatxt)
|
||||
|
||||
def _check_network_internal_connectivity(self, network,
|
||||
should_connect=True):
|
||||
"""
|
||||
via ssh check VM internal connectivity:
|
||||
- ping internal gateway and DHCP port, implying in-tenant connectivity
|
||||
pinging both, because L3 and DHCP agents might be on different nodes
|
||||
"""
|
||||
floating_ip, server = self.floating_ip_tuple
|
||||
# get internal ports' ips:
|
||||
# get all network ports in the new network
|
||||
internal_ips = (p['fixed_ips'][0]['ip_address'] for p in
|
||||
self._list_ports(tenant_id=server['tenant_id'],
|
||||
network_id=network.id)
|
||||
if (p['device_owner'].startswith('network') and
|
||||
not p['device_owner'].endswith('dhcp')))
|
||||
|
||||
self._check_server_connectivity(floating_ip,
|
||||
internal_ips,
|
||||
should_connect)
|
||||
|
||||
def _check_network_external_connectivity(self):
|
||||
"""
|
||||
ping public network default gateway to imply external connectivity
|
||||
|
||||
"""
|
||||
if not CONF.network.public_network_id:
|
||||
msg = 'public network not defined.'
|
||||
LOG.debug(msg)
|
||||
return
|
||||
|
||||
# We ping the external IP from the instance using its floating IP
|
||||
# which is always IPv4, so we must only test connectivity to
|
||||
# external IPv4 IPs if the external network is dualstack.
|
||||
v4_subnets = [s for s in self._list_subnets(
|
||||
network_id=CONF.network.public_network_id) if s['ip_version'] == 4]
|
||||
self.assertEqual(1, len(v4_subnets),
|
||||
"Found %d IPv4 subnets" % len(v4_subnets))
|
||||
|
||||
external_ips = [v4_subnets[0]['gateway_ip']]
|
||||
self._check_server_connectivity(self.floating_ip_tuple.floating_ip,
|
||||
external_ips)
|
||||
|
||||
def _check_server_connectivity(self, floating_ip, address_list,
|
||||
should_connect=True):
|
||||
ip_address = floating_ip.floating_ip_address
|
||||
private_key = self._get_server_key(self.floating_ip_tuple.server)
|
||||
ssh_source = self._ssh_to_server(ip_address, 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.debug("Unable to access {dest} via ssh to "
|
||||
"floating-ip {src}".format(dest=remote_ip,
|
||||
src=floating_ip))
|
||||
raise
|
||||
|
||||
@test.idempotent_id('62eb50a8-45f3-4eec-acc4-f01cee10a011')
|
||||
@test.services('compute', 'network')
|
||||
def test_dvr_network_basic_ops(self):
|
||||
"""
|
||||
For a freshly-booted VM with an IP address ("port") on a given
|
||||
network:
|
||||
|
||||
- the Tempest host can ping the IP address. This implies, but
|
||||
does not guarantee (see the ssh check that follows), that the
|
||||
VM has been assigned the correct IP address and has
|
||||
connectivity to the Tempest host.
|
||||
|
||||
- the Tempest host can perform key-based authentication to an
|
||||
ssh server hosted at the IP address. This check guarantees
|
||||
that the IP address is associated with the target VM.
|
||||
|
||||
- the Tempest host can ssh into the VM via the IP address and
|
||||
successfully execute the following:
|
||||
|
||||
- ping an external IP address, implying external connectivity.
|
||||
|
||||
- ping an external hostname, implying that dns is correctly
|
||||
configured.
|
||||
|
||||
- ping an internal IP address, implying connectivity to another
|
||||
VM on the same network.
|
||||
|
||||
- detach the floating-ip from the VM and verify that it becomes
|
||||
unreachable
|
||||
|
||||
- associate detached floating ip to a new VM and verify connectivity.
|
||||
VMs are created with unique keypair so connectivity also asserts that
|
||||
floating IP is associated with the new VM instead of the old one
|
||||
|
||||
Verifies that floating IP status is updated correctly after each change
|
||||
|
||||
|
||||
"""
|
||||
self._setup_network_and_servers(distributed=True)
|
||||
LOG.debug("Sleeping %ss after associate floating ip %s" %
|
||||
(FIP_OPS_TIMEOUT, self.floating_ip_tuple))
|
||||
self.check_public_network_connectivity(should_connect=True)
|
||||
self._check_network_internal_connectivity(network=self.network)
|
||||
self._check_network_external_connectivity()
|
||||
self._disassociate_floating_ips()
|
||||
LOG.debug("Sleeping %ss after disassociate floating ip %s" %
|
||||
(FIP_OPS_TIMEOUT, self.floating_ip_tuple))
|
||||
self.check_public_network_connectivity(should_connect=False,
|
||||
msg="after disassociate "
|
||||
"floating ip")
|
||||
self._reassociate_floating_ips()
|
||||
LOG.debug("Sleeping %ss after reassociate floating ip %s" %
|
||||
(FIP_OPS_TIMEOUT, self.floating_ip_tuple))
|
||||
self.check_public_network_connectivity(should_connect=True,
|
||||
msg="after re-associate "
|
||||
"floating ip")
|
||||
|
||||
@test.idempotent_id('d99b62ec-28ce-44db-a195-edb74037a354')
|
||||
@testtools.skipIf(CONF.baremetal.driver_enabled,
|
||||
'Baremetal relies on a shared physical network.')
|
||||
@test.services('compute', 'network')
|
||||
def test_dvr_connectivity_between_vms_on_different_networks(self):
|
||||
"""
|
||||
For a freshly-booted VM with an IP address ("port") on a given
|
||||
network:
|
||||
|
||||
- the Tempest host can ping the IP address.
|
||||
|
||||
- the Tempest host can ssh into the VM via the IP address and
|
||||
successfully execute the following:
|
||||
|
||||
- ping an external IP address, implying external connectivity.
|
||||
|
||||
- ping an external hostname, implying that dns is correctly
|
||||
configured.
|
||||
|
||||
- ping an internal IP address, implying connectivity to another
|
||||
VM on the same network.
|
||||
|
||||
- Create another network on the same tenant with subnet, create
|
||||
an VM on the new network.
|
||||
|
||||
- Ping the new VM from previous VM failed since the new network
|
||||
was not attached to router yet.
|
||||
|
||||
- Attach the new network to the router, Ping the new VM from
|
||||
previous VM succeed.
|
||||
|
||||
"""
|
||||
self._setup_network_and_servers(distributed=True)
|
||||
LOG.debug("Sleeping %ss after associate floating ip %s" %
|
||||
(FIP_OPS_TIMEOUT, self.floating_ip_tuple))
|
||||
time.sleep(FIP_OPS_TIMEOUT)
|
||||
self.check_public_network_connectivity(should_connect=True)
|
||||
self._check_network_internal_connectivity(network=self.network)
|
||||
self._check_network_external_connectivity()
|
||||
self._create_new_network(create_gateway=True)
|
||||
name = data_utils.rand_name('server-smoke')
|
||||
self._create_server(name, self.new_net)
|
||||
self._check_network_internal_connectivity(network=self.new_net,
|
||||
should_connect=False)
|
||||
self.new_subnet.add_to_router(self.router.id)
|
||||
self._check_network_internal_connectivity(network=self.new_net,
|
||||
should_connect=True)
|
||||
|
||||
@test.idempotent_id('a73fd605-d55e-4151-b25e-41e7a7ff2258')
|
||||
@testtools.skipIf(CONF.baremetal.driver_enabled,
|
||||
'Router state cannot be altered on a shared baremetal '
|
||||
'network')
|
||||
@test.services('compute', 'network')
|
||||
def test_dvr_update_router_admin_state(self):
|
||||
"""
|
||||
1. Check public connectivity before updating
|
||||
admin_state_up attribute of router to False
|
||||
2. Check public connectivity after updating
|
||||
admin_state_up attribute of router to False
|
||||
3. Check public connectivity after updating
|
||||
admin_state_up attribute of router to True
|
||||
"""
|
||||
self._setup_network_and_servers(distributed=True)
|
||||
LOG.debug("Sleeping %ss after associate floating ip %s" %
|
||||
(FIP_OPS_TIMEOUT, self.floating_ip_tuple))
|
||||
time.sleep(FIP_OPS_TIMEOUT)
|
||||
self.check_public_network_connectivity(
|
||||
should_connect=True, msg="before updating "
|
||||
"admin_state_up of router to False")
|
||||
self._update_router_admin_state(self.router, False)
|
||||
# TODO(alokmaurya): Remove should_check_floating_ip_status=False check
|
||||
# once bug 1396310 is fixed
|
||||
|
||||
self.check_public_network_connectivity(
|
||||
should_connect=False, msg="after updating "
|
||||
"admin_state_up of router to False",
|
||||
should_check_floating_ip_status=False)
|
||||
self._update_router_admin_state(self.router, True)
|
||||
self.check_public_network_connectivity(
|
||||
should_connect=True, msg="after updating "
|
||||
"admin_state_up of router to True")
|
@ -0,0 +1,425 @@
|
||||
# Copyright 2014 Mirantis.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 six
|
||||
import tempfile
|
||||
import time
|
||||
import urllib2
|
||||
|
||||
from tempest_lib.common.utils import data_utils
|
||||
|
||||
from tempest.common import commands
|
||||
from tempest import config
|
||||
from tempest import exceptions
|
||||
from tempest.scenario import manager
|
||||
from tempest.services.network import resources as net_resources
|
||||
from tempest import test
|
||||
|
||||
from vmware_nsx_tempest.services import load_balancer_v1_client as LBV1C
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class TestLBaaSBasicOps(manager.NetworkScenarioTest):
|
||||
|
||||
"""This test checks basic load balancing.
|
||||
|
||||
The following is the scenario outline:
|
||||
1. Create an instance
|
||||
2. SSH to the instance and start two servers
|
||||
3. Create a load balancer with two members and with ROUND_ROBIN algorithm
|
||||
associate the VIP with a floating ip
|
||||
4. Send NUM requests to the floating ip and check that they are shared
|
||||
between the two servers.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(TestLBaaSBasicOps, cls).skip_checks()
|
||||
cfg = CONF.network
|
||||
if not test.is_extension_enabled('lbaas', 'network'):
|
||||
msg = 'LBaaS Extension is not enabled'
|
||||
raise cls.skipException(msg)
|
||||
if not (cfg.tenant_networks_reachable or cfg.public_network_id):
|
||||
msg = ('Either tenant_networks_reachable must be "true", or '
|
||||
'public_network_id must be defined.')
|
||||
raise cls.skipException(msg)
|
||||
|
||||
@classmethod
|
||||
def setup_credentials(cls):
|
||||
# Ask framework to not create network resources for these tests.
|
||||
cls.set_network_resources()
|
||||
super(TestLBaaSBasicOps, cls).setup_credentials()
|
||||
|
||||
def setUp(self):
|
||||
super(TestLBaaSBasicOps, self).setUp()
|
||||
# https://review.openstack.org/#/c/262571/
|
||||
CONF.validation.ssh_shell_prologue = ''
|
||||
self.servers_keypairs = {}
|
||||
self.members = []
|
||||
self.floating_ips = {}
|
||||
self.server_ips = {}
|
||||
self.port1 = 80
|
||||
self.port2 = 88
|
||||
self.num = 50
|
||||
self.server_ips = {}
|
||||
self.server_fixed_ips = {}
|
||||
self.lbv1_client = LBV1C.get_client(self.manager)
|
||||
self._create_security_group_for_test()
|
||||
self._set_net_and_subnet()
|
||||
|
||||
def tearDown(self):
|
||||
for s_id in self.server_ips.keys():
|
||||
try:
|
||||
self.servers_client.delete_server(s_id)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
for mem in self.members:
|
||||
mem.delete()
|
||||
self.vip.delete()
|
||||
self.pool.delete()
|
||||
except Exception:
|
||||
pass
|
||||
super(TestLBaaSBasicOps, self).tearDown()
|
||||
|
||||
def _set_net_and_subnet(self):
|
||||
"""Create network, subnet and router.
|
||||
|
||||
Query and set appropriate network and subnet attributes to be used
|
||||
for the test. Existing tenant networks are used if they are found.
|
||||
The configured private network and associated subnet is used as a
|
||||
fallback in absence of tenant networking.
|
||||
"""
|
||||
self.network, self.subnet, self.router = (
|
||||
self.create_networks(router_type='exclusive'))
|
||||
self.check_networks()
|
||||
|
||||
# overwrite super class who does not accept router attributes
|
||||
def create_networks(self, dns_nameservers=None, **kwargs):
|
||||
client = self.network_client
|
||||
networks_client = self.networks_client
|
||||
subnets_client = self.subnets_client
|
||||
|
||||
router_kwargs = {}
|
||||
for k in kwargs.keys():
|
||||
if k in ('distributed', 'router_type', 'router_size'):
|
||||
router_kwargs[k] = kwargs.pop(k)
|
||||
router = self._create_router(**router_kwargs)
|
||||
router.set_gateway(CONF.network.public_network_id)
|
||||
|
||||
network = self._create_network(
|
||||
client=client, networks_client=networks_client,
|
||||
tenant_id=self.tenant_id)
|
||||
|
||||
subnet_kwargs = dict(network=network, client=client,
|
||||
subnets_client=subnets_client)
|
||||
# use explicit check because empty list is a valid option
|
||||
if dns_nameservers is not None:
|
||||
subnet_kwargs['dns_nameservers'] = dns_nameservers
|
||||
subnet = self._create_subnet(**subnet_kwargs)
|
||||
subnet.add_to_router(router.id)
|
||||
return network, subnet, router
|
||||
|
||||
# overwrite super class
|
||||
def _create_router(self, client=None, tenant_id=None,
|
||||
namestart='router-smoke', **kwargs):
|
||||
if not client:
|
||||
client = self.network_client
|
||||
if not tenant_id:
|
||||
tenant_id = client.tenant_id
|
||||
name = data_utils.rand_name(namestart)
|
||||
result = client.create_router(name=name,
|
||||
admin_state_up=True,
|
||||
tenant_id=tenant_id,
|
||||
**kwargs)
|
||||
router = net_resources.DeletableRouter(client=client,
|
||||
**result['router'])
|
||||
self.assertEqual(router.name, name)
|
||||
self.addCleanup(self.delete_wrapper, router.delete)
|
||||
return router
|
||||
|
||||
def check_networks(self):
|
||||
"""Checks that we see the newly created network/subnet/router.
|
||||
|
||||
checking the result of list_[networks,routers,subnets]
|
||||
"""
|
||||
|
||||
seen_nets = self._list_networks()
|
||||
seen_names = [n['name'] for n in seen_nets]
|
||||
seen_ids = [n['id'] for n in seen_nets]
|
||||
self.assertIn(self.network.name, seen_names)
|
||||
self.assertIn(self.network.id, seen_ids)
|
||||
|
||||
if self.subnet:
|
||||
seen_subnets = self._list_subnets()
|
||||
seen_net_ids = [n['network_id'] for n in seen_subnets]
|
||||
seen_subnet_ids = [n['id'] for n in seen_subnets]
|
||||
self.assertIn(self.network.id, seen_net_ids)
|
||||
self.assertIn(self.subnet.id, seen_subnet_ids)
|
||||
|
||||
if self.router:
|
||||
seen_routers = self._list_routers()
|
||||
seen_router_ids = [n['id'] for n in seen_routers]
|
||||
seen_router_names = [n['name'] for n in seen_routers]
|
||||
self.assertIn(self.router.name,
|
||||
seen_router_names)
|
||||
self.assertIn(self.router.id,
|
||||
seen_router_ids)
|
||||
|
||||
def _create_security_group_for_test(self):
|
||||
self.security_group = self._create_security_group(
|
||||
tenant_id=self.tenant_id)
|
||||
self._create_security_group_rules_for_port(self.port1)
|
||||
self._create_security_group_rules_for_port(self.port2)
|
||||
|
||||
def _create_security_group_rules_for_port(self, port):
|
||||
rule = {
|
||||
'direction': 'ingress',
|
||||
'protocol': 'tcp',
|
||||
'port_range_min': port,
|
||||
'port_range_max': port,
|
||||
}
|
||||
self._create_security_group_rule(
|
||||
secgroup=self.security_group,
|
||||
tenant_id=self.tenant_id,
|
||||
**rule)
|
||||
|
||||
def _create_server(self, name):
|
||||
keypair = self.create_keypair()
|
||||
security_groups = [{'name': self.security_group['name']}]
|
||||
create_kwargs = {
|
||||
'networks': [
|
||||
{'uuid': self.network['id']},
|
||||
],
|
||||
'key_name': keypair['name'],
|
||||
'security_groups': security_groups,
|
||||
'wait_until': 'ACTIVE',
|
||||
}
|
||||
net_name = self.network['name']
|
||||
server = self.create_server(name=name, **create_kwargs)
|
||||
serv_id = server['id']
|
||||
self.servers_keypairs[serv_id] = keypair
|
||||
if (CONF.network.public_network_id and not
|
||||
CONF.network.tenant_networks_reachable):
|
||||
public_network_id = CONF.network.public_network_id
|
||||
floating_ip = self.create_floating_ip(
|
||||
server, public_network_id)
|
||||
self.floating_ips[floating_ip] = server
|
||||
self.server_ips[serv_id] = floating_ip.floating_ip_address
|
||||
else:
|
||||
self.server_ips[serv_id] = self._server_ip(server, net_name)
|
||||
self.server_fixed_ips[serv_id] = self._server_ip(server, net_name)
|
||||
self.assertTrue(self.servers_keypairs)
|
||||
return server
|
||||
|
||||
def _server_ip(self, server, net_name):
|
||||
return server['addresses'][net_name][0]['addr']
|
||||
|
||||
def _create_servers(self):
|
||||
for count in range(2):
|
||||
self._create_server(name=("server%s" % (count + 1)))
|
||||
self.assertEqual(len(self.servers_keypairs), 2)
|
||||
|
||||
def _start_servers(self):
|
||||
"""Start two hardcoded named servers: server1 & server2
|
||||
|
||||
1. SSH to the instance
|
||||
2. Start two http backends listening on ports 80 and 88 respectively
|
||||
"""
|
||||
for server_id, ip in six.iteritems(self.server_ips):
|
||||
private_key = self.servers_keypairs[server_id]['private_key']
|
||||
# server = self.servers_client.show_server(server_id)['server']
|
||||
# server['name'] is not 'server1' as 2015-12 due to upstream change
|
||||
# server_name = server['name']
|
||||
username = CONF.validation.image_ssh_user
|
||||
ssh_client = self.get_remote_client(
|
||||
server_or_ip=ip,
|
||||
private_key=private_key)
|
||||
|
||||
# Write a backend's response into a file
|
||||
resp = ('echo -ne "HTTP/1.1 200 OK\r\nContent-Length: 7\r\n'
|
||||
'Connection: close\r\nContent-Type: text/html; '
|
||||
'charset=UTF-8\r\n\r\n%s"; cat >/dev/null')
|
||||
|
||||
with tempfile.NamedTemporaryFile() as script:
|
||||
script.write(resp % 'server1')
|
||||
script.flush()
|
||||
with tempfile.NamedTemporaryFile() as key:
|
||||
key.write(private_key)
|
||||
key.flush()
|
||||
commands.copy_file_to_host(script.name,
|
||||
"/tmp/script1",
|
||||
ip,
|
||||
username, key.name)
|
||||
|
||||
# Start netcat
|
||||
start_server = ('while true; do '
|
||||
'sudo nc -ll -p %(port)s -e sh /tmp/%(script)s; '
|
||||
'done > /dev/null &')
|
||||
cmd = start_server % {'port': self.port1,
|
||||
'script': 'script1'}
|
||||
# https://review.openstack.org/#/c/262571/
|
||||
# ssh_client.exec_command(cmd, False)
|
||||
ssh_client.exec_command(cmd)
|
||||
|
||||
if len(self.server_ips) == 1:
|
||||
with tempfile.NamedTemporaryFile() as script:
|
||||
script.write(resp % 'server2')
|
||||
script.flush()
|
||||
with tempfile.NamedTemporaryFile() as key:
|
||||
key.write(private_key)
|
||||
key.flush()
|
||||
commands.copy_file_to_host(script.name,
|
||||
"/tmp/script2", ip,
|
||||
username, key.name)
|
||||
cmd = start_server % {'port': self.port2,
|
||||
'script': 'script2'}
|
||||
# https://review.openstack.org/#/c/262571/
|
||||
# ssh_client.exec_command(cmd, False)
|
||||
ssh_client.exec_command(cmd)
|
||||
|
||||
def _check_connection(self, check_ip, port=80):
|
||||
def try_connect(ip, port):
|
||||
try:
|
||||
resp = urllib2.urlopen("http://{0}:{1}/".format(ip, port))
|
||||
if resp.getcode() == 200:
|
||||
return True
|
||||
return False
|
||||
except IOError:
|
||||
return False
|
||||
except urllib2.HTTPError:
|
||||
return False
|
||||
timeout = CONF.validation.ping_timeout
|
||||
start = time.time()
|
||||
while not try_connect(check_ip, port):
|
||||
if (time.time() - start) > timeout:
|
||||
message = "Timed out trying to connect to %s" % check_ip
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
def _create_pool(self):
|
||||
"""Create a pool with ROUND_ROBIN algorithm."""
|
||||
pool_name = data_utils.rand_name('pool-')
|
||||
pool = self.lbv1_client.create_pool(
|
||||
pool_name,
|
||||
lb_method='ROUND_ROBIN',
|
||||
protocol='HTTP',
|
||||
subnet_id=self.subnet.id)['pool']
|
||||
self.pool = net_resources.DeletablePool(client=self.lbv1_client,
|
||||
**pool)
|
||||
self.assertTrue(self.pool)
|
||||
return self.pool
|
||||
|
||||
def _create_vip(self, pool_id, **kwargs):
|
||||
result = self.lbv1_client.create_vip(pool_id, **kwargs)
|
||||
vip = net_resources.DeletableVip(client=self.lbv1_client,
|
||||
**result['vip'])
|
||||
return vip
|
||||
|
||||
def _create_member(self, protocol_port, pool_id, ip_version=4, **kwargs):
|
||||
result = self.lbv1_client.create_member(protocol_port, pool_id,
|
||||
ip_version, **kwargs)
|
||||
member = net_resources.DeletableMember(client=self.lbv1_client,
|
||||
**result['member'])
|
||||
return member
|
||||
|
||||
def _create_members(self):
|
||||
"""Create two members.
|
||||
|
||||
In case there is only one server, create both members with the same ip
|
||||
but with different ports to listen on.
|
||||
"""
|
||||
|
||||
for server_id, ip in six.iteritems(self.server_fixed_ips):
|
||||
if len(self.server_fixed_ips) == 1:
|
||||
member1 = self._create_member(address=ip,
|
||||
protocol_port=self.port1,
|
||||
pool_id=self.pool.id)
|
||||
member2 = self._create_member(address=ip,
|
||||
protocol_port=self.port2,
|
||||
pool_id=self.pool.id)
|
||||
self.members.extend([member1, member2])
|
||||
else:
|
||||
member = self._create_member(address=ip,
|
||||
protocol_port=self.port1,
|
||||
pool_id=self.pool.id)
|
||||
self.members.append(member)
|
||||
self.assertTrue(self.members)
|
||||
|
||||
def _assign_floating_ip_to_vip(self, vip):
|
||||
public_network_id = CONF.network.public_network_id
|
||||
port_id = vip.port_id
|
||||
floating_ip = self.create_floating_ip(vip, public_network_id,
|
||||
port_id=port_id)
|
||||
self.floating_ips.setdefault(vip.id, [])
|
||||
self.floating_ips[vip.id].append(floating_ip)
|
||||
# Check for floating ip status before you check load-balancer
|
||||
self.check_floating_ip_status(floating_ip, "ACTIVE")
|
||||
|
||||
def _create_load_balancer(self):
|
||||
self._create_pool()
|
||||
self._create_members()
|
||||
self.vip = self._create_vip(protocol='HTTP',
|
||||
protocol_port=80,
|
||||
subnet_id=self.subnet.id,
|
||||
pool_id=self.pool.id)
|
||||
self.vip.wait_for_status('ACTIVE')
|
||||
if (CONF.network.public_network_id and not
|
||||
CONF.network.tenant_networks_reachable):
|
||||
self._assign_floating_ip_to_vip(self.vip)
|
||||
self.vip_ip = self.floating_ips[
|
||||
self.vip.id][0]['floating_ip_address']
|
||||
else:
|
||||
self.vip_ip = self.vip.address
|
||||
|
||||
# Currently the ovs-agent is not enforcing security groups on the
|
||||
# vip port - see https://bugs.launchpad.net/neutron/+bug/1163569
|
||||
# However the linuxbridge-agent does, and it is necessary to add a
|
||||
# security group with a rule that allows tcp port 80 to the vip port.
|
||||
self.ports_client.update_port(
|
||||
self.vip.port_id, security_groups=[self.security_group.id])
|
||||
|
||||
def _check_load_balancing(self):
|
||||
"""http to load balancer to check message handled by both servers.
|
||||
|
||||
1. Send NUM requests on the floating ip associated with the VIP
|
||||
2. Check that the requests are shared between the two servers
|
||||
"""
|
||||
|
||||
self._check_connection(self.vip_ip)
|
||||
self._send_requests(self.vip_ip, ["server1", "server2"])
|
||||
|
||||
def _send_requests(self, vip_ip, servers):
|
||||
counters = dict.fromkeys(servers, 0)
|
||||
for i in range(self.num):
|
||||
try:
|
||||
server = urllib2.urlopen("http://{0}/".format(vip_ip)).read()
|
||||
counters[server] += 1
|
||||
# HTTP exception means fail of server, so don't increase counter
|
||||
# of success and continue connection tries
|
||||
except urllib2.HTTPError:
|
||||
continue
|
||||
# Assert that each member of the pool gets balanced at least once
|
||||
for member, counter in six.iteritems(counters):
|
||||
self.assertGreater(counter, 0, 'Member %s never balanced' % member)
|
||||
|
||||
@test.idempotent_id('e81b5af1-d854-4e16-9d2d-16187bdf1334')
|
||||
@test.services('compute', 'network')
|
||||
def test_load_balancer_basic(self):
|
||||
self._create_server('server1')
|
||||
self._start_servers()
|
||||
self._create_load_balancer()
|
||||
self._check_load_balancing()
|
28
vmware_nsx_tempest/tests/test_vmware_nsx_tempest.py
Normal file
28
vmware_nsx_tempest/tests/test_vmware_nsx_tempest.py
Normal file
@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
test_vmware_nsx_tempest
|
||||
----------------------------------
|
||||
|
||||
Tests for `vmware_nsx_tempest` module.
|
||||
"""
|
||||
|
||||
from vmware_nsx_tempest.tests import base
|
||||
|
||||
|
||||
class TestVmware_nsx_tempest(base.TestCase):
|
||||
|
||||
def test_something(self):
|
||||
pass
|
Loading…
Reference in New Issue
Block a user