aboutsummaryrefslogtreecommitdiffstats
path: root/charms/trusty/ceilometer/lib/ceilometer_utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'charms/trusty/ceilometer/lib/ceilometer_utils.py')
-rw-r--r--charms/trusty/ceilometer/lib/ceilometer_utils.py391
1 files changed, 391 insertions, 0 deletions
diff --git a/charms/trusty/ceilometer/lib/ceilometer_utils.py b/charms/trusty/ceilometer/lib/ceilometer_utils.py
new file mode 100644
index 0000000..eca9c15
--- /dev/null
+++ b/charms/trusty/ceilometer/lib/ceilometer_utils.py
@@ -0,0 +1,391 @@
+import os
+import shutil
+import uuid
+
+from collections import OrderedDict
+
+import yaml
+
+from charmhelpers.contrib.openstack import (
+ templating,
+ context,
+)
+from ceilometer_contexts import (
+ ApacheSSLContext,
+ LoggingConfigContext,
+ MongoDBContext,
+ CeilometerContext,
+ HAProxyContext,
+ CEILOMETER_PORT,
+)
+from charmhelpers.contrib.openstack.utils import (
+ get_os_codename_package,
+ get_os_codename_install_source,
+ configure_installation_source,
+ pause_unit,
+ resume_unit,
+ make_assess_status_func,
+)
+from charmhelpers.core.hookenv import (
+ config,
+ log,
+ related_units,
+ relation_get,
+ relation_ids,
+)
+from charmhelpers.fetch import apt_update, apt_install, apt_upgrade
+from copy import deepcopy
+
+HAPROXY_CONF = '/etc/haproxy/haproxy.cfg'
+CEILOMETER_CONF_DIR = "/etc/ceilometer"
+CEILOMETER_CONF = "%s/ceilometer.conf" % CEILOMETER_CONF_DIR
+CEILOMETER_PIPELINE_CONF = '%s/pipeline.yaml' % CEILOMETER_CONF_DIR
+CEILOMETER_PIPELINE_CONF_ORIG = '%s.orig' % CEILOMETER_PIPELINE_CONF
+HTTPS_APACHE_CONF = "/etc/apache2/sites-available/openstack_https_frontend"
+HTTPS_APACHE_24_CONF = "/etc/apache2/sites-available/" \
+ "openstack_https_frontend.conf"
+CLUSTER_RES = 'grp_ceilometer_vips'
+
+CEILOMETER_BASE_SERVICES = [
+ 'ceilometer-agent-central',
+ 'ceilometer-collector',
+ 'ceilometer-api',
+]
+
+ICEHOUSE_SERVICES = [
+ 'ceilometer-alarm-notifier',
+ 'ceilometer-alarm-evaluator',
+ 'ceilometer-agent-notification'
+]
+
+MITAKA_SERVICES = [
+ 'ceilometer-agent-notification'
+]
+
+CEILOMETER_DB = "ceilometer"
+CEILOMETER_SERVICE = "ceilometer"
+
+CEILOMETER_BASE_PACKAGES = [
+ 'haproxy',
+ 'apache2',
+ 'ceilometer-agent-central',
+ 'ceilometer-collector',
+ 'ceilometer-api',
+ 'python-pymongo',
+]
+
+ICEHOUSE_PACKAGES = [
+ 'ceilometer-alarm-notifier',
+ 'ceilometer-alarm-evaluator',
+ 'ceilometer-agent-notification'
+]
+
+MITAKA_PACKAGES = [
+ 'ceilometer-agent-notification'
+]
+
+REQUIRED_INTERFACES = {
+ 'database': ['mongodb'],
+ 'messaging': ['amqp'],
+ 'identity': ['identity-service'],
+}
+
+CEILOMETER_ROLE = "ResellerAdmin"
+SVC = 'ceilometer'
+
+CONFIG_FILES = OrderedDict([
+ (CEILOMETER_CONF, {
+ 'hook_contexts': [context.IdentityServiceContext(service=SVC,
+ service_user=SVC),
+ context.AMQPContext(ssl_dir=CEILOMETER_CONF_DIR),
+ LoggingConfigContext(),
+ MongoDBContext(),
+ CeilometerContext(),
+ context.SyslogContext(),
+ HAProxyContext()],
+ 'services': CEILOMETER_BASE_SERVICES
+ }),
+ (HAPROXY_CONF, {
+ 'hook_contexts': [context.HAProxyContext(singlenode_mode=True),
+ HAProxyContext()],
+ 'services': ['haproxy'],
+ }),
+ (HTTPS_APACHE_CONF, {
+ 'hook_contexts': [ApacheSSLContext()],
+ 'services': ['apache2'],
+ }),
+ (HTTPS_APACHE_24_CONF, {
+ 'hook_contexts': [ApacheSSLContext()],
+ 'services': ['apache2'],
+ })
+])
+
+TEMPLATES = 'templates'
+
+JUJU_HEADER = '# [ WARNING ] config file maintained by Juju, local changes may be overwritten.'
+
+SHARED_SECRET = "/etc/ceilometer/secret.txt"
+
+
+def ordereddict_constructor(loader, node):
+ return OrderedDict(loader.construct_pairs(node))
+
+
+def ordereddict_representer(dumper, data):
+ return dumper.represent_mapping('tag:yaml.org,2002:map', data.items())
+
+
+yaml.add_constructor('tag:yaml.org,2002:map', ordereddict_constructor)
+yaml.add_representer(OrderedDict, ordereddict_representer)
+
+
+def register_configs():
+ """
+ Register config files with their respective contexts.
+ Regstration of some configs may not be required depending on
+ existing of certain relations.
+ """
+ # if called without anything installed (eg during install hook)
+ # just default to earliest supported release. configs dont get touched
+ # till post-install, anyway.
+ release = get_os_codename_package('ceilometer-common', fatal=False) \
+ or 'grizzly'
+ configs = templating.OSConfigRenderer(templates_dir=TEMPLATES,
+ openstack_release=release)
+
+ for conf in CONFIG_FILES:
+ configs.register(conf, CONFIG_FILES[conf]['hook_contexts'])
+
+ if os.path.exists('/etc/apache2/conf-available'):
+ configs.register(HTTPS_APACHE_24_CONF,
+ CONFIG_FILES[HTTPS_APACHE_24_CONF]['hook_contexts'])
+ else:
+ configs.register(HTTPS_APACHE_CONF,
+ CONFIG_FILES[HTTPS_APACHE_CONF]['hook_contexts'])
+ return configs
+
+
+def restart_map():
+ """
+ Determine the correct resource map to be passed to
+ charmhelpers.core.restart_on_change() based on the services configured.
+
+ :returns: dict: A dictionary mapping config file to lists of services
+ that should be restarted when file changes.
+ """
+ _map = {}
+ for f, ctxt in CONFIG_FILES.iteritems():
+ svcs = []
+ for svc in ctxt['services']:
+ svcs.append(svc)
+ if f == CEILOMETER_CONF:
+ for svc in ceilometer_release_services():
+ svcs.append(svc)
+ if svcs:
+ _map[f] = svcs
+
+ _map[CEILOMETER_PIPELINE_CONF] = CEILOMETER_BASE_SERVICES
+
+ return _map
+
+
+def services():
+ """ Returns a list of services associate with this charm """
+ _services = []
+ for v in restart_map().values():
+ _services = _services + v
+ return list(set(_services))
+
+
+def determine_ports():
+ """Assemble a list of API ports for services the charm is managing
+
+ @returns [ports] - list of ports that the charm manages.
+ """
+ # TODO(ajkavanagh) - determine what other ports the service listens on
+ # apart from the main CEILOMETER port
+ ports = [CEILOMETER_PORT]
+ return ports
+
+
+def get_ceilometer_context():
+ """ Retrieve a map of all current relation data for agent configuration """
+ ctxt = {}
+ for hcontext in CONFIG_FILES[CEILOMETER_CONF]['hook_contexts']:
+ ctxt.update(hcontext())
+ return ctxt
+
+
+def do_openstack_upgrade(configs):
+ """
+ Perform an upgrade. Takes care of upgrading packages, rewriting
+ configs, database migrations and potentially any other post-upgrade
+ actions.
+
+ :param configs: The charms main OSConfigRenderer object.
+ """
+ new_src = config('openstack-origin')
+ new_os_rel = get_os_codename_install_source(new_src)
+
+ log('Performing OpenStack upgrade to %s.' % (new_os_rel))
+
+ configure_installation_source(new_src)
+ dpkg_opts = [
+ '--option', 'Dpkg::Options::=--force-confnew',
+ '--option', 'Dpkg::Options::=--force-confdef',
+ ]
+ apt_update(fatal=True)
+ apt_upgrade(options=dpkg_opts, fatal=True, dist=True)
+ apt_install(packages=get_packages(),
+ options=dpkg_opts,
+ fatal=True)
+
+ # set CONFIGS to load templates from new release
+ configs.set_release(openstack_release=new_os_rel)
+
+
+def ceilometer_release_services():
+ codename = get_os_codename_install_source(config('openstack-origin'))
+ if codename >= 'mitaka':
+ return MITAKA_SERVICES
+ elif codename >= 'icehouse':
+ return ICEHOUSE_SERVICES
+ else:
+ return []
+
+
+def ceilometer_release_packages():
+ codename = get_os_codename_install_source(config('openstack-origin'))
+ if codename >= 'mitaka':
+ return MITAKA_PACKAGES
+ elif codename >= 'icehouse':
+ return ICEHOUSE_PACKAGES
+ else:
+ return []
+
+
+def get_packages():
+ packages = (deepcopy(CEILOMETER_BASE_PACKAGES) +
+ ceilometer_release_packages())
+ return packages
+
+
+def get_shared_secret():
+ """
+ Returns the current shared secret for the ceilometer node. If the shared
+ secret does not exist, this method will generate one.
+ """
+ secret = None
+ if not os.path.exists(SHARED_SECRET):
+ secret = str(uuid.uuid4())
+ set_shared_secret(secret)
+ else:
+ with open(SHARED_SECRET, 'r') as secret_file:
+ secret = secret_file.read().strip()
+ return secret
+
+
+def set_shared_secret(secret):
+ """
+ Sets the shared secret which is used to sign ceilometer messages.
+
+ :param secret: the secret to set
+ """
+ with open(SHARED_SECRET, 'w') as secret_file:
+ secret_file.write(secret)
+
+
+def assess_status(configs):
+ """Assess status of current unit
+
+ Decides what the state of the unit should be based on the current
+ configuration.
+
+ SIDE EFFECT: calls set_os_workload_status(...) which sets the workload
+ status of the unit.
+ Also calls status_set(...) directly if paused state isn't complete.
+
+ @param configs: a templating.OSConfigRenderer() object
+ @returns None - this function is executed for its side-effect
+ """
+ assess_status_func(configs)()
+
+
+def assess_status_func(configs):
+ """Helper function to create the function that will assess_status() for
+ the unit.
+ Uses charmhelpers.contrib.openstack.utils.make_assess_status_func() to
+ create the appropriate status function and then returns it.
+ Used directly by assess_status() and also for pausing and resuming
+ the unit.
+
+ @param configs: a templating.OSConfigRenderer() object
+ @return f() -> None : a function that assesses the unit's workload status
+ """
+ return make_assess_status_func(
+ configs, REQUIRED_INTERFACES,
+ services=services(), ports=determine_ports())
+
+
+def pause_unit_helper(configs):
+ """Helper function to pause a unit, and then call assess_status(...) in
+ effect, so that the status is correctly updated.
+ Uses charmhelpers.contrib.openstack.utils.pause_unit() to do the work.
+
+ @param configs: a templating.OSConfigRenderer() object
+ @returns None - this function is executed for its side-effect
+ """
+ _pause_resume_helper(pause_unit, configs)
+
+
+def resume_unit_helper(configs):
+ """Helper function to resume a unit, and then call assess_status(...) in
+ effect, so that the status is correctly updated.
+ Uses charmhelpers.contrib.openstack.utils.resume_unit() to do the work.
+
+ @param configs: a templating.OSConfigRenderer() object
+ @returns None - this function is executed for its side-effect
+ """
+ _pause_resume_helper(resume_unit, configs)
+
+
+def _pause_resume_helper(f, configs):
+ """Helper function that uses the make_assess_status_func(...) from
+ charmhelpers.contrib.openstack.utils to create an assess_status(...)
+ function that can be used with the pause/resume of the unit
+
+ @param f: the function to be used with the assess_status(...) function
+ @returns None - this function is executed for its side-effect
+ """
+ # TODO(ajkavanagh) - ports= has been left off because of the race hazard
+ # that exists due to service_start()
+ f(assess_status_func(configs),
+ services=services(),
+ ports=determine_ports())
+
+
+def configure_pipeline():
+ if not os.path.exists(CEILOMETER_PIPELINE_CONF_ORIG):
+ shutil.copy(CEILOMETER_PIPELINE_CONF,
+ CEILOMETER_PIPELINE_CONF_ORIG)
+ with open(CEILOMETER_PIPELINE_CONF_ORIG) as f:
+ conf = yaml.load(f)
+
+ sources = conf['sources']
+ sinks = conf['sinks']
+ for rid in relation_ids('ceilometer-plugin'):
+ for unit in related_units(rid):
+ srcs = relation_get('meter-sources', unit, rid)
+ if srcs:
+ srcs = yaml.load(srcs)
+ sources.extend(srcs)
+ sks = relation_get('meter-sinks', unit, rid)
+ if sks:
+ sks = yaml.load(sks)
+ sinks.extend(sks)
+
+ log('Writing config %s' % CEILOMETER_PIPELINE_CONF)
+ with open(CEILOMETER_PIPELINE_CONF, 'w') as f:
+ f.write(JUJU_HEADER)
+ f.write('\n')
+ yaml.dump(conf, f, default_flow_style=False, explicit_start=True)