Files
zaza-openstack-tests/zaza/openstack/utilities/upgrade_utils.py
Chris MacNaughton 74652f2523 Add unit test coverage for series upgrades
This also includes some tidying
2020-04-10 10:50:43 +02:00

187 lines
6.2 KiB
Python

# Copyright 2020 Canonical Ltd.
#
# 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.
"""Collection of functions to support upgrade testing."""
import re
import logging
import collections
import zaza.model
SERVICE_GROUPS = collections.OrderedDict([
('Stateful Services', ['percona-cluster', 'rabbitmq-server', 'ceph-mon']),
('Core Identity', ['keystone']),
('Control Plane', [
'aodh', 'barbican', 'ceilometer', 'ceph-fs',
'ceph-radosgw', 'cinder', 'designate',
'designate-bind', 'glance', 'gnocchi', 'heat', 'manila',
'manila-generic', 'neutron-api', 'neutron-gateway', 'placement',
'nova-cloud-controller', 'openstack-dashboard']),
('Data Plane', [
'nova-compute', 'ceph-osd',
'swift-proxy', 'swift-storage'])
])
UPGRADE_EXCLUDE_LIST = ['rabbitmq-server', 'percona-cluster']
def get_upgrade_candidates(model_name=None, filters=None):
"""Extract list of apps from model that can be upgraded.
:param model_name: Name of model to query.
:type model_name: str
:param filters: List of filter functions to apply
:type filters: List[fn]
:returns: List of application that can have their payload upgraded.
:rtype: []
"""
if filters is None:
filters = []
status = zaza.model.get_status(model_name=model_name)
candidates = {}
for app, app_config in status.applications.items():
if _include_app(app, app_config, filters, model_name=model_name):
candidates[app] = app_config
return candidates
def _include_app(app, app_config, filters, model_name=None):
for filt in filters:
if filt(app, app_config, model_name=model_name):
return False
return True
def _filter_subordinates(app, app_config, model_name=None):
if app_config.get("subordinate-to"):
logging.warning(
"Excluding {} from upgrade, it is a subordinate".format(app))
return True
return False
def _filter_openstack_upgrade_list(app, app_config, model_name=None):
charm_name = extract_charm_name_from_url(app_config['charm'])
if app in UPGRADE_EXCLUDE_LIST or charm_name in UPGRADE_EXCLUDE_LIST:
print("Excluding {} from upgrade, on the exclude list".format(app))
logging.warning(
"Excluding {} from upgrade, on the exclude list".format(app))
return True
return False
def _filter_non_openstack_services(app, app_config, model_name=None):
charm_options = zaza.model.get_application_config(
app, model_name=model_name).keys()
src_options = ['openstack-origin', 'source']
if not [x for x in src_options if x in charm_options]:
logging.warning(
"Excluding {} from upgrade, no src option".format(app))
return True
return False
def get_upgrade_groups(model_name=None, extra_filters=None):
"""Place apps in the model into their upgrade groups.
Place apps in the model into their upgrade groups. If an app is deployed
but is not in SERVICE_GROUPS then it is placed in a sweep_up group.
:param model_name: Name of model to query.
:type model_name: str
:returns: Dict of group lists keyed on group name.
:rtype: collections.OrderedDict
"""
filters = [
_filter_subordinates,
_filter_openstack_upgrade_list,
_filter_non_openstack_services,
]
if extra_filters:
if isinstance(extra_filters, list):
filters.extend(extra_filters)
elif callable(extra_filters):
filters.append(extra_filters)
else:
raise RuntimeError(
"extra_filters should be a list of "
"callables")
apps_in_model = get_upgrade_candidates(
model_name=model_name,
filters=filters,)
return _build_service_groups(apps_in_model)
def get_series_upgrade_groups(model_name=None, extra_filters=None):
"""Place apps in the model into their upgrade groups.
Place apps in the model into their upgrade groups. If an app is deployed
but is not in SERVICE_GROUPS then it is placed in a sweep_up group.
:param model_name: Name of model to query.
:type model_name: str
:returns: Dict of group lists keyed on group name.
:rtype: collections.OrderedDict
"""
filters = [_filter_subordinates]
if extra_filters:
if isinstance(extra_filters, list):
filters.extend(extra_filters)
elif callable(extra_filters):
filters.append(extra_filters)
else:
raise RuntimeError(
"extra_filters should be a list of "
"callables")
apps_in_model = get_upgrade_candidates(
model_name=model_name,
filters=filters)
return _build_service_groups(apps_in_model)
def _build_service_groups(applications):
groups = collections.OrderedDict()
for phase_name, charms in SERVICE_GROUPS.items():
group = []
for app, app_config in applications.items():
charm_name = extract_charm_name_from_url(app_config['charm'])
if charm_name in charms:
group.append(app)
groups[phase_name] = group
sweep_up = []
for app in applications:
if not (app in [a for group in groups.values() for a in group]):
sweep_up.append(app)
groups['sweep_up'] = sweep_up
for name, group in groups.items():
group.sort()
return groups
def extract_charm_name_from_url(charm_url):
"""Extract the charm name from the charm url.
E.g. Extract 'heat' from local:bionic/heat-12
:param charm_url: Name of model to query.
:type charm_url: str
:returns: Charm name
:rtype: str
"""
charm_name = re.sub(r'-[0-9]+$', '', charm_url.split('/')[-1])
return charm_name.split(':')[-1]