Sync charm-helpers for py38, distro, and other updates

Change-Id: I9e8848cc6dff2addd4db087740ba5bbc967577cf
This commit is contained in:
Ryan Beisner 2020-03-05 13:30:33 +01:00
parent fcfa499f11
commit 61ffd77184
No known key found for this signature in database
GPG Key ID: 952BACDC1C1A05FB
7 changed files with 144 additions and 23 deletions

View File

@ -1,5 +1,5 @@
#!/usr/bin/make #!/usr/bin/make
PYTHON := /usr/bin/env python PYTHON := /usr/bin/env python3
lint: lint:
@tox -e pep8 @tox -e pep8

View File

@ -17,7 +17,6 @@ import contextlib
import os import os
import six import six
import shutil import shutil
import sys
import yaml import yaml
import zipfile import zipfile
@ -531,7 +530,7 @@ def clean_policyd_dir_for(service, keep_paths=None, user=None, group=None):
hookenv.log("Cleaning path: {}".format(path), level=hookenv.DEBUG) hookenv.log("Cleaning path: {}".format(path), level=hookenv.DEBUG)
if not os.path.exists(path): if not os.path.exists(path):
ch_host.mkdir(path, owner=_user, group=_group, perms=0o775) ch_host.mkdir(path, owner=_user, group=_group, perms=0o775)
_scanner = os.scandir if sys.version_info > (3, 4) else _py2_scandir _scanner = os.scandir if hasattr(os, 'scandir') else _fallback_scandir
for direntry in _scanner(path): for direntry in _scanner(path):
# see if the path should be kept. # see if the path should be kept.
if direntry.path in keep_paths: if direntry.path in keep_paths:
@ -560,23 +559,25 @@ def maybe_create_directory_for(path, user, group):
@contextlib.contextmanager @contextlib.contextmanager
def _py2_scandir(path): def _fallback_scandir(path):
"""provide a py2 implementation of os.scandir if this module ever gets used """Fallback os.scandir implementation.
in a py2 charm (unlikely). uses os.listdir() to get the names in the path,
and then mocks the is_dir() function using os.path.isdir() to check for a provide a fallback implementation of os.scandir if this module ever gets
used in a py2 or py34 charm. Uses os.listdir() to get the names in the path,
and then mocks the is_dir() function using os.path.isdir() to check for
directory. directory.
:param path: the path to list the directories for :param path: the path to list the directories for
:type path: str :type path: str
:returns: Generator that provides _P27Direntry objects :returns: Generator that provides _FBDirectory objects
:rtype: ContextManager[_P27Direntry] :rtype: ContextManager[_FBDirectory]
""" """
for f in os.listdir(path): for f in os.listdir(path):
yield _P27Direntry(f) yield _FBDirectory(f)
class _P27Direntry(object): class _FBDirectory(object):
"""Mock a scandir Direntry object with enough to use in """Mock a scandir Directory object with enough to use in
clean_policyd_dir_for clean_policyd_dir_for
""" """

View File

@ -278,7 +278,7 @@ PACKAGE_CODENAMES = {
('14', 'rocky'), ('14', 'rocky'),
('15', 'stein'), ('15', 'stein'),
('16', 'train'), ('16', 'train'),
('17', 'ussuri'), ('18', 'ussuri'),
]), ]),
'ceilometer-common': OrderedDict([ 'ceilometer-common': OrderedDict([
('5', 'liberty'), ('5', 'liberty'),
@ -326,7 +326,7 @@ PACKAGE_CODENAMES = {
('14', 'rocky'), ('14', 'rocky'),
('15', 'stein'), ('15', 'stein'),
('16', 'train'), ('16', 'train'),
('17', 'ussuri'), ('18', 'ussuri'),
]), ]),
} }
@ -555,9 +555,8 @@ def reset_os_release():
_os_rel = None _os_rel = None
def os_release(package, base=None, reset_cache=False): def os_release(package, base=None, reset_cache=False, source_key=None):
''' """Returns OpenStack release codename from a cached global.
Returns OpenStack release codename from a cached global.
If reset_cache then unset the cached os_release version and return the If reset_cache then unset the cached os_release version and return the
freshly determined version. freshly determined version.
@ -565,7 +564,20 @@ def os_release(package, base=None, reset_cache=False):
If the codename can not be determined from either an installed package or If the codename can not be determined from either an installed package or
the installation source, the earliest release supported by the charm should the installation source, the earliest release supported by the charm should
be returned. be returned.
'''
:param package: Name of package to determine release from
:type package: str
:param base: Fallback codename if endavours to determine from package fail
:type base: Optional[str]
:param reset_cache: Reset any cached codename value
:type reset_cache: bool
:param source_key: Name of source configuration option
(default: 'openstack-origin')
:type source_key: Optional[str]
:returns: OpenStack release codename
:rtype: str
"""
source_key = source_key or 'openstack-origin'
if not base: if not base:
base = UBUNTU_OPENSTACK_RELEASE[lsb_release()['DISTRIB_CODENAME']] base = UBUNTU_OPENSTACK_RELEASE[lsb_release()['DISTRIB_CODENAME']]
global _os_rel global _os_rel
@ -575,7 +587,7 @@ def os_release(package, base=None, reset_cache=False):
return _os_rel return _os_rel
_os_rel = ( _os_rel = (
get_os_codename_package(package, fatal=False) or get_os_codename_package(package, fatal=False) or
get_os_codename_install_source(config('openstack-origin')) or get_os_codename_install_source(config(source_key)) or
base) base)
return _os_rel return _os_rel
@ -658,6 +670,93 @@ def config_value_changed(option):
return current != saved return current != saved
def get_endpoint_key(service_name, relation_id, unit_name):
"""Return the key used to refer to an ep changed notification from a unit.
:param service_name: Service name eg nova, neutron, placement etc
:type service_name: str
:param relation_id: The id of the relation the unit is on.
:type relation_id: str
:param unit_name: The name of the unit publishing the notification.
:type unit_name: str
:returns: The key used to refer to an ep changed notification from a unit
:rtype: str
"""
return '{}-{}-{}'.format(
service_name,
relation_id.replace(':', '_'),
unit_name.replace('/', '_'))
def get_endpoint_notifications(service_names, rel_name='identity-service'):
"""Return all notifications for the given services.
:param service_names: List of service name.
:type service_name: List
:param rel_name: Name of the relation to query
:type rel_name: str
:returns: A dict containing the source of the notification and its nonce.
:rtype: Dict[str, str]
"""
notifications = {}
for rid in relation_ids(rel_name):
for unit in related_units(relid=rid):
ep_changed_json = relation_get(
rid=rid,
unit=unit,
attribute='ep_changed')
if ep_changed_json:
ep_changed = json.loads(ep_changed_json)
for service in service_names:
if ep_changed.get(service):
key = get_endpoint_key(service, rid, unit)
notifications[key] = ep_changed[service]
return notifications
def endpoint_changed(service_name, rel_name='identity-service'):
"""Whether a new notification has been recieved for an endpoint.
:param service_name: Service name eg nova, neutron, placement etc
:type service_name: str
:param rel_name: Name of the relation to query
:type rel_name: str
:returns: Whether endpoint has changed
:rtype: bool
"""
changed = False
with unitdata.HookData()() as t:
db = t[0]
notifications = get_endpoint_notifications(
[service_name],
rel_name=rel_name)
for key, nonce in notifications.items():
if db.get(key) != nonce:
juju_log(('New endpoint change notification found: '
'{}={}').format(key, nonce),
'INFO')
changed = True
break
return changed
def save_endpoint_changed_triggers(service_names, rel_name='identity-service'):
"""Save the enpoint triggers in db so it can be tracked if they changed.
:param service_names: List of service name.
:type service_name: List
:param rel_name: Name of the relation to query
:type rel_name: str
"""
with unitdata.HookData()() as t:
db = t[0]
notifications = get_endpoint_notifications(
service_names,
rel_name=rel_name)
for key, nonce in notifications.items():
db.set(key, nonce)
def save_script_rc(script_path="scripts/scriptrc", **env_vars): def save_script_rc(script_path="scripts/scriptrc", **env_vars):
""" """
Write an rc file in the charm-delivered directory containing Write an rc file in the charm-delivered directory containing

View File

@ -1042,7 +1042,7 @@ def filesystem_mounted(fs):
def make_filesystem(blk_device, fstype='ext4', timeout=10): def make_filesystem(blk_device, fstype='ext4', timeout=10):
"""Make a new filesystem on the specified block device.""" """Make a new filesystem on the specified block device."""
count = 0 count = 0
e_noent = os.errno.ENOENT e_noent = errno.ENOENT
while not os.path.exists(blk_device): while not os.path.exists(blk_device):
if count >= timeout: if count >= timeout:
log('Gave up waiting on block device %s' % blk_device, log('Gave up waiting on block device %s' % blk_device,

View File

@ -25,6 +25,7 @@ UBUNTU_RELEASES = (
'cosmic', 'cosmic',
'disco', 'disco',
'eoan', 'eoan',
'focal'
) )

View File

@ -1,4 +1,5 @@
import platform import platform
import os
def get_platform(): def get_platform():
@ -9,9 +10,13 @@ def get_platform():
This string is used to decide which platform module should be imported. This string is used to decide which platform module should be imported.
""" """
# linux_distribution is deprecated and will be removed in Python 3.7 # linux_distribution is deprecated and will be removed in Python 3.7
# Warings *not* disabled, as we certainly need to fix this. # Warnings *not* disabled, as we certainly need to fix this.
tuple_platform = platform.linux_distribution() if hasattr(platform, 'linux_distribution'):
current_platform = tuple_platform[0] tuple_platform = platform.linux_distribution()
current_platform = tuple_platform[0]
else:
current_platform = _get_platform_from_fs()
if "Ubuntu" in current_platform: if "Ubuntu" in current_platform:
return "ubuntu" return "ubuntu"
elif "CentOS" in current_platform: elif "CentOS" in current_platform:
@ -26,3 +31,16 @@ def get_platform():
else: else:
raise RuntimeError("This module is not supported on {}." raise RuntimeError("This module is not supported on {}."
.format(current_platform)) .format(current_platform))
def _get_platform_from_fs():
"""Get Platform from /etc/os-release."""
with open(os.path.join(os.sep, 'etc', 'os-release')) as fin:
content = dict(
line.split('=', 1)
for line in fin.read().splitlines()
if '=' in line
)
for k, v in content.items():
content[k] = v.strip('"')
return content["NAME"]

View File

@ -1372,6 +1372,8 @@ def create_keyrings():
if not output: if not output:
# NOTE: key not yet created, raise exception and retry # NOTE: key not yet created, raise exception and retry
raise Exception raise Exception
# NOTE: octopus wants newline at end of file LP: #1864706
output += '\n'
write_file(_client_admin_keyring, output, write_file(_client_admin_keyring, output,
owner=ceph_user(), group=ceph_user(), owner=ceph_user(), group=ceph_user(),
perms=0o400) perms=0o400)