Merge pull request #30 from thedac/feature/dragent

Neutron dynamic routing testing
This commit is contained in:
Frode Nordahl
2018-04-20 19:40:34 +02:00
committed by GitHub
16 changed files with 845 additions and 76 deletions
+1
View File
@@ -14,6 +14,7 @@ install_require = [
'juju',
'juju-wait',
'PyYAML',
'tenacity',
]
tests_require = [
+1
View File
@@ -24,6 +24,7 @@ python-keystoneclient
python-neutronclient
python-novaclient
python-swiftclient
tenacity
distro-info
paramiko
@@ -152,7 +152,7 @@ class TestCharmLifecycleDeploy(ut_utils.BaseTestCase):
self.patch_object(lc_deploy.juju_wait, 'wait')
lc_deploy.deploy('bun.yaml', 'newmodel')
self.deploy_bundle.assert_called_once_with('bun.yaml', 'newmodel')
self.wait.assert_called_once_with()
self.wait.assert_called_once_with(wait_for_workload=True)
def test_deploy_nowait(self):
self.patch_object(lc_deploy, 'deploy_bundle')
View File
@@ -0,0 +1,88 @@
import mock
import unit_tests.utils as ut_utils
from zaza.utilities import _local_utils
class TestLocalUtils(ut_utils.BaseTestCase):
def setUp(self):
super(TestLocalUtils, self).setUp()
def test_get_yaml_config(self):
self.patch("builtins.open",
new_callable=mock.mock_open(),
name="_open")
_yaml = "data: somedata"
_yaml_dict = {"data": "somedata"}
_filename = "filename"
_fileobj = mock.MagicMock()
_fileobj.read.return_value = _yaml
self._open.return_value = _fileobj
self.assertEqual(_local_utils.get_yaml_config(_filename),
_yaml_dict)
self._open.assert_called_once_with(_filename, "r")
def test_get_undercloud_env_vars(self):
self.patch_object(_local_utils.os.environ, "get")
def _get_env(key):
return _env.get(key)
self.get.side_effect = _get_env
# OSCI backward compatible env vars
_env = {"NET_ID": "netid",
"NAMESERVER": "10.0.0.10",
"GATEWAY": "10.0.0.1",
"CIDR_EXT": "10.0.0.0/24",
"FIP_RANGE": "10.0.200.0:10.0.200.254"}
_expected_result = {}
_expected_result["net_id"] = _env["NET_ID"]
_expected_result["external_dns"] = _env["NAMESERVER"]
_expected_result["default_gateway"] = _env["GATEWAY"]
_expected_result["external_net_cidr"] = _env["CIDR_EXT"]
_expected_result["start_floating_ip"] = _env["FIP_RANGE"].split(":")[0]
_expected_result["end_floating_ip"] = _env["FIP_RANGE"].split(":")[1]
self.assertEqual(_local_utils.get_undercloud_env_vars(),
_expected_result)
# Overriding configure.network named variables
_override = {"start_floating_ip": "10.100.50.0",
"end_floating_ip": "10.100.50.254",
"default_gateway": "10.100.0.1",
"external_net_cidr": "10.100.0.0/16"}
_env.update(_override)
_expected_result.update(_override)
self.assertEqual(_local_utils.get_undercloud_env_vars(),
_expected_result)
def test_get_net_info(self):
self.patch_object(_local_utils.os.path, "exists")
self.patch_object(_local_utils, "get_yaml_config")
self.patch_object(_local_utils, "get_undercloud_env_vars")
net_topology = "topo"
_data = {net_topology: {"network": "DATA"}}
self.get_yaml_config.return_value = _data
# YAML file does not exist
self.exists.return_value = False
with self.assertRaises(Exception):
_local_utils.get_net_info(net_topology)
# No environmental variables
self.exists.return_value = True
self.assertEqual(
_local_utils.get_net_info(net_topology, ignore_env_vars=True),
_data[net_topology])
self.get_yaml_config.assert_called_once_with("network.yaml")
self.get_undercloud_env_vars.assert_not_called()
# Update with environmental variables
_more_data = {"network": "NEW",
"other": "DATA"}
self.get_undercloud_env_vars.return_value = _more_data
_data[net_topology].update(_more_data)
self.assertEqual(
_local_utils.get_net_info(net_topology),
_data[net_topology])
self.get_undercloud_env_vars.assert_called_once_with()
@@ -1,3 +1,4 @@
import copy
import mock
import unit_tests.utils as ut_utils
from zaza.utilities import openstack_utils
@@ -7,20 +8,41 @@ class TestOpenStackUtils(ut_utils.BaseTestCase):
def setUp(self):
super(TestOpenStackUtils, self).setUp()
self.port_name = 'port_name'
self.net_uuid = 'uuid'
self.port_name = "port_name"
self.net_uuid = "net_uuid"
self.project_id = "project_uuid"
self.ext_net = "ext_net"
self.private_net = "private_net"
self.port = {
'port': {'id': 'port_id',
'name': self.port_name,
'network_id': self.net_uuid}}
self.ports = {'ports': [self.port['port']]}
"port": {"id": "port_id",
"name": self.port_name,
"network_id": self.net_uuid}}
self.ports = {"ports": [self.port["port"]]}
self.floatingip = {
'floatingip': {'id': 'floatingip_id',
'floating_network_id': self.net_uuid,
'port_id': 'port_id'}}
"floatingip": {"id": "floatingip_id",
"floating_network_id": self.net_uuid,
"port_id": "port_id"}}
self.floatingips = {"floatingips": [self.floatingip["floatingip"]]}
self.address_scope_name = "address_scope_name"
self.address_scope = {
"address_scope": {"id": "address_scope_id",
"name": self.address_scope_name,
"shared": True,
"ip_version": 4,
"tenant_id": self.project_id}}
self.address_scopes = {
"address_scopes": [self.address_scope["address_scope"]]}
self.floatingips = {'floatingips': [self.floatingip['floatingip']]}
self.network = {
"network": {"id": "network_id",
"name": self.ext_net,
"tenant_id": self.project_id,
"router:external": True,
"provider:physical_network": "physnet1",
"provider:network_type": "flat"}}
self.networks = {
"networks": [self.network["network"]]}
self.neutronclient = mock.MagicMock()
self.neutronclient.list_ports.return_value = self.ports
@@ -28,42 +50,124 @@ class TestOpenStackUtils(ut_utils.BaseTestCase):
self.neutronclient.list_floatingips.return_value = self.floatingips
self.neutronclient.create_floatingip.return_value = self.floatingip
self.ext_net = 'ext_net'
self.private_net = 'private_net'
self.neutronclient.list_address_scopes.return_value = (
self.address_scopes)
self.neutronclient.create_address_scope.return_value = (
self.address_scope)
self.neutronclient.list_networks.return_value = self.networks
self.neutronclient.create_network.return_value = self.network
def test_create_port(self):
self.patch_object(openstack_utils, 'get_net_uuid')
self.patch_object(openstack_utils, "get_net_uuid")
self.get_net_uuid.return_value = self.net_uuid
# Already exists
port = openstack_utils.create_port(
self.neutronclient, self.port_name, self.private_net)
self.assertEqual(port, self.port['port'])
self.assertEqual(port, self.port["port"])
self.neutronclient.create_port.assert_not_called()
# Does not yet exist
self.neutronclient.list_ports.return_value = {'ports': []}
self.port['port'].pop('id')
self.neutronclient.list_ports.return_value = {"ports": []}
self.port["port"].pop("id")
port = openstack_utils.create_port(
self.neutronclient, self.port_name, self.private_net)
self.assertEqual(port, self.port['port'])
self.assertEqual(port, self.port["port"])
self.neutronclient.create_port.assert_called_once_with(self.port)
def test_create_floating_ip(self):
self.patch_object(openstack_utils, 'get_net_uuid')
self.patch_object(openstack_utils, "get_net_uuid")
self.get_net_uuid.return_value = self.net_uuid
# Already exists
floatingip = openstack_utils.create_floating_ip(
self.neutronclient, self.ext_net, port=self.port['port'])
self.assertEqual(floatingip, self.floatingip['floatingip'])
self.neutronclient, self.ext_net, port=self.port["port"])
self.assertEqual(floatingip, self.floatingip["floatingip"])
self.neutronclient.create_floatingip.assert_not_called()
# Does not yet exist
self.neutronclient.list_floatingips.return_value = {'floatingips': []}
self.floatingip['floatingip'].pop('id')
self.neutronclient.list_floatingips.return_value = {"floatingips": []}
self.floatingip["floatingip"].pop("id")
floatingip = openstack_utils.create_floating_ip(
self.neutronclient, self.private_net, port=self.port['port'])
self.assertEqual(floatingip, self.floatingip['floatingip'])
self.neutronclient, self.private_net, port=self.port["port"])
self.assertEqual(floatingip, self.floatingip["floatingip"])
self.neutronclient.create_floatingip.assert_called_once_with(
self.floatingip)
def test_create_address_scope(self):
self.patch_object(openstack_utils, "get_net_uuid")
self.get_net_uuid.return_value = self.net_uuid
# Already exists
address_scope = openstack_utils.create_address_scope(
self.neutronclient, self.project_id, self.address_scope_name)
self.assertEqual(address_scope, self.address_scope["address_scope"])
self.neutronclient.create_address_scope.assert_not_called()
# Does not yet exist
self.neutronclient.list_address_scopes.return_value = {
"address_scopes": []}
address_scope_msg = copy.deepcopy(self.address_scope)
address_scope_msg["address_scope"].pop("id")
address_scope = openstack_utils.create_address_scope(
self.neutronclient, self.project_id, self.address_scope_name)
self.assertEqual(address_scope, self.address_scope["address_scope"])
self.neutronclient.create_address_scope.assert_called_once_with(
address_scope_msg)
def test_create_external_network(self):
self.patch_object(openstack_utils, "get_net_uuid")
self.get_net_uuid.return_value = self.net_uuid
# Already exists
network = openstack_utils.create_external_network(
self.neutronclient, self.project_id, False)
self.assertEqual(network, self.network["network"])
self.neutronclient.create_network.assert_not_called()
# Does not yet exist
self.neutronclient.list_networks.return_value = {
"networks": []}
network_msg = copy.deepcopy(self.network)
network_msg["network"].pop("id")
network = openstack_utils.create_external_network(
self.neutronclient, self.project_id, False)
self.assertEqual(network, self.network["network"])
self.neutronclient.create_network.assert_called_once_with(
network_msg)
def test_get_keystone_scope(self):
self.patch_object(openstack_utils, "get_current_os_versions")
# <= Liberty
self.get_current_os_versions.return_value = {"keystone": "liberty"}
self.assertEqual(openstack_utils.get_keystone_scope(), "DOMAIN")
# > Liberty
self.get_current_os_versions.return_value = {"keystone": "mitaka"}
self.assertEqual(openstack_utils.get_keystone_scope(), "PROJECT")
def test_get_overcloud_keystone_session(self):
self.patch_object(openstack_utils, "get_keystone_session")
self.patch_object(openstack_utils, "get_keystone_scope")
self.patch_object(openstack_utils, "get_overcloud_auth")
_auth = "FAKE_AUTH"
_scope = "PROJECT"
self.get_keystone_scope.return_value = _scope
self.get_overcloud_auth.return_value = _auth
openstack_utils.get_overcloud_keystone_session()
self.get_keystone_session.assert_called_once_with(_auth, scope=_scope)
def test_get_undercloud_keystone_session(self):
self.patch_object(openstack_utils, "get_keystone_session")
self.patch_object(openstack_utils, "get_keystone_scope")
self.patch_object(openstack_utils, "get_undercloud_auth")
_auth = "FAKE_AUTH"
_scope = "PROJECT"
self.get_keystone_scope.return_value = _scope
self.get_undercloud_auth.return_value = _auth
openstack_utils.get_undercloud_keystone_session()
self.get_keystone_session.assert_called_once_with(_auth, scope=_scope)
+1 -1
View File
@@ -224,7 +224,7 @@ def deploy(bundle, model, wait=True):
if wait:
logging.info("Waiting for environment to settle")
utils.set_juju_model(model)
juju_wait.wait()
juju_wait.wait(wait_for_workload=True)
def parse_args(args):
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
from zaza.configure import (
network,
bgp_speaker,
)
from zaza.utilities import (
_local_utils,
openstack_utils,
)
DEFAULT_PEER_APPLICATION_NAME = "quagga"
# The overcloud network configuration settings are declared.
# These are the network configuration settings under test.
OVERCLOUD_NETWORK_CONFIG = {
"network_type": "gre",
"router_name": "provider-router",
"ip_version": "4",
"address_scope": "public",
"external_net_name": "ext_net",
"external_subnet_name": "ext_net_subnet",
"prefix_len": "24",
"subnetpool_name": "pooled_subnets",
"subnetpool_prefix": "192.168.0.0/16",
}
# The undercloud network configuration settings are substrate specific to
# the environment where the tests are being executed. These settings may be
# overridden by environment variables. See the doc string documentation for
# zaza.utilities._local_utils.get_overcloud_env_vars for the environment
# variables required to be exported and available to zaza.
# These are default settings provided as an example.
DEFAULT_UNDERCLOUD_NETWORK_CONFIG = {
"start_floating_ip": "10.5.150.0",
"end_floating_ip": "10.5.150.254",
"external_dns": "10.5.0.2",
"external_net_cidr": "10.5.0.0/16",
"default_gateway": "10.5.0.1",
}
def setup():
"""Setup BGP networking
Configure the following:
The overcloud network using subnet pools
The overcloud BGP speaker
The BGP peer
Advertising of the FIPs via BGP
Advertising of the project network(s) via BGP
:returns: None
:rtype: None
"""
_local_utils.setup_logging()
# Get network configuration settings
network_config = {}
# Declared overcloud settings
network_config.update(OVERCLOUD_NETWORK_CONFIG)
# Default undercloud settings
network_config.update(DEFAULT_UNDERCLOUD_NETWORK_CONFIG)
# Environment specific settings
network_config.update(_local_utils.get_undercloud_env_vars())
# Get keystone session
keystone_session = openstack_utils.get_overcloud_keystone_session()
# Confugre the overcloud network
network.setup_sdn(network_config, keystone_session=keystone_session)
# Configure BGP
bgp_speaker.setup_bgp_speaker(
peer_application_name=DEFAULT_PEER_APPLICATION_NAME,
keystone_session=keystone_session)
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env python3
import argparse
import logging
import sys
import tenacity
from zaza import model
from zaza.charm_lifecycle import utils as lifecycle_utils
from zaza.utilities import (
_local_utils,
openstack_utils,
)
def test_bgp_routes(peer_application_name="quagga", keystone_session=None):
"""Test BGP routes
:param peer_application_name: String name of BGP peer application
:type peer_application_name: string
:param keystone_session: Keystone session object for overcloud
:type keystone_session: keystoneauth1.session.Session object
:raises: AssertionError if expected BGP routes are not found
:returns: None
:rtype: None
"""
# If a session has not been provided, acquire one
if not keystone_session:
keystone_session = openstack_utils.get_overcloud_keystone_session()
# Get authenticated clients
neutron_client = openstack_utils.get_neutron_session_client(
keystone_session)
# Get the peer unit
peer_unit = model.get_units(
lifecycle_utils.get_juju_model(), peer_application_name)[0].entity_id
# Get expected advertised routes
private_cidr = neutron_client.list_subnets(
name="private_subnet")["subnets"][0]["cidr"]
floating_ip_cidr = "{}/32".format(
neutron_client.list_floatingips()
["floatingips"][0]["floating_ip_address"])
# This test may run immediately after configuration. It may take time for
# routes to propogate via BGP. Do a binary backoff.
@tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, max=60),
reraise=True, stop=tenacity.stop_after_attempt(8))
def _assert_cidr_in_peer_routing_table(peer_unit, cidr):
logging.debug("Checking for {} on BGP peer {}"
.format(cidr, peer_unit))
# Run show ip route bgp on BGP peer
routes = _local_utils.remote_run(
peer_unit, remote_cmd='vtysh -c "show ip route bgp"')
logging.debug(routes)
assert cidr in routes, (
"CIDR, {}, not found in BGP peer's routing table" .format(cidr))
_assert_cidr_in_peer_routing_table(peer_unit, private_cidr)
logging.info("Private subnet CIDR, {}, found in routing table"
.format(private_cidr))
_assert_cidr_in_peer_routing_table(peer_unit, floating_ip_cidr)
logging.info("Floating IP CIDR, {}, found in routing table"
.format(floating_ip_cidr))
def run_from_cli():
"""Run test for BGP routes from CLI
:returns: None
:rtype: None
"""
_local_utils.setup_logging()
parser = argparse.ArgumentParser()
parser.add_argument("--peer-application", "-a",
help="BGP Peer application name. Default: quagga",
default="quagga")
options = parser.parse_args()
peer_application_name = _local_utils.parse_arg(options,
"peer_application")
test_bgp_routes(peer_application_name)
if __name__ == "__main__":
sys.exit(run_from_cli())
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env python3
import unittest
from zaza.utilities import _local_utils
from zaza.charm_tests.dragent import test
class DRAgentTest(unittest.TestCase):
BGP_PEER_APPLICATION = 'quagga'
@classmethod
def setUpClass(cls):
_local_utils.setup_logging()
def test_bgp_routes(self):
test.test_bgp_routes(peer_application_name=self.BGP_PEER_APPLICATION)
if __name__ == "__main__":
unittest.main()
View File
+91
View File
@@ -0,0 +1,91 @@
#!/usr/bin/env python3
import argparse
import logging
import sys
from zaza.utilities import _local_utils
from zaza.utilities import openstack_utils
EXT_NET = "ext_net"
PRIVATE_NET = "private"
FIP_TEST = "FIP TEST"
def setup_bgp_speaker(peer_application_name, keystone_session=None):
"""Setup BGP Speaker
:param peer_application_name: String name of BGP peer application
:type peer_application_name: string
:param keystone_session: Keystone session object for overcloud
:type keystone_session: keystoneauth1.session.Session object
:returns: None
:rtype: None
"""
# If a session has not been provided, acquire one
if not keystone_session:
keystone_session = openstack_utils.get_overcloud_keystone_session()
# Get authenticated clients
neutron_client = openstack_utils.get_neutron_session_client(
keystone_session)
# Create BGP speaker
logging.info("Setting up BGP speaker")
bgp_speaker = openstack_utils.create_bgp_speaker(
neutron_client, local_as=12345)
# Add networks to bgp speaker
logging.info("Advertising BGP routes")
openstack_utils.add_network_to_bgp_speaker(
neutron_client, bgp_speaker, EXT_NET)
openstack_utils.add_network_to_bgp_speaker(
neutron_client, bgp_speaker, PRIVATE_NET)
logging.debug("Advertised routes: {}"
.format(
neutron_client.list_route_advertised_from_bgp_speaker(
bgp_speaker["id"])))
# Create peer
logging.info("Setting up BGP peer")
bgp_peer = openstack_utils.create_bgp_peer(neutron_client,
peer_application_name,
remote_as=10000)
# Add peer to bgp speaker
logging.info("Adding BGP peer to BGP speaker")
openstack_utils.add_peer_to_bgp_speaker(
neutron_client, bgp_speaker, bgp_peer)
# Create Floating IP to advertise
logging.info("Creating floating IP to advertise")
port = openstack_utils.create_port(neutron_client, FIP_TEST, PRIVATE_NET)
floating_ip = openstack_utils.create_floating_ip(neutron_client, EXT_NET,
port=port)
logging.info(
"Advertised floating IP: {}".format(
floating_ip["floating_ip_address"]))
def run_from_cli():
"""Run BGP Speaker setup from CLI
:returns: None
:rtype: None
"""
_local_utils.setup_logging()
parser = argparse.ArgumentParser()
parser.add_argument("--peer-application", "-a",
help="BGP peer application name. Default: quagga",
default="quagga")
options = parser.parse_args()
peer_application_name = _local_utils.parse_arg(options,
"peer_application")
setup_bgp_speaker(peer_application_name)
if __name__ == "__main__":
sys.exit(run_from_cli())
+254
View File
@@ -0,0 +1,254 @@
#!/usr/bin/env python3
import argparse
import logging
import sys
from zaza.utilities import _local_utils
from zaza.utilities import openstack_utils
"""Configure network
For these network configuration functions there are two distinct sets of
settings. There is the configuration of the overcloud's network, the network
under test, settings which may vary from test to test. Then there is the
configuration of the substrate specific undercloud which may vary from one test
environment to another. All of this information is required to setup a valid
test. For the purposes of using these functions please consider the following:
The overcloud network configuration settings are declared by the test caller.
These are the network configuration settings under test and they may range from
a simple GRE setup, to a DVR configuration, to subnetpools with address scopes.
Each test caller may declare a slightly different set of configuration
settings. Here is a simple GRE example:
EXAMPLE_OVERCLOUD_NETWORK_CONFIG = {
"network_type": "gre",
"router_name": "provider-router",
"private_net_cidr": "192.168.21.0/24",
"external_net_name": "ext_net",
"external_subnet_name": "ext_net_subnet",
}
The undercloud network configuration settings are substrate specific to the
environment where the tests are being executed. They primarily focus on the
provider network settings. These settings may be overridden by environment
variables. See the doc string documentation for
zaza.utilities._local_utils.get_overcloud_env_vars for the environment
variables required to be exported and available to zaza. Here is an example of
undercloud settings:
EXAMPLE_DEFAULT_UNDERCLOUD_NETWORK_CONFIG = {
"start_floating_ip": "10.5.150.0",
"end_floating_ip": "10.5.150.254",
"external_dns": "10.5.0.2",
"external_net_cidr": "10.5.0.0/16",
"default_gateway": "10.5.0.1",
}
The network configuration functions take in a dictionary parameter called
network_config and it is a combination of the above including environmental
overrides. The recommended use case is as follows:
As a python module:
import zaza
# Build network configuration settings
network_config = {}
# Declared overcloud settings for the network under test
network_config.update(EXAMPLE_OVERCLOUD_NETWORK_CONFIG)
# Default undercloud settings
network_config.update(EXAMPLE_DEFAULT_UNDERCLOUD_NETWORK_CONFIG)
# Environment specific settings
network_config.update(
zaza.utilities._local_utils.get_undercloud_env_vars())
# Configure the SDN network
zaza.configure.network.setup_sdn(network_config)
As a script from CLI with a YAML file of configuration:
./network toploogy_name -f network.yaml
"""
def setup_sdn(network_config, keystone_session=None):
"""Setup Software Defined Network
:param network_config: Network configuration settings dictionary
:type network_config: dict
:param keystone_session: Keystone session object for overcloud
:type keystone_session: keystoneauth1.session.Session object
:returns: None
:rtype: None
"""
# If a session has not been provided, acquire one
if not keystone_session:
keystone_session = openstack_utils.get_overcloud_keystone_session()
# Get authenticated clients
keystone_client = openstack_utils.get_keystone_session_client(
keystone_session)
neutron_client = openstack_utils.get_neutron_session_client(
keystone_session)
# Resolve the project name from the overcloud openrc into a project id
project_id = openstack_utils.get_project_id(
keystone_client,
"admin",
)
# Network Setup
subnetpools = False
if network_config.get("subnetpool_prefix"):
subnetpools = True
logging.info("Configuring overcloud network")
# Create the external network
ext_network = openstack_utils.create_external_network(
neutron_client,
project_id,
network_config.get("dvr_enabled", False),
network_config["external_net_name"])
openstack_utils.create_external_subnet(
neutron_client,
project_id,
ext_network,
network_config["default_gateway"],
network_config["external_net_cidr"],
network_config["start_floating_ip"],
network_config["end_floating_ip"],
network_config["external_subnet_name"])
provider_router = (
openstack_utils.create_provider_router(neutron_client, project_id))
openstack_utils.plug_extnet_into_router(
neutron_client,
provider_router,
ext_network)
ip_version = network_config.get("ip_version") or 4
subnetpool = None
if subnetpools:
address_scope = openstack_utils.create_address_scope(
neutron_client,
project_id,
network_config.get("address_scope"),
ip_version=ip_version)
subnetpool = openstack_utils.create_subnetpool(
neutron_client,
project_id,
network_config.get("subnetpool_name"),
network_config.get("subnetpool_prefix"),
address_scope)
project_network = openstack_utils.create_project_network(
neutron_client,
project_id,
shared=False,
network_type=network_config["network_type"])
project_subnet = openstack_utils.create_project_subnet(
neutron_client,
project_id,
project_network,
network_config.get("private_net_cidr"),
subnetpool=subnetpool,
ip_version=ip_version)
openstack_utils.update_subnet_dns(
neutron_client,
project_subnet,
network_config["external_dns"])
openstack_utils.plug_subnet_into_router(
neutron_client,
network_config["router_name"],
project_network,
project_subnet)
openstack_utils.add_neutron_secgroup_rules(neutron_client, project_id)
def setup_gateway_ext_port(network_config, keystone_session=None):
"""Setup external port on Neutron Gateway.
For OpenStack on OpenStack scenarios
:param network_config: Network configuration dictionary
:type network_config: dict
:param keystone_session: Keystone session object for undercloud
:type keystone_session: keystoneauth1.session.Session object
:returns: None
:rtype: None
"""
# If a session has not been provided, acquire one
if not keystone_session:
keystone_session = openstack_utils.get_undercloud_keystone_session()
# Get authenticated clients
nova_client = openstack_utils.get_nova_session_client(keystone_session)
neutron_client = openstack_utils.get_neutron_session_client(
keystone_session)
# Add an interface to the neutron-gateway units and tell juju to use it
# as the external port.
if "net_id" in network_config.keys():
net_id = network_config["net_id"]
else:
net_id = None
logging.info("Configuring network for OpenStack undercloud/provider")
openstack_utils.configure_gateway_ext_port(
nova_client,
neutron_client,
dvr_mode=network_config.get("dvr_enabled", False),
net_id=net_id)
def run_from_cli():
"""Run network configurations from CLI
Use a YAML file of network configuration settings to configure the
overcloud network. YAML file of the form:
topology_name:
network_type: gre
router_name: provider-router
private_net_cidr: 192.168.21.0/24
external_dns: 10.5.0.2
external_net_cidr: 10.5.0.0/16
external_net_name: ext_net
external_subnet_name: ext_net_subnet
default_gateway: 10.5.0.1
start_floating_ip: 10.5.150.0
end_floating_ip: 10.5.200.254
:returns: None
:rtype: None
"""
_local_utils.setup_logging()
parser = argparse.ArgumentParser()
parser.add_argument("net_topology",
help="network topology type, default is GRE",
default="gre", nargs="?")
parser.add_argument("--ignore_env_vars", "-i",
help="do not override using environment variables",
action="store_true",
default=False)
parser.add_argument("--net_topology_file", "-f",
help="Network topology file location",
default="network.yaml")
# Handle CLI options
options = parser.parse_args()
net_topology = _local_utils.parse_arg(options, "net_topology")
net_topology_file = _local_utils.parse_arg(options, "net_topology_file")
ignore_env_vars = _local_utils.parse_arg(options, "ignore_env_vars")
logging.info("Setting up %s network" % (net_topology))
network_config = _local_utils.get_network_config(
net_topology, ignore_env_vars, net_topology_file)
# Handle network for Openstack-on-Openstack scenarios
if _local_utils.get_provider_type() == "openstack":
setup_gateway_ext_port(network_config)
setup_sdn(network_config)
if __name__ == "__main__":
sys.exit(run_from_cli())
+49 -41
View File
@@ -18,65 +18,64 @@ from zaza import model
from zaza.charm_lifecycle import utils as lifecycle_utils
# XXX Tech Debt Begins Here
def get_undercloud_env_vars():
""" Get environment specific undercloud network configuration settings from
environment variables.
def get_network_env_vars():
"""Get environment variables with names which are consistent with
network.yaml keys; Also get network environment variables as commonly
used by openstack-charm-testing and ubuntu-openstack-ci automation.
Return a dictionary compatible with openstack-mojo-specs network.yaml
key structure.
For each testing substrate, specific undercloud network configuration
settings should be exported into the environment to enable testing on that
substrate.
Note: *Overcloud* settings should be declared by the test caller and should
not be overridden here.
Return a dictionary compatible with zaza.configure.network functions'
expected key structure.
Example exported environment variables:
export default_gateway="172.17.107.1"
export external_net_cidr="172.17.107.0/24"
export external_dns="10.5.0.2"
export start_floating_ip="172.17.107.200"
export end_floating_ip="172.17.107.249"
Example o-c-t & uosci non-standard environment variables:
export NET_ID="a705dd0f-5571-4818-8c30-4132cc494668"
export GATEWAY="172.17.107.1"
export CIDR_EXT="172.17.107.0/24"
export NAMESERVER="10.5.0.2"
export FIP_RANGE="172.17.107.200:172.17.107.249"
:returns: Network environment variables
:rtype: dict
"""
# Example o-c-t & uosci environment variables:
# NET_ID="a705dd0f-5571-4818-8c30-4132cc494668"
# GATEWAY="172.17.107.1"
# CIDR_EXT="172.17.107.0/24"
# CIDR_PRIV="192.168.121.0/24"
# NAMESERVER="10.5.0.2"
# FIP_RANGE="172.17.107.200:172.17.107.249"
# AMULET_OS_VIP00="172.17.107.250"
# AMULET_OS_VIP01="172.17.107.251"
# AMULET_OS_VIP02="172.17.107.252"
# AMULET_OS_VIP03="172.17.107.253"
# Handle backward compatibile OSCI enviornment variables
_vars = {}
_vars['net_id'] = os.environ.get('NET_ID')
_vars['external_dns'] = os.environ.get('NAMESERVER')
_vars['default_gateway'] = os.environ.get('GATEWAY')
_vars['external_net_cidr'] = os.environ.get('CIDR_EXT')
_vars['private_net_cidr'] = os.environ.get('CIDR_PRIV')
# Take FIP_RANGE and create start and end floating ips
_fip_range = os.environ.get('FIP_RANGE')
if _fip_range and ':' in _fip_range:
_vars['start_floating_ip'] = os.environ.get('FIP_RANGE').split(':')[0]
_vars['end_floating_ip'] = os.environ.get('FIP_RANGE').split(':')[1]
_vips = [os.environ.get('AMULET_OS_VIP00'),
os.environ.get('AMULET_OS_VIP01'),
os.environ.get('AMULET_OS_VIP02'),
os.environ.get('AMULET_OS_VIP03')]
# Env var naming consistent with network.yaml takes priority
_keys = ['default_gateway'
# Env var naming consistent with zaza.configure.network functions takes
# priority. Override backward compatible settings.
_keys = ['default_gateway',
'start_floating_ip',
'end_floating_ip',
'external_dns',
'external_net_cidr',
'external_net_name',
'external_subnet_name',
'network_type',
'private_net_cidr',
'router_name']
'external_net_cidr']
for _key in _keys:
_val = os.environ.get(_key)
if _val:
_vars[_key] = _val
# Remove keys and items with a None value
_vars['vips'] = [_f for _f in _vips if _f]
for k, v in list(_vars.items()):
if not v:
del _vars[k]
@@ -112,9 +111,13 @@ def get_yaml_config(config_file):
return yaml.load(open(config_file, 'r').read())
def get_net_info(net_topology, ignore_env_vars=False):
def get_net_info(net_topology, ignore_env_vars=False,
net_topology_file="network.yaml"):
"""Get network info from network.yaml, override the values if specific
environment variables are set.
environment variables are set for the undercloud.
This function may be used when running network configuration from CLI to
pass in network configuration settings from a YAML file.
:param net_topology: Network topology name from network.yaml
:type net_topology: string
@@ -124,13 +127,18 @@ def get_net_info(net_topology, ignore_env_vars=False):
:rtype: dict
"""
net_info = get_yaml_config('network.yaml')[net_topology]
if os.path.exists(net_topology_file):
net_info = get_yaml_config(net_topology_file)[net_topology]
else:
raise Exception("Network topology file: {} not found."
.format(net_topology_file))
if not ignore_env_vars:
logging.info('Consuming network environment variables as overrides.')
net_info.update(get_network_env_vars())
logging.info("Consuming network environment variables as overrides "
"for the undercloud.")
net_info.update(get_undercloud_env_vars())
logging.info('Network info: {}'.format(dict_to_yaml(net_info)))
logging.info("Network info: {}".format(dict_to_yaml(net_info)))
return net_info
@@ -173,8 +181,8 @@ def remote_run(unit, remote_cmd, timeout=None, fatal=None):
"""
if fatal is None:
fatal = True
result = model.run_on_unit(unit,
lifecycle_utils.get_juju_model(),
result = model.run_on_unit(lifecycle_utils.get_juju_model(),
unit,
remote_cmd,
timeout=timeout)
if result:
+42 -8
View File
@@ -139,6 +139,23 @@ def get_neutron_session_client(session):
return neutronclient.Client(session=session)
def get_keystone_scope():
"""Return Keystone scope based on OpenStack release
:returns: String keystone scope
:rtype: string
"""
os_version = get_current_os_versions("keystone")["keystone"]
# Keystone policy.json shipped the charm with liberty requires a domain
# scoped token. Bug #1649106
if os_version == "liberty":
scope = "DOMAIN"
else:
scope = "PROJECT"
return scope
def get_keystone_session(opentackrc_creds, insecure=True, scope='PROJECT'):
"""Return keystone session
@@ -160,6 +177,28 @@ def get_keystone_session(opentackrc_creds, insecure=True, scope='PROJECT'):
return session.Session(auth=auth, verify=not insecure)
def get_overcloud_keystone_session():
"""Return Over cloud keystone session
:returns keystone_session: keystoneauth1.session.Session object
:rtype: keystoneauth1.session.Session
"""
return get_keystone_session(get_overcloud_auth(),
scope=get_keystone_scope())
def get_undercloud_keystone_session():
"""Return Under cloud keystone session
:returns keystone_session: keystoneauth1.session.Session object
:rtype: keystoneauth1.session.Session
"""
return get_keystone_session(get_undercloud_auth(),
scope=get_keystone_scope())
def get_keystone_session_client(session):
"""Return keystoneclient authenticated by keystone session
@@ -362,10 +401,6 @@ def configure_gateway_ext_port(novaclient, neutronclient,
else:
application_name = 'neutron-gateway'
# XXX Trying to track down a failure with juju run neutron-gateway/0 in
# the post juju_set check. Try a sleep here to see if some network
# reconfigureing on the gateway is still in progress and that's
# causing the issue
if ext_br_macs:
logging.info('Setting {} on {} external port to {}'.format(
config_key, application_name, ext_br_macs_str))
@@ -377,7 +412,7 @@ def configure_gateway_ext_port(novaclient, neutronclient,
model.set_application_config(
lifecycle_utils.get_juju_model(), application_name,
configuration={config_key: ext_br_macs_str})
juju_wait.wait()
juju_wait.wait(wait_for_workload=True)
def create_project_network(neutron_client, project_id, net_name='private',
@@ -444,10 +479,9 @@ def create_external_network(neutron_client, project_id, dvr_mode,
'name': net_name,
'router:external': True,
'tenant_id': project_id,
'provider:physical_network': 'physnet1',
'provider:network_type': 'flat',
}
if not deprecated_external_networking(dvr_mode):
network_msg['provider:physical_network'] = 'physnet1'
network_msg['provider:network_type'] = 'flat'
logging.info('Creating new external network definition: %s',
net_name)