From 23c546d4b94487059c8e56d58db579805ab0618c Mon Sep 17 00:00:00 2001 From: Liam Young Date: Tue, 4 Feb 2020 16:39:28 +0000 Subject: [PATCH] Charmhelper sync for 20.02 Change-Id: I707290fd8ffc4b26d068244a00142416e1984d48 --- .../charmhelpers/contrib/hahelpers/cluster.py | 45 +++++++++++++++++++ .../charmhelpers/contrib/openstack/context.py | 12 +---- hooks/charmhelpers/contrib/openstack/utils.py | 35 +++++++++++++++ hooks/charmhelpers/fetch/ubuntu_apt_pkg.py | 30 +++++++++++++ hooks/charmhelpers/osplatform.py | 3 ++ 5 files changed, 114 insertions(+), 11 deletions(-) diff --git a/hooks/charmhelpers/contrib/hahelpers/cluster.py b/hooks/charmhelpers/contrib/hahelpers/cluster.py index 4a737e24..ba34fba0 100644 --- a/hooks/charmhelpers/contrib/hahelpers/cluster.py +++ b/hooks/charmhelpers/contrib/hahelpers/cluster.py @@ -25,6 +25,7 @@ Helpers for clustering and determining "cluster leadership" and other clustering-related helpers. """ +import functools import subprocess import os import time @@ -281,6 +282,10 @@ def determine_apache_port(public_port, singlenode_mode=False): return public_port - (i * 10) +determine_apache_port_single = functools.partial( + determine_apache_port, singlenode_mode=True) + + def get_hacluster_config(exclude_keys=None): ''' Obtains all relevant configuration from charm configuration required @@ -404,3 +409,43 @@ def distributed_wait(modulo=None, wait=None, operation_name='operation'): log(msg, DEBUG) status_set('maintenance', msg) time.sleep(calculated_wait) + + +def get_managed_services_and_ports(services, external_ports, + external_services=None, + port_conv_f=determine_apache_port_single): + """Get the services and ports managed by this charm. + + Return only the services and corresponding ports that are managed by this + charm. This excludes haproxy when there is a relation with hacluster. This + is because this charm passes responsability for stopping and starting + haproxy to hacluster. + + Similarly, if a relation with hacluster exists then the ports returned by + this method correspond to those managed by the apache server rather than + haproxy. + + :param services: List of services. + :type services: List[str] + :param external_ports: List of ports managed by external services. + :type external_ports: List[int] + :param external_services: List of services to be removed if ha relation is + present. + :type external_services: List[str] + :param port_conv_f: Function to apply to ports to calculate the ports + managed by services controlled by this charm. + :type port_convert_func: f() + :returns: A tuple containing a list of services first followed by a list of + ports. + :rtype: Tuple[List[str], List[int]] + """ + if external_services is None: + external_services = ['haproxy'] + if relation_ids('ha'): + for svc in external_services: + try: + services.remove(svc) + except ValueError: + pass + external_ports = [port_conv_f(p) for p in external_ports] + return services, external_ports diff --git a/hooks/charmhelpers/contrib/openstack/context.py b/hooks/charmhelpers/contrib/openstack/context.py index f67ebb1d..bc90804b 100644 --- a/hooks/charmhelpers/contrib/openstack/context.py +++ b/hooks/charmhelpers/contrib/openstack/context.py @@ -738,10 +738,6 @@ class AMQPContext(OSContextGenerator): if notification_topics: ctxt['notification_topics'] = notification_topics - notification_topics = conf.get('notification-topics', None) - if notification_topics: - ctxt['notification_topics'] = notification_topics - send_notifications_to_logs = conf.get('send-notifications-to-logs', None) if send_notifications_to_logs: ctxt['send_notifications_to_logs'] = send_notifications_to_logs @@ -2390,19 +2386,13 @@ class DHCPAgentContext(OSContextGenerator): :returns: Value to use for ovs_use_veth setting :rtype: Bool """ - # If this is Trust return True - release = lsb_release()['DISTRIB_CODENAME'].lower() - if CompareHostReleases(release) <= 'trusty': - return True - # If there is an existing setting return that _existing = self.get_existing_ovs_use_veth() if _existing is not None: return _existing - # If the config value is unset return False + _config = self.parse_ovs_use_veth() if _config is None: # New better default return False - # If the config value is set return it else: return _config diff --git a/hooks/charmhelpers/contrib/openstack/utils.py b/hooks/charmhelpers/contrib/openstack/utils.py index 566404a0..161199c4 100644 --- a/hooks/charmhelpers/contrib/openstack/utils.py +++ b/hooks/charmhelpers/contrib/openstack/utils.py @@ -44,6 +44,7 @@ from charmhelpers.core.hookenv import ( INFO, ERROR, related_units, + relation_get, relation_ids, relation_set, status_set, @@ -331,6 +332,10 @@ PACKAGE_CODENAMES = { DEFAULT_LOOPBACK_SIZE = '5G' +DB_SERIES_UPGRADING_KEY = 'cluster-series-upgrading' + +DB_MAINTENANCE_KEYS = [DB_SERIES_UPGRADING_KEY] + class CompareOpenStackReleases(BasicStringComparator): """Provide comparisons of OpenStack releases. @@ -1912,3 +1917,33 @@ def set_db_initialised(): """ juju_log('Setting db-initialised to True', 'DEBUG') leader_set({'db-initialised': True}) + + +def is_db_maintenance_mode(relid=None): + """Check relation data from notifications of db in maintenance mode. + + :returns: Whether db has notified it is in maintenance mode. + :rtype: bool + """ + juju_log('Checking for maintenance notifications', 'DEBUG') + if relid: + r_ids = [relid] + else: + r_ids = relation_ids('shared-db') + rids_units = [(r, u) for r in r_ids for u in related_units(r)] + notifications = [] + for r_id, unit in rids_units: + settings = relation_get(unit=unit, rid=r_id) + for key, value in settings.items(): + if value and key in DB_MAINTENANCE_KEYS: + juju_log( + 'Unit: {}, Key: {}, Value: {}'.format(unit, key, value), + 'DEBUG') + try: + notifications.append(bool_from_string(value)) + except ValueError: + juju_log( + 'Could not discern bool from {}'.format(value), + 'WARN') + pass + return True in notifications diff --git a/hooks/charmhelpers/fetch/ubuntu_apt_pkg.py b/hooks/charmhelpers/fetch/ubuntu_apt_pkg.py index 104f91f1..929a75d7 100644 --- a/hooks/charmhelpers/fetch/ubuntu_apt_pkg.py +++ b/hooks/charmhelpers/fetch/ubuntu_apt_pkg.py @@ -38,6 +38,7 @@ so with this we get rid of the dependency. import locale import os import subprocess +import sys class _container(dict): @@ -59,6 +60,13 @@ class Cache(object): def __init__(self, progress=None): pass + def __contains__(self, package): + try: + pkg = self.__getitem__(package) + return pkg is not None + except KeyError: + return False + def __getitem__(self, package): """Get information about a package from apt and dpkg databases. @@ -178,6 +186,28 @@ class Cache(object): return pkgs +class Config(_container): + def __init__(self): + super(Config, self).__init__(self._populate()) + + def _populate(self): + cfgs = {} + cmd = ['apt-config', 'dump'] + output = subprocess.check_output(cmd, + stderr=subprocess.STDOUT, + universal_newlines=True) + for line in output.splitlines(): + if not line.startswith("CommandLine"): + k, v = line.split(" ", 1) + cfgs[k] = v.strip(";").strip("\"") + + return cfgs + + +# Backwards compatibility with old apt_pkg module +sys.modules[__name__].config = Config() + + def init(): """Compability shim that does nothing.""" pass diff --git a/hooks/charmhelpers/osplatform.py b/hooks/charmhelpers/osplatform.py index d9a4d5c0..c7fd1363 100644 --- a/hooks/charmhelpers/osplatform.py +++ b/hooks/charmhelpers/osplatform.py @@ -20,6 +20,9 @@ def get_platform(): # Stock Python does not detect Ubuntu and instead returns debian. # Or at least it does in some build environments like Travis CI return "ubuntu" + elif "elementary" in current_platform: + # ElementaryOS fails to run tests locally without this. + return "ubuntu" else: raise RuntimeError("This module is not supported on {}." .format(current_platform))