Skip to content

Commit

Permalink
Handle OVS to OVN migration with neutron-gateway topologies
Browse files Browse the repository at this point in the history
Merge two similar config set patterns into a common method.

Add missing docstrings.

Fix typos.
  • Loading branch information
fnordahl committed Sep 10, 2020
1 parent 5a10779 commit 332294d
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 44 deletions.
93 changes: 54 additions & 39 deletions zaza/openstack/charm_tests/ovn/setup.py
Expand Up @@ -40,12 +40,54 @@ def _get_instance_mtu_from_global_physnet_mtu():
return int(n_api_config['global-physnet-mtu']['value']) - (
GENEVE_ENCAP_OVERHEAD + IP4_HEADER_SIZE)

def _configure_apps(self, apps, cfg,
first_match_raise_if_none_found=False):
"""Conditionally configure a set of applications.
:param apps: Applications.
:type apps: Iterator[str]
:param cfg: Configuration to apply.
:type cfg: Dict[str,any]
:param first_match_raise_if_none_found: When set the method will
configure the first application
it finds in the model and raise
an exception if none are found.
:type first_match_raise_if_none_found: bool
:raises: RuntimeError
"""
for app in apps:
try:
zaza.model.get_application(app)
for k, v in cfg.items():
logging.info('Setting `{}` to "{}" on "{}"...'
.format(k, v, app))
with self.config_change(cfg, cfg, app):
# The intent here is to change the config and not
# restore it. We accomplish that by passing in the same
# value for default and alternate.
#
# The reason for using the `config_change` helper for
# this is that it already deals with all the
# permutations of config already being set etc and does
# not get into trouble if the test bundle already has
# the values we try to set.
if first_match_raise_if_none_found:
break
else:
continue
else:
if first_match_raise_if_none_found:
raise RuntimeError(
'None of the expected apps ({}) are present in '
'the model.'
.format(apps)
)
except KeyError:
pass

def configure_ngw_novs(self):
"""Configure n-ovs and n-gw units."""
cfg = {
# To be able to successfully clean up after the Neutron agents we
# need to use the 'openvswitch' `firewall-driver`.
'firewall-driver': 'openvswitch',
# To be able to have instances successfully survive the migration
# without communication issues we need to lower the MTU announced
# to instances prior to migration.
Expand All @@ -59,25 +101,13 @@ def configure_ngw_novs(self):
'instance-mtu': self._get_instance_mtu_from_global_physnet_mtu()
}
apps = ('neutron-gateway', 'neutron-openvswitch')
for app in apps:
try:
zaza.model.get_application(app)
for k, v in cfg.items():
logging.info('Setting `{}` to "{}" on "{}"...'
.format(k, v, app))
with self.config_change(cfg, cfg, app):
# The intent here is to change the config and not restore
# it. We accomplish that by passing in the same value for
# default and alternate.
#
# The reason for using the `config_change` helper for this
# is that it already deals with all the permutations of
# config already being set etc and does not get into
# trouble if the test bundle already have the values we try
# to set.
continue
except KeyError:
pass
self._configure_apps(apps, cfg)
cfg_ovs = {
# To be able to successfully clean up after the Neutron agents we
# need to use the 'openvswitch' `firewall-driver`.
'firewall-driver': 'openvswitch',
}
self._configure_apps(('neutron-openvswitch',), cfg_ovs)

def configure_ovn_mappings(self):
"""Copy mappings from n-gw or n-ovs application."""
Expand All @@ -102,23 +132,8 @@ def configure_ovn_mappings(self):
.format(src_apps)
)

for app in dst_apps:
try:
zaza.model.get_application(app)
for k, v in ovn_cfg.items():
logging.info('Setting `{}` to "{}" on "{}"...'
.format(k, v, app))
with self.config_change(ovn_cfg, ovn_cfg, app):
# Set values only on ovn-dedicated-chassis when present,
# otherwise we set them on ovn-chassis.
break
except KeyError:
pass
else:
raise RuntimeError(
'None of the expected apps ({}) are present in the model.'
.format(dst_apps)
)
self._configure_apps(
dst_apps, ovn_cfg, first_match_raise_if_none_found=True)


def pre_migration_configuration():
Expand Down
54 changes: 49 additions & 5 deletions zaza/openstack/charm_tests/ovn/tests.py
Expand Up @@ -84,7 +84,7 @@ class OVSOVNMigrationTest(test_utils.BaseCharmTest):
def setUp(self):
"""Perform migration steps prior to validation."""
super(OVSOVNMigrationTest, self).setUp()
# These steps here due to them having to be executed once and in a
# These steps are here due to them having to be executed once and in a
# specific order prior to running any tests. The steps should still
# be idempotent if at all possible as a courtesy to anyone iterating
# on the test code.
Expand All @@ -104,6 +104,11 @@ def setUp(self):

# Stop Neutron agents on hypervisors
self._pause_units('neutron-openvswitch')
try:
self._pause_units('neutron-gateway')
except KeyError:
logging.info(
'No neutron-gateway in deployment, skip pausing it.')

# Add the neutron-api-plugin-ovn subordinate which will make the
# `neutron-api-plugin-ovn` unit appear in the deployment.
Expand Down Expand Up @@ -155,7 +160,13 @@ def setUp(self):
self._resume_units('neutron-api')

# Run `cleanup` action on neutron-openvswitch units/hypervisors
self._run_cleanup_action()
self._run_cleanup_action('neutron-openvswitch')
# Run `cleanup` action on neutron-gateway units when present
try:
self._run_cleanup_action('neutron-gateway')
except KeyError:
logging.info(
'No neutron-gateway in deployment, skip cleanup of it.')

# Start the OVN controller on hypervisors
#
Expand All @@ -164,11 +175,18 @@ def setUp(self):
# you will program the network to a state of infinite loop.
self._resume_units('ovn-chassis')

try:
self._resume_units('ovn-dedicated-chassis')
except KeyError:
logging.info(
'No ovn-dedicated-chassis in deployment, skip resume.')

# And we should be off to the races

self.one_time_init_done = True

def _add_neutron_api_plugin_ovn_subordinate_relation(self):
"""Add relation between neutron-api and neutron-api-plugin-ovn."""
try:
logging.info('Adding relation neutron-api-plugin-ovn '
'-> neutron-api')
Expand Down Expand Up @@ -200,6 +218,7 @@ def _configure_neutron_api(self):
logging.info('done')

def _run_offline_neutron_morph_db_action(self):
"""Run offline-neutron-morph-db action."""
logging.info('Running the optional `offline-neutron-morph-db` action '
'on neutron-api-plugin-ovn/leader')
generic_utils.assertActionRanOK(
Expand All @@ -213,6 +232,7 @@ def _run_offline_neutron_morph_db_action(self):
)

def _run_migrate_ovn_db_action(self):
"""Run migrate-ovn-db action."""
logging.info('Running `migrate-ovn-db` action on '
'neutron-api-plugin-ovn/leader')
generic_utils.assertActionRanOK(
Expand All @@ -230,6 +250,14 @@ def _run_migrate_ovn_db_action(self):
@tenacity.retry(wait=tenacity.wait_exponential(min=5, max=60),
reraise=True, stop=tenacity.stop_after_attempt(3))
def _run_migrate_mtu_action(self):
"""Run migrate-mtu action with retry.
The action is idempotent.
Due to LP: #1854518 and the point in time of the test life cycle we run
this action the probability for the Neutron API not being available
for the script to do its job is high, thus we retry.
"""
logging.info('Running `migrate-mtu` action on '
'neutron-api-plugin-ovn/leader')
generic_utils.assertActionRanOK(
Expand All @@ -243,6 +271,11 @@ def _run_migrate_mtu_action(self):
)

def _pause_units(self, application):
"""Pause units of application.
:param application: Name of application
:type application: str
"""
logging.info('Pausing {} units'.format(application))
zaza.model.run_action_on_units(
[unit.entity_id
Expand All @@ -259,18 +292,29 @@ def _pause_units(self, application):
},
)

def _run_cleanup_action(self):
logging.info('Running `cleanup` action on neutron-openvswitch units.')
def _run_cleanup_action(self, application):
"""Run cleanup action on application units.
:param application: Name of application
:type application: str
"""
logging.info('Running `cleanup` action on {} units.'
.format(application))
zaza.model.run_action_on_units(
[unit.entity_id
for unit in zaza.model.get_units('neutron-openvswitch')],
for unit in zaza.model.get_units(application)],
'cleanup',
action_params={
'i-really-mean-it': True},
raise_on_failure=True,
)

def _resume_units(self, application):
"""Resume units of application.
:param application: Name of application
:type application: str
"""
logging.info('Resuming {} units'.format(application))
zaza.model.run_action_on_units(
[unit.entity_id
Expand Down

0 comments on commit 332294d

Please sign in to comment.