Sync charm-helpers for Ussuri/Focal release and version details

Change-Id: I4e9ca451157527ca0f19ef981970c5447d510e92
This commit is contained in:
Corey Bryant 2020-01-17 14:22:47 -05:00
parent 8ec35986df
commit 0496ab77ed
12 changed files with 299 additions and 40 deletions

View File

@ -484,3 +484,17 @@ def add_haproxy_checks(nrpe, unit_name):
shortname='haproxy_queue',
description='Check HAProxy queue depth {%s}' % unit_name,
check_cmd='check_haproxy_queue_depth.sh')
def remove_deprecated_check(nrpe, deprecated_services):
"""
Remove checks fro deprecated services in list
:param nrpe: NRPE object to remove check from
:type nrpe: NRPE
:param deprecated_services: List of deprecated services that are removed
:type deprecated_services: list
"""
for dep_svc in deprecated_services:
log('Deprecated service: {}'.format(dep_svc))
nrpe.remove_check(shortname=dep_svc)

View File

@ -295,9 +295,11 @@ class OpenStackAmuletDeployment(AmuletDeployment):
('bionic', 'cloud:bionic-rocky'): self.bionic_rocky,
('bionic', 'cloud:bionic-stein'): self.bionic_stein,
('bionic', 'cloud:bionic-train'): self.bionic_train,
('bionic', 'cloud:bionic-ussuri'): self.bionic_ussuri,
('cosmic', None): self.cosmic_rocky,
('disco', None): self.disco_stein,
('eoan', None): self.eoan_train,
('focal', None): self.focal_ussuri,
}
return releases[(self.series, self.openstack)]
@ -316,6 +318,7 @@ class OpenStackAmuletDeployment(AmuletDeployment):
('cosmic', 'rocky'),
('disco', 'stein'),
('eoan', 'train'),
('focal', 'ussuri'),
])
if self.openstack:
os_origin = self.openstack.split(':')[1]

View File

@ -62,6 +62,7 @@ OPENSTACK_RELEASES_PAIRS = [
'bionic_rocky', 'cosmic_rocky',
'bionic_stein', 'disco_stein',
'bionic_train', 'eoan_train',
'bionic_ussuri', 'focal_ussuri',
]

View File

@ -244,8 +244,8 @@ def validate_file_permissions(config):
@audit(is_audit_type(AuditType.OpenStackSecurityGuide))
def validate_uses_keystone(audit_options):
"""Validate that the service uses Keystone for authentication."""
section = _config_section(audit_options, 'DEFAULT')
assert section is not None, "Missing section 'DEFAULT'"
section = _config_section(audit_options, 'api') or _config_section(audit_options, 'DEFAULT')
assert section is not None, "Missing section 'api / DEFAULT'"
assert section.get('auth_strategy') == "keystone", \
"Application is not using Keystone"

View File

@ -730,6 +730,10 @@ class AMQPContext(OSContextGenerator):
if notification_format:
ctxt['notification_format'] = notification_format
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
@ -2177,9 +2181,66 @@ class LogrotateContext(OSContextGenerator):
class HostInfoContext(OSContextGenerator):
"""Context to provide host information."""
def __init__(self, use_fqdn_hint_cb=None):
"""Initialize HostInfoContext
:param use_fqdn_hint_cb: Callback whose return value used to populate
`use_fqdn_hint`
:type use_fqdn_hint_cb: Callable[[], bool]
"""
# Store callback used to get hint for whether FQDN should be used
# Depending on the workload a charm manages, the use of FQDN vs.
# shortname may be a deploy-time decision, i.e. behaviour can not
# change on charm upgrade or post-deployment configuration change.
# The hint is passed on as a flag in the context to allow the decision
# to be made in the Jinja2 configuration template.
self.use_fqdn_hint_cb = use_fqdn_hint_cb
def _get_canonical_name(self, name=None):
"""Get the official FQDN of the host
The implementation of ``socket.getfqdn()`` in the standard Python
library does not exhaust all methods of getting the official name
of a host ref Python issue https://bugs.python.org/issue5004
This function mimics the behaviour of a call to ``hostname -f`` to
get the official FQDN but returns an empty string if it is
unsuccessful.
:param name: Shortname to get FQDN on
:type name: Optional[str]
:returns: The official FQDN for host or empty string ('')
:rtype: str
"""
name = name or socket.gethostname()
fqdn = ''
if six.PY2:
exc = socket.error
else:
exc = OSError
try:
addrs = socket.getaddrinfo(
name, None, 0, socket.SOCK_DGRAM, 0, socket.AI_CANONNAME)
except exc:
pass
else:
for addr in addrs:
if addr[3]:
if '.' in addr[3]:
fqdn = addr[3]
break
return fqdn
def __call__(self):
name = socket.gethostname()
ctxt = {
'host_fqdn': socket.getfqdn(),
'host': socket.gethostname(),
'host_fqdn': self._get_canonical_name(name) or name,
'host': name,
'use_fqdn_hint': (
self.use_fqdn_hint_cb() if self.use_fqdn_hint_cb else False)
}
return ctxt

View File

@ -157,10 +157,11 @@ def generate_ha_relation_data(service,
_relation_data = {'resources': {}, 'resource_params': {}}
if haproxy_enabled:
_meta = 'meta migration-threshold="INFINITY" failure-timeout="5s"'
_haproxy_res = 'res_{}_haproxy'.format(service)
_relation_data['resources'] = {_haproxy_res: 'lsb:haproxy'}
_relation_data['resource_params'] = {
_haproxy_res: 'op monitor interval="5s"'
_haproxy_res: '{} op monitor interval="5s"'.format(_meta)
}
_relation_data['init_services'] = {_haproxy_res: 'haproxy'}
_relation_data['clones'] = {

View File

@ -307,9 +307,9 @@ def maybe_do_policyd_overrides(openstack_release,
blacklist_paths,
user=_user,
group=_group)
if (os.path.isfile(_policy_success_file())
and restart_handler is not None
and callable(restart_handler)):
if (os.path.isfile(_policy_success_file()) and
restart_handler is not None and
callable(restart_handler)):
restart_handler()
remove_policy_success_file()
return
@ -685,9 +685,9 @@ def process_policy_resource_file(resource_file,
black list.
The yaml filename can be modified in two ways. If the `preserve_topdir`
param is True, then files will be flattened to the this top dir. This
allows for creating sets of files that can be grouped into a single level
tree structure.
param is True, then files will be flattened to the top dir. This allows
for creating sets of files that can be grouped into a single level tree
structure.
Secondly, if the `preprocess_filename` param is not None and callable()
then the name is passed to that function for preprocessing before being

View File

@ -0,0 +1,19 @@
[placement]
{% if auth_host -%}
auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }}
auth_type = password
{% if api_version == "3" -%}
project_domain_name = {{ admin_domain_name }}
user_domain_name = {{ admin_domain_name }}
{% else -%}
project_domain_name = default
user_domain_name = default
{% endif -%}
project_name = {{ admin_tenant_name }}
username = {{ admin_user }}
password = {{ admin_password }}
{% endif -%}
{% if region -%}
os_region_name = {{ region }}
{% endif -%}
randomize_allocation_candidates = true

View File

@ -50,9 +50,14 @@ from charmhelpers.core.hookenv import (
hook_name,
application_version_set,
cached,
leader_set,
leader_get,
)
from charmhelpers.core.strutils import BasicStringComparator
from charmhelpers.core.strutils import (
BasicStringComparator,
bool_from_string,
)
from charmhelpers.contrib.storage.linux.lvm import (
deactivate_lvm_volume_group,
@ -126,6 +131,7 @@ OPENSTACK_RELEASES = (
'rocky',
'stein',
'train',
'ussuri',
)
UBUNTU_OPENSTACK_RELEASE = OrderedDict([
@ -146,6 +152,7 @@ UBUNTU_OPENSTACK_RELEASE = OrderedDict([
('cosmic', 'rocky'),
('disco', 'stein'),
('eoan', 'train'),
('focal', 'ussuri'),
])
@ -167,6 +174,7 @@ OPENSTACK_CODENAMES = OrderedDict([
('2018.2', 'rocky'),
('2019.1', 'stein'),
('2019.2', 'train'),
('2020.1', 'ussuri'),
])
# The ugly duckling - must list releases oldest to newest
@ -204,7 +212,9 @@ SWIFT_CODENAMES = OrderedDict([
('stein',
['2.20.0', '2.21.0']),
('train',
['2.22.0']),
['2.22.0', '2.23.0']),
('ussuri',
['2.24.0']),
])
# >= Liberty version->codename mapping
@ -219,6 +229,7 @@ PACKAGE_CODENAMES = {
('18', 'rocky'),
('19', 'stein'),
('20', 'train'),
('21', 'ussuri'),
]),
'neutron-common': OrderedDict([
('7', 'liberty'),
@ -230,6 +241,7 @@ PACKAGE_CODENAMES = {
('13', 'rocky'),
('14', 'stein'),
('15', 'train'),
('16', 'ussuri'),
]),
'cinder-common': OrderedDict([
('7', 'liberty'),
@ -241,6 +253,7 @@ PACKAGE_CODENAMES = {
('13', 'rocky'),
('14', 'stein'),
('15', 'train'),
('16', 'ussuri'),
]),
'keystone': OrderedDict([
('8', 'liberty'),
@ -252,6 +265,7 @@ PACKAGE_CODENAMES = {
('14', 'rocky'),
('15', 'stein'),
('16', 'train'),
('17', 'ussuri'),
]),
'horizon-common': OrderedDict([
('8', 'liberty'),
@ -263,6 +277,7 @@ PACKAGE_CODENAMES = {
('14', 'rocky'),
('15', 'stein'),
('16', 'train'),
('17', 'ussuri'),
]),
'ceilometer-common': OrderedDict([
('5', 'liberty'),
@ -274,6 +289,7 @@ PACKAGE_CODENAMES = {
('11', 'rocky'),
('12', 'stein'),
('13', 'train'),
('14', 'ussuri'),
]),
'heat-common': OrderedDict([
('5', 'liberty'),
@ -285,6 +301,7 @@ PACKAGE_CODENAMES = {
('11', 'rocky'),
('12', 'stein'),
('13', 'train'),
('14', 'ussuri'),
]),
'glance-common': OrderedDict([
('11', 'liberty'),
@ -296,6 +313,7 @@ PACKAGE_CODENAMES = {
('17', 'rocky'),
('18', 'stein'),
('19', 'train'),
('20', 'ussuri'),
]),
'openstack-dashboard': OrderedDict([
('8', 'liberty'),
@ -307,6 +325,7 @@ PACKAGE_CODENAMES = {
('14', 'rocky'),
('15', 'stein'),
('16', 'train'),
('17', 'ussuri'),
]),
}
@ -674,7 +693,7 @@ def openstack_upgrade_available(package):
else:
try:
avail_vers = get_os_version_install_source(src)
except:
except Exception:
avail_vers = cur_vers
apt.init()
return apt.version_compare(avail_vers, cur_vers) >= 1
@ -1868,3 +1887,28 @@ def series_upgrade_complete(resume_unit_helper=None, configs=None):
configs.write_all()
if resume_unit_helper:
resume_unit_helper(configs)
def is_db_initialised():
"""Check leader storage to see if database has been initialised.
:returns: Whether DB has been initialised
:rtype: bool
"""
db_initialised = None
if leader_get('db-initialised') is None:
juju_log(
'db-initialised key missing, assuming db is not initialised',
'DEBUG')
db_initialised = False
else:
db_initialised = bool_from_string(leader_get('db-initialised'))
juju_log('Database initialised: {}'.format(db_initialised), 'DEBUG')
return db_initialised
def set_db_initialised():
"""Add flag to leader storage to indicate database has been initialised.
"""
juju_log('Setting db-initialised to True', 'DEBUG')
leader_set({'db-initialised': True})

View File

@ -37,9 +37,13 @@ class VaultKVContext(context.OSContextGenerator):
)
def __call__(self):
import hvac
ctxt = {}
# NOTE(hopem): see https://bugs.launchpad.net/charm-helpers/+bug/1849323
db = unitdata.kv()
last_token = db.get('last-token')
# currently known-good secret-id
secret_id = db.get('secret-id')
for relation_id in hookenv.relation_ids(self.interfaces[0]):
for unit in hookenv.related_units(relation_id):
data = hookenv.relation_get(unit=unit,
@ -54,27 +58,48 @@ class VaultKVContext(context.OSContextGenerator):
# Tokens may change when secret_id's are being
# reissued - if so use token to get new secret_id
if token != last_token:
token_success = False
try:
secret_id = retrieve_secret_id(
url=vault_url,
token=token
)
token_success = True
except hvac.exceptions.InvalidRequest:
# Try next
pass
if token_success:
db.set('secret-id', secret_id)
db.set('last-token', token)
db.flush()
ctxt = {
'vault_url': vault_url,
'role_id': json.loads(role_id),
'secret_id': secret_id,
'secret_backend': self.secret_backend,
}
vault_ca = data.get('vault_ca')
if vault_ca:
ctxt['vault_ca'] = json.loads(vault_ca)
self.complete = True
return ctxt
return {}
ctxt['vault_url'] = vault_url
ctxt['role_id'] = json.loads(role_id)
ctxt['secret_id'] = secret_id
ctxt['secret_backend'] = self.secret_backend
vault_ca = data.get('vault_ca')
if vault_ca:
ctxt['vault_ca'] = json.loads(vault_ca)
self.complete = True
break
else:
if secret_id:
ctxt['vault_url'] = vault_url
ctxt['role_id'] = json.loads(role_id)
ctxt['secret_id'] = secret_id
ctxt['secret_backend'] = self.secret_backend
vault_ca = data.get('vault_ca')
if vault_ca:
ctxt['vault_ca'] = json.loads(vault_ca)
if self.complete:
break
if ctxt:
self.complete = True
return ctxt
def write_vaultlocker_conf(context, priority=100):

View File

@ -34,6 +34,8 @@ import errno
import tempfile
from subprocess import CalledProcessError
from charmhelpers import deprecate
import six
if not six.PY3:
from UserDict import UserDict
@ -119,19 +121,19 @@ def log(message, level=None):
raise
def action_log(message):
"""Write an action progress message"""
command = ['action-log']
def function_log(message):
"""Write a function progress message"""
command = ['function-log']
if not isinstance(message, six.string_types):
message = repr(message)
command += [message[:SH_MAX_ARG]]
# Missing action-log should not cause failures in unit tests
# Send action_log output to stderr
# Missing function-log should not cause failures in unit tests
# Send function_log output to stderr
try:
subprocess.call(command)
except OSError as e:
if e.errno == errno.ENOENT:
message = "action-log: {}".format(message)
message = "function-log: {}".format(message)
print(message, file=sys.stderr)
else:
raise
@ -964,9 +966,23 @@ def charm_dir():
return os.environ.get('CHARM_DIR')
def cmd_exists(cmd):
"""Return True if the specified cmd exists in the path"""
return any(
os.access(os.path.join(path, cmd), os.X_OK)
for path in os.environ["PATH"].split(os.pathsep)
)
@cached
@deprecate("moved to function_get()", log=log)
def action_get(key=None):
"""Gets the value of an action parameter, or all key/value param pairs"""
"""
.. deprecated:: 0.20.7
Alias for :func:`function_get`.
Gets the value of an action parameter, or all key/value param pairs.
"""
cmd = ['action-get']
if key is not None:
cmd.append(key)
@ -975,36 +991,103 @@ def action_get(key=None):
return action_data
@cached
def function_get(key=None):
"""Gets the value of an action parameter, or all key/value param pairs"""
cmd = ['function-get']
# Fallback for older charms.
if not cmd_exists('function-get'):
cmd = ['action-get']
if key is not None:
cmd.append(key)
cmd.append('--format=json')
function_data = json.loads(subprocess.check_output(cmd).decode('UTF-8'))
return function_data
@deprecate("moved to function_set()", log=log)
def action_set(values):
"""Sets the values to be returned after the action finishes"""
"""
.. deprecated:: 0.20.7
Alias for :func:`function_set`.
Sets the values to be returned after the action finishes.
"""
cmd = ['action-set']
for k, v in list(values.items()):
cmd.append('{}={}'.format(k, v))
subprocess.check_call(cmd)
def action_fail(message):
"""Sets the action status to failed and sets the error message.
def function_set(values):
"""Sets the values to be returned after the function finishes"""
cmd = ['function-set']
# Fallback for older charms.
if not cmd_exists('function-get'):
cmd = ['action-set']
The results set by action_set are preserved."""
for k, v in list(values.items()):
cmd.append('{}={}'.format(k, v))
subprocess.check_call(cmd)
@deprecate("moved to function_fail()", log=log)
def action_fail(message):
"""
.. deprecated:: 0.20.7
Alias for :func:`function_fail`.
Sets the action status to failed and sets the error message.
The results set by action_set are preserved.
"""
subprocess.check_call(['action-fail', message])
def function_fail(message):
"""Sets the function status to failed and sets the error message.
The results set by function_set are preserved."""
cmd = ['function-fail']
# Fallback for older charms.
if not cmd_exists('function-fail'):
cmd = ['action-fail']
cmd.append(message)
subprocess.check_call(cmd)
def action_name():
"""Get the name of the currently executing action."""
return os.environ.get('JUJU_ACTION_NAME')
def function_name():
"""Get the name of the currently executing function."""
return os.environ.get('JUJU_FUNCTION_NAME') or action_name()
def action_uuid():
"""Get the UUID of the currently executing action."""
return os.environ.get('JUJU_ACTION_UUID')
def function_id():
"""Get the ID of the currently executing function."""
return os.environ.get('JUJU_FUNCTION_ID') or action_uuid()
def action_tag():
"""Get the tag for the currently executing action."""
return os.environ.get('JUJU_ACTION_TAG')
def function_tag():
"""Get the tag for the currently executing function."""
return os.environ.get('JUJU_FUNCTION_TAG') or action_tag()
def status_set(workload_state, message):
"""Set the workload state with a message

View File

@ -182,6 +182,14 @@ CLOUD_ARCHIVE_POCKETS = {
'train/proposed': 'bionic-proposed/train',
'bionic-train/proposed': 'bionic-proposed/train',
'bionic-proposed/train': 'bionic-proposed/train',
# Ussuri
'ussuri': 'bionic-updates/ussuri',
'bionic-ussuri': 'bionic-updates/ussuri',
'bionic-ussuri/updates': 'bionic-updates/ussuri',
'bionic-updates/ussuri': 'bionic-updates/ussuri',
'ussuri/proposed': 'bionic-proposed/ussuri',
'bionic-ussuri/proposed': 'bionic-proposed/ussuri',
'bionic-proposed/ussuri': 'bionic-proposed/ussuri',
}