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:
Alex Kang 2016-01-12 21:56:47 -08:00
parent 8785d5fdde
commit 00c98dce02
36 changed files with 5722 additions and 0 deletions

View File

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

View 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.

View 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()

View 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)

View 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"),
]

View 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)]

View 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

View File

View 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

View 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

View 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

View 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

View 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}

View 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']])

View 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

View File

View 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."""

View File

@ -0,0 +1,6 @@
Placeholder for NSX-v plugin specific automated tests
directory:
nsxv/
api/
scenario/
scale/

View File

@ -0,0 +1 @@
Placeholder for nsxv neutron plugin specific API tests.

View 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 {}

View 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)

View 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)

View 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)

View 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)

View 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)

View 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

View 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'])

View 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)

View 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

View 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)

View 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")

View File

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

View 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