Merge pull request #108 from openstack-charmers/ovn-charm

Teach `basic_overcloud_network` configure job about OVN
This commit is contained in:
Liam Young
2019-11-05 10:01:50 +00:00
committed by GitHub
6 changed files with 192 additions and 85 deletions

View File

@@ -101,8 +101,8 @@ class TestJujuUtils(ut_utils.BaseTestCase):
# Machine data
self.assertEqual(
juju_utils.get_machines_for_application(self.application),
[self.machine])
next(juju_utils.get_machines_for_application(self.application)),
self.machine)
self.get_application_status.assert_called_once()
# Subordinate application has no units
@@ -115,9 +115,9 @@ class TestJujuUtils(ut_utils.BaseTestCase):
self.get_application_status.side_effect = _get_application_status
self.assertEqual(
juju_utils.get_machines_for_application(
self.subordinate_application),
[self.machine])
next(juju_utils.get_machines_for_application(
self.subordinate_application)),
self.machine)
def test_get_unit_name_from_host_name(self):
unit_mock1 = mock.MagicMock()
@@ -151,8 +151,9 @@ class TestJujuUtils(ut_utils.BaseTestCase):
self.get_machines_for_application.return_value = [self.machine]
self.assertEqual(
juju_utils.get_machine_uuids_for_application(self.application),
[self.machine_data.get("instance-id")])
next(juju_utils.get_machine_uuids_for_application(
self.application)),
self.machine_data.get("instance-id"))
self.get_machines_for_application.assert_called_once_with(
self.application)

View File

@@ -15,6 +15,7 @@
import copy
import datetime
import io
import itertools
import mock
import tenacity
@@ -159,7 +160,7 @@ class TestOpenStackUtils(ut_utils.BaseTestCase):
# Already exists
network = openstack_utils.create_external_network(
self.neutronclient, self.project_id, False)
self.neutronclient, self.project_id)
self.assertEqual(network, self.network["network"])
self.neutronclient.create_network.assert_not_called()
@@ -169,7 +170,7 @@ class TestOpenStackUtils(ut_utils.BaseTestCase):
network_msg = copy.deepcopy(self.network)
network_msg["network"].pop("id")
network = openstack_utils.create_external_network(
self.neutronclient, self.project_id, False)
self.neutronclient, self.project_id)
self.assertEqual(network, self.network["network"])
self.neutronclient.create_network.assert_called_once_with(
network_msg)
@@ -814,12 +815,13 @@ class TestOpenStackUtils(ut_utils.BaseTestCase):
)
# No machine returned
self._get_machines.return_value = []
self._get_machines.side_effect = StopIteration
with self.assertRaises(exceptions.ApplicationNotFound):
openstack_utils.get_current_os_release_pair()
self._get_machines.side_effect = None
# No series returned
self._get_machines.return_value = ['6']
self._get_machines.return_value = itertools.repeat('6')
self._get_machine_series.return_value = None
with self.assertRaises(exceptions.SeriesNotFound):
openstack_utils.get_current_os_release_pair()
@@ -1165,3 +1167,45 @@ class TestOpenStackUtils(ut_utils.BaseTestCase):
'OS_PROJECT_NAME': 'services'},
scope='PROJECT',
verify=None)
def test_get_gateway_uuids(self):
self.patch_object(openstack_utils.juju_utils,
'get_machine_uuids_for_application')
self.get_machine_uuids_for_application.return_value = 'ret'
self.assertEquals(openstack_utils.get_gateway_uuids(), 'ret')
self.get_machine_uuids_for_application.assert_called_once_with(
'neutron-gateway')
def test_get_ovs_uuids(self):
self.patch_object(openstack_utils.juju_utils,
'get_machine_uuids_for_application')
self.get_machine_uuids_for_application.return_value = 'ret'
self.assertEquals(openstack_utils.get_ovs_uuids(), 'ret')
self.get_machine_uuids_for_application.assert_called_once_with(
'neutron-openvswitch')
def test_get_ovn_uuids(self):
self.patch_object(openstack_utils.juju_utils,
'get_machine_uuids_for_application')
self.get_machine_uuids_for_application.return_value = ['ret']
self.assertEquals(list(openstack_utils.get_ovn_uuids()),
['ret', 'ret'])
self.get_machine_uuids_for_application.assert_has_calls([
mock.call('ovn-chassis'),
mock.call('ovn-dedicated-chassis'),
])
def test_dvr_enabled(self):
self.patch_object(openstack_utils, 'get_application_config_option')
openstack_utils.dvr_enabled()
self.get_application_config_option.assert_called_once_with(
'neutron-api', 'enable-dvr')
def test_ovn_present(self):
self.patch_object(openstack_utils.model, 'get_application')
self.get_application.side_effect = [None, KeyError]
self.assertTrue(openstack_utils.ovn_present())
self.get_application.side_effect = [KeyError, None]
self.assertTrue(openstack_utils.ovn_present())
self.get_application.side_effect = [KeyError, KeyError]
self.assertFalse(openstack_utils.ovn_present())

View File

@@ -16,6 +16,8 @@
"""Setup for Neutron deployments."""
import functools
from zaza.openstack.configure import (
network,
)
@@ -25,7 +27,6 @@ from zaza.openstack.utilities import (
juju as juju_utils,
openstack as openstack_utils,
)
import zaza.model as model
# The overcloud network configuration settings are declared.
@@ -57,12 +58,14 @@ DEFAULT_UNDERCLOUD_NETWORK_CONFIG = {
}
def basic_overcloud_network():
def basic_overcloud_network(limit_gws=None):
"""Run setup for neutron networking.
Configure the following:
The overcloud network using subnet pools
:param limit_gws: Limit the number of gateways that get a port attached
:type limit_gws: int
"""
cli_utils.setup_logging()
@@ -74,10 +77,6 @@ def basic_overcloud_network():
network_config.update(DEFAULT_UNDERCLOUD_NETWORK_CONFIG)
# Environment specific settings
network_config.update(generic_utils.get_undercloud_env_vars())
# Deployed model settings
if (model.get_application_config('neutron-api')
.get('enable-dvr').get('value')):
network_config.update({"dvr_enabled": True})
# Get keystone session
keystone_session = openstack_utils.get_overcloud_keystone_session()
@@ -86,7 +85,20 @@ def basic_overcloud_network():
if juju_utils.get_provider_type() == "openstack":
undercloud_ks_sess = openstack_utils.get_undercloud_keystone_session()
network.setup_gateway_ext_port(network_config,
keystone_session=undercloud_ks_sess)
keystone_session=undercloud_ks_sess,
limit_gws=None)
# Confugre the overcloud network
network.setup_sdn(network_config, keystone_session=keystone_session)
# Configure function to get one gateway with external network
overcloud_network_one_gw = functools.partial(
basic_overcloud_network,
limit_gws=1)
# Configure function to get two gateways with external network
overcloud_network_two_gws = functools.partial(
basic_overcloud_network,
limit_gws=2)

View File

@@ -129,7 +129,6 @@ def setup_sdn(network_config, keystone_session=None):
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,
@@ -184,7 +183,8 @@ def setup_sdn(network_config, keystone_session=None):
openstack_utils.add_neutron_secgroup_rules(neutron_client, project_id)
def setup_gateway_ext_port(network_config, keystone_session=None):
def setup_gateway_ext_port(network_config, keystone_session=None,
limit_gws=None):
"""Perform setup external port on Neutron Gateway.
For OpenStack on OpenStack scenarios.
@@ -193,6 +193,8 @@ def setup_gateway_ext_port(network_config, keystone_session=None):
:type network_config: dict
:param keystone_session: Keystone session object for undercloud
:type keystone_session: keystoneauth1.session.Session object
:param limit_gws: Limit the number of gateways that get a port attached
:type limit_gws: Optional[int]
:returns: None
:rtype: None
"""
@@ -226,9 +228,9 @@ def setup_gateway_ext_port(network_config, keystone_session=None):
openstack_utils.configure_gateway_ext_port(
nova_client,
neutron_client,
dvr_mode=network_config.get("dvr_enabled", False),
net_id=net_id,
add_dataport_to_netplan=add_dataport_to_netplan)
add_dataport_to_netplan=add_dataport_to_netplan,
limit_gws=limit_gws)
def run_from_cli(**kwargs):

View File

@@ -81,21 +81,20 @@ def get_machines_for_application(application):
:param application: Application name
:type application: string
:returns: List of machines for an application
:rtype: list
:returns: machines for an application
:rtype: Iterator[str]
"""
status = get_application_status(application)
if not status:
raise StopIteration
# libjuju juju status no longer has units for subordinate charms
# Use the application it is subordinate-to to find machines
if status.get("units") is None and status.get("subordinate-to"):
return get_machines_for_application(status.get("subordinate-to")[0])
status = get_application_status(status.get("subordinate-to")[0])
machines = []
for unit in status.get("units").keys():
machines.append(
status.get("units").get(unit).get("machine"))
return machines
yield status.get("units").get(unit).get("machine")
def get_unit_name_from_host_name(host_name, application):
@@ -151,13 +150,11 @@ def get_machine_uuids_for_application(application):
:param application: Application name
:type application: string
:returns: List of machine uuuids for an application
:rtype: list
:returns: machine uuuids for an application
:rtype: Iterator[str]
"""
uuids = []
for machine in get_machines_for_application(application):
uuids.append(get_machine_status(machine, key="instance-id"))
return uuids
yield get_machine_status(machine, key="instance-id")
def get_provider_type():

View File

@@ -46,7 +46,7 @@ from swiftclient import client as swiftclient
import datetime
import io
import juju_wait
import itertools
import logging
import os
import paramiko
@@ -56,9 +56,10 @@ import subprocess
import sys
import tempfile
import tenacity
import urllib
import textwrap
import urllib
import zaza
from zaza import model
from zaza.openstack.utilities import (
exceptions,
@@ -453,7 +454,7 @@ def get_gateway_uuids():
"""Return machine uuids for neutron-gateway(s).
:returns: List of uuids
:rtype: list
:rtype: Iterator[str]
"""
return juju_utils.get_machine_uuids_for_application('neutron-gateway')
@@ -462,28 +463,65 @@ def get_ovs_uuids():
"""Return machine uuids for neutron-openvswitch(s).
:returns: List of uuids
:rtype: list
:rtype: Iterator[str]
"""
return (juju_utils
.get_machine_uuids_for_application('neutron-openvswitch'))
def get_ovn_uuids():
"""Provide machine uuids for OVN Chassis.
:returns: List of uuids
:rtype: Iterator[str]
"""
return itertools.chain(
juju_utils.get_machine_uuids_for_application('ovn-chassis'),
juju_utils.get_machine_uuids_for_application('ovn-dedicated-chassis'),
)
def dvr_enabled():
"""Check whether DVR is enabled in deployment.
:returns: True when DVR is enabled, False otherwise
:rtype: bool
"""
return get_application_config_option('neutron-api', 'enable-dvr')
def ovn_present():
"""Check whether OVN is present in deployment.
:returns: True when OVN is present, False otherwise
:rtype: bool
"""
app_presence = []
for name in ('ovn-chassis', 'ovn-dedicated-chassis'):
try:
model.get_application(name)
app_presence.append(True)
except KeyError:
app_presence.append(False)
return any(app_presence)
BRIDGE_MAPPINGS = 'bridge-mappings'
NEW_STYLE_NETWORKING = 'physnet1:br-ex'
def deprecated_external_networking(dvr_mode=False):
def deprecated_external_networking():
"""Determine whether deprecated external network mode is in use.
:param dvr_mode: Using DVR mode or not
:type dvr_mode: boolean
:returns: True or False
:rtype: boolean
"""
bridge_mappings = None
if dvr_mode:
if dvr_enabled():
bridge_mappings = get_application_config_option('neutron-openvswitch',
BRIDGE_MAPPINGS)
elif ovn_present():
return False
else:
bridge_mappings = get_application_config_option('neutron-gateway',
BRIDGE_MAPPINGS)
@@ -520,17 +558,15 @@ def get_admin_net(neutron_client):
return net
def add_interface_to_netplan(server_name, mac_address, dvr_mode=None):
def add_interface_to_netplan(server_name, mac_address):
"""In guest server_name, add nic with mac_address to netplan.
:param server_name: Hostname of instance
:type server_name: string
:param mac_address: mac address of nic to be added to netplan
:type mac_address: string
:param dvr_mode: Using DVR mode or not
:type dvr_mode: boolean
"""
if dvr_mode:
if dvr_enabled():
application_name = 'neutron-openvswitch'
else:
application_name = 'neutron-gateway'
@@ -578,33 +614,49 @@ def add_interface_to_netplan(server_name, mac_address, dvr_mode=None):
model.run_on_unit(unit_name, "sudo netplan apply")
def configure_gateway_ext_port(novaclient, neutronclient,
dvr_mode=None, net_id=None,
add_dataport_to_netplan=False):
def configure_gateway_ext_port(novaclient, neutronclient, net_id=None,
add_dataport_to_netplan=False,
limit_gws=None):
"""Configure the neturong-gateway external port.
:param novaclient: Authenticated novaclient
:type novaclient: novaclient.Client object
:param neutronclient: Authenticated neutronclient
:type neutronclient: neutronclient.Client object
:param dvr_mode: Using DVR mode or not
:type dvr_mode: boolean
:param net_id: Network ID
:type net_id: string
:param limit_gws: Limit the number of gateways that get a port attached
:type limit_gws: Optional[int]
"""
if dvr_mode:
uuids = get_ovs_uuids()
deprecated_extnet_mode = deprecated_external_networking()
port_config_key = 'data-port'
if deprecated_extnet_mode:
port_config_key = 'ext-port'
config = {}
if dvr_enabled():
uuids = itertools.islice(get_ovs_uuids(), limit_gws)
# If dvr, do not attempt to persist nic in netplan
# https://github.com/openstack-charmers/zaza-openstack-tests/issues/78
add_dataport_to_netplan = False
application_names = ['neutron-openvswitch']
elif ovn_present():
uuids = itertools.islice(get_ovn_uuids(), limit_gws)
application_names = ['ovn-chassis']
try:
ovn_dc_name = 'ovn-dedicated-chassis'
next(juju_utils.get_machine_uuids_for_application(ovn_dc_name))
application_names.append(ovn_dc_name)
except StopIteration:
# ovn-dedicated-chassis not in deployment
pass
port_config_key = 'interface-bridge-mappings'
config.update({'ovn-bridge-mappings': 'physnet1:br-ex'})
add_dataport_to_netplan = False
else:
uuids = get_gateway_uuids()
deprecated_extnet_mode = deprecated_external_networking(dvr_mode)
config_key = 'data-port'
if deprecated_extnet_mode:
config_key = 'ext-port'
uuids = itertools.islice(get_gateway_uuids(), limit_gws)
application_names = ['neutron-gateway']
if not net_id:
net_id = get_admin_net(neutronclient)['id']
@@ -633,34 +685,37 @@ def configure_gateway_ext_port(novaclient, neutronclient,
if add_dataport_to_netplan:
mac_address = get_mac_from_port(port, neutronclient)
add_interface_to_netplan(server.name,
mac_address=mac_address,
dvr_mode=dvr_mode)
mac_address=mac_address)
ext_br_macs = []
for port in neutronclient.list_ports(network_id=net_id)['ports']:
if 'ext-port' in port['name']:
if deprecated_extnet_mode:
ext_br_macs.append(port['mac_address'])
elif ovn_present():
ext_br_macs.append('{}:br-ex'.format(port['mac_address']))
else:
ext_br_macs.append('br-ex:{}'.format(port['mac_address']))
ext_br_macs.sort()
ext_br_macs_str = ' '.join(ext_br_macs)
if dvr_mode:
application_name = 'neutron-openvswitch'
else:
application_name = 'neutron-gateway'
if ext_br_macs:
logging.info('Setting {} on {} external port to {}'.format(
config_key, application_name, ext_br_macs_str))
current_data_port = get_application_config_option(application_name,
config_key)
if current_data_port == ext_br_macs_str:
logging.info('Config already set to value')
return
model.set_application_config(
application_name,
configuration={config_key: ext_br_macs_str})
juju_wait.wait(wait_for_workload=True)
config.update({port_config_key: ext_br_macs_str})
for application_name in application_names:
logging.info('Setting {} on {}'.format(
config, application_name))
current_data_port = get_application_config_option(application_name,
port_config_key)
if current_data_port == ext_br_macs_str:
logging.info('Config already set to value')
return
model.set_application_config(
application_name,
configuration=config)
zaza.model.wait_for_agent_status()
test_config = zaza.charm_lifecycle.utils.get_charm_config()
zaza.model.wait_for_application_states(
states=test_config.get('target_deploy_status', {}))
@tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, max=60),
@@ -721,16 +776,13 @@ def create_project_network(neutron_client, project_id, net_name='private',
return network
def create_external_network(neutron_client, project_id, dvr_mode,
net_name='ext_net'):
def create_external_network(neutron_client, project_id, net_name='ext_net'):
"""Create the external network.
:param neutron_client: Authenticated neutronclient
:type neutron_client: neutronclient.Client object
:param project_id: Project ID
:type project_id: string
:param dvr_mode: Using DVR mode or not
:type dvr_mode: boolean
:param net_name: Network name
:type net_name: string
:returns: Network object
@@ -1377,10 +1429,9 @@ def get_current_os_release_pair(application='keystone'):
:raises: exceptions.SeriesNotFound
:raises: exceptions.OSVersionNotFound
"""
machines = juju_utils.get_machines_for_application(application)
if len(machines) >= 1:
machine = machines[0]
else:
try:
machine = next(juju_utils.get_machines_for_application(application))
except StopIteration:
raise exceptions.ApplicationNotFound(application)
series = juju_utils.get_machine_series(machine)