diff --git a/setup.py b/setup.py index 95aaf6f..7d20665 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,7 @@ from setuptools.command.test import test as TestCommand version = "0.0.1.dev1" install_require = [ + 'async_generator', 'hvac', 'jinja2', 'juju', diff --git a/unit_tests/test_zaza_charm_lifecycle_deploy.py b/unit_tests/test_zaza_charm_lifecycle_deploy.py index 9884e3c..111be4e 100644 --- a/unit_tests/test_zaza_charm_lifecycle_deploy.py +++ b/unit_tests/test_zaza_charm_lifecycle_deploy.py @@ -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') diff --git a/unit_tests/utilitites/__init__.py b/unit_tests/utilitites/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/unit_tests/utilitites/test_zaza_utilitites_local_utils.py b/unit_tests/utilitites/test_zaza_utilitites_local_utils.py new file mode 100644 index 0000000..742bdd4 --- /dev/null +++ b/unit_tests/utilitites/test_zaza_utilitites_local_utils.py @@ -0,0 +1,76 @@ +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_network_env_vars(self): + self.patch_object(_local_utils.os.environ, "get") + _env = {"NET_ID": "netid", + "NAMESERVER": "10.0.0.10", + "GATEWAY": "10.0.0.1", + "CIDR_EXT": "10.0.0.0/24", + "CIDR_PRIV": "192.168.0.0/24"} + _result = {} + _result["net_id"] = _env["NET_ID"] + _result["external_dns"] = _env["NAMESERVER"] + _result["default_gateway"] = _env["GATEWAY"] + _result["external_net_cidr"] = _env["CIDR_EXT"] + _result["private_net_cidr"] = _env["CIDR_PRIV"] + + def _get_env(key): + return _env.get(key) + self.get.side_effect = _get_env + + self.assertEqual(_local_utils.get_network_env_vars(), + _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_network_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_network_env_vars.assert_not_called() + + # Update with environmental variables + _more_data = {"network": "NEW", + "other": "DATA"} + self.get_network_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_network_env_vars.assert_called_once_with() diff --git a/unit_tests/utilitites/test_zaza_utilitites_openstack_utils.py b/unit_tests/utilitites/test_zaza_utilitites_openstack_utils.py index 29e1685..7434b0e 100644 --- a/unit_tests/utilitites/test_zaza_utilitites_openstack_utils.py +++ b/unit_tests/utilitites/test_zaza_utilitites_openstack_utils.py @@ -1,3 +1,4 @@ +import copy import mock import unit_tests.utils as ut_utils from zaza.utilities import openstack_utils @@ -7,63 +8,130 @@ 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.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'}} - - self.floatingips = {'floatingips': [self.floatingip['floatingip']]} - + "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.neutronclient = mock.MagicMock() self.neutronclient.list_ports.return_value = self.ports self.neutronclient.create_port.return_value = self.port 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.ext_net = "ext_net" + self.private_net = "private_net" 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_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) diff --git a/zaza/charm_lifecycle/deploy.py b/zaza/charm_lifecycle/deploy.py index c87aad3..ab49bcb 100755 --- a/zaza/charm_lifecycle/deploy.py +++ b/zaza/charm_lifecycle/deploy.py @@ -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): diff --git a/zaza/charm_tests/dragent/__init__.py b/zaza/charm_tests/dragent/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zaza/charm_tests/dragent/configure_dragent.py b/zaza/charm_tests/dragent/configure_dragent.py new file mode 100644 index 0000000..f22df00 --- /dev/null +++ b/zaza/charm_tests/dragent/configure_dragent.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 + +import os + +from zaza.configure import ( + network, + bgp_speaker, +) +from zaza.utilities import ( + _local_utils, + openstack_utils, +) + +DEFAULT_PEER_APPLICATION_NAME = "quagga" +# Find a default network.yaml in tests/bundles/network.yaml of the charm +DEFAULT_TOPOLOGY_FILE = os.path.join("tests", "bundles", "network.yaml") + + +def setup(net_topology="pool", net_topology_file="network.yaml"): + """Setup BGP networking + + :param net_topology: String name of network topology + :type net_topology: string + :param net_info: Network configuration dictionary + :type net_info: dict + :param keystone_session: Keystone session object for overcloud + :type keystone_session: keystoneauth1.session.Session object + :returns: None + :rtype: None + """ + + _local_utils.setup_logging() + + # Check for the network.yaml configuration + if (not os.path.exists(net_topology_file) and + os.path.exists(DEFAULT_TOPOLOGY_FILE)): + net_topology_file = DEFAULT_TOPOLOGY_FILE + + # Get network configuration from YAML and/or environment variables + net_info = _local_utils.get_net_info(net_topology, + net_topology_file=net_topology_file) + + # Get keystone session + keystone_session = openstack_utils.get_overcloud_keystone_session() + + # Confugre the network + network.setup_sdn( + net_topology, net_info, keystone_session=keystone_session) + # Configure BGP + bgp_speaker.setup_bgp_speaker( + peer_application_name=DEFAULT_PEER_APPLICATION_NAME, + keystone_session=keystone_session) diff --git a/zaza/charm_tests/dragent/test_dragent.py b/zaza/charm_tests/dragent/test_dragent.py new file mode 100644 index 0000000..b56e8c6 --- /dev/null +++ b/zaza/charm_tests/dragent/test_dragent.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 + +import argparse +import logging +import time +import sys + +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] + + # 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 up to ~2 minutes long. + backoff = 2 + max_wait = 129 + logging.info("Checking routes on BGP peer {}".format(peer_unit.entity_id)) + while backoff < max_wait: + # Run show ip route bgp on BGP peer + routes = _local_utils.remote_run( + peer_unit.entity_id, remote_cmd='vtysh -c "show ip route bgp"') + try: + logging.debug(routes) + assert private_cidr in routes, ( + "Private subnet CIDR, {}, not advertised to BGP peer" + .format(private_cidr)) + logging.info("Private subnet CIDR, {}, found in routing table" + .format(private_cidr)) + break + except AssertionError: + logging.info("Binary backoff waiting {} seconds for bgp " + "routes on peer".format(backoff)) + time.sleep(backoff) + backoff = backoff * 2 + if backoff > max_wait: + raise + + assert floating_ip_cidr in routes, ("Floating IP, {}, not advertised " + "to BGP peer".format(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()) diff --git a/zaza/charm_tests/dragent/tests.py b/zaza/charm_tests/dragent/tests.py new file mode 100644 index 0000000..baffcf8 --- /dev/null +++ b/zaza/charm_tests/dragent/tests.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +import unittest + +from zaza.utilities import _local_utils +from zaza.charm_tests.dragent import test_dragent + + +class DRAgentTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + _local_utils.setup_logging() + + def test_bgp_routes(self): + test_dragent.test_bgp_routes(peer_application_name="quagga") + + +if __name__ == "__main__": + unittest.main() diff --git a/zaza/configure/__init__.py b/zaza/configure/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zaza/configure/bgp_speaker.py b/zaza/configure/bgp_speaker.py new file mode 100755 index 0000000..acff045 --- /dev/null +++ b/zaza/configure/bgp_speaker.py @@ -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()) diff --git a/zaza/configure/network.py b/zaza/configure/network.py new file mode 100755 index 0000000..8009955 --- /dev/null +++ b/zaza/configure/network.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 + +import argparse +import logging +import sys + +from zaza.utilities import _local_utils +from zaza.utilities import openstack_utils + + +def setup_sdn(net_topology, net_info, keystone_session=None): + """Setup Software Defined Network + + :param net_topology: String name of network topology + :type net_topology: string + :param net_info: Network configuration dictionary + :type net_info: 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 opentackrc into a project id + project_id = openstack_utils.get_project_id( + keystone_client, + "admin", + ) + # Network Setup + subnetpools = False + if net_info.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, + net_info.get("dvr_enabled", False), + net_info["external_net_name"]) + openstack_utils.create_external_subnet( + neutron_client, + project_id, + ext_network, + net_info["default_gateway"], + net_info["external_net_cidr"], + net_info["start_floating_ip"], + net_info["end_floating_ip"], + net_info["external_subnet_name"]) + # Should this be --enable_snat = False + 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 = net_info.get("ip_version") or 4 + subnetpool = None + if subnetpools: + address_scope = openstack_utils.create_address_scope( + neutron_client, + project_id, + net_info.get("address_scope"), + ip_version=ip_version) + subnetpool = openstack_utils.create_subnetpool( + neutron_client, + project_id, + net_info.get("subnetpool_name"), + net_info.get("subnetpool_prefix"), + address_scope) + project_network = openstack_utils.create_project_network( + neutron_client, + project_id, + shared=False, + network_type=net_info["network_type"]) + project_subnet = openstack_utils.create_project_subnet( + neutron_client, + project_id, + project_network, + net_info.get("private_net_cidr"), + subnetpool=subnetpool, + ip_version=ip_version) + openstack_utils.update_subnet_dns( + neutron_client, + project_subnet, + net_info["external_dns"]) + openstack_utils.plug_subnet_into_router( + neutron_client, + net_info["router_name"], + project_network, + project_subnet) + openstack_utils.add_neutron_secgroup_rules(neutron_client, project_id) + + +def setup_gateway_ext_port(net_info, keystone_session=None): + """Setup external port on Neutron Gateway. + For OpenStack on OpenStack scenarios + + :param net_info: Network configuration dictionary + :type net_info: 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 net_info.keys(): + net_id = net_info["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=net_info.get("dvr_enabled", False), + net_id=net_id) + + +def run_from_cli(): + """Run network configurations from CLI + + :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)) + net_info = _local_utils.get_net_info( + 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(net_info) + + setup_sdn(net_topology, net_info) + + +if __name__ == "__main__": + sys.exit(run_from_cli()) diff --git a/zaza/utilities/_local_utils.py b/zaza/utilities/_local_utils.py index 3a4a1b6..77ac5f3 100644 --- a/zaza/utilities/_local_utils.py +++ b/zaza/utilities/_local_utils.py @@ -112,7 +112,8 @@ 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. @@ -124,13 +125,17 @@ 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.') + logging.info("Consuming network environment variables as overrides.") net_info.update(get_network_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 +178,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: diff --git a/zaza/utilities/openstack_utils.py b/zaza/utilities/openstack_utils.py index c381e87..1691f92 100644 --- a/zaza/utilities/openstack_utils.py +++ b/zaza/utilities/openstack_utils.py @@ -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 @@ -377,7 +416,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 +483,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)