# -*- coding: us-ascii -*-
"""
install_osc +------------------------> render_config+--------------> do_restart
^ + ^
conf_kst_user+---> save_creds +-----------+ v |
create_endpoints +-----+
Available states:
identity-credentials.available: kst creds available
identity-credentials.connected: kst relation joined
nrpe-external-master.available: nrpe relation joined
openstack-service-checks.configured: render_config allowed
openstack-service-checks.endpoints.configured: create_endpoints allowed
openstack-service-checks.installed: install_osc entrypoint
openstack-service-checks.started: if not set, restart nagios-nrpe-server
openstack-service-checks.stored-creds: kst creds available for the unit
"""
import base64
import subprocess
from charmhelpers.core import hookenv, host, unitdata
from charms.reactive import any_flags_set, clear_flag, is_flag_set, set_flag, when, when_not
from lib_openstack_service_checks import (
OSCHelper,
OSCCredentialsError,
OSCEndpointError
)
CERT_FILE = '/usr/local/share/ca-certificates/openstack-service-checks.crt'
helper = OSCHelper()
@when('config.changed')
def config_changed():
clear_flag('openstack-service-checks.configured')
@when_not('openstack-service-checks.installed')
@when('nrpe-external-master.available')
def install_openstack_service_checks():
"""Entry point to start configuring the unit
Triggered if related to the nrpe-external-master relation.
Some relation data can be initialized if the application is related to
keystone.
"""
set_flag('openstack-service-checks.installed')
clear_flag('openstack-service-checks.configured')
@when_not('identity-credentials.available')
@when('identity-credentials.connected')
def configure_ident_username(keystone):
"""Requests a user to the Identity Service
"""
username = 'nagios'
keystone.request_credentials(username)
clear_flag('openstack-service-checks.stored-creds')
@when_not('openstack-service-checks.stored-creds')
@when('identity-credentials.available')
def save_creds(keystone):
"""Collect and save credentials from Keystone relation.
Get credentials from the Keystone relation,
reformat them into something the Keystone client can use, and
save them into the unitdata.
"""
creds = {'username': keystone.credentials_username(),
'password': keystone.credentials_password(),
'region': keystone.region(),
}
if keystone.api_version() == '3':
api_url = 'v3'
try:
domain = keystone.domain()
except AttributeError:
domain = 'service_domain'
# keystone relation sends info back with funny names, fix here
creds.update({'project_name': keystone.credentials_project(),
'auth_version': '3',
'user_domain_name': domain,
'project_domain_name': domain})
else:
api_url = 'v2.0'
creds['tenant_name'] = keystone.credentials_project()
creds['auth_url'] = '{proto}://{host}:{port}/{api_url}'.format(
proto=keystone.auth_protocol(), host=keystone.auth_host(),
port=keystone.auth_port(), api_url=api_url)
helper.store_keystone_credentials(creds)
set_flag('openstack-service-checks.stored-creds')
clear_flag('openstack-service-checks.configured')
@when_not('identity-credentials.connected')
@when_not('identity-credentials.available')
@when('openstack-service-checks.stored-creds')
def allow_keystone_store_overwrite():
"""Allow unitdata overwrite if keystone relation is recycled.
"""
clear_flag('openstack-service-checks.stored-creds')
@when('identity-credentials.available.updated')
def update_keystone_store():
"""when identity-service-relation-changed is triggered, update the stored credentials
"""
allow_keystone_store_overwrite()
def get_credentials():
"""Get credential info from either config or relation data
If config 'os-credentials' is set, return it. Otherwise look for a
keystonecreds relation data.
"""
try:
creds = helper.get_os_credentials()
except OSCCredentialsError as error:
creds = helper.get_keystone_credentials()
if not creds:
hookenv.log('render_config: No credentials yet, skipping')
hookenv.status_set('blocked',
'Missing os-credentials vars: {}'.format(error))
return
return creds
@when('openstack-service-checks.installed')
@when_not('openstack-service-checks.configured')
def render_config():
"""Render nrpe checks from the templates
This code is only triggered after the nrpe relation is set. If a relation
with keystone is later set, it will be re-triggered. On the other hand,
if a keystone relation exists but not a nrpe relation, it won't be run.
Furthermore, juju config os-credentials take precedence over keystone
related data.
"""
def block_tls_failure(error):
hookenv.log('update-ca-certificates failed: {}'.format(error),
hookenv.ERROR)
hookenv.status_set('blocked',
'update-ca-certificates error. check logs')
return
creds = get_credentials()
if not creds:
return
# Fix TLS
if helper.charm_config['trusted_ssl_ca'].strip():
trusted_ssl_ca = helper.charm_config['trusted_ssl_ca'].strip()
hookenv.log('Writing ssl ca cert:{}'.format(trusted_ssl_ca))
cert_content = base64.b64decode(trusted_ssl_ca).decode()
try:
with open(CERT_FILE, 'w') as fd:
fd.write(cert_content)
subprocess.call(['/usr/sbin/update-ca-certificates'])
except subprocess.CalledProcessError as error:
block_tls_failure(error)
return
except PermissionError as error:
block_tls_failure(error)
return
hookenv.log('render_config: Got credentials for'
' username={}'.format(creds.get('username')))
try:
helper.render_checks(creds)
set_flag('openstack-service-checks.endpoints.configured')
except OSCEndpointError as error:
hookenv.log(error)
if not helper.deploy_rally():
# Rally could not be installed (if enabled). No further actions taken
return
set_flag('openstack-service-checks.configured')
clear_flag('openstack-service-checks.started')
@when('openstack-service-checks.installed')
@when('openstack-service-checks.configured')
@when_not('openstack-service-checks.endpoints.configured')
def configure_nrpe_endpoints():
"""Create an NRPE check for each Keystone catalog endpoint.
Read the Keystone catalog, and create a check for each endpoint listed.
If there is a healthcheck endpoint for the API, use that URL. Otherwise,
check the url '/'.
If TLS is enabled, add a check for the cert.
"""
creds = get_credentials()
if not creds:
return
try:
helper.create_endpoint_checks(creds)
set_flag('openstack-service-checks.endpoints.configured')
clear_flag('openstack-service-checks.started')
except OSCEndpointError as error:
hookenv.log(error)
@when('identity-notifications.available.updated')
def endpoints_changed():
clear_flag('openstack-service-checks.endpoints.configured')
@when('openstack-service-checks.configured')
@when_not('openstack-service-checks.started')
def do_restart():
hookenv.log('Reloading nagios-nrpe-server')
host.service_restart('nagios-nrpe-server')
hookenv.status_set('active', 'Unit is ready')
set_flag('openstack-service-checks.started')
@when('nrpe-external-master.available')
def do_reconfigure_nrpe():
os_credentials_flag = 'config.changed.os-credentials'
flags = ['config.changed.check_{}_urls'.format(interface) for interface in ['admin', 'internal', 'public']]
flags.extend(os_credentials_flag)
if is_flag_set('config.changed'):
clear_flag('openstack-service-checks.configured')
if any_flags_set(*flags):
if is_flag_set(os_credentials_flag):
clear_flag('openstack-service-checks.configured')
clear_flag('openstack-service-checks.endpoints.configured')
if helper.is_rally_enabled:
helper.reconfigure_tempest()
if is_flag_set('config.changed.skip-rally'):
helper.update_rally_checkfiles()
@when_not('nrpe-external-master.available')
def missing_nrpe():
"""Avoid a user action to be missed or overwritten by another hook
"""
if hookenv.hook_name() != 'update-status':
hookenv.status_set('blocked', 'Missing relations: nrpe')
@when('openstack-service-checks.installed')
@when('nrpe-external-master.available')
def parse_hooks():
if hookenv.hook_name() == 'upgrade-charm':
# Check if creds storage needs to be migrated
# Old key: keystone-relation-creds
# New key: keystonecreds
kv = unitdata.kv()
creds = kv.get('keystonecreds')
old_creds = kv.get('keystone-relation-creds')
if old_creds and not creds:
# This set of creds needs an update to a newer format
creds = {
'username': old_creds['credentials_username'],
'password': old_creds['credentials_password'],
'project_name': old_creds['credentials_project'],
'tenant_name': old_creds['credentials_project'],
'user_domain_name': old_creds.get('credentials_user_domain'),
'project_domain_name': old_creds.get('credentials_project_domain'),
}
kv.set('keystonecreds', creds)
if old_creds:
kv.unset('keystone-relation-creds')
# update rally check files and plugins, which may have changed
helper.update_plugins()
helper.update_rally_checkfiles()
# render configs again
do_reconfigure_nrpe()