diff --git a/charmhelpers/__init__.py b/charmhelpers/__init__.py index 61ef907..1f57ed2 100644 --- a/charmhelpers/__init__.py +++ b/charmhelpers/__init__.py @@ -49,7 +49,8 @@ __deprecated_functions = {} def deprecate(warning, date=None, log=None): """Add a deprecation warning the first time the function is used. - The date, which is a string in semi-ISO8660 format indicate the year-month + + The date which is a string in semi-ISO8660 format indicates the year-month that the function is officially going to be removed. usage: @@ -62,10 +63,11 @@ def deprecate(warning, date=None, log=None): The reason for passing the logging function (log) is so that hookenv.log can be used for a charm if needed. - :param warning: String to indicat where it has moved ot. - :param date: optional sting, in YYYY-MM format to indicate when the + :param warning: String to indicate what is to be used instead. + :param date: Optional string in YYYY-MM format to indicate when the function will definitely (probably) be removed. - :param log: The log function to call to log. If not, logs to stdout + :param log: The log function to call in order to log. If None, logs to + stdout """ def wrap(f): diff --git a/charmhelpers/contrib/charmsupport/nrpe.py b/charmhelpers/contrib/charmsupport/nrpe.py index d775861..14b80d9 100644 --- a/charmhelpers/contrib/charmsupport/nrpe.py +++ b/charmhelpers/contrib/charmsupport/nrpe.py @@ -18,14 +18,14 @@ # Authors: # Matthew Wedgwood -import subprocess -import pwd +import glob import grp import os -import glob -import shutil +import pwd import re import shlex +import shutil +import subprocess import yaml from charmhelpers.core.hookenv import ( @@ -265,6 +265,11 @@ class NRPE(object): relation_set(relation_id=rid, relation_settings={'primary': self.primary}) self.remove_check_queue = set() + @classmethod + def does_nrpe_conf_dir_exist(cls): + """Return True if th nrpe_confdif directory exists.""" + return os.path.isdir(cls.nrpe_confdir) + def add_check(self, *args, **kwargs): shortname = None if kwargs.get('shortname') is None: @@ -310,6 +315,12 @@ class NRPE(object): nrpe_monitors = {} monitors = {"monitors": {"remote": {"nrpe": nrpe_monitors}}} + + # check that the charm can write to the conf dir. If not, then nagios + # probably isn't installed, and we can defer. + if not self.does_nrpe_conf_dir_exist(): + return + for nrpecheck in self.checks: nrpecheck.write(self.nagios_context, self.hostname, self.nagios_servicegroups) @@ -400,7 +411,7 @@ def add_init_service_checks(nrpe, services, unit_name, immediate_check=True): upstart_init = '/etc/init/%s.conf' % svc sysv_init = '/etc/init.d/%s' % svc - if host.init_is_systemd(): + if host.init_is_systemd(service_name=svc): nrpe.add_check( shortname=svc, description='process check {%s}' % unit_name, diff --git a/charmhelpers/contrib/database/mysql.py b/charmhelpers/contrib/database/mysql.py index c9ecce5..e38d8a9 100644 --- a/charmhelpers/contrib/database/mysql.py +++ b/charmhelpers/contrib/database/mysql.py @@ -422,7 +422,8 @@ class MySQLHelper(object): This is typically used to provide shared-db relations with a list of which units have been granted access to the given database. """ - self.connect(password=self.get_mysql_root_password()) + if not self.connection: + self.connect(password=self.get_mysql_root_password()) allowed_units = set() for unit in related_units(relation_id): settings = relation_get(rid=relation_id, unit=unit) @@ -459,7 +460,8 @@ class MySQLHelper(object): def configure_db(self, hostname, database, username, admin=False): """Configure access to database for username from hostname.""" - self.connect(password=self.get_mysql_root_password()) + if not self.connection: + self.connect(password=self.get_mysql_root_password()) if not self.database_exists(database): self.create_database(database) diff --git a/charmhelpers/contrib/openstack/utils.py b/charmhelpers/contrib/openstack/utils.py index fbf0156..0aa797c 100644 --- a/charmhelpers/contrib/openstack/utils.py +++ b/charmhelpers/contrib/openstack/utils.py @@ -143,6 +143,7 @@ OPENSTACK_RELEASES = ( 'stein', 'train', 'ussuri', + 'victoria', ) UBUNTU_OPENSTACK_RELEASE = OrderedDict([ @@ -164,6 +165,7 @@ UBUNTU_OPENSTACK_RELEASE = OrderedDict([ ('disco', 'stein'), ('eoan', 'train'), ('focal', 'ussuri'), + ('groovy', 'victoria'), ]) @@ -186,6 +188,7 @@ OPENSTACK_CODENAMES = OrderedDict([ ('2019.1', 'stein'), ('2019.2', 'train'), ('2020.1', 'ussuri'), + ('2020.2', 'victoria'), ]) # The ugly duckling - must list releases oldest to newest @@ -226,6 +229,8 @@ SWIFT_CODENAMES = OrderedDict([ ['2.22.0', '2.23.0']), ('ussuri', ['2.24.0', '2.25.0']), + ('victoria', + ['2.25.0']), ]) # >= Liberty version->codename mapping @@ -241,6 +246,7 @@ PACKAGE_CODENAMES = { ('19', 'stein'), ('20', 'train'), ('21', 'ussuri'), + ('22', 'victoria'), ]), 'neutron-common': OrderedDict([ ('7', 'liberty'), @@ -253,6 +259,7 @@ PACKAGE_CODENAMES = { ('14', 'stein'), ('15', 'train'), ('16', 'ussuri'), + ('17', 'victoria'), ]), 'cinder-common': OrderedDict([ ('7', 'liberty'), @@ -265,6 +272,7 @@ PACKAGE_CODENAMES = { ('14', 'stein'), ('15', 'train'), ('16', 'ussuri'), + ('17', 'victoria'), ]), 'keystone': OrderedDict([ ('8', 'liberty'), @@ -277,6 +285,7 @@ PACKAGE_CODENAMES = { ('15', 'stein'), ('16', 'train'), ('17', 'ussuri'), + ('18', 'victoria'), ]), 'horizon-common': OrderedDict([ ('8', 'liberty'), @@ -289,6 +298,7 @@ PACKAGE_CODENAMES = { ('15', 'stein'), ('16', 'train'), ('18', 'ussuri'), + ('19', 'victoria'), ]), 'ceilometer-common': OrderedDict([ ('5', 'liberty'), @@ -301,6 +311,7 @@ PACKAGE_CODENAMES = { ('12', 'stein'), ('13', 'train'), ('14', 'ussuri'), + ('15', 'victoria'), ]), 'heat-common': OrderedDict([ ('5', 'liberty'), @@ -313,6 +324,7 @@ PACKAGE_CODENAMES = { ('12', 'stein'), ('13', 'train'), ('14', 'ussuri'), + ('15', 'victoria'), ]), 'glance-common': OrderedDict([ ('11', 'liberty'), @@ -325,6 +337,7 @@ PACKAGE_CODENAMES = { ('18', 'stein'), ('19', 'train'), ('20', 'ussuri'), + ('21', 'victoria'), ]), 'openstack-dashboard': OrderedDict([ ('8', 'liberty'), @@ -337,6 +350,7 @@ PACKAGE_CODENAMES = { ('15', 'stein'), ('16', 'train'), ('18', 'ussuri'), + ('19', 'victoria'), ]), } @@ -2227,10 +2241,13 @@ def inform_peers_unit_state(state, relation_name='cluster'): if state not in UNIT_STATES: raise ValueError( "Setting invalid state {} for unit".format(state)) + this_unit = local_unit() for r_id in relation_ids(relation_name): + juju_log('Telling peer behind relation {} that {} is {}'.format( + r_id, this_unit, state), 'DEBUG') relation_set(relation_id=r_id, relation_settings={ - get_peer_key(local_unit()): state}) + get_peer_key(this_unit): state}) def get_peers_unit_state(relation_name='cluster'): @@ -2262,8 +2279,10 @@ def are_peers_ready(relation_name='cluster'): :returns: Whether all units are ready. :rtype: bool """ - unit_states = get_peers_unit_state(relation_name) - return all(v == UNIT_READY for v in unit_states.values()) + unit_states = get_peers_unit_state(relation_name).values() + juju_log('{} peers are in the following states: {}'.format( + relation_name, unit_states), 'DEBUG') + return all(state == UNIT_READY for state in unit_states) def inform_peers_if_ready(check_unit_ready_func, relation_name='cluster'): @@ -2346,7 +2365,9 @@ def get_api_application_status(): app_state, msg = get_api_unit_status() if app_state == WORKLOAD_STATES.ACTIVE: if are_peers_ready(): - return WORKLOAD_STATES.ACTIVE, 'Application Ready' + msg = 'Application Ready' else: - return WORKLOAD_STATES.WAITING, 'Some units are not ready' + app_state = WORKLOAD_STATES.WAITING + msg = 'Some units are not ready' + juju_log(msg, 'DEBUG') return app_state, msg diff --git a/charmhelpers/core/hookenv.py b/charmhelpers/core/hookenv.py index d7c37c1..db7ce72 100644 --- a/charmhelpers/core/hookenv.py +++ b/charmhelpers/core/hookenv.py @@ -372,8 +372,10 @@ class Config(dict): try: self._prev_dict = json.load(f) except ValueError as e: - log('Unable to parse previous config data - {}'.format(str(e)), - level=ERROR) + log('Found but was unable to parse previous config data, ' + 'ignoring which will report all values as changed - {}' + .format(str(e)), level=ERROR) + return for k, v in copy.deepcopy(self._prev_dict).items(): if k not in self: self[k] = v diff --git a/charmhelpers/core/host.py b/charmhelpers/core/host.py index b33ac90..a785efd 100644 --- a/charmhelpers/core/host.py +++ b/charmhelpers/core/host.py @@ -193,7 +193,7 @@ def service_pause(service_name, init_dir="/etc/init", initd_dir="/etc/init.d", stopped = service_stop(service_name, **kwargs) upstart_file = os.path.join(init_dir, "{}.conf".format(service_name)) sysv_file = os.path.join(initd_dir, service_name) - if init_is_systemd(): + if init_is_systemd(service_name=service_name): service('disable', service_name) service('mask', service_name) elif os.path.exists(upstart_file): @@ -227,7 +227,7 @@ def service_resume(service_name, init_dir="/etc/init", """ upstart_file = os.path.join(init_dir, "{}.conf".format(service_name)) sysv_file = os.path.join(initd_dir, service_name) - if init_is_systemd(): + if init_is_systemd(service_name=service_name): service('unmask', service_name) service('enable', service_name) elif os.path.exists(upstart_file): @@ -257,7 +257,7 @@ def service(action, service_name, **kwargs): :param **kwargs: additional params to be passed to the service command in the form of key=value. """ - if init_is_systemd(): + if init_is_systemd(service_name=service_name): cmd = ['systemctl', action, service_name] else: cmd = ['service', service_name, action] @@ -281,7 +281,7 @@ def service_running(service_name, **kwargs): units (e.g. service ceph-osd status id=2). The kwargs are ignored in systemd services. """ - if init_is_systemd(): + if init_is_systemd(service_name=service_name): return service('is-active', service_name) else: if os.path.exists(_UPSTART_CONF.format(service_name)): @@ -311,8 +311,14 @@ def service_running(service_name, **kwargs): SYSTEMD_SYSTEM = '/run/systemd/system' -def init_is_systemd(): - """Return True if the host system uses systemd, False otherwise.""" +def init_is_systemd(service_name=None): + """ + Returns whether the host uses systemd for the specified service. + + @param Optional[str] service_name: specific name of service + """ + if str(service_name).startswith("snap."): + return True if lsb_release()['DISTRIB_CODENAME'] == 'trusty': return False return os.path.isdir(SYSTEMD_SYSTEM) diff --git a/charmhelpers/fetch/ubuntu.py b/charmhelpers/fetch/ubuntu.py index 3ddaf0d..3315284 100644 --- a/charmhelpers/fetch/ubuntu.py +++ b/charmhelpers/fetch/ubuntu.py @@ -190,6 +190,14 @@ CLOUD_ARCHIVE_POCKETS = { 'ussuri/proposed': 'bionic-proposed/ussuri', 'bionic-ussuri/proposed': 'bionic-proposed/ussuri', 'bionic-proposed/ussuri': 'bionic-proposed/ussuri', + # Victoria + 'victoria': 'focal-updates/victoria', + 'focal-victoria': 'focal-updates/victoria', + 'focal-victoria/updates': 'focal-updates/victoria', + 'focal-updates/victoria': 'focal-updates/victoria', + 'victoria/proposed': 'focal-proposed/victoria', + 'focal-victoria/proposed': 'focal-proposed/victoria', + 'focal-proposed/victoria': 'focal-proposed/victoria', } diff --git a/requirements.txt b/requirements.txt index 343beed..2316401 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,5 +13,9 @@ netifaces>=0.10.4 netaddr>=0.7.12,!=0.7.16 Jinja2>=2.6 # BSD License (3 clause) six>=1.9.0 -dnspython>=1.12.0 + +# dnspython 2.0.0 dropped py3.5 support +dnspython<2.0.0; python_version < '3.6' +dnspython; python_version >= '3.6' + psutil>=1.1.1,<2.0.0