Add fullstack testing for neutron-fwaas
Add a fullstack testing framework for neutron-fwaas. This is a basic first pass just to get the framework deployed; detailed non-plagiaristic tests will be added after. For more information on what fullstack testing is, see: http://git.openstack.org/cgit/openstack/neutron/tree/TESTING.rst#n213 Includes definition of fullstack job in new zuul syntax. Co-Authored-By: Reedip Banerjee <reedip14@gmail.com> Change-Id: I16cd2432606ef9aa6b7bf3e08efe82882e5585d9
This commit is contained in:
parent
9346ced4b0
commit
1f971199f1
32
.zuul.yaml
32
.zuul.yaml
@ -24,3 +24,35 @@
|
||||
- openstack/neutron-dynamic-routing
|
||||
- openstack/networking-l2gw
|
||||
- openstack/tap-as-a-service
|
||||
|
||||
- job:
|
||||
name: neutron-fwaas-fullstack
|
||||
parent: legacy-dsvm-base
|
||||
run: playbooks/neutron-fwaas-fullstack/run.yaml
|
||||
post-run: playbooks/neutron-fwaas-fullstack/post.yaml
|
||||
timeout: 7800
|
||||
required-projects:
|
||||
- openstack-infra/devstack-gate
|
||||
- openstack/neutron
|
||||
irrelevant-files:
|
||||
- ^.*\.rst$
|
||||
- ^doc/.*$
|
||||
- ^neutron/locale/.*$
|
||||
- ^releasenotes/.*$
|
||||
|
||||
|
||||
- job:
|
||||
name: neutron-fwaas-fullstack-python35
|
||||
parent: legacy-dsvm-base
|
||||
run: playbooks/legacy/neutron-fullstack-python35/run.yaml
|
||||
post-run: playbooks/legacy/neutron-fullstack-python35/post.yaml
|
||||
timeout: 7800
|
||||
required-projects:
|
||||
- openstack-infra/devstack-gate
|
||||
- openstack/neutron
|
||||
irrelevant-files:
|
||||
- ^.*\.rst$
|
||||
- ^doc/.*$
|
||||
- ^neutron/locale/.*$
|
||||
- ^releasenotes/.*$
|
||||
voting: false
|
||||
|
@ -1,3 +1,3 @@
|
||||
The files in this directory are intended for use by the
|
||||
infra jobs that run the various functional test
|
||||
suite in the gate for the neutron-fwaas repo.
|
||||
Neutron infra jobs that run the various functional test
|
||||
suites in the gate.
|
||||
|
@ -4,4 +4,7 @@
|
||||
# This file should be owned by (and only-writeable by) the root user
|
||||
|
||||
[Filters]
|
||||
#none currently
|
||||
# enable ping from namespace
|
||||
ping_filter: CommandFilter, ping, root
|
||||
ping6_filter: CommandFilter, ping6, root
|
||||
ping_kill: KillFilter, root, ping, -2
|
||||
|
0
neutron_fwaas/tests/contrib/gate_hook.sh
Executable file → Normal file
0
neutron_fwaas/tests/contrib/gate_hook.sh
Executable file → Normal file
4
neutron_fwaas/tests/contrib/hooks/iptables_verify
Normal file
4
neutron_fwaas/tests/contrib/hooks/iptables_verify
Normal file
@ -0,0 +1,4 @@
|
||||
[[post-config|/etc/neutron/neutron.conf]]
|
||||
|
||||
[AGENT]
|
||||
debug_iptables_rules=True
|
46
neutron_fwaas/tests/contrib/post_test_hook.sh
Executable file → Normal file
46
neutron_fwaas/tests/contrib/post_test_hook.sh
Executable file → Normal file
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -xe
|
||||
|
||||
@ -7,12 +7,14 @@ NEUTRON_DIR="$BASE/new/neutron"
|
||||
TEMPEST_DIR="$BASE/new/tempest"
|
||||
SCRIPTS_DIR="/usr/os-testr-env/bin"
|
||||
|
||||
venv=${1:-"dsvm-functional"}
|
||||
|
||||
function generate_testr_results {
|
||||
# Give job user rights to access tox logs
|
||||
sudo -H -u $owner chmod o+rw .
|
||||
sudo -H -u $owner chmod o+rw -R .testrepository
|
||||
if [ -f ".testrepository/0" ] ; then
|
||||
.tox/dsvm-functional/bin/subunit-1to2 < .testrepository/0 > ./testrepository.subunit
|
||||
.tox/$venv/bin/subunit-1to2 < .testrepository/0 > ./testrepository.subunit
|
||||
$SCRIPTS_DIR/subunit2html ./testrepository.subunit testr_results.html
|
||||
gzip -9 ./testrepository.subunit
|
||||
gzip -9 ./testr_results.html
|
||||
@ -20,29 +22,29 @@ function generate_testr_results {
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function dsvm_functional_prep_func {
|
||||
:
|
||||
}
|
||||
|
||||
if [[ "$venv" == dsvm-functional* ]] || [[ "$venv" == dsvm-fullstack* ]]
|
||||
then
|
||||
owner=stack
|
||||
sudo_env=
|
||||
# Set owner permissions according to job's requirements.
|
||||
cd $FWAAS_DIR
|
||||
sudo chown -R $owner:stack $FWAAS_DIR
|
||||
sudo chown -R $owner:stack $NEUTRON_DIR
|
||||
# Prep the environment according to job's requirements.
|
||||
$prep_func
|
||||
|
||||
owner=stack
|
||||
prep_func="dsvm_functional_prep_func"
|
||||
# Run tests
|
||||
echo "Running neutron-fwaas $venv test suite"
|
||||
set +e
|
||||
sudo -H -u $owner $sudo_env tox -e $venv
|
||||
testr_exit_code=$?
|
||||
set -e
|
||||
|
||||
# Set owner permissions according to job's requirements.
|
||||
cd $FWAAS_DIR
|
||||
sudo chown -R $owner:stack $FWAAS_DIR
|
||||
sudo chown -R $owner:stack $NEUTRON_DIR
|
||||
# Prep the environment according to job's requirements.
|
||||
$prep_func
|
||||
|
||||
# Run tests
|
||||
echo "Running neutron dsvm-functional test suite"
|
||||
set +e
|
||||
sudo -H -u $owner tox -e dsvm-functional
|
||||
testr_exit_code=$?
|
||||
set -e
|
||||
|
||||
# Collect and parse results
|
||||
generate_testr_results
|
||||
exit $testr_exit_code
|
||||
# Collect and parse results
|
||||
generate_testr_results
|
||||
exit $testr_exit_code
|
||||
fi
|
||||
|
1
neutron_fwaas/tests/fullstack/README
Normal file
1
neutron_fwaas/tests/fullstack/README
Normal file
@ -0,0 +1 @@
|
||||
Please see neutron/TESTING.rst for more information about what Fullstack tests are.
|
16
neutron_fwaas/tests/fullstack/__init__.py
Normal file
16
neutron_fwaas/tests/fullstack/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
# 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 neutron.common import eventlet_utils
|
||||
|
||||
|
||||
eventlet_utils.monkey_patch()
|
67
neutron_fwaas/tests/fullstack/base.py
Normal file
67
neutron_fwaas/tests/fullstack/base.py
Normal file
@ -0,0 +1,67 @@
|
||||
# Copyright 2015 Red Hat, Inc.
|
||||
#
|
||||
# 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 oslo_config import cfg
|
||||
|
||||
from neutron.tests import base as tests_base
|
||||
from neutron.tests.fullstack.resources import client as client_resource
|
||||
from neutron.tests import tools
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
|
||||
# This is the directory from which infra fetches log files for fullstack tests
|
||||
DEFAULT_LOG_DIR = os.path.join('/opt/stack/logs/neutron-fwaas/',
|
||||
'dsvm-fullstack-logs')
|
||||
|
||||
|
||||
class BaseFullStackTestCase(testlib_api.MySQLTestCaseMixin,
|
||||
testlib_api.SqlTestCase):
|
||||
"""Base test class for full-stack tests."""
|
||||
|
||||
BUILD_WITH_MIGRATIONS = True
|
||||
|
||||
def setUp(self, environment):
|
||||
super(BaseFullStackTestCase, self).setUp()
|
||||
|
||||
tests_base.setup_test_logging(
|
||||
cfg.CONF, DEFAULT_LOG_DIR, '%s.txt' % self.get_name())
|
||||
|
||||
# NOTE(zzzeek): the opportunistic DB fixtures have built for
|
||||
# us a per-test (or per-process) database. Set the URL of this
|
||||
# database in CONF as the full stack tests need to actually run a
|
||||
# neutron server against this database.
|
||||
_orig_db_url = cfg.CONF.database.connection
|
||||
cfg.CONF.set_override(
|
||||
'connection', str(self.engine.url), group='database')
|
||||
self.addCleanup(
|
||||
cfg.CONF.set_override,
|
||||
"connection", _orig_db_url, group="database"
|
||||
)
|
||||
|
||||
# NOTE(ihrachys): seed should be reset before environment fixture below
|
||||
# since the latter starts services that may rely on generated port
|
||||
# numbers
|
||||
tools.reset_random_seed()
|
||||
self.environment = environment
|
||||
self.environment.test_name = self.get_name()
|
||||
self.useFixture(self.environment)
|
||||
self.client = self.environment.neutron_server.client
|
||||
self.safe_client = self.useFixture(
|
||||
client_resource.ClientFixture(self.client))
|
||||
|
||||
def get_name(self):
|
||||
class_name, test_name = self.id().split(".")[-2:]
|
||||
return "%s.%s" % (class_name, test_name)
|
0
neutron_fwaas/tests/fullstack/resources/__init__.py
Normal file
0
neutron_fwaas/tests/fullstack/resources/__init__.py
Normal file
247
neutron_fwaas/tests/fullstack/resources/client.py
Normal file
247
neutron_fwaas/tests/fullstack/resources/client.py
Normal file
@ -0,0 +1,247 @@
|
||||
# Copyright (c) 2015 Thales Services SAS
|
||||
#
|
||||
# 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 functools
|
||||
|
||||
import netaddr
|
||||
|
||||
import fixtures
|
||||
from neutron_lib import constants
|
||||
from neutronclient.common import exceptions
|
||||
|
||||
from neutron.common import utils
|
||||
from neutron.extensions import portbindings
|
||||
|
||||
|
||||
def _safe_method(f):
|
||||
@functools.wraps(f)
|
||||
def delete(*args, **kwargs):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except exceptions.NotFound:
|
||||
pass
|
||||
return delete
|
||||
|
||||
|
||||
class ClientFixture(fixtures.Fixture):
|
||||
"""Manage and cleanup neutron resources."""
|
||||
|
||||
def __init__(self, client):
|
||||
super(ClientFixture, self).__init__()
|
||||
self.client = client
|
||||
|
||||
def _create_resource(self, resource_type, spec):
|
||||
create = getattr(self.client, 'create_%s' % resource_type)
|
||||
delete = getattr(self.client, 'delete_%s' % resource_type)
|
||||
|
||||
body = {resource_type: spec}
|
||||
resp = create(body=body)
|
||||
data = resp[resource_type]
|
||||
self.addCleanup(_safe_method(delete), data['id'])
|
||||
return data
|
||||
|
||||
def create_router(self, tenant_id, name=None, ha=False,
|
||||
external_network=None):
|
||||
resource_type = 'router'
|
||||
|
||||
name = name or utils.get_rand_name(prefix=resource_type)
|
||||
spec = {'tenant_id': tenant_id, 'name': name, 'ha': ha}
|
||||
if external_network:
|
||||
spec['external_gateway_info'] = {"network_id": external_network}
|
||||
|
||||
return self._create_resource(resource_type, spec)
|
||||
|
||||
def create_network(self, tenant_id, name=None, external=False):
|
||||
resource_type = 'network'
|
||||
|
||||
name = name or utils.get_rand_name(prefix=resource_type)
|
||||
spec = {'tenant_id': tenant_id, 'name': name}
|
||||
spec['router:external'] = external
|
||||
return self._create_resource(resource_type, spec)
|
||||
|
||||
def create_subnet(self, tenant_id, network_id,
|
||||
cidr, gateway_ip=None, name=None, enable_dhcp=True,
|
||||
ipv6_address_mode='slaac', ipv6_ra_mode='slaac'):
|
||||
resource_type = 'subnet'
|
||||
|
||||
name = name or utils.get_rand_name(prefix=resource_type)
|
||||
ip_version = netaddr.IPNetwork(cidr).version
|
||||
spec = {'tenant_id': tenant_id, 'network_id': network_id, 'name': name,
|
||||
'cidr': cidr, 'enable_dhcp': enable_dhcp,
|
||||
'ip_version': ip_version}
|
||||
if ip_version == constants.IP_VERSION_6:
|
||||
spec['ipv6_address_mode'] = ipv6_address_mode
|
||||
spec['ipv6_ra_mode'] = ipv6_ra_mode
|
||||
|
||||
if gateway_ip:
|
||||
spec['gateway_ip'] = gateway_ip
|
||||
|
||||
return self._create_resource(resource_type, spec)
|
||||
|
||||
def create_port(self, tenant_id, network_id, hostname=None,
|
||||
qos_policy_id=None, **kwargs):
|
||||
spec = {
|
||||
'network_id': network_id,
|
||||
'tenant_id': tenant_id,
|
||||
}
|
||||
spec.update(kwargs)
|
||||
if hostname is not None:
|
||||
spec[portbindings.HOST_ID] = hostname
|
||||
if qos_policy_id:
|
||||
spec['qos_policy_id'] = qos_policy_id
|
||||
return self._create_resource('port', spec)
|
||||
|
||||
def create_floatingip(self, tenant_id, floating_network_id,
|
||||
fixed_ip_address, port_id):
|
||||
spec = {
|
||||
'floating_network_id': floating_network_id,
|
||||
'tenant_id': tenant_id,
|
||||
'fixed_ip_address': fixed_ip_address,
|
||||
'port_id': port_id
|
||||
}
|
||||
|
||||
return self._create_resource('floatingip', spec)
|
||||
|
||||
def add_router_interface(self, router_id, subnet_id):
|
||||
body = {'subnet_id': subnet_id}
|
||||
router_interface_info = self.client.add_interface_router(
|
||||
router=router_id, body=body)
|
||||
self.addCleanup(_safe_method(self.client.remove_interface_router),
|
||||
router=router_id, body=body)
|
||||
return router_interface_info
|
||||
|
||||
def create_qos_policy(self, tenant_id, name, description, shared):
|
||||
policy = self.client.create_qos_policy(
|
||||
body={'policy': {'name': name,
|
||||
'description': description,
|
||||
'shared': shared,
|
||||
'tenant_id': tenant_id}})
|
||||
|
||||
def detach_and_delete_policy():
|
||||
qos_policy_id = policy['policy']['id']
|
||||
ports_with_policy = self.client.list_ports(
|
||||
qos_policy_id=qos_policy_id)['ports']
|
||||
for port in ports_with_policy:
|
||||
self.client.update_port(
|
||||
port['id'],
|
||||
body={'port': {'qos_policy_id': None}})
|
||||
self.client.delete_qos_policy(qos_policy_id)
|
||||
|
||||
# NOTE: We'll need to add support for detaching from network once
|
||||
# create_network() supports qos_policy_id.
|
||||
self.addCleanup(_safe_method(detach_and_delete_policy))
|
||||
|
||||
return policy['policy']
|
||||
|
||||
def create_bandwidth_limit_rule(self, tenant_id, qos_policy_id, limit=None,
|
||||
burst=None):
|
||||
rule = {'tenant_id': tenant_id}
|
||||
if limit:
|
||||
rule['max_kbps'] = limit
|
||||
if burst:
|
||||
rule['max_burst_kbps'] = burst
|
||||
rule = self.client.create_bandwidth_limit_rule(
|
||||
policy=qos_policy_id,
|
||||
body={'bandwidth_limit_rule': rule})
|
||||
|
||||
self.addCleanup(_safe_method(self.client.delete_bandwidth_limit_rule),
|
||||
rule['bandwidth_limit_rule']['id'],
|
||||
qos_policy_id)
|
||||
|
||||
return rule['bandwidth_limit_rule']
|
||||
|
||||
def create_dscp_marking_rule(self, tenant_id, qos_policy_id, dscp_mark=0):
|
||||
rule = {'tenant_id': tenant_id}
|
||||
if dscp_mark:
|
||||
rule['dscp_mark'] = dscp_mark
|
||||
rule = self.client.create_dscp_marking_rule(
|
||||
policy=qos_policy_id,
|
||||
body={'dscp_marking_rule': rule})
|
||||
|
||||
self.addCleanup(_safe_method(self.client.delete_dscp_marking_rule),
|
||||
rule['dscp_marking_rule']['id'],
|
||||
qos_policy_id)
|
||||
|
||||
return rule['dscp_marking_rule']
|
||||
|
||||
def create_trunk(self, tenant_id, port_id, name=None,
|
||||
admin_state_up=None, sub_ports=None):
|
||||
"""Create a trunk via API.
|
||||
|
||||
:param tenant_id: ID of the tenant.
|
||||
:param port_id: Parent port of trunk.
|
||||
:param name: Name of the trunk.
|
||||
:param admin_state_up: Admin state of the trunk.
|
||||
:param sub_ports: List of subport dictionaries in format
|
||||
{'port_id': <ID of neutron port for subport>,
|
||||
'segmentation_type': 'vlan',
|
||||
'segmentation_id': <VLAN tag>}
|
||||
|
||||
:return: Dictionary with trunk's data returned from Neutron API.
|
||||
"""
|
||||
spec = {
|
||||
'port_id': port_id,
|
||||
'tenant_id': tenant_id,
|
||||
}
|
||||
if name is not None:
|
||||
spec['name'] = name
|
||||
if sub_ports is not None:
|
||||
spec['sub_ports'] = sub_ports
|
||||
if admin_state_up is not None:
|
||||
spec['admin_state_up'] = admin_state_up
|
||||
|
||||
trunk = self.client.create_trunk({'trunk': spec})['trunk']
|
||||
|
||||
if sub_ports:
|
||||
self.addCleanup(
|
||||
_safe_method(self.trunk_remove_subports),
|
||||
tenant_id, trunk['id'], trunk['sub_ports'])
|
||||
self.addCleanup(_safe_method(self.client.delete_trunk), trunk['id'])
|
||||
|
||||
return trunk
|
||||
|
||||
def trunk_add_subports(self, tenant_id, trunk_id, sub_ports):
|
||||
"""Add subports to the trunk.
|
||||
|
||||
:param tenant_id: ID of the tenant.
|
||||
:param trunk_id: ID of the trunk.
|
||||
:param sub_ports: List of subport dictionaries to be added in format
|
||||
{'port_id': <ID of neutron port for subport>,
|
||||
'segmentation_type': 'vlan',
|
||||
'segmentation_id': <VLAN tag>}
|
||||
"""
|
||||
spec = {
|
||||
'tenant_id': tenant_id,
|
||||
'sub_ports': sub_ports,
|
||||
}
|
||||
trunk = self.client.trunk_add_subports(trunk_id, spec)
|
||||
|
||||
sub_ports_to_remove = [
|
||||
sub_port for sub_port in trunk['sub_ports']
|
||||
if sub_port in sub_ports]
|
||||
self.addCleanup(
|
||||
_safe_method(self.trunk_remove_subports), tenant_id, trunk_id,
|
||||
sub_ports_to_remove)
|
||||
|
||||
def trunk_remove_subports(self, tenant_id, trunk_id, sub_ports):
|
||||
"""Remove subports from the trunk.
|
||||
|
||||
:param trunk_id: ID of the trunk.
|
||||
:param sub_ports: List of subport port IDs.
|
||||
"""
|
||||
spec = {
|
||||
'tenant_id': tenant_id,
|
||||
'sub_ports': sub_ports,
|
||||
}
|
||||
return self.client.trunk_remove_subports(trunk_id, spec)
|
294
neutron_fwaas/tests/fullstack/resources/config.py
Normal file
294
neutron_fwaas/tests/fullstack/resources/config.py
Normal file
@ -0,0 +1,294 @@
|
||||
# Copyright 2015 Red Hat, Inc.
|
||||
#
|
||||
# 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 tempfile
|
||||
|
||||
import fixtures
|
||||
from neutron_lib import constants
|
||||
|
||||
from neutron.common import utils
|
||||
from neutron.plugins.ml2.extensions import qos as qos_ext
|
||||
from neutron.tests.common import config_fixtures
|
||||
from neutron.tests.common.exclusive_resources import port
|
||||
from neutron.tests.common import helpers as c_helpers
|
||||
|
||||
|
||||
class ConfigFixture(fixtures.Fixture):
|
||||
"""A fixture that holds an actual Neutron configuration.
|
||||
|
||||
Note that 'self.config' is intended to only be updated once, during
|
||||
the constructor, so if this fixture is re-used (setUp is called twice),
|
||||
then the dynamic configuration values won't change. The correct usage
|
||||
is initializing a new instance of the class.
|
||||
"""
|
||||
def __init__(self, env_desc, host_desc, temp_dir, base_filename):
|
||||
super(ConfigFixture, self).__init__()
|
||||
self.config = config_fixtures.ConfigDict()
|
||||
self.env_desc = env_desc
|
||||
self.host_desc = host_desc
|
||||
self.temp_dir = temp_dir
|
||||
self.base_filename = base_filename
|
||||
|
||||
def _setUp(self):
|
||||
cfg_fixture = config_fixtures.ConfigFileFixture(
|
||||
self.base_filename, self.config, self.temp_dir)
|
||||
self.useFixture(cfg_fixture)
|
||||
self.filename = cfg_fixture.filename
|
||||
|
||||
|
||||
class NeutronConfigFixture(ConfigFixture):
|
||||
|
||||
def __init__(self, env_desc, host_desc, temp_dir,
|
||||
connection, rabbitmq_environment):
|
||||
super(NeutronConfigFixture, self).__init__(
|
||||
env_desc, host_desc, temp_dir, base_filename='neutron.conf')
|
||||
|
||||
service_plugins = ['router', 'trunk']
|
||||
if env_desc.qos:
|
||||
service_plugins.append('qos')
|
||||
|
||||
self.config.update({
|
||||
'DEFAULT': {
|
||||
'host': self._generate_host(),
|
||||
'state_path': self._generate_state_path(self.temp_dir),
|
||||
'api_paste_config': self._generate_api_paste(),
|
||||
'core_plugin': 'ml2',
|
||||
'service_plugins': ','.join(service_plugins),
|
||||
'auth_strategy': 'noauth',
|
||||
'debug': 'True',
|
||||
'transport_url':
|
||||
'rabbit://%(user)s:%(password)s@%(host)s:5672/%(vhost)s' %
|
||||
{'user': rabbitmq_environment.user,
|
||||
'password': rabbitmq_environment.password,
|
||||
'host': rabbitmq_environment.host,
|
||||
'vhost': rabbitmq_environment.vhost},
|
||||
},
|
||||
'database': {
|
||||
'connection': connection,
|
||||
},
|
||||
'oslo_concurrency': {
|
||||
'lock_path': '$state_path/lock',
|
||||
},
|
||||
'oslo_policy': {
|
||||
'policy_file': self._generate_policy_json(),
|
||||
},
|
||||
})
|
||||
|
||||
def _setUp(self):
|
||||
self.config['DEFAULT'].update({
|
||||
'bind_port': self.useFixture(
|
||||
port.ExclusivePort(constants.PROTO_NAME_TCP)).port
|
||||
})
|
||||
super(NeutronConfigFixture, self)._setUp()
|
||||
|
||||
def _generate_host(self):
|
||||
return utils.get_rand_name(prefix='host-')
|
||||
|
||||
def _generate_state_path(self, temp_dir):
|
||||
# Assume that temp_dir will be removed by the caller
|
||||
self.state_path = tempfile.mkdtemp(prefix='state_path', dir=temp_dir)
|
||||
return self.state_path
|
||||
|
||||
def _generate_api_paste(self):
|
||||
return c_helpers.find_sample_file('api-paste.ini')
|
||||
|
||||
def _generate_policy_json(self):
|
||||
return c_helpers.find_sample_file('policy.json')
|
||||
|
||||
|
||||
class ML2ConfigFixture(ConfigFixture):
|
||||
|
||||
def __init__(self, env_desc, host_desc, temp_dir, tenant_network_types):
|
||||
super(ML2ConfigFixture, self).__init__(
|
||||
env_desc, host_desc, temp_dir, base_filename='ml2_conf.ini')
|
||||
|
||||
mechanism_drivers = self.env_desc.mech_drivers
|
||||
if self.env_desc.l2_pop:
|
||||
mechanism_drivers += ',l2population'
|
||||
|
||||
self.config.update({
|
||||
'ml2': {
|
||||
'tenant_network_types': tenant_network_types,
|
||||
'mechanism_drivers': mechanism_drivers,
|
||||
},
|
||||
'ml2_type_vlan': {
|
||||
'network_vlan_ranges': 'physnet1:1000:2999',
|
||||
},
|
||||
'ml2_type_gre': {
|
||||
'tunnel_id_ranges': '1:1000',
|
||||
},
|
||||
'ml2_type_vxlan': {
|
||||
'vni_ranges': '1001:2000',
|
||||
},
|
||||
})
|
||||
|
||||
if env_desc.qos:
|
||||
self.config['ml2']['extension_drivers'] =\
|
||||
qos_ext.QOS_EXT_DRIVER_ALIAS
|
||||
|
||||
|
||||
class OVSConfigFixture(ConfigFixture):
|
||||
|
||||
def __init__(self, env_desc, host_desc, temp_dir, local_ip):
|
||||
super(OVSConfigFixture, self).__init__(
|
||||
env_desc, host_desc, temp_dir,
|
||||
base_filename='openvswitch_agent.ini')
|
||||
|
||||
self.tunneling_enabled = self.env_desc.tunneling_enabled
|
||||
self.config.update({
|
||||
'ovs': {
|
||||
'local_ip': local_ip,
|
||||
'integration_bridge': self._generate_integration_bridge(),
|
||||
'of_interface': host_desc.of_interface,
|
||||
'ovsdb_interface': host_desc.ovsdb_interface,
|
||||
},
|
||||
'securitygroup': {
|
||||
'firewall_driver': 'noop',
|
||||
},
|
||||
'agent': {
|
||||
'l2_population': str(self.env_desc.l2_pop),
|
||||
'arp_responder': str(self.env_desc.arp_responder),
|
||||
}
|
||||
})
|
||||
|
||||
if self.tunneling_enabled:
|
||||
self.config['agent'].update({
|
||||
'tunnel_types': self.env_desc.network_type})
|
||||
self.config['ovs'].update({
|
||||
'tunnel_bridge': self._generate_tunnel_bridge(),
|
||||
'int_peer_patch_port': self._generate_int_peer(),
|
||||
'tun_peer_patch_port': self._generate_tun_peer()})
|
||||
else:
|
||||
self.config['ovs']['bridge_mappings'] = (
|
||||
self._generate_bridge_mappings())
|
||||
|
||||
if env_desc.qos:
|
||||
self.config['agent']['extensions'] = 'qos'
|
||||
|
||||
def _setUp(self):
|
||||
if self.config['ovs']['of_interface'] == 'native':
|
||||
self.config['ovs'].update({
|
||||
'of_listen_port': self.useFixture(
|
||||
port.ExclusivePort(constants.PROTO_NAME_TCP)).port
|
||||
})
|
||||
super(OVSConfigFixture, self)._setUp()
|
||||
|
||||
def _generate_bridge_mappings(self):
|
||||
return 'physnet1:%s' % utils.get_rand_device_name(prefix='br-eth')
|
||||
|
||||
def _generate_integration_bridge(self):
|
||||
return utils.get_rand_device_name(prefix='br-int')
|
||||
|
||||
def _generate_tunnel_bridge(self):
|
||||
return utils.get_rand_device_name(prefix='br-tun')
|
||||
|
||||
def _generate_int_peer(self):
|
||||
return utils.get_rand_device_name(prefix='patch-tun')
|
||||
|
||||
def _generate_tun_peer(self):
|
||||
return utils.get_rand_device_name(prefix='patch-int')
|
||||
|
||||
def get_br_int_name(self):
|
||||
return self.config.ovs.integration_bridge
|
||||
|
||||
def get_br_phys_name(self):
|
||||
return self.config.ovs.bridge_mappings.split(':')[1]
|
||||
|
||||
def get_br_tun_name(self):
|
||||
return self.config.ovs.tunnel_bridge
|
||||
|
||||
|
||||
class LinuxBridgeConfigFixture(ConfigFixture):
|
||||
|
||||
def __init__(self, env_desc, host_desc, temp_dir, local_ip,
|
||||
physical_device_name):
|
||||
super(LinuxBridgeConfigFixture, self).__init__(
|
||||
env_desc, host_desc, temp_dir,
|
||||
base_filename="linuxbridge_agent.ini"
|
||||
)
|
||||
self.config.update({
|
||||
'VXLAN': {
|
||||
'enable_vxlan': str(self.env_desc.tunneling_enabled),
|
||||
'local_ip': local_ip,
|
||||
'l2_population': str(self.env_desc.l2_pop),
|
||||
}
|
||||
})
|
||||
if env_desc.qos:
|
||||
self.config.update({
|
||||
'AGENT': {
|
||||
'extensions': 'qos'
|
||||
}
|
||||
})
|
||||
if self.env_desc.tunneling_enabled:
|
||||
self.config.update({
|
||||
'LINUX_BRIDGE': {
|
||||
'bridge_mappings': self._generate_bridge_mappings(
|
||||
physical_device_name
|
||||
)
|
||||
}
|
||||
})
|
||||
else:
|
||||
self.config.update({
|
||||
'LINUX_BRIDGE': {
|
||||
'physical_interface_mappings':
|
||||
self._generate_bridge_mappings(
|
||||
physical_device_name
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
def _generate_bridge_mappings(self, device_name):
|
||||
return 'physnet1:%s' % device_name
|
||||
|
||||
|
||||
class L3ConfigFixture(ConfigFixture):
|
||||
|
||||
def __init__(self, env_desc, host_desc, temp_dir, integration_bridge=None):
|
||||
super(L3ConfigFixture, self).__init__(
|
||||
env_desc, host_desc, temp_dir, base_filename='l3_agent.ini')
|
||||
if host_desc.l2_agent_type == constants.AGENT_TYPE_OVS:
|
||||
self._prepare_config_with_ovs_agent(integration_bridge)
|
||||
elif host_desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE:
|
||||
self._prepare_config_with_linuxbridge_agent()
|
||||
self.config['DEFAULT'].update({
|
||||
'debug': 'True',
|
||||
'test_namespace_suffix': self._generate_namespace_suffix(),
|
||||
})
|
||||
|
||||
def _prepare_config_with_ovs_agent(self, integration_bridge):
|
||||
self.config.update({
|
||||
'DEFAULT': {
|
||||
'interface_driver': ('neutron.agent.linux.interface.'
|
||||
'OVSInterfaceDriver'),
|
||||
'ovs_integration_bridge': integration_bridge,
|
||||
'external_network_bridge': self._generate_external_bridge(),
|
||||
}
|
||||
})
|
||||
|
||||
def _prepare_config_with_linuxbridge_agent(self):
|
||||
self.config.update({
|
||||
'DEFAULT': {
|
||||
'interface_driver': ('neutron.agent.linux.interface.'
|
||||
'BridgeInterfaceDriver'),
|
||||
}
|
||||
})
|
||||
|
||||
def _generate_external_bridge(self):
|
||||
return utils.get_rand_device_name(prefix='br-ex')
|
||||
|
||||
def get_external_bridge(self):
|
||||
return self.config.DEFAULT.external_network_bridge
|
||||
|
||||
def _generate_namespace_suffix(self):
|
||||
return utils.get_rand_name(prefix='test')
|
366
neutron_fwaas/tests/fullstack/resources/environment.py
Normal file
366
neutron_fwaas/tests/fullstack/resources/environment.py
Normal file
@ -0,0 +1,366 @@
|
||||
# Copyright 2015 Red Hat, Inc.
|
||||
#
|
||||
# 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 fixtures
|
||||
from neutron_lib import constants
|
||||
from neutronclient.common import exceptions as nc_exc
|
||||
from oslo_config import cfg
|
||||
|
||||
from neutron.agent.linux import ip_lib
|
||||
from neutron.common import utils as common_utils
|
||||
from neutron.plugins.ml2.drivers.linuxbridge.agent import \
|
||||
linuxbridge_neutron_agent as lb_agent
|
||||
from neutron.tests.common.exclusive_resources import ip_address
|
||||
from neutron.tests.common.exclusive_resources import ip_network
|
||||
from neutron.tests.common import net_helpers
|
||||
from neutron.tests.fullstack.resources import config
|
||||
from neutron.tests.fullstack.resources import process
|
||||
|
||||
|
||||
class EnvironmentDescription(object):
|
||||
"""A set of characteristics of an environment setup.
|
||||
|
||||
Does the setup, as a whole, support tunneling? How about l2pop?
|
||||
"""
|
||||
def __init__(self, network_type='vxlan', l2_pop=True, qos=False,
|
||||
mech_drivers='openvswitch,linuxbridge', arp_responder=False):
|
||||
self.network_type = network_type
|
||||
self.l2_pop = l2_pop
|
||||
self.qos = qos
|
||||
self.network_range = None
|
||||
self.mech_drivers = mech_drivers
|
||||
self.arp_responder = arp_responder
|
||||
|
||||
@property
|
||||
def tunneling_enabled(self):
|
||||
return self.network_type in ('vxlan', 'gre')
|
||||
|
||||
|
||||
class HostDescription(object):
|
||||
"""A set of characteristics of an environment Host.
|
||||
|
||||
What agents should the host spawn? What mode should each agent operate
|
||||
under?
|
||||
"""
|
||||
def __init__(self, l3_agent=False, of_interface='ovs-ofctl',
|
||||
ovsdb_interface='vsctl',
|
||||
l2_agent_type=constants.AGENT_TYPE_OVS):
|
||||
self.l2_agent_type = l2_agent_type
|
||||
self.l3_agent = l3_agent
|
||||
self.of_interface = of_interface
|
||||
self.ovsdb_interface = ovsdb_interface
|
||||
|
||||
|
||||
class Host(fixtures.Fixture):
|
||||
"""The Host class models a physical host running agents, all reporting with
|
||||
the same hostname.
|
||||
|
||||
OpenStack installers or administrators connect compute nodes to the
|
||||
physical tenant network by connecting the provider bridges to their
|
||||
respective physical NICs. Or, if using tunneling, by configuring an
|
||||
IP address on the appropriate physical NIC. The Host class does the same
|
||||
with the connect_* methods.
|
||||
|
||||
TODO(amuller): Add start/stop/restart methods that will start/stop/restart
|
||||
all of the agents on this host. Add a kill method that stops all agents
|
||||
and disconnects the host from other hosts.
|
||||
"""
|
||||
|
||||
def __init__(self, env_desc, host_desc,
|
||||
test_name, neutron_config,
|
||||
central_data_bridge, central_external_bridge):
|
||||
self.env_desc = env_desc
|
||||
self.host_desc = host_desc
|
||||
self.test_name = test_name
|
||||
self.neutron_config = neutron_config
|
||||
self.central_data_bridge = central_data_bridge
|
||||
self.central_external_bridge = central_external_bridge
|
||||
self.host_namespace = None
|
||||
self.agents = {}
|
||||
# we need to cache already created "per network" bridges if linuxbridge
|
||||
# agent is used on host:
|
||||
self.network_bridges = {}
|
||||
|
||||
def _setUp(self):
|
||||
self.local_ip = self.allocate_local_ip()
|
||||
|
||||
if self.host_desc.l2_agent_type == constants.AGENT_TYPE_OVS:
|
||||
self.setup_host_with_ovs_agent()
|
||||
elif self.host_desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE:
|
||||
self.setup_host_with_linuxbridge_agent()
|
||||
if self.host_desc.l3_agent:
|
||||
self.l3_agent = self.useFixture(
|
||||
process.L3AgentFixture(
|
||||
self.env_desc, self.host_desc,
|
||||
self.test_name,
|
||||
self.neutron_config,
|
||||
self.l3_agent_cfg_fixture))
|
||||
|
||||
def setup_host_with_ovs_agent(self):
|
||||
agent_cfg_fixture = config.OVSConfigFixture(
|
||||
self.env_desc, self.host_desc, self.neutron_config.temp_dir,
|
||||
self.local_ip)
|
||||
self.useFixture(agent_cfg_fixture)
|
||||
|
||||
if self.env_desc.tunneling_enabled:
|
||||
self.useFixture(
|
||||
net_helpers.OVSBridgeFixture(
|
||||
agent_cfg_fixture.get_br_tun_name())).bridge
|
||||
self.connect_to_internal_network_via_tunneling()
|
||||
else:
|
||||
br_phys = self.useFixture(
|
||||
net_helpers.OVSBridgeFixture(
|
||||
agent_cfg_fixture.get_br_phys_name())).bridge
|
||||
self.connect_to_internal_network_via_vlans(br_phys)
|
||||
|
||||
self.ovs_agent = self.useFixture(
|
||||
process.OVSAgentFixture(
|
||||
self.env_desc, self.host_desc,
|
||||
self.test_name, self.neutron_config, agent_cfg_fixture))
|
||||
|
||||
if self.host_desc.l3_agent:
|
||||
self.l3_agent_cfg_fixture = self.useFixture(
|
||||
config.L3ConfigFixture(
|
||||
self.env_desc, self.host_desc,
|
||||
self.neutron_config.temp_dir,
|
||||
self.ovs_agent.agent_cfg_fixture.get_br_int_name()))
|
||||
br_ex = self.useFixture(
|
||||
net_helpers.OVSBridgeFixture(
|
||||
self.l3_agent_cfg_fixture.get_external_bridge())).bridge
|
||||
self.connect_to_external_network(br_ex)
|
||||
|
||||
def setup_host_with_linuxbridge_agent(self):
|
||||
#First we need to provide connectivity for agent to prepare proper
|
||||
#bridge mappings in agent's config:
|
||||
self.host_namespace = self.useFixture(
|
||||
net_helpers.NamespaceFixture(prefix="host-")
|
||||
).name
|
||||
|
||||
self.connect_namespace_to_control_network()
|
||||
|
||||
agent_cfg_fixture = config.LinuxBridgeConfigFixture(
|
||||
self.env_desc, self.host_desc,
|
||||
self.neutron_config.temp_dir,
|
||||
self.local_ip,
|
||||
physical_device_name=self.host_port.name
|
||||
)
|
||||
self.useFixture(agent_cfg_fixture)
|
||||
|
||||
self.linuxbridge_agent = self.useFixture(
|
||||
process.LinuxBridgeAgentFixture(
|
||||
self.env_desc, self.host_desc,
|
||||
self.test_name, self.neutron_config, agent_cfg_fixture,
|
||||
namespace=self.host_namespace
|
||||
)
|
||||
)
|
||||
|
||||
if self.host_desc.l3_agent:
|
||||
self.l3_agent_cfg_fixture = self.useFixture(
|
||||
config.L3ConfigFixture(
|
||||
self.env_desc, self.host_desc,
|
||||
self.neutron_config.temp_dir))
|
||||
|
||||
def _connect_ovs_port(self, cidr_address):
|
||||
ovs_device = self.useFixture(
|
||||
net_helpers.OVSPortFixture(
|
||||
bridge=self.central_data_bridge,
|
||||
namespace=self.host_namespace)).port
|
||||
# NOTE: This sets an IP address on the host's root namespace
|
||||
# which is cleaned up when the device is deleted.
|
||||
ovs_device.addr.add(cidr_address)
|
||||
return ovs_device
|
||||
|
||||
def connect_namespace_to_control_network(self):
|
||||
self.host_port = self._connect_ovs_port(
|
||||
common_utils.ip_to_cidr(self.local_ip, 24)
|
||||
)
|
||||
self.host_port.link.set_up()
|
||||
|
||||
def connect_to_internal_network_via_tunneling(self):
|
||||
veth_1, veth_2 = self.useFixture(
|
||||
net_helpers.VethFixture()).ports
|
||||
|
||||
# NOTE: This sets an IP address on the host's root namespace
|
||||
# which is cleaned up when the device is deleted.
|
||||
veth_1.addr.add(common_utils.ip_to_cidr(self.local_ip, 32))
|
||||
|
||||
veth_1.link.set_up()
|
||||
veth_2.link.set_up()
|
||||
|
||||
def connect_to_internal_network_via_vlans(self, host_data_bridge):
|
||||
# If using VLANs as a segmentation device, it's needed to connect
|
||||
# a provider bridge to a centralized, shared bridge.
|
||||
net_helpers.create_patch_ports(
|
||||
self.central_data_bridge, host_data_bridge)
|
||||
|
||||
def connect_to_external_network(self, host_external_bridge):
|
||||
net_helpers.create_patch_ports(
|
||||
self.central_external_bridge, host_external_bridge)
|
||||
|
||||
def allocate_local_ip(self):
|
||||
if not self.env_desc.network_range:
|
||||
return str(self.useFixture(
|
||||
ip_address.ExclusiveIPAddress(
|
||||
'240.0.0.1', '240.255.255.254')).address)
|
||||
return str(self.useFixture(
|
||||
ip_address.ExclusiveIPAddress(
|
||||
str(self.env_desc.network_range[2]),
|
||||
str(self.env_desc.network_range[-2]))).address)
|
||||
|
||||
def get_bridge(self, network_id):
|
||||
if "ovs" in self.agents.keys():
|
||||
return self.ovs_agent.br_int
|
||||
elif "linuxbridge" in self.agents.keys():
|
||||
bridge = self.network_bridges.get(network_id, None)
|
||||
if not bridge:
|
||||
br_prefix = lb_agent.LinuxBridgeManager.get_bridge_name(
|
||||
network_id)
|
||||
bridge = self.useFixture(
|
||||
net_helpers.LinuxBridgeFixture(
|
||||
prefix=br_prefix,
|
||||
namespace=self.host_namespace,
|
||||
prefix_is_full_name=True)).bridge
|
||||
self.network_bridges[network_id] = bridge
|
||||
return bridge
|
||||
|
||||
@property
|
||||
def hostname(self):
|
||||
return self.neutron_config.config.DEFAULT.host
|
||||
|
||||
@property
|
||||
def l3_agent(self):
|
||||
return self.agents['l3']
|
||||
|
||||
@l3_agent.setter
|
||||
def l3_agent(self, agent):
|
||||
self.agents['l3'] = agent
|
||||
|
||||
@property
|
||||
def ovs_agent(self):
|
||||
return self.agents['ovs']
|
||||
|
||||
@ovs_agent.setter
|
||||
def ovs_agent(self, agent):
|
||||
self.agents['ovs'] = agent
|
||||
|
||||
@property
|
||||
def linuxbridge_agent(self):
|
||||
return self.agents['linuxbridge']
|
||||
|
||||
@linuxbridge_agent.setter
|
||||
def linuxbridge_agent(self, agent):
|
||||
self.agents['linuxbridge'] = agent
|
||||
|
||||
|
||||
class Environment(fixtures.Fixture):
|
||||
"""Represents a deployment topology.
|
||||
|
||||
Environment is a collection of hosts. It starts a Neutron server
|
||||
and a parametrized number of Hosts, each a collection of agents.
|
||||
The Environment accepts a collection of HostDescription, each describing
|
||||
the type of Host to create.
|
||||
"""
|
||||
|
||||
def __init__(self, env_desc, hosts_desc):
|
||||
"""
|
||||
:param env_desc: An EnvironmentDescription instance.
|
||||
:param hosts_desc: A list of HostDescription instances.
|
||||
"""
|
||||
|
||||
super(Environment, self).__init__()
|
||||
self.env_desc = env_desc
|
||||
self.hosts_desc = hosts_desc
|
||||
self.hosts = []
|
||||
|
||||
def wait_until_env_is_up(self):
|
||||
common_utils.wait_until_true(self._processes_are_ready)
|
||||
|
||||
def _processes_are_ready(self):
|
||||
try:
|
||||
running_agents = self.neutron_server.client.list_agents()['agents']
|
||||
agents_count = sum(len(host.agents) for host in self.hosts)
|
||||
return len(running_agents) == agents_count
|
||||
except nc_exc.NeutronClientException:
|
||||
return False
|
||||
|
||||
def _create_host(self, host_desc):
|
||||
temp_dir = self.useFixture(fixtures.TempDir()).path
|
||||
neutron_config = config.NeutronConfigFixture(
|
||||
self.env_desc, host_desc, temp_dir,
|
||||
cfg.CONF.database.connection, self.rabbitmq_environment)
|
||||
self.useFixture(neutron_config)
|
||||
|
||||
return self.useFixture(
|
||||
Host(self.env_desc,
|
||||
host_desc,
|
||||
self.test_name,
|
||||
neutron_config,
|
||||
self.central_data_bridge,
|
||||
self.central_external_bridge))
|
||||
|
||||
def _setUp(self):
|
||||
self.temp_dir = self.useFixture(fixtures.TempDir()).path
|
||||
|
||||
#we need this bridge before rabbit and neutron service will start
|
||||
self.central_data_bridge = self.useFixture(
|
||||
net_helpers.OVSBridgeFixture('cnt-data')).bridge
|
||||
self.central_external_bridge = self.useFixture(
|
||||
net_helpers.OVSBridgeFixture('cnt-ex')).bridge
|
||||
|
||||
#Get rabbitmq address (and cnt-data network)
|
||||
rabbitmq_ip_address = self._configure_port_for_rabbitmq()
|
||||
self.rabbitmq_environment = self.useFixture(
|
||||
process.RabbitmqEnvironmentFixture(host=rabbitmq_ip_address)
|
||||
)
|
||||
|
||||
plugin_cfg_fixture = self.useFixture(
|
||||
config.ML2ConfigFixture(
|
||||
self.env_desc, self.hosts_desc, self.temp_dir,
|
||||
self.env_desc.network_type))
|
||||
neutron_cfg_fixture = self.useFixture(
|
||||
config.NeutronConfigFixture(
|
||||
self.env_desc, None, self.temp_dir,
|
||||
cfg.CONF.database.connection, self.rabbitmq_environment))
|
||||
self.neutron_server = self.useFixture(
|
||||
process.NeutronServerFixture(
|
||||
self.env_desc, None,
|
||||
self.test_name, neutron_cfg_fixture, plugin_cfg_fixture))
|
||||
|
||||
self.hosts = [self._create_host(desc) for desc in self.hosts_desc]
|
||||
|
||||
self.wait_until_env_is_up()
|
||||
|
||||
def _configure_port_for_rabbitmq(self):
|
||||
self.env_desc.network_range = self._get_network_range()
|
||||
if not self.env_desc.network_range:
|
||||
return "127.0.0.1"
|
||||
rabbitmq_ip = str(self.env_desc.network_range[1])
|
||||
rabbitmq_port = ip_lib.IPDevice(self.central_data_bridge.br_name)
|
||||
rabbitmq_port.addr.add(common_utils.ip_to_cidr(rabbitmq_ip, 24))
|
||||
rabbitmq_port.link.set_up()
|
||||
|
||||
return rabbitmq_ip
|
||||
|
||||
def _get_network_range(self):
|
||||
#NOTE(slaweq): We need to choose IP address on which rabbitmq will be
|
||||
# available because LinuxBridge agents are spawned in their own
|
||||
# namespaces and need to know where the rabbitmq server is listening.
|
||||
# For ovs agent it is not necessary because agents are spawned in
|
||||
# globalscope together with rabbitmq server so default localhost
|
||||
# address is fine for them
|
||||
for desc in self.hosts_desc:
|
||||
if desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE:
|
||||
return self.useFixture(
|
||||
ip_network.ExclusiveIPNetwork(
|
||||
"240.0.0.0", "240.255.255.255", "24")).network
|
168
neutron_fwaas/tests/fullstack/resources/machine.py
Normal file
168
neutron_fwaas/tests/fullstack/resources/machine.py
Normal file
@ -0,0 +1,168 @@
|
||||
# Copyright 2015 Red Hat, Inc.
|
||||
#
|
||||
# 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 itertools
|
||||
|
||||
import netaddr
|
||||
|
||||
from neutron_lib import constants
|
||||
|
||||
from neutron.agent.linux import ip_lib
|
||||
from neutron.common import utils
|
||||
from neutron.extensions import portbindings as pbs
|
||||
from neutron.tests.common import machine_fixtures
|
||||
from neutron.tests.common import net_helpers
|
||||
|
||||
|
||||
class FakeFullstackMachinesList(list):
|
||||
"""A list of items implementing the FakeFullstackMachine interface."""
|
||||
|
||||
def block_until_all_boot(self):
|
||||
for vm in self:
|
||||
vm.block_until_boot()
|
||||
|
||||
def ping_all(self):
|
||||
# Generate an iterable of all unique pairs. For example:
|
||||
# itertools.combinations(range(3), 2) results in:
|
||||
# ((0, 1), (0, 2), (1, 2))
|
||||
for vm_1, vm_2 in itertools.combinations(self, 2):
|
||||
vm_1.block_until_ping(vm_2.ip)
|
||||
|
||||
|
||||
class FakeFullstackMachine(machine_fixtures.FakeMachineBase):
|
||||
|
||||
def __init__(self, host, network_id, tenant_id, safe_client,
|
||||
neutron_port=None, bridge_name=None):
|
||||
super(FakeFullstackMachine, self).__init__()
|
||||
self.host = host
|
||||
self.tenant_id = tenant_id
|
||||
self.network_id = network_id
|
||||
self.safe_client = safe_client
|
||||
self.neutron_port = neutron_port
|
||||
self.bridge_name = bridge_name
|
||||
|
||||
def _setUp(self):
|
||||
super(FakeFullstackMachine, self)._setUp()
|
||||
|
||||
self.bridge = self._get_bridge()
|
||||
|
||||
if not self.neutron_port:
|
||||
self.neutron_port = self.safe_client.create_port(
|
||||
network_id=self.network_id,
|
||||
tenant_id=self.tenant_id,
|
||||
hostname=self.host.hostname)
|
||||
mac_address = self.neutron_port['mac_address']
|
||||
hybrid_plug = self.neutron_port[pbs.VIF_DETAILS].get(
|
||||
pbs.OVS_HYBRID_PLUG, False)
|
||||
|
||||
self.bind_port_if_needed()
|
||||
self.port = self.useFixture(
|
||||
net_helpers.PortFixture.get(
|
||||
self.bridge, self.namespace, mac_address,
|
||||
self.neutron_port['id'], hybrid_plug)).port
|
||||
|
||||
for fixed_ip in self.neutron_port['fixed_ips']:
|
||||
self._configure_ipaddress(fixed_ip)
|
||||
|
||||
def bind_port_if_needed(self):
|
||||
if self.neutron_port[pbs.VIF_TYPE] == pbs.VIF_TYPE_UNBOUND:
|
||||
self.safe_client.client.update_port(
|
||||
self.neutron_port['id'],
|
||||
{'port': {pbs.HOST_ID: self.host.hostname}})
|
||||
self.addCleanup(self.safe_client.client.update_port,
|
||||
self.neutron_port['id'],
|
||||
{'port': {pbs.HOST_ID: ''}})
|
||||
|
||||
def _get_bridge(self):
|
||||
if self.bridge_name is None:
|
||||
return self.host.get_bridge(self.network_id)
|
||||
agent_type = self.host.host_desc.l2_agent_type
|
||||
if agent_type == constants.AGENT_TYPE_OVS:
|
||||
new_bridge = self.useFixture(
|
||||
net_helpers.OVSTrunkBridgeFixture(self.bridge_name)).bridge
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"Support for %s agent is not implemented." % agent_type)
|
||||
|
||||
return new_bridge
|
||||
|
||||
def _configure_ipaddress(self, fixed_ip):
|
||||
if (netaddr.IPAddress(fixed_ip['ip_address']).version ==
|
||||
constants.IP_VERSION_6):
|
||||
# v6Address/default_route is auto-configured.
|
||||
self._ipv6 = fixed_ip['ip_address']
|
||||
else:
|
||||
self._ip = fixed_ip['ip_address']
|
||||
subnet_id = fixed_ip['subnet_id']
|
||||
subnet = self.safe_client.client.show_subnet(subnet_id)
|
||||
prefixlen = netaddr.IPNetwork(subnet['subnet']['cidr']).prefixlen
|
||||
self._ip_cidr = '%s/%s' % (self._ip, prefixlen)
|
||||
|
||||
# TODO(amuller): Support DHCP
|
||||
self.port.addr.add(self.ip_cidr)
|
||||
|
||||
self.gateway_ip = subnet['subnet']['gateway_ip']
|
||||
if self.gateway_ip:
|
||||
net_helpers.set_namespace_gateway(self.port, self.gateway_ip)
|
||||
|
||||
@property
|
||||
def ipv6(self):
|
||||
return self._ipv6
|
||||
|
||||
@property
|
||||
def ip(self):
|
||||
return self._ip
|
||||
|
||||
@property
|
||||
def ip_cidr(self):
|
||||
return self._ip_cidr
|
||||
|
||||
def block_until_boot(self):
|
||||
utils.wait_until_true(
|
||||
lambda: (self.safe_client.client.show_port(self.neutron_port['id'])
|
||||
['port']['status'] == 'ACTIVE'),
|
||||
sleep=3)
|
||||
|
||||
def destroy(self):
|
||||
"""Destroy this fake machine.
|
||||
|
||||
This should simulate deletion of a vm. It doesn't call cleanUp().
|
||||
"""
|
||||
self.safe_client.client.update_port(
|
||||
self.neutron_port['id'],
|
||||
{'port': {pbs.HOST_ID: ''}}
|
||||
)
|
||||
# All associated vlan interfaces are deleted too
|
||||
self.bridge.delete_port(self.port.name)
|
||||
|
||||
ip_wrap = ip_lib.IPWrapper(self.namespace)
|
||||
ip_wrap.netns.delete(self.namespace)
|
||||
|
||||
|
||||
class FakeFullstackTrunkMachine(FakeFullstackMachine):
|
||||
def __init__(self, trunk, *args, **kwargs):
|
||||
super(FakeFullstackTrunkMachine, self).__init__(*args, **kwargs)
|
||||
self.trunk = trunk
|
||||
|
||||
def add_vlan_interface(self, mac_address, ip_address, segmentation_id):
|
||||
"""Add VLAN interface to VM's namespace.
|
||||
|
||||
:param mac_address: MAC address to be set on VLAN interface.
|
||||
:param ip_address: The IPNetwork instance containing IP address
|
||||
assigned to the interface.
|
||||
:param segmentation_id: VLAN tag added to the interface.
|
||||
"""
|
||||
net_helpers.create_vlan_interface(
|
||||
self.namespace, self.port.name, mac_address, ip_address,
|
||||
segmentation_id)
|
235
neutron_fwaas/tests/fullstack/resources/process.py
Normal file
235
neutron_fwaas/tests/fullstack/resources/process.py
Normal file
@ -0,0 +1,235 @@
|
||||
# Copyright 2015 Red Hat, Inc.
|
||||
#
|
||||
# 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 datetime
|
||||
from distutils import spawn
|
||||
import os
|
||||
import signal
|
||||
|
||||
import fixtures
|
||||
from neutronclient.common import exceptions as nc_exc
|
||||
from neutronclient.v2_0 import client
|
||||
|
||||
from neutron.agent.linux import async_process
|
||||
from neutron.agent.linux import utils
|
||||
from neutron.common import utils as common_utils
|
||||
from neutron.tests import base
|
||||
from neutron.tests.common import net_helpers
|
||||
from neutron.tests.fullstack import base as fullstack_base
|
||||
|
||||
|
||||
class ProcessFixture(fixtures.Fixture):
|
||||
def __init__(self, test_name, process_name, exec_name, config_filenames,
|
||||
namespace=None, kill_signal=signal.SIGKILL):
|
||||
super(ProcessFixture, self).__init__()
|
||||
self.test_name = test_name
|
||||
self.process_name = process_name
|
||||
self.exec_name = exec_name
|
||||
self.config_filenames = config_filenames
|
||||
self.process = None
|
||||
self.kill_signal = kill_signal
|
||||
self.namespace = namespace
|
||||
|
||||
def _setUp(self):
|
||||
self.start()
|
||||
self.addCleanup(self.stop)
|
||||
|
||||
def start(self):
|
||||
test_name = base.sanitize_log_path(self.test_name)
|
||||
|
||||
log_dir = os.path.join(fullstack_base.DEFAULT_LOG_DIR, test_name)
|
||||
common_utils.ensure_dir(log_dir)
|
||||
|
||||
timestamp = datetime.datetime.now().strftime("%Y-%m-%d--%H-%M-%S-%f")
|
||||
log_file = "%s--%s.log" % (self.process_name, timestamp)
|
||||
cmd = [spawn.find_executable(self.exec_name),
|
||||
'--log-dir', log_dir,
|
||||
'--log-file', log_file]
|
||||
for filename in self.config_filenames:
|
||||
cmd += ['--config-file', filename]
|
||||
run_as_root = bool(self.namespace)
|
||||
self.process = async_process.AsyncProcess(
|
||||
cmd, run_as_root=run_as_root, namespace=self.namespace
|
||||
)
|
||||
self.process.start(block=True)
|
||||
|
||||
def stop(self):
|
||||
self.process.stop(block=True, kill_signal=self.kill_signal)
|
||||
|
||||
|
||||
class RabbitmqEnvironmentFixture(fixtures.Fixture):
|
||||
|
||||
def __init__(self, host="127.0.0.1"):
|
||||
super(RabbitmqEnvironmentFixture, self).__init__()
|
||||
self.host = host
|
||||
|
||||
def _setUp(self):
|
||||
self.user = common_utils.get_rand_name(prefix='user')
|
||||
self.password = common_utils.get_rand_name(prefix='pass')
|
||||
self.vhost = common_utils.get_rand_name(prefix='vhost')
|
||||
|
||||
self._execute('add_user', self.user, self.password)
|
||||
self.addCleanup(self._execute, 'delete_user', self.user)
|
||||
|
||||
self._execute('add_vhost', self.vhost)
|
||||
self.addCleanup(self._execute, 'delete_vhost', self.vhost)
|
||||
|
||||
self._execute('set_permissions', '-p', self.vhost, self.user,
|
||||
'.*', '.*', '.*')
|
||||
|
||||
def _execute(self, *args):
|
||||
cmd = ['rabbitmqctl']
|
||||
cmd.extend(args)
|
||||
utils.execute(cmd, run_as_root=True)
|
||||
|
||||
|
||||
class NeutronServerFixture(fixtures.Fixture):
|
||||
|
||||
NEUTRON_SERVER = "neutron-server"
|
||||
|
||||
def __init__(self, env_desc, host_desc,
|
||||
test_name, neutron_cfg_fixture, plugin_cfg_fixture):
|
||||
super(NeutronServerFixture, self).__init__()
|
||||
self.env_desc = env_desc
|
||||
self.host_desc = host_desc
|
||||
self.test_name = test_name
|
||||
self.neutron_cfg_fixture = neutron_cfg_fixture
|
||||
self.plugin_cfg_fixture = plugin_cfg_fixture
|
||||
|
||||
def _setUp(self):
|
||||
config_filenames = [self.neutron_cfg_fixture.filename,
|
||||
self.plugin_cfg_fixture.filename]
|
||||
|
||||
self.process_fixture = self.useFixture(ProcessFixture(
|
||||
test_name=self.test_name,
|
||||
process_name=self.NEUTRON_SERVER,
|
||||
exec_name=self.NEUTRON_SERVER,
|
||||
config_filenames=config_filenames,
|
||||
kill_signal=signal.SIGTERM))
|
||||
|
||||
common_utils.wait_until_true(self.server_is_live)
|
||||
|
||||
def server_is_live(self):
|
||||
try:
|
||||
self.client.list_networks()
|
||||
return True
|
||||
except nc_exc.NeutronClientException:
|
||||
return False
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
url = ("http://127.0.0.1:%s" %
|
||||
self.neutron_cfg_fixture.config.DEFAULT.bind_port)
|
||||
return client.Client(auth_strategy="noauth", endpoint_url=url)
|
||||
|
||||
|
||||
class OVSAgentFixture(fixtures.Fixture):
|
||||
|
||||
NEUTRON_OVS_AGENT = "neutron-openvswitch-agent"
|
||||
|
||||
def __init__(self, env_desc, host_desc,
|
||||
test_name, neutron_cfg_fixture, agent_cfg_fixture):
|
||||
super(OVSAgentFixture, self).__init__()
|
||||
self.env_desc = env_desc
|
||||
self.host_desc = host_desc
|
||||
self.test_name = test_name
|
||||
self.neutron_cfg_fixture = neutron_cfg_fixture
|
||||
self.neutron_config = self.neutron_cfg_fixture.config
|
||||
self.agent_cfg_fixture = agent_cfg_fixture
|
||||
self.agent_config = agent_cfg_fixture.config
|
||||
|
||||
def _setUp(self):
|
||||
self.br_int = self.useFixture(
|
||||
net_helpers.OVSBridgeFixture(
|
||||
self.agent_cfg_fixture.get_br_int_name())).bridge
|
||||
|
||||
config_filenames = [self.neutron_cfg_fixture.filename,
|
||||
self.agent_cfg_fixture.filename]
|
||||
|
||||
self.process_fixture = self.useFixture(ProcessFixture(
|
||||
test_name=self.test_name,
|
||||
process_name=self.NEUTRON_OVS_AGENT,
|
||||
exec_name=spawn.find_executable(
|
||||
'ovs_agent.py',
|
||||
path=os.path.join(base.ROOTDIR, 'common', 'agents')),
|
||||
config_filenames=config_filenames,
|
||||
kill_signal=signal.SIGTERM))
|
||||
|
||||
|
||||
class LinuxBridgeAgentFixture(fixtures.Fixture):
|
||||
|
||||
NEUTRON_LINUXBRIDGE_AGENT = "neutron-linuxbridge-agent"
|
||||
|
||||
def __init__(self, env_desc, host_desc, test_name,
|
||||
neutron_cfg_fixture, agent_cfg_fixture,
|
||||
namespace=None):
|
||||
super(LinuxBridgeAgentFixture, self).__init__()
|
||||
self.env_desc = env_desc
|
||||
self.host_desc = host_desc
|
||||
self.test_name = test_name
|
||||
self.neutron_cfg_fixture = neutron_cfg_fixture
|
||||
self.neutron_config = self.neutron_cfg_fixture.config
|
||||
self.agent_cfg_fixture = agent_cfg_fixture
|
||||
self.agent_config = agent_cfg_fixture.config
|
||||
self.namespace = namespace
|
||||
|
||||
def _setUp(self):
|
||||
config_filenames = [self.neutron_cfg_fixture.filename,
|
||||
self.agent_cfg_fixture.filename]
|
||||
|
||||
self.process_fixture = self.useFixture(
|
||||
ProcessFixture(
|
||||
test_name=self.test_name,
|
||||
process_name=self.NEUTRON_LINUXBRIDGE_AGENT,
|
||||
exec_name=self.NEUTRON_LINUXBRIDGE_AGENT,
|
||||
config_filenames=config_filenames,
|
||||
namespace=self.namespace
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class L3AgentFixture(fixtures.Fixture):
|
||||
|
||||
NEUTRON_L3_AGENT = "neutron-l3-agent"
|
||||
|
||||
def __init__(self, env_desc, host_desc, test_name,
|
||||
neutron_cfg_fixture, l3_agent_cfg_fixture,
|
||||
namespace=None):
|
||||
super(L3AgentFixture, self).__init__()
|
||||
self.env_desc = env_desc
|
||||
self.host_desc = host_desc
|
||||
self.test_name = test_name
|
||||
self.neutron_cfg_fixture = neutron_cfg_fixture
|
||||
self.l3_agent_cfg_fixture = l3_agent_cfg_fixture
|
||||
self.namespace = namespace
|
||||
|
||||
def _setUp(self):
|
||||
self.plugin_config = self.l3_agent_cfg_fixture.config
|
||||
|
||||
config_filenames = [self.neutron_cfg_fixture.filename,
|
||||
self.l3_agent_cfg_fixture.filename]
|
||||
self.process_fixture = self.useFixture(
|
||||
ProcessFixture(
|
||||
test_name=self.test_name,
|
||||
process_name=self.NEUTRON_L3_AGENT,
|
||||
exec_name=spawn.find_executable(
|
||||
'l3_agent.py',
|
||||
path=os.path.join(base.ROOTDIR, 'common', 'agents')),
|
||||
config_filenames=config_filenames,
|
||||
namespace=self.namespace
|
||||
)
|
||||
)
|
||||
|
||||
def get_namespace_suffix(self):
|
||||
return self.plugin_config.DEFAULT.test_namespace_suffix
|
183
neutron_fwaas/tests/fullstack/test_l3_agent.py
Normal file
183
neutron_fwaas/tests/fullstack/test_l3_agent.py
Normal file
@ -0,0 +1,183 @@
|
||||
# Copyright 2015 Red Hat, Inc.
|
||||
#
|
||||
# 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 functools
|
||||
|
||||
import netaddr
|
||||
|
||||
from neutron_lib import constants
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron.agent.l3 import agent as l3_agent
|
||||
from neutron.agent.l3 import namespaces
|
||||
from neutron.agent.linux import ip_lib
|
||||
from neutron.common import utils as common_utils
|
||||
from neutron.tests.common.exclusive_resources import ip_network
|
||||
from neutron.tests.common import machine_fixtures
|
||||
from neutron.tests.fullstack import base
|
||||
from neutron.tests.fullstack.resources import environment
|
||||
from neutron.tests.fullstack.resources import machine
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
load_tests = testlib_api.module_load_tests
|
||||
|
||||
|
||||
class TestL3Agent(base.BaseFullStackTestCase):
|
||||
|
||||
def _create_external_network_and_subnet(self, tenant_id):
|
||||
network = self.safe_client.create_network(
|
||||
tenant_id, name='public', external=True)
|
||||
cidr = self.useFixture(
|
||||
ip_network.ExclusiveIPNetwork(
|
||||
"240.0.0.0", "240.255.255.255", "24")).network
|
||||
subnet = self.safe_client.create_subnet(
|
||||
tenant_id, network['id'], cidr,
|
||||
enable_dhcp=False)
|
||||
return network, subnet
|
||||
|
||||
def block_until_port_status_active(self, port_id):
|
||||
def is_port_status_active():
|
||||
port = self.client.show_port(port_id)
|
||||
return port['port']['status'] == 'ACTIVE'
|
||||
common_utils.wait_until_true(lambda: is_port_status_active(), sleep=1)
|
||||
|
||||
def _create_net_subnet_and_vm(self, tenant_id, subnet_cidrs, host, router):
|
||||
network = self.safe_client.create_network(tenant_id)
|
||||
for cidr in subnet_cidrs:
|
||||
# For IPv6 subnets, enable_dhcp should be set to true.
|
||||
enable_dhcp = (netaddr.IPNetwork(cidr).version ==
|
||||
constants.IP_VERSION_6)
|
||||
subnet = self.safe_client.create_subnet(
|
||||
tenant_id, network['id'], cidr, enable_dhcp=enable_dhcp)
|
||||
|
||||
router_interface_info = self.safe_client.add_router_interface(
|
||||
router['id'], subnet['id'])
|
||||
self.block_until_port_status_active(
|
||||
router_interface_info['port_id'])
|
||||
|
||||
vm = self.useFixture(
|
||||
machine.FakeFullstackMachine(
|
||||
host, network['id'], tenant_id, self.safe_client))
|
||||
vm.block_until_boot()
|
||||
return vm
|
||||
|
||||
|
||||
class TestLegacyL3Agent(TestL3Agent):
|
||||
|
||||
def setUp(self):
|
||||
host_descriptions = [
|
||||
environment.HostDescription(l3_agent=True),
|
||||
environment.HostDescription()]
|
||||
env = environment.Environment(
|
||||
environment.EnvironmentDescription(
|
||||
network_type='vlan', l2_pop=False),
|
||||
host_descriptions)
|
||||
super(TestLegacyL3Agent, self).setUp(env)
|
||||
|
||||
def _get_namespace(self, router_id):
|
||||
return namespaces.build_ns_name(l3_agent.NS_PREFIX, router_id)
|
||||
|
||||
def _assert_namespace_exists(self, ns_name):
|
||||
ip = ip_lib.IPWrapper(ns_name)
|
||||
common_utils.wait_until_true(lambda: ip.netns.exists(ns_name))
|
||||
|
||||
def test_namespace_exists(self):
|
||||
tenant_id = uuidutils.generate_uuid()
|
||||
|
||||
router = self.safe_client.create_router(tenant_id)
|
||||
network = self.safe_client.create_network(tenant_id)
|
||||
subnet = self.safe_client.create_subnet(
|
||||
tenant_id, network['id'], '20.0.0.0/24', gateway_ip='20.0.0.1')
|
||||
self.safe_client.add_router_interface(router['id'], subnet['id'])
|
||||
|
||||
namespace = "%s@%s" % (
|
||||
self._get_namespace(router['id']),
|
||||
self.environment.hosts[0].l3_agent.get_namespace_suffix(), )
|
||||
self._assert_namespace_exists(namespace)
|
||||
|
||||
def test_east_west_traffic(self):
|
||||
tenant_id = uuidutils.generate_uuid()
|
||||
router = self.safe_client.create_router(tenant_id)
|
||||
|
||||
vm1 = self._create_net_subnet_and_vm(
|
||||
tenant_id, ['20.0.0.0/24', '2001:db8:aaaa::/64'],
|
||||
self.environment.hosts[0], router)
|
||||
vm2 = self._create_net_subnet_and_vm(
|
||||
tenant_id, ['21.0.0.0/24', '2001:db8:bbbb::/64'],
|
||||
self.environment.hosts[1], router)
|
||||
|
||||
vm1.block_until_ping(vm2.ip)
|
||||
# Verify ping6 from vm2 to vm1 IPv6 Address
|
||||
vm2.block_until_ping(vm1.ipv6)
|
||||
|
||||
def test_snat_and_floatingip(self):
|
||||
# This function creates external network and boots an extrenal vm
|
||||
# on it with gateway ip and connected to central_external_bridge.
|
||||
# Later it creates a tenant vm on tenant network, with tenant router
|
||||
# connected to tenant network and external network.
|
||||
# To test snat and floatingip, try ping between tenant and external vms
|
||||
tenant_id = uuidutils.generate_uuid()
|
||||
ext_net, ext_sub = self._create_external_network_and_subnet(tenant_id)
|
||||
external_vm = self.useFixture(
|
||||
machine_fixtures.FakeMachine(
|
||||
self.environment.central_external_bridge,
|
||||
common_utils.ip_to_cidr(ext_sub['gateway_ip'], 24)))
|
||||
|
||||
router = self.safe_client.create_router(tenant_id,
|
||||
external_network=ext_net['id'])
|
||||
vm = self._create_net_subnet_and_vm(
|
||||
tenant_id, ['20.0.0.0/24'],
|
||||
self.environment.hosts[1], router)
|
||||
|
||||
# ping external vm to test snat
|
||||
vm.block_until_ping(external_vm.ip)
|
||||
|
||||
fip = self.safe_client.create_floatingip(
|
||||
tenant_id, ext_net['id'], vm.ip, vm.neutron_port['id'])
|
||||
|
||||
# ping floating ip from external vm
|
||||
external_vm.block_until_ping(fip['floating_ip_address'])
|
||||
|
||||
|
||||
class TestHAL3Agent(base.BaseFullStackTestCase):
|
||||
|
||||
def setUp(self):
|
||||
host_descriptions = [
|
||||
environment.HostDescription(l3_agent=True) for _ in range(2)]
|
||||
env = environment.Environment(
|
||||
environment.EnvironmentDescription(
|
||||
network_type='vxlan', l2_pop=True),
|
||||
host_descriptions)
|
||||
super(TestHAL3Agent, self).setUp(env)
|
||||
|
||||
def _is_ha_router_active_on_one_agent(self, router_id):
|
||||
agents = self.client.list_l3_agent_hosting_routers(router_id)
|
||||
return (
|
||||
agents['agents'][0]['ha_state'] != agents['agents'][1]['ha_state'])
|
||||
|
||||
def test_ha_router(self):
|
||||
# TODO(amuller): Test external connectivity before and after a
|
||||
# failover, see: https://review.openstack.org/#/c/196393/
|
||||
|
||||
tenant_id = uuidutils.generate_uuid()
|
||||
router = self.safe_client.create_router(tenant_id, ha=True)
|
||||
agents = self.client.list_l3_agent_hosting_routers(router['id'])
|
||||
self.assertEqual(2, len(agents['agents']),
|
||||
'HA router must be scheduled to both nodes')
|
||||
|
||||
common_utils.wait_until_true(
|
||||
functools.partial(
|
||||
self._is_ha_router_active_on_one_agent,
|
||||
router['id']),
|
||||
timeout=90)
|
24
neutron_fwaas/tests/fullstack/utils.py
Normal file
24
neutron_fwaas/tests/fullstack/utils.py
Normal file
@ -0,0 +1,24 @@
|
||||
# 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 get_ovs_interface_scenarios():
|
||||
return [
|
||||
('openflow-cli_ovsdb-cli', {'of_interface': 'ovs-ofctl',
|
||||
'ovsdb_interface': 'vsctl'}),
|
||||
('openflow-native_ovsdb-cli', {'of_interface': 'native',
|
||||
'ovsdb_interface': 'vsctl'}),
|
||||
('openflow-cli_ovsdb-native', {'of_interface': 'ovs-ofctl',
|
||||
'ovsdb_interface': 'native'}),
|
||||
('openflow-native_ovsdb-native', {'of_interface': 'native',
|
||||
'ovsdb_interface': 'native'}),
|
||||
]
|
80
playbooks/neutron-fwaas-fullstack/post.yaml
Normal file
80
playbooks/neutron-fwaas-fullstack/post.yaml
Normal file
@ -0,0 +1,80 @@
|
||||
- hosts: primary
|
||||
tasks:
|
||||
|
||||
- name: Copy files from {{ ansible_user_dir }}/workspace/ on node
|
||||
synchronize:
|
||||
src: '{{ ansible_user_dir }}/workspace/'
|
||||
dest: '{{ zuul.executor.log_root }}'
|
||||
mode: pull
|
||||
copy_links: true
|
||||
verify_host: true
|
||||
rsync_opts:
|
||||
- --include=**/*nose_results.html
|
||||
- --include=*/
|
||||
- --exclude=*
|
||||
- --prune-empty-dirs
|
||||
|
||||
- name: Copy files from {{ ansible_user_dir }}/workspace/ on node
|
||||
synchronize:
|
||||
src: '{{ ansible_user_dir }}/workspace/'
|
||||
dest: '{{ zuul.executor.log_root }}'
|
||||
mode: pull
|
||||
copy_links: true
|
||||
verify_host: true
|
||||
rsync_opts:
|
||||
- --include=**/*testr_results.html.gz
|
||||
- --include=*/
|
||||
- --exclude=*
|
||||
- --prune-empty-dirs
|
||||
|
||||
- name: Copy files from {{ ansible_user_dir }}/workspace/ on node
|
||||
synchronize:
|
||||
src: '{{ ansible_user_dir }}/workspace/'
|
||||
dest: '{{ zuul.executor.log_root }}'
|
||||
mode: pull
|
||||
copy_links: true
|
||||
verify_host: true
|
||||
rsync_opts:
|
||||
- --include=/.testrepository/tmp*
|
||||
- --include=*/
|
||||
- --exclude=*
|
||||
- --prune-empty-dirs
|
||||
|
||||
- name: Copy files from {{ ansible_user_dir }}/workspace/ on node
|
||||
synchronize:
|
||||
src: '{{ ansible_user_dir }}/workspace/'
|
||||
dest: '{{ zuul.executor.log_root }}'
|
||||
mode: pull
|
||||
copy_links: true
|
||||
verify_host: true
|
||||
rsync_opts:
|
||||
- --include=**/*testrepository.subunit.gz
|
||||
- --include=*/
|
||||
- --exclude=*
|
||||
- --prune-empty-dirs
|
||||
|
||||
- name: Copy files from {{ ansible_user_dir }}/workspace/ on node
|
||||
synchronize:
|
||||
src: '{{ ansible_user_dir }}/workspace/'
|
||||
dest: '{{ zuul.executor.log_root }}/tox'
|
||||
mode: pull
|
||||
copy_links: true
|
||||
verify_host: true
|
||||
rsync_opts:
|
||||
- --include=/.tox/*/log/*
|
||||
- --include=*/
|
||||
- --exclude=*
|
||||
- --prune-empty-dirs
|
||||
|
||||
- name: Copy files from {{ ansible_user_dir }}/workspace/ on node
|
||||
synchronize:
|
||||
src: '{{ ansible_user_dir }}/workspace/'
|
||||
dest: '{{ zuul.executor.log_root }}'
|
||||
mode: pull
|
||||
copy_links: true
|
||||
verify_host: true
|
||||
rsync_opts:
|
||||
- --include=/logs/**
|
||||
- --include=*/
|
||||
- --exclude=*
|
||||
- --prune-empty-dirs
|
54
playbooks/neutron-fwaas-fullstack/run.yaml
Normal file
54
playbooks/neutron-fwaas-fullstack/run.yaml
Normal file
@ -0,0 +1,54 @@
|
||||
- hosts: all
|
||||
name: neutron-fwaas-fullstack
|
||||
tasks:
|
||||
|
||||
- name: Ensure legacy workspace directory
|
||||
file:
|
||||
path: '{{ ansible_user_dir }}/workspace'
|
||||
state: directory
|
||||
|
||||
- shell:
|
||||
cmd: |
|
||||
set -e
|
||||
set -x
|
||||
cat > clonemap.yaml << EOF
|
||||
clonemap:
|
||||
- name: openstack-infra/devstack-gate
|
||||
dest: devstack-gate
|
||||
EOF
|
||||
/usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \
|
||||
git://git.openstack.org \
|
||||
openstack-infra/devstack-gate
|
||||
executable: /bin/bash
|
||||
chdir: '{{ ansible_user_dir }}/workspace'
|
||||
environment: '{{ zuul | zuul_legacy_vars }}'
|
||||
|
||||
- shell:
|
||||
cmd: |
|
||||
set -e
|
||||
set -x
|
||||
export PYTHONUNBUFFERED=true
|
||||
export DEVSTACK_GATE_TEMPEST=0
|
||||
export DEVSTACK_GATE_EXERCISES=0
|
||||
export DEVSTACK_GATE_NEUTRON=1
|
||||
export DEVSTACK_GATE_INSTALL_TESTONLY=1
|
||||
export BRANCH_OVERRIDE=default
|
||||
if [ "$BRANCH_OVERRIDE" != "default" ] ; then
|
||||
export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE
|
||||
fi
|
||||
|
||||
function gate_hook {
|
||||
bash -xe $BASE/new/neutron/neutron/tests/contrib/gate_hook.sh dsvm-fullstack
|
||||
}
|
||||
export -f gate_hook
|
||||
|
||||
function post_test_hook {
|
||||
bash -xe $BASE/new/neutron/neutron/tests/contrib/post_test_hook.sh dsvm-fullstack
|
||||
}
|
||||
export -f post_test_hook
|
||||
|
||||
cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
|
||||
./safe-devstack-vm-gate-wrap.sh
|
||||
executable: /bin/bash
|
||||
chdir: '{{ ansible_user_dir }}/workspace'
|
||||
environment: '{{ zuul | zuul_legacy_vars }}'
|
281
tools/configure_for_func_testing.sh
Executable file
281
tools/configure_for_func_testing.sh
Executable file
@ -0,0 +1,281 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 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.
|
||||
|
||||
|
||||
set -e
|
||||
|
||||
|
||||
# Control variable used to determine whether to execute this script
|
||||
# directly or allow the gate_hook to import.
|
||||
IS_GATE=${IS_GATE:-False}
|
||||
USE_CONSTRAINT_ENV=${USE_CONSTRAINT_ENV:-True}
|
||||
|
||||
|
||||
if [[ "$IS_GATE" != "True" ]] && [[ "$#" -lt 1 ]]; then
|
||||
>&2 echo "Usage: $0 /path/to/devstack [-i]
|
||||
Configure a host to run Neutron's functional test suite.
|
||||
|
||||
-i Install Neutron's package dependencies. By default, it is assumed
|
||||
that devstack has already been used to deploy neutron-fwaas to the
|
||||
target host and that package dependencies need not be installed.
|
||||
|
||||
Warning: This script relies on devstack to perform extensive
|
||||
modification to the underlying host. It is recommended that it be
|
||||
invoked only on a throw-away VM."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# Skip the first argument
|
||||
OPTIND=2
|
||||
while getopts ":i" opt; do
|
||||
case $opt in
|
||||
i)
|
||||
INSTALL_BASE_DEPENDENCIES=True
|
||||
;;
|
||||
esac
|
||||
|
||||
done
|
||||
|
||||
# Default to environment variables to permit the gate_hook to override
|
||||
# when sourcing.
|
||||
VENV=${VENV:-dsvm-functional}
|
||||
DEVSTACK_PATH=${DEVSTACK_PATH:-$1}
|
||||
PROJECT_NAME=${PROJECT_NAME:-neutron-fwaas}
|
||||
REPO_BASE=${GATE_DEST:-$(cd $(dirname "$0")/../.. && pwd)}
|
||||
INSTALL_MYSQL_ONLY=${INSTALL_MYSQL_ONLY:-False}
|
||||
# The gate should automatically install dependencies.
|
||||
INSTALL_BASE_DEPENDENCIES=${INSTALL_BASE_DEPENDENCIES:-$IS_GATE}
|
||||
|
||||
|
||||
if [ ! -f "$DEVSTACK_PATH/stack.sh" ]; then
|
||||
>&2 echo "Unable to find devstack at '$DEVSTACK_PATH'. Please verify that the specified path points to a valid devstack repo."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
set -x
|
||||
|
||||
|
||||
function _init {
|
||||
# Subsequently-called devstack functions depend on the following variables.
|
||||
HOST_IP=127.0.0.1
|
||||
FILES=$DEVSTACK_PATH/files
|
||||
TOP_DIR=$DEVSTACK_PATH
|
||||
|
||||
source $DEVSTACK_PATH/stackrc
|
||||
|
||||
# Allow the gate to override values set by stackrc.
|
||||
DEST=${GATE_DEST:-$DEST}
|
||||
STACK_USER=${GATE_STACK_USER:-$STACK_USER}
|
||||
}
|
||||
|
||||
|
||||
function _install_base_deps {
|
||||
echo_summary "Installing base dependencies"
|
||||
|
||||
INSTALL_TESTONLY_PACKAGES=True
|
||||
PACKAGES=$(get_packages general,neutron,q-agt,q-l3)
|
||||
# Do not install 'python-' prefixed packages other than
|
||||
# python-dev*. Neutron's functional testing relies on deployment
|
||||
# to a tox env so there is no point in installing python
|
||||
# dependencies system-wide.
|
||||
PACKAGES=$(echo $PACKAGES | perl -pe 's|python-(?!dev)[^ ]*||g')
|
||||
install_package $PACKAGES
|
||||
}
|
||||
|
||||
|
||||
function _install_rpc_backend {
|
||||
echo_summary "Installing rabbitmq"
|
||||
|
||||
RABBIT_USERID=${RABBIT_USERID:-stackrabbit}
|
||||
RABBIT_HOST=${RABBIT_HOST:-$SERVICE_HOST}
|
||||
RABBIT_PASSWORD=${RABBIT_HOST:-secretrabbit}
|
||||
|
||||
source $DEVSTACK_PATH/lib/rpc_backend
|
||||
|
||||
enable_service rabbit
|
||||
install_rpc_backend
|
||||
restart_rpc_backend
|
||||
}
|
||||
|
||||
|
||||
# _install_databases [install_pg]
|
||||
function _install_databases {
|
||||
local install_pg=${1:-True}
|
||||
|
||||
echo_summary "Installing databases"
|
||||
|
||||
# Avoid attempting to configure the db if it appears to already
|
||||
# have run. The setup as currently defined is not idempotent.
|
||||
if mysql openstack_citest > /dev/null 2>&1 < /dev/null; then
|
||||
echo_summary "DB config appears to be complete, skipping."
|
||||
return 0
|
||||
fi
|
||||
|
||||
MYSQL_PASSWORD=${MYSQL_PASSWORD:-secretmysql}
|
||||
DATABASE_PASSWORD=${DATABASE_PASSWORD:-secretdatabase}
|
||||
|
||||
source $DEVSTACK_PATH/lib/database
|
||||
|
||||
enable_service mysql
|
||||
initialize_database_backends
|
||||
install_database
|
||||
configure_database_mysql
|
||||
|
||||
if [[ "$install_pg" == "True" ]]; then
|
||||
enable_service postgresql
|
||||
initialize_database_backends
|
||||
install_database
|
||||
configure_database_postgresql
|
||||
fi
|
||||
|
||||
# Set up the 'openstack_citest' user and database in each backend
|
||||
tmp_dir=$(mktemp -d)
|
||||
trap "rm -rf $tmp_dir" EXIT
|
||||
|
||||
cat << EOF > $tmp_dir/mysql.sql
|
||||
CREATE DATABASE openstack_citest;
|
||||
CREATE USER 'openstack_citest'@'localhost' IDENTIFIED BY 'openstack_citest';
|
||||
CREATE USER 'openstack_citest' IDENTIFIED BY 'openstack_citest';
|
||||
GRANT ALL PRIVILEGES ON *.* TO 'openstack_citest'@'localhost';
|
||||
GRANT ALL PRIVILEGES ON *.* TO 'openstack_citest';
|
||||
FLUSH PRIVILEGES;
|
||||
EOF
|
||||
/usr/bin/mysql -u root < $tmp_dir/mysql.sql
|
||||
|
||||
if [[ "$install_pg" == "True" ]]; then
|
||||
cat << EOF > $tmp_dir/postgresql.sql
|
||||
CREATE USER openstack_citest WITH CREATEDB LOGIN PASSWORD 'openstack_citest';
|
||||
CREATE DATABASE openstack_citest WITH OWNER openstack_citest;
|
||||
EOF
|
||||
|
||||
# User/group postgres needs to be given access to tmp_dir
|
||||
setfacl -m g:postgres:rwx $tmp_dir
|
||||
sudo -u postgres /usr/bin/psql --file=$tmp_dir/postgresql.sql
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function _install_agent_deps {
|
||||
echo_summary "Installing agent dependencies"
|
||||
|
||||
ENABLED_SERVICES=q-agt,q-dhcp,q-l3
|
||||
install_neutron_agent_packages
|
||||
}
|
||||
|
||||
|
||||
# Set up the rootwrap sudoers for neutron to target the rootwrap
|
||||
# configuration deployed in the venv.
|
||||
function _install_rootwrap_sudoers {
|
||||
echo_summary "Installing rootwrap sudoers file"
|
||||
|
||||
PROJECT_VENV=$REPO_BASE/$PROJECT_NAME/.tox/$VENV
|
||||
ROOTWRAP_SUDOER_CMD="$PROJECT_VENV/bin/neutron-rootwrap $PROJECT_VENV/etc/neutron/rootwrap.conf *"
|
||||
ROOTWRAP_DAEMON_SUDOER_CMD="$PROJECT_VENV/bin/neutron-rootwrap-daemon $PROJECT_VENV/etc/neutron/rootwrap.conf"
|
||||
TEMPFILE=$(mktemp)
|
||||
cat << EOF > $TEMPFILE
|
||||
# A bug in oslo.rootwrap [1] prevents commands executed with 'ip netns
|
||||
# exec' from being automatically qualified with a prefix from
|
||||
# rootwrap's configured exec_dirs. To work around this problem, add
|
||||
# the venv bin path to a user-specific secure_path.
|
||||
#
|
||||
# While it might seem preferable to set a command-specific
|
||||
# secure_path, this would only ensure the correct path for 'ip netns
|
||||
# exec' and the command targeted for execution in the namespace would
|
||||
# not inherit the path.
|
||||
#
|
||||
# 1: https://bugs.launchpad.net/oslo.rootwrap/+bug/1417331
|
||||
#
|
||||
Defaults:$STACK_USER secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PROJECT_VENV/bin"
|
||||
$STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_SUDOER_CMD
|
||||
$STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_DAEMON_SUDOER_CMD
|
||||
EOF
|
||||
chmod 0440 $TEMPFILE
|
||||
sudo chown root:root $TEMPFILE
|
||||
# Name the functional testing rootwrap to ensure that it will be
|
||||
# loaded after the devstack rootwrap (50_stack_sh if present) so
|
||||
# that the functional testing secure_path (a superset of what
|
||||
# devstack expects) will not be overwritten.
|
||||
sudo mv $TEMPFILE /etc/sudoers.d/60-neutron-func-test-rootwrap
|
||||
}
|
||||
|
||||
|
||||
function _install_post_devstack {
|
||||
echo_summary "Performing post-devstack installation"
|
||||
|
||||
_install_databases
|
||||
_install_rootwrap_sudoers
|
||||
|
||||
if is_ubuntu; then
|
||||
install_package isc-dhcp-client
|
||||
install_package netcat-openbsd
|
||||
elif is_fedora; then
|
||||
install_package dhclient
|
||||
else
|
||||
exit_distro_not_supported "installing dhclient package"
|
||||
fi
|
||||
|
||||
# Installing python-openvswitch from packages is a stop-gap while
|
||||
# python-openvswitch remains unavailable from pypi. This also
|
||||
# requires that sitepackages=True be set in tox.ini to allow the
|
||||
# venv to use the installed package. Once python-openvswitch
|
||||
# becomes available on pypi, this will no longer be required.
|
||||
#
|
||||
# NOTE: the package name 'python-openvswitch' is common across
|
||||
# supported distros.
|
||||
install_package python-openvswitch
|
||||
|
||||
enable_kernel_bridge_firewall
|
||||
}
|
||||
|
||||
|
||||
function _configure_iptables_rules {
|
||||
# For linuxbridge agent fullstack tests we need to add special rules to
|
||||
# iptables for connection of agents to rabbitmq:
|
||||
CHAIN_NAME="openstack-INPUT"
|
||||
sudo iptables -n --list $CHAIN_NAME 1> /dev/null 2>&1 || CHAIN_NAME="INPUT"
|
||||
sudo iptables -I $CHAIN_NAME -s 240.0.0.0/8 -p tcp -m tcp -d 240.0.0.0/8 --dport 5672 -j ACCEPT
|
||||
}
|
||||
|
||||
|
||||
function configure_host_for_func_testing {
|
||||
echo_summary "Configuring host for functional testing"
|
||||
|
||||
if [[ "$INSTALL_BASE_DEPENDENCIES" == "True" ]]; then
|
||||
# Installing of the following can be achieved via devstack by
|
||||
# installing neutron, so their installation is conditional to
|
||||
# minimize the work to do on a devstack-configured host.
|
||||
_install_base_deps
|
||||
_install_agent_deps
|
||||
_install_rpc_backend
|
||||
fi
|
||||
_install_post_devstack
|
||||
}
|
||||
|
||||
|
||||
_init
|
||||
|
||||
|
||||
if [[ "$IS_GATE" != "True" ]]; then
|
||||
if [[ "$INSTALL_MYSQL_ONLY" == "True" ]]; then
|
||||
_install_databases nopg
|
||||
else
|
||||
configure_host_for_func_testing
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$VENV" =~ "dsvm-fullstack" ]]; then
|
||||
_configure_iptables_rules
|
||||
fi
|
29
tox.ini
29
tox.ini
@ -18,11 +18,38 @@ commands =
|
||||
# there is also secret magic in ostestr which lets you run in a fail only
|
||||
# mode. To do this define the TRACE_FAILONLY environmental variable.
|
||||
|
||||
|
||||
[testenv:common]
|
||||
# Fake job to define environment variables shared between dsvm/non-dsvm jobs
|
||||
setenv = OS_TEST_TIMEOUT=180
|
||||
commands = false
|
||||
|
||||
[testenv:dsvm]
|
||||
# Fake job to define environment variables shared between dsvm jobs
|
||||
setenv = OS_SUDO_TESTING=1
|
||||
OS_ROOTWRAP_CMD=sudo {envdir}/bin/neutron-rootwrap {envdir}/etc/neutron/rootwrap.conf
|
||||
OS_ROOTWRAP_DAEMON_CMD=sudo {envdir}/bin/neutron-rootwrap-daemon {envdir}/etc/neutron/rootwrap.conf
|
||||
OS_FAIL_ON_MISSING_DEPS=1
|
||||
OS_LOG_PATH={env:OS_LOG_PATH:/opt/stack/logs}
|
||||
commands = false
|
||||
|
||||
[testenv:functional]
|
||||
setenv = OS_TEST_PATH=./neutron_fwaas/tests/functional
|
||||
setenv = {[testenv]setenv}
|
||||
{[testenv:common]setenv}
|
||||
OS_TEST_PATH=./neutron_fwaas/tests/functional
|
||||
OS_LOG_PATH={env:OS_LOG_PATH:/opt/stack/logs}
|
||||
commands =
|
||||
python setup.py testr --slowest --testr-args='{posargs}'
|
||||
|
||||
[testenv:dsvm-fullstack]
|
||||
setenv = {[testenv]setenv}
|
||||
{[testenv:common]setenv}
|
||||
{[testenv:dsvm]setenv}
|
||||
# workaround for DB teardown lock contention (bug/1541742)
|
||||
OS_TEST_TIMEOUT=600
|
||||
OS_TEST_PATH=./neutron_fwaas/tests/fullstack
|
||||
sitepackages=True
|
||||
|
||||
[testenv:api]
|
||||
sitepackages=True
|
||||
setenv =
|
||||
|
Loading…
Reference in New Issue
Block a user