Release sync for 20.08

- Classic charms: sync charm-helpers.
- Classic ceph based charms:  also sync charms.ceph
- Reactive charms: trigger a rebuild
- sync tox.ini
- sync requirements.txt and test-requirements.txt

Change-Id: If1671c83cd83caebb05a167011f554b76650b07b
This commit is contained in:
Alex Kavanagh 2020-07-27 20:09:25 +01:00
parent de30b2f73d
commit a8f1661d61
9 changed files with 139 additions and 43 deletions

View File

@ -49,7 +49,8 @@ __deprecated_functions = {}
def deprecate(warning, date=None, log=None): def deprecate(warning, date=None, log=None):
"""Add a deprecation warning the first time the function is used. """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. that the function is officially going to be removed.
usage: 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 The reason for passing the logging function (log) is so that hookenv.log
can be used for a charm if needed. can be used for a charm if needed.
:param warning: String to indicat where it has moved ot. :param warning: String to indicate what is to be used instead.
:param date: optional sting, in YYYY-MM format to indicate when the :param date: Optional string in YYYY-MM format to indicate when the
function will definitely (probably) be removed. 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): def wrap(f):

View File

@ -18,14 +18,14 @@
# Authors: # Authors:
# Matthew Wedgwood <matthew.wedgwood@canonical.com> # Matthew Wedgwood <matthew.wedgwood@canonical.com>
import subprocess import glob
import pwd
import grp import grp
import os import os
import glob import pwd
import shutil
import re import re
import shlex import shlex
import shutil
import subprocess
import yaml import yaml
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
@ -265,6 +265,11 @@ class NRPE(object):
relation_set(relation_id=rid, relation_settings={'primary': self.primary}) relation_set(relation_id=rid, relation_settings={'primary': self.primary})
self.remove_check_queue = set() 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): def add_check(self, *args, **kwargs):
shortname = None shortname = None
if kwargs.get('shortname') is None: if kwargs.get('shortname') is None:
@ -310,6 +315,12 @@ class NRPE(object):
nrpe_monitors = {} nrpe_monitors = {}
monitors = {"monitors": {"remote": {"nrpe": 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: for nrpecheck in self.checks:
nrpecheck.write(self.nagios_context, self.hostname, nrpecheck.write(self.nagios_context, self.hostname,
self.nagios_servicegroups) 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 upstart_init = '/etc/init/%s.conf' % svc
sysv_init = '/etc/init.d/%s' % svc sysv_init = '/etc/init.d/%s' % svc
if host.init_is_systemd(): if host.init_is_systemd(service_name=svc):
nrpe.add_check( nrpe.add_check(
shortname=svc, shortname=svc,
description='process check {%s}' % unit_name, description='process check {%s}' % unit_name,

View File

@ -14,10 +14,12 @@
''' Helpers for interacting with OpenvSwitch ''' ''' Helpers for interacting with OpenvSwitch '''
import hashlib import hashlib
import subprocess
import os import os
import re
import six import six
import subprocess
from charmhelpers import deprecate
from charmhelpers.fetch import apt_install from charmhelpers.fetch import apt_install
@ -25,6 +27,8 @@ from charmhelpers.core.hookenv import (
log, WARNING, INFO, DEBUG log, WARNING, INFO, DEBUG
) )
from charmhelpers.core.host import ( from charmhelpers.core.host import (
CompareHostReleases,
lsb_release,
service service
) )
@ -246,18 +250,21 @@ def add_bridge_port(name, port, promisc=False, ifdata=None, exclusive=False,
subprocess.check_call(["ip", "link", "set", port, "promisc", "off"]) subprocess.check_call(["ip", "link", "set", port, "promisc", "off"])
def del_bridge_port(name, port): def del_bridge_port(name, port, linkdown=True):
"""Delete a port from the named openvswitch bridge """Delete a port from the named openvswitch bridge
:param name: Name of bridge to remove port from :param name: Name of bridge to remove port from
:type name: str :type name: str
:param port: Name of port to remove :param port: Name of port to remove
:type port: str :type port: str
:param linkdown: Whether to set link down on interface (default: True)
:type linkdown: bool
:raises: subprocess.CalledProcessError :raises: subprocess.CalledProcessError
""" """
log('Deleting port {} from bridge {}'.format(port, name)) log('Deleting port {} from bridge {}'.format(port, name))
subprocess.check_call(["ovs-vsctl", "--", "--if-exists", "del-port", subprocess.check_call(["ovs-vsctl", "--", "--if-exists", "del-port",
name, port]) name, port])
if linkdown:
subprocess.check_call(["ip", "link", "set", port, "down"]) subprocess.check_call(["ip", "link", "set", port, "down"])
subprocess.check_call(["ip", "link", "set", port, "promisc", "off"]) subprocess.check_call(["ip", "link", "set", port, "promisc", "off"])
@ -315,7 +322,8 @@ def add_bridge_bond(bridge, port, interfaces, portdata=None, ifdatamap=None,
subprocess.check_call(cmd) subprocess.check_call(cmd)
def add_ovsbridge_linuxbridge(name, bridge, ifdata=None): @deprecate('see lp:1877594', '2021-01', log=log)
def add_ovsbridge_linuxbridge(name, bridge, ifdata=None, portdata=None):
"""Add linux bridge to the named openvswitch bridge """Add linux bridge to the named openvswitch bridge
:param name: Name of ovs bridge to be added to Linux bridge :param name: Name of ovs bridge to be added to Linux bridge
@ -343,6 +351,17 @@ def add_ovsbridge_linuxbridge(name, bridge, ifdata=None):
20dac08fdcce4b7fda1d07add3b346aa9751cfbc/ 20dac08fdcce4b7fda1d07add3b346aa9751cfbc/
lib/db-ctl-base.c#L189-L215 lib/db-ctl-base.c#L189-L215
:type ifdata: Optional[Dict[str,Union[str,Dict[str,str]]]] :type ifdata: Optional[Dict[str,Union[str,Dict[str,str]]]]
:param portdata: Additional data to attach to port. Similar to ifdata.
:type portdata: Optional[Dict[str,Union[str,Dict[str,str]]]]
WARNINGS:
* The `ifup` command (NetworkManager) must be available on the system for
this to work. Before bionic this was shipped by default. On bionic and
newer you need to install the package `ifupdown`. This might however cause
issues when deploying to LXD, see lp:1877594, which is why this function
isn't supported anymore.
* On focal and newer this function won't even try to run `ifup` and raise
directly.
""" """
try: try:
import netifaces import netifaces
@ -377,25 +396,42 @@ def add_ovsbridge_linuxbridge(name, bridge, ifdata=None):
ovsbridge_port = "cvo{}".format(base) ovsbridge_port = "cvo{}".format(base)
linuxbridge_port = "cvb{}".format(base) linuxbridge_port = "cvb{}".format(base)
network_interface_already_exists = False
interfaces = netifaces.interfaces() interfaces = netifaces.interfaces()
for interface in interfaces: for interface in interfaces:
if interface == ovsbridge_port or interface == linuxbridge_port: if interface == ovsbridge_port or interface == linuxbridge_port:
log('Interface {} already exists'.format(interface), level=INFO) log('Interface {} already exists'.format(interface), level=INFO)
return network_interface_already_exists = True
break
log('Adding linuxbridge {} to ovsbridge {}'.format(bridge, name), log('Adding linuxbridge {} to ovsbridge {}'.format(bridge, name),
level=INFO) level=INFO)
check_for_eni_source() if not network_interface_already_exists:
setup_eni() # will raise on focal+
with open('/etc/network/interfaces.d/{}.cfg'.format( with open('/etc/network/interfaces.d/{}.cfg'.format(
linuxbridge_port), 'w') as config: linuxbridge_port), 'w') as config:
config.write(BRIDGE_TEMPLATE.format(linuxbridge_port=linuxbridge_port, config.write(BRIDGE_TEMPLATE.format(
ovsbridge_port=ovsbridge_port, linuxbridge_port=linuxbridge_port,
bridge=bridge)) ovsbridge_port=ovsbridge_port, bridge=bridge))
subprocess.check_call(["ifup", linuxbridge_port]) try:
add_bridge_port(name, linuxbridge_port, ifdata=ifdata) # NOTE(lourot): 'ifup <name>' can't be replaced by
# 'ip link set <name> up' as the latter won't parse
# /etc/network/interfaces*
subprocess.check_call(['ifup', linuxbridge_port])
except FileNotFoundError:
# NOTE(lourot): on bionic and newer, 'ifup' isn't installed by
# default. It has been replaced by netplan.io but we can't use it
# yet because of lp:1876730. For the time being, charms using this
# have to install 'ifupdown' on bionic and newer. This will however
# cause issues when deploying to LXD, see lp:1877594.
raise RuntimeError('ifup: command not found. Did this charm forget '
'to install ifupdown?')
add_bridge_port(name, linuxbridge_port, ifdata=ifdata, exclusive=False,
portdata=portdata)
def is_linuxbridge_interface(port): def is_linuxbridge_interface(port):
@ -455,18 +491,35 @@ def get_certificate():
return None return None
def check_for_eni_source(): @deprecate('see lp:1877594', '2021-01', log=log)
''' Juju removes the source line when setting up interfaces, def setup_eni():
replace if missing ''' """Makes sure /etc/network/interfaces.d/ exists and will be parsed.
When setting up interfaces, Juju removes from
/etc/network/interfaces the line sourcing interfaces.d/
WARNING: Not supported on focal and newer anymore. Will raise.
"""
release = CompareHostReleases(lsb_release()['DISTRIB_CODENAME'])
if release >= 'focal':
raise RuntimeError("NetworkManager isn't supported anymore")
if not os.path.exists('/etc/network/interfaces.d'):
os.makedirs('/etc/network/interfaces.d', mode=0o755)
with open('/etc/network/interfaces', 'r') as eni: with open('/etc/network/interfaces', 'r') as eni:
for line in eni: for line in eni:
if line == 'source /etc/network/interfaces.d/*': if re.search(r'^\s*source\s+/etc/network/interfaces.d/\*\s*$',
line):
return return
with open('/etc/network/interfaces', 'a') as eni: with open('/etc/network/interfaces', 'a') as eni:
eni.write('\nsource /etc/network/interfaces.d/*') eni.write('\nsource /etc/network/interfaces.d/*')
@deprecate('use setup_eni() instead', '2021-01', log=log)
def check_for_eni_source():
setup_eni()
def full_restart(): def full_restart():
''' Full restart and reload of openvswitch ''' ''' Full restart and reload of openvswitch '''
if os.path.exists('/etc/init/openvswitch-force-reload-kmod.conf'): if os.path.exists('/etc/init/openvswitch-force-reload-kmod.conf'):

View File

@ -6,8 +6,14 @@ Listen {{ ext_port }}
<VirtualHost {{ address }}:{{ ext }}> <VirtualHost {{ address }}:{{ ext }}>
ServerName {{ endpoint }} ServerName {{ endpoint }}
SSLEngine on SSLEngine on
SSLProtocol +TLSv1 +TLSv1.1 +TLSv1.2
SSLCipherSuite HIGH:!RC4:!MD5:!aNULL:!eNULL:!EXP:!LOW:!MEDIUM # This section is based on Mozilla's recommendation
# as the "intermediate" profile as of July 7th, 2020.
# https://wiki.mozilla.org/Security/Server_Side_TLS
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }} SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }}
# See LP 1484489 - this is to support <= 2.4.7 and >= 2.4.8 # See LP 1484489 - this is to support <= 2.4.7 and >= 2.4.8
SSLCertificateChainFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }} SSLCertificateChainFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }}

View File

@ -6,8 +6,14 @@ Listen {{ ext_port }}
<VirtualHost {{ address }}:{{ ext }}> <VirtualHost {{ address }}:{{ ext }}>
ServerName {{ endpoint }} ServerName {{ endpoint }}
SSLEngine on SSLEngine on
SSLProtocol +TLSv1 +TLSv1.1 +TLSv1.2
SSLCipherSuite HIGH:!RC4:!MD5:!aNULL:!eNULL:!EXP:!LOW:!MEDIUM # This section is based on Mozilla's recommendation
# as the "intermediate" profile as of July 7th, 2020.
# https://wiki.mozilla.org/Security/Server_Side_TLS
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }} SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }}
# See LP 1484489 - this is to support <= 2.4.7 and >= 2.4.8 # See LP 1484489 - this is to support <= 2.4.7 and >= 2.4.8
SSLCertificateChainFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }} SSLCertificateChainFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }}

View File

@ -2241,10 +2241,13 @@ def inform_peers_unit_state(state, relation_name='cluster'):
if state not in UNIT_STATES: if state not in UNIT_STATES:
raise ValueError( raise ValueError(
"Setting invalid state {} for unit".format(state)) "Setting invalid state {} for unit".format(state))
this_unit = local_unit()
for r_id in relation_ids(relation_name): 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_set(relation_id=r_id,
relation_settings={ relation_settings={
get_peer_key(local_unit()): state}) get_peer_key(this_unit): state})
def get_peers_unit_state(relation_name='cluster'): def get_peers_unit_state(relation_name='cluster'):
@ -2276,8 +2279,10 @@ def are_peers_ready(relation_name='cluster'):
:returns: Whether all units are ready. :returns: Whether all units are ready.
:rtype: bool :rtype: bool
""" """
unit_states = get_peers_unit_state(relation_name) unit_states = get_peers_unit_state(relation_name).values()
return all(v == UNIT_READY for v in unit_states.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'): def inform_peers_if_ready(check_unit_ready_func, relation_name='cluster'):
@ -2360,7 +2365,9 @@ def get_api_application_status():
app_state, msg = get_api_unit_status() app_state, msg = get_api_unit_status()
if app_state == WORKLOAD_STATES.ACTIVE: if app_state == WORKLOAD_STATES.ACTIVE:
if are_peers_ready(): if are_peers_ready():
return WORKLOAD_STATES.ACTIVE, 'Application Ready' msg = 'Application Ready'
else: 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 return app_state, msg

View File

@ -193,7 +193,7 @@ def service_pause(service_name, init_dir="/etc/init", initd_dir="/etc/init.d",
stopped = service_stop(service_name, **kwargs) stopped = service_stop(service_name, **kwargs)
upstart_file = os.path.join(init_dir, "{}.conf".format(service_name)) upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
sysv_file = os.path.join(initd_dir, 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('disable', service_name)
service('mask', service_name) service('mask', service_name)
elif os.path.exists(upstart_file): 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)) upstart_file = os.path.join(init_dir, "{}.conf".format(service_name))
sysv_file = os.path.join(initd_dir, 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('unmask', service_name)
service('enable', service_name) service('enable', service_name)
elif os.path.exists(upstart_file): 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 :param **kwargs: additional params to be passed to the service command in
the form of key=value. the form of key=value.
""" """
if init_is_systemd(): if init_is_systemd(service_name=service_name):
cmd = ['systemctl', action, service_name] cmd = ['systemctl', action, service_name]
else: else:
cmd = ['service', service_name, action] 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 units (e.g. service ceph-osd status id=2). The kwargs
are ignored in systemd services. are ignored in systemd services.
""" """
if init_is_systemd(): if init_is_systemd(service_name=service_name):
return service('is-active', service_name) return service('is-active', service_name)
else: else:
if os.path.exists(_UPSTART_CONF.format(service_name)): if os.path.exists(_UPSTART_CONF.format(service_name)):
@ -311,8 +311,14 @@ def service_running(service_name, **kwargs):
SYSTEMD_SYSTEM = '/run/systemd/system' SYSTEMD_SYSTEM = '/run/systemd/system'
def init_is_systemd(): def init_is_systemd(service_name=None):
"""Return True if the host system uses systemd, False otherwise.""" """
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': if lsb_release()['DISTRIB_CODENAME'] == 'trusty':
return False return False
return os.path.isdir(SYSTEMD_SYSTEM) return os.path.isdir(SYSTEMD_SYSTEM)

View File

@ -13,5 +13,9 @@ netifaces>=0.10.4
netaddr>=0.7.12,!=0.7.16 netaddr>=0.7.12,!=0.7.16
Jinja2>=2.6 # BSD License (3 clause) Jinja2>=2.6 # BSD License (3 clause)
six>=1.9.0 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 psutil>=1.1.1,<2.0.0

View File

@ -9,6 +9,7 @@
# #
charm-tools>=2.4.4 charm-tools>=2.4.4
requests>=2.18.4 requests>=2.18.4
mock>=1.2
flake8>=2.2.4,<=2.4.1 flake8>=2.2.4,<=2.4.1
stestr>=2.2.0 stestr>=2.2.0
coverage>=4.5.2 coverage>=4.5.2