Merge "When resuming, exclude haproxy"

This commit is contained in:
Zuul 2020-01-29 13:33:39 +00:00 committed by Gerrit Code Review
commit 4808e06ffa
6 changed files with 130 additions and 11 deletions

View File

@ -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

View File

@ -52,7 +52,7 @@ class RestrictedPackages(BaseAudit):
def __init__(self, pkgs, **kwargs):
super(RestrictedPackages, self).__init__(**kwargs)
if isinstance(pkgs, string_types) or not hasattr(pkgs, '__iter__'):
self.pkgs = [pkgs]
self.pkgs = pkgs.split()
else:
self.pkgs = pkgs
@ -100,4 +100,5 @@ class RestrictedPackages(BaseAudit):
apt_purge(pkg.name)
def is_virtual_package(self, pkg):
return pkg.has_provides and not pkg.has_versions
return (pkg.get('has_provides', False) and
not pkg.get('has_versions', False))

View File

@ -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

View File

@ -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

View File

@ -1747,10 +1747,11 @@ def assess_status_func(configs):
if cmp_os_release >= 'train':
required_interfaces.update(REQUIRED_INTERFACES_TRAIN)
required_interfaces.update(get_optional_interfaces())
_services, _ = ch_cluster.get_managed_services_and_ports(services(), [])
return ch_utils.make_assess_status_func(
configs, required_interfaces,
charm_func=check_optional_relations,
services=services(), ports=None)
services=_services, ports=None)
def pause_unit_helper(configs):
@ -1782,8 +1783,9 @@ def _pause_resume_helper(f, configs):
"""
# TODO(ajkavanagh) - ports= has been left off because of the race hazard
# that exists due to service_start()
_services, _ = ch_cluster.get_managed_services_and_ports(services(), [])
f(assess_status_func(configs),
services=services(),
services=_services,
ports=None)

View File

@ -1254,6 +1254,7 @@ class NovaCCUtilsTests(CharmTestCase):
utils.VERSION_PACKAGE
)
@patch.object(utils.ch_cluster, 'get_managed_services_and_ports')
@patch.object(utils, 'get_optional_interfaces')
@patch.object(utils, 'check_optional_relations')
@patch.object(utils, 'REQUIRED_INTERFACES')
@ -1268,8 +1269,10 @@ class NovaCCUtilsTests(CharmTestCase):
services,
REQUIRED_INTERFACES,
check_optional_relations,
get_optional_interfaces):
get_optional_interfaces,
get_managed_services_and_ports):
compare_releases.return_value = 'stein'
get_managed_services_and_ports.return_value = (['s1'], ['p1'])
services.return_value = 's1'
REQUIRED_INTERFACES.copy.return_value = {'int': ['test 1']}
get_optional_interfaces.return_value = {'opt': ['test 2']}
@ -1279,7 +1282,7 @@ class NovaCCUtilsTests(CharmTestCase):
make_assess_status_func.assert_called_once_with(
'test-config',
{'int': ['test 1'], 'opt': ['test 2']},
charm_func=check_optional_relations, services='s1',
charm_func=check_optional_relations, services=['s1'],
ports=None)
make_assess_status_func.reset_mock()
@ -1288,7 +1291,7 @@ class NovaCCUtilsTests(CharmTestCase):
make_assess_status_func.assert_called_once_with(
'test-config',
{'int': ['test 1'], 'placement': ['placement'], 'opt': ['test 2']},
charm_func=check_optional_relations, services='s1',
charm_func=check_optional_relations, services=['s1'],
ports=None)
def test_pause_unit_helper(self):
@ -1301,18 +1304,21 @@ class NovaCCUtilsTests(CharmTestCase):
prh.assert_called_once_with(utils.ch_utils.resume_unit,
'random-config')
@patch.object(utils.ch_cluster, 'get_managed_services_and_ports')
@patch.object(utils, 'services')
@patch.object(utils, 'determine_ports')
def test_pause_resume_helper(self, determine_ports, services):
def test_pause_resume_helper(self, determine_ports, services,
get_managed_services_and_ports):
f = MagicMock()
services.return_value = 's1'
determine_ports.return_value = 'p1'
get_managed_services_and_ports.return_value = (['s1'], ['p1'])
services.return_value = ['s1']
determine_ports.return_value = ['p1']
with patch.object(utils, 'assess_status_func') as asf:
asf.return_value = 'assessor'
utils._pause_resume_helper(f, 'some-config')
asf.assert_called_once_with('some-config')
# ports=None whilst port checks are disabled.
f.assert_called_once_with('assessor', services='s1', ports=None)
f.assert_called_once_with('assessor', services=['s1'], ports=None)
@patch('charmhelpers.fetch.filter_installed_packages')
def test_disable_aws_compat_services_uninstalled(