Skip to content

Commit

Permalink
Add support for gnocchi
Browse files Browse the repository at this point in the history
Add new metric-service interface to support use of
Gnocchi as a storage backend for resource and metric
data.

Configure ceilometer to use the gnocchi dispatcher
in the event that ceilometer is related to gnocchi.
This has the side effect of disabling the ceilometer
API - Aodh and Gnocchi API's should be used directly
in this deployment topology.

Note that Gnocchi is only supported in OpenStack
Mitaka or later; 'metrics-service' is added to the
required interfaces configuration as an alternative
to 'mongodb' for >= Mitaka.

Change-Id: Ia31dfefd5efa3fb5ec2ba5d132ee865c567bd8df
  • Loading branch information
javacruft committed Aug 10, 2017
1 parent 5dd3d9d commit 72522a3
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 110 deletions.
8 changes: 7 additions & 1 deletion config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ options:
api-workers:
type: int
default: 1
description: |
description: |
Number of workers for Ceilometer API server. (>= Kilo).
# Monitoring config
nagios_context:
Expand Down Expand Up @@ -222,3 +222,9 @@ options:
description: |
Connect timeout configuration in ms for haproxy, used in HA
configurations. If not provided, default value of 5000ms is used.
gnocchi-archive-policy:
type: string
default: low
description: |
Archive retention policy to use when Ceilometer is deployed with
Gnocchi for resource, metric and measures storage.
43 changes: 24 additions & 19 deletions hooks/ceilometer_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
set_shared_secret,
assess_status,
reload_systemd,
ceilometer_upgrade,
)
from ceilometer_contexts import CEILOMETER_PORT
from charmhelpers.contrib.openstack.ip import (
Expand Down Expand Up @@ -134,32 +135,36 @@ def db_joined():
relation_set(ceilometer_database=CEILOMETER_DB)


@hooks.hook("metric-service-relation-joined")
def metric_service_joined():
# NOTE(jamespage): gnocchiclient is required to support
# the gnocchi event dispatcher
apt_install(filter_installed_packages(['python-gnocchiclient']),
fatal=True)


@hooks.hook("amqp-relation-changed",
"amqp-relation-departed",
"shared-db-relation-changed",
"shared-db-relation-departed")
"shared-db-relation-departed",
"identity-service-relation-changed",
"identity-service-relation-departed",
"metric-service-relation-changed",
"metric-service-relation-departed")
@restart_on_change(restart_map())
def any_changed():
CONFIGS.write_all()
configure_https()
for rid in relation_ids('identity-service'):
keystone_joined(relid=rid)
ceilometer_joined()


@hooks.hook("identity-service-relation-changed")
@restart_on_change(restart_map())
def identity_service_relation_changed():
CONFIGS.write_all()
configure_https()
keystone_joined()
ceilometer_joined()


@hooks.hook("amqp-relation-departed")
@restart_on_change(restart_map())
def amqp_departed():
if 'amqp' not in CONFIGS.complete_contexts():
log('amqp relation incomplete. Peer not ready?')
return
CONFIGS.write_all()
# NOTE(jamespage): ceilometer@ocata requires both gnocchi
# and mongodb to be configured to successfully
# upgrade the underlying data stores.
if ('metric-service' in CONFIGS.complete_contexts() and
'identity-service' in CONFIGS.complete_contexts() and
'mongodb' in CONFIGS.complete_contexts()):
ceilometer_upgrade()


def configure_https():
Expand Down
1 change: 1 addition & 0 deletions hooks/metric-service-relation-broken
1 change: 1 addition & 0 deletions hooks/metric-service-relation-changed
1 change: 1 addition & 0 deletions hooks/metric-service-relation-departed
1 change: 1 addition & 0 deletions hooks/metric-service-relation-joined
14 changes: 14 additions & 0 deletions lib/ceilometer_contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,17 @@ class ApacheSSLContext(SSLContext):

external_ports = [CEILOMETER_PORT]
service_namespace = "ceilometer"


class MetricServiceContext(OSContextGenerator):
interfaces = ['metric-service']

def __call__(self):

for relid in relation_ids('metric-service'):
for unit in related_units(relid):
gnocchi_url = relation_get('gnocchi_url', unit=unit, rid=relid)
if gnocchi_url:
return {'gnocchi_url': gnocchi_url,
'archive_policy': config('gnocchi-archive-policy')}
return {}
34 changes: 31 additions & 3 deletions lib/ceilometer_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
MongoDBContext,
CeilometerContext,
HAProxyContext,
MetricServiceContext,
CEILOMETER_PORT,
)
from charmhelpers.contrib.openstack.utils import (
Expand All @@ -43,9 +44,10 @@
enable_memcache,
CompareOpenStackReleases,
)
from charmhelpers.core.hookenv import config, log
from charmhelpers.core.hookenv import config, log, is_leader
from charmhelpers.fetch import apt_update, apt_install, apt_upgrade
from charmhelpers.core.host import init_is_systemd
from charmhelpers.core.decorators import retry_on_exception
from copy import deepcopy

HAPROXY_CONF = '/etc/haproxy/haproxy.cfg'
Expand Down Expand Up @@ -119,7 +121,8 @@
CeilometerContext(),
context.SyslogContext(),
HAProxyContext(),
context.MemcacheContext()],
context.MemcacheContext(),
MetricServiceContext()],
'services': CEILOMETER_BASE_SERVICES
}),
(CEILOMETER_API_SYSTEMD_CONF, {
Expand Down Expand Up @@ -363,6 +366,18 @@ def assess_status(configs):
os_application_version_set(VERSION_PACKAGE)


def resolve_required_interfaces():
"""Helper function to build a map of required interfaces based on the
OpenStack release being deployed.
@returns dict - a dictionary keyed by high-level type of interfaces names
"""
required_ints = deepcopy(REQUIRED_INTERFACES)
if CompareOpenStackReleases(os_release('ceilometer-common')) >= 'mitaka':
required_ints['database'].append('metric-service')
return required_ints


def assess_status_func(configs):
"""Helper function to create the function that will assess_status() for
the unit.
Expand All @@ -375,7 +390,7 @@ def assess_status_func(configs):
@return f() -> None : a function that assesses the unit's workload status
"""
return make_assess_status_func(
configs, REQUIRED_INTERFACES,
configs, resolve_required_interfaces(),
services=services(), ports=determine_ports())


Expand Down Expand Up @@ -437,3 +452,16 @@ def disable_package_apache_site():
"""
if os.path.exists(PACKAGE_CEILOMETER_API_CONF):
subprocess.check_call(['a2dissite', 'ceilometer-api'])


@retry_on_exception(5, exc_type=subprocess.CalledProcessError)
def ceilometer_upgrade():
"""Execute ceilometer-upgrade command, with retry on failure if gnocchi
API is not ready for requests"""
if is_leader():
if (CompareOpenStackReleases(os_release('ceilometer-common')) >=
'newton'):
cmd = ['ceilometer-upgrade']
else:
cmd = ['ceilometer-dbsync']
subprocess.check_call(cmd)
2 changes: 2 additions & 0 deletions metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ requires:
ha:
interface: hacluster
scope: container
metric-service:
interface: gnocchi
peers:
cluster:
interface: ceilometer-ha
14 changes: 14 additions & 0 deletions templates/mitaka/ceilometer.conf
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ verbose = {{ verbose }}
use_syslog = {{ use_syslog }}
event_pipeline_cfg_file = /etc/ceilometer/event_pipeline_alarm.yaml

{% if gnocchi_url -%}
meter_dispatchers = gnocchi
event_dispatchers = gnocchi
{%- endif %}

[api]
port = {{ port }}
workers = {{ api_workers }}
Expand All @@ -30,6 +35,7 @@ user_domain_name = default
auth_type = password
{% endif -%}

{% if db_host or db_mongo_servers -%}
[database]
{% if db_replset: -%}
connection = mongodb://{{ db_mongo_servers }}/{{ db_name }}?readPreference=primaryPreferred&replicaSet={{ db_replset }}
Expand All @@ -39,10 +45,18 @@ connection = mongodb://{{ db_host }}:{{ db_port }}/{{ db_name }}
{% endif %}
metering_time_to_live = {{ metering_time_to_live }}
event_time_to_live = {{ event_time_to_live }}
{%- endif %}

[publisher]
telemetry_secret = {{ metering_secret }}

{% if gnocchi_url -%}
[dispatcher_gnocchi]
filter_service_activity = False
archive_policy = {{ archive_policy }}
url = {{ gnocchi_url }}
{%- endif %}

{% include "section-keystone-authtoken-mitaka" %}

{% include "section-rabbitmq-oslo" %}
81 changes: 0 additions & 81 deletions tests/basic_deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,44 +438,6 @@ def test_208_ceilometer_agent_ceilometer_relation(self):

u.log.debug('OK')

def test_209_nova_compute_ceilometer_agent_relation(self):
"""Verify the nova-compute to ceilometer relation data"""
u.log.debug('Checking nova-compute:ceilometer relation data...')
unit = self.nova_sentry
relation = ['nova-ceilometer', 'ceilometer-agent:nova-ceilometer']
expected = {'private-address': u.valid_ip}

ret = u.validate_relation_data(unit, relation, expected)
if ret:
message = u.relation_error('ceilometer-service', ret)
amulet.raise_status(amulet.FAIL, msg=message)

u.log.debug('OK')

def test_210_ceilometer_agent_nova_compute_relation(self):
"""Verify the ceilometer to nova-compute relation data"""
u.log.debug('Checking ceilometer:nova-compute relation data...')
unit = self.ceil_agent_sentry
relation = ['nova-ceilometer', 'nova-compute:nova-ceilometer']
sub = ('{"nova": {"/etc/nova/nova.conf": {"sections": {"DEFAULT": '
'[["instance_usage_audit", "True"], '
'["instance_usage_audit_period", "hour"], '
'["notify_on_state_change", "vm_and_task_state"], '
'["notification_driver", "ceilometer.compute.nova_notifier"], '
'["notification_driver", '
'"nova.openstack.common.notifier.rpc_notifier"]]}}}}')
expected = {
'subordinate_configuration': sub,
'private-address': u.valid_ip
}

ret = u.validate_relation_data(unit, relation, expected)
if ret:
message = u.relation_error('ceilometer-service', ret)
amulet.raise_status(amulet.FAIL, msg=message)

u.log.debug('OK')

def test_300_ceilometer_config(self):
"""Verify the data in the ceilometer config file."""
u.log.debug('Checking ceilometer config file data...')
Expand Down Expand Up @@ -549,49 +511,6 @@ def test_300_ceilometer_config(self):

u.log.debug('OK')

def test_301_nova_config(self):
"""Verify data in the nova compute nova config file"""
u.log.debug('Checking nova compute config file...')
unit = self.nova_sentry
conf = '/etc/nova/nova.conf'
expected = {
'DEFAULT': {
'verbose': 'False',
'debug': 'False',
'use_syslog': 'False',
'my_ip': u.valid_ip,
}
}

# NOTE(beisner): notification_driver is not checked like the
# others, as configparser does not support duplicate config
# options, and dicts cant have duplicate keys.
# Ex. from conf file:
# notification_driver = ceilometer.compute.nova_notifier
# notification_driver = nova.openstack.common.notifier.rpc_notifier
for section, pairs in expected.iteritems():
ret = u.validate_config_data(unit, conf, section, pairs)
if ret:
message = "ceilometer config error: {}".format(ret)
amulet.raise_status(amulet.FAIL, msg=message)

# Check notification_driver existence via simple grep cmd
lines = [('notification_driver = '
'ceilometer.compute.nova_notifier'),
('notification_driver = '
'nova.openstack.common.notifier.rpc_notifier')]

sentry_units = [unit]
cmds = []
for line in lines:
cmds.append('grep "{}" {}'.format(line, conf))

ret = u.check_commands_on_units(cmds, sentry_units)
if ret:
amulet.raise_status(amulet.FAIL, msg=ret)

u.log.debug('OK')

def test_400_api_connection(self):
"""Simple api calls to check service is up and responding"""
u.log.debug('Checking api functionality...')
Expand Down

0 comments on commit 72522a3

Please sign in to comment.