Add support for remote consumers of Ceilometer event data.
There is a use-case to forward events received by ceilometer from OpenStack services to an external system via http/udp. It comes from the fact that aodh does not include all information about an event alarm into POSTed data. Raw event data can be published by ceilometer via http/udp to an external system without giving a user direct access to rabbitmq. This patch adds a context generator, associated templates, and configuration entry for configuring multiple event-sinks in /etc/ceilometer/event_pipeline.yaml. This also modifies the ceilometer.conf templates to reflect the correct naming of the same file to align with upstream Ceilometer. Also adds support for a future Panko charm event consumer using the event-service interface in the above context with associated metadata and hook symlinks. This change will only effect Mitaka and later clouds. Closes Bug: 1763321 Change-Id: I931438c720272bd9a3d2b958ebabcd3584790bd0
This commit is contained in:
10
config.yaml
10
config.yaml
@@ -239,3 +239,13 @@ options:
|
|||||||
Openstack mostly defaults to using public endpoints for
|
Openstack mostly defaults to using public endpoints for
|
||||||
internal communication between services. If set to True this option
|
internal communication between services. If set to True this option
|
||||||
will configure services to use internal endpoints where possible.
|
will configure services to use internal endpoints where possible.
|
||||||
|
remote-sink:
|
||||||
|
type: string
|
||||||
|
default:
|
||||||
|
description: |
|
||||||
|
Space delimited list of remote consumers of Ceilometer event reporting
|
||||||
|
which reside outside of the deployed model. Only supported for Mitaka
|
||||||
|
and later clouds. e.g.
|
||||||
|
.
|
||||||
|
'udp://<host>:<port>/'
|
||||||
|
'prometheus://pushgateway-host:9091/metrics/job/openstack-telemetry'
|
||||||
|
|||||||
@@ -153,7 +153,9 @@ def metric_service_joined():
|
|||||||
"identity-credentials-relation-changed",
|
"identity-credentials-relation-changed",
|
||||||
"identity-credentials-relation-departed",
|
"identity-credentials-relation-departed",
|
||||||
"metric-service-relation-changed",
|
"metric-service-relation-changed",
|
||||||
"metric-service-relation-departed")
|
"metric-service-relation-departed",
|
||||||
|
"event-service-relation-changed",
|
||||||
|
"event-service-relation-departed",)
|
||||||
@restart_on_change(restart_map())
|
@restart_on_change(restart_map())
|
||||||
def any_changed():
|
def any_changed():
|
||||||
CONFIGS.write_all()
|
CONFIGS.write_all()
|
||||||
|
|||||||
1
hooks/event-service-relation-broken
Symbolic link
1
hooks/event-service-relation-broken
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
ceilometer_hooks.py
|
||||||
1
hooks/event-service-relation-changed
Symbolic link
1
hooks/event-service-relation-changed
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
ceilometer_hooks.py
|
||||||
1
hooks/event-service-relation-departed
Symbolic link
1
hooks/event-service-relation-departed
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
ceilometer_hooks.py
|
||||||
1
hooks/event-service-relation-joined
Symbolic link
1
hooks/event-service-relation-joined
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
ceilometer_hooks.py
|
||||||
@@ -140,6 +140,26 @@ class HAProxyContext(OSContextGenerator):
|
|||||||
return ctxt
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteSinksContext(OSContextGenerator):
|
||||||
|
interfaces = ['event-service']
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
'''Generates context for remote sinks for Panko and other compatible
|
||||||
|
remote consumers of Ceilometer event data.
|
||||||
|
'''
|
||||||
|
ctxt = {}
|
||||||
|
if config('remote-sink'):
|
||||||
|
ctxt['remote_sinks'] = config('remote-sink').split(' ')
|
||||||
|
for relid in relation_ids('event-service'):
|
||||||
|
for unit in related_units(relid):
|
||||||
|
publisher = relation_get('publisher', unit=unit, rid=relid)
|
||||||
|
if publisher:
|
||||||
|
if not ctxt.get('internal_sinks'):
|
||||||
|
ctxt['internal_sinks'] = {}
|
||||||
|
ctxt['internal_sinks'][unit.split('/')[0]] = publisher
|
||||||
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
class ApacheSSLContext(SSLContext):
|
class ApacheSSLContext(SSLContext):
|
||||||
|
|
||||||
external_ports = [CEILOMETER_PORT]
|
external_ports = [CEILOMETER_PORT]
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ from ceilometer_contexts import (
|
|||||||
HAProxyContext,
|
HAProxyContext,
|
||||||
MetricServiceContext,
|
MetricServiceContext,
|
||||||
CEILOMETER_PORT,
|
CEILOMETER_PORT,
|
||||||
|
RemoteSinksContext,
|
||||||
)
|
)
|
||||||
from charmhelpers.contrib.openstack.utils import (
|
from charmhelpers.contrib.openstack.utils import (
|
||||||
get_os_codename_package,
|
get_os_codename_package,
|
||||||
@@ -51,6 +52,7 @@ from charmhelpers.core.hookenv import (
|
|||||||
is_leader,
|
is_leader,
|
||||||
log,
|
log,
|
||||||
DEBUG,
|
DEBUG,
|
||||||
|
relation_ids,
|
||||||
)
|
)
|
||||||
from charmhelpers.fetch import apt_update, apt_install, apt_upgrade
|
from charmhelpers.fetch import apt_update, apt_install, apt_upgrade
|
||||||
from charmhelpers.core.host import init_is_systemd
|
from charmhelpers.core.host import init_is_systemd
|
||||||
@@ -67,6 +69,7 @@ HTTPS_APACHE_24_CONF = "/etc/apache2/sites-available/" \
|
|||||||
"openstack_https_frontend.conf"
|
"openstack_https_frontend.conf"
|
||||||
CLUSTER_RES = 'grp_ceilometer_vips'
|
CLUSTER_RES = 'grp_ceilometer_vips'
|
||||||
MEMCACHED_CONF = '/etc/memcached.conf'
|
MEMCACHED_CONF = '/etc/memcached.conf'
|
||||||
|
PIPELINE_CONF = '/etc/ceilometer/event_pipeline.yaml'
|
||||||
|
|
||||||
CEILOMETER_BASE_SERVICES = [
|
CEILOMETER_BASE_SERVICES = [
|
||||||
'ceilometer-agent-central',
|
'ceilometer-agent-central',
|
||||||
@@ -238,6 +241,8 @@ def register_configs():
|
|||||||
CeilometerContext(),
|
CeilometerContext(),
|
||||||
HAProxyContext()]
|
HAProxyContext()]
|
||||||
)
|
)
|
||||||
|
if CompareOpenStackReleases(release) >= 'mitaka':
|
||||||
|
configs.register(PIPELINE_CONF, [RemoteSinksContext()])
|
||||||
return configs
|
return configs
|
||||||
|
|
||||||
|
|
||||||
@@ -420,6 +425,17 @@ def set_shared_secret(secret):
|
|||||||
secret_file.write(secret)
|
secret_file.write(secret)
|
||||||
|
|
||||||
|
|
||||||
|
def get_optional_relations():
|
||||||
|
"""Return a dictionary of optional relations.
|
||||||
|
|
||||||
|
@returns {relation: relation_name}
|
||||||
|
"""
|
||||||
|
optional_interfaces = {}
|
||||||
|
if relation_ids('event-service'):
|
||||||
|
optional_interfaces['event-service'] = ['event-service']
|
||||||
|
return optional_interfaces
|
||||||
|
|
||||||
|
|
||||||
def assess_status(configs):
|
def assess_status(configs):
|
||||||
"""Assess status of current unit
|
"""Assess status of current unit
|
||||||
|
|
||||||
@@ -444,6 +460,7 @@ def resolve_required_interfaces():
|
|||||||
@returns dict - a dictionary keyed by high-level type of interfaces names
|
@returns dict - a dictionary keyed by high-level type of interfaces names
|
||||||
"""
|
"""
|
||||||
required_ints = deepcopy(REQUIRED_INTERFACES)
|
required_ints = deepcopy(REQUIRED_INTERFACES)
|
||||||
|
required_ints.update(get_optional_relations())
|
||||||
if CompareOpenStackReleases(os_release('ceilometer-common')) >= 'mitaka':
|
if CompareOpenStackReleases(os_release('ceilometer-common')) >= 'mitaka':
|
||||||
required_ints['database'].append('metric-service')
|
required_ints['database'].append('metric-service')
|
||||||
if CompareOpenStackReleases(os_release('ceilometer-common')) >= 'queens':
|
if CompareOpenStackReleases(os_release('ceilometer-common')) >= 'queens':
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ requires:
|
|||||||
scope: container
|
scope: container
|
||||||
metric-service:
|
metric-service:
|
||||||
interface: gnocchi
|
interface: gnocchi
|
||||||
|
event-service:
|
||||||
|
interface: event-service
|
||||||
peers:
|
peers:
|
||||||
cluster:
|
cluster:
|
||||||
interface: ceilometer-ha
|
interface: ceilometer-ha
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ api_workers = {{ workers }}
|
|||||||
collector_workers = {{ workers }}
|
collector_workers = {{ workers }}
|
||||||
notification_workers = {{ workers }}
|
notification_workers = {{ workers }}
|
||||||
|
|
||||||
event_pipeline_cfg_file = /etc/ceilometer/event_pipeline_alarm.yaml
|
event_pipeline_cfg_file = /etc/ceilometer/event_pipeline.yaml
|
||||||
|
|
||||||
{% if gnocchi_url -%}
|
{% if gnocchi_url -%}
|
||||||
meter_dispatchers = gnocchi
|
meter_dispatchers = gnocchi
|
||||||
|
|||||||
37
templates/mitaka/event_pipeline.yaml
Normal file
37
templates/mitaka/event_pipeline.yaml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
sources:
|
||||||
|
- name: event_source
|
||||||
|
events:
|
||||||
|
- "*"
|
||||||
|
sinks:
|
||||||
|
- event_sink
|
||||||
|
{%- if remote_sinks %}
|
||||||
|
- remote_sink
|
||||||
|
{% endif %}
|
||||||
|
{%- if internal_sinks %}
|
||||||
|
{%- for item in internal_sinks.keys() %}
|
||||||
|
- {{ item }}
|
||||||
|
{% endfor -%}
|
||||||
|
{% endif %}
|
||||||
|
sinks:
|
||||||
|
{%- if remote_sinks %}
|
||||||
|
- name: remote_sink
|
||||||
|
transformers:
|
||||||
|
publishers:
|
||||||
|
{% for item in remote_sinks -%}
|
||||||
|
- {{ item }}
|
||||||
|
{% endfor %}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- if internal_sinks %}
|
||||||
|
{%- for item, target in internal_sinks.items() -%}
|
||||||
|
- name: {{ item }}
|
||||||
|
transformers:
|
||||||
|
publishers:
|
||||||
|
- {{ target }}
|
||||||
|
{%- endfor %}
|
||||||
|
{% endif %}
|
||||||
|
- name: event_sink
|
||||||
|
transformers:
|
||||||
|
publishers:
|
||||||
|
- notifier://
|
||||||
|
- notifier://?topic=alarm.all
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
debug = {{ debug }}
|
debug = {{ debug }}
|
||||||
verbose = {{ verbose }}
|
verbose = {{ verbose }}
|
||||||
use_syslog = {{ use_syslog }}
|
use_syslog = {{ use_syslog }}
|
||||||
event_pipeline_cfg_file = /etc/ceilometer/event_pipeline_alarm.yaml
|
event_pipeline_cfg_file = /etc/ceilometer/event_pipeline.yaml
|
||||||
|
|
||||||
{% if gnocchi_url -%}
|
{% if gnocchi_url -%}
|
||||||
meter_dispatchers = gnocchi
|
meter_dispatchers = gnocchi
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ class CeilometerBasicDeployment(OpenStackAmuletDeployment):
|
|||||||
}
|
}
|
||||||
if self._get_openstack_release() >= self.xenial_pike:
|
if self._get_openstack_release() >= self.xenial_pike:
|
||||||
configs['ceph-osd'] = {'osd-devices': '/dev/vdb',
|
configs['ceph-osd'] = {'osd-devices': '/dev/vdb',
|
||||||
'osd-reformat': 'yes',
|
'osd-reformat': True,
|
||||||
'ephemeral-unmount': '/mnt'}
|
'ephemeral-unmount': '/mnt'}
|
||||||
super(CeilometerBasicDeployment, self)._configure_services(configs)
|
super(CeilometerBasicDeployment, self)._configure_services(configs)
|
||||||
|
|
||||||
|
|||||||
@@ -193,3 +193,33 @@ class CeilometerContextsTest(CharmTestCase):
|
|||||||
'port': api_port
|
'port': api_port
|
||||||
}
|
}
|
||||||
self.assertEqual(contexts.HAProxyContext()(), expected)
|
self.assertEqual(contexts.HAProxyContext()(), expected)
|
||||||
|
|
||||||
|
def test_remote_sink_context_no_config(self):
|
||||||
|
self.relation_ids.return_value = []
|
||||||
|
self.os_release.return_value = 'mitaka'
|
||||||
|
self.assertEqual(contexts.RemoteSinksContext()(), {})
|
||||||
|
|
||||||
|
def test_remote_sink_context_event_service_relation(self):
|
||||||
|
self.relation_ids.return_value = ['event-service:0']
|
||||||
|
self.related_units.return_value = ['panko/0']
|
||||||
|
self.os_release.return_value = 'mitaka'
|
||||||
|
data = {
|
||||||
|
'publisher': 'panko://'
|
||||||
|
}
|
||||||
|
self.test_relation.set(data)
|
||||||
|
self.assertEqual(contexts.RemoteSinksContext()(),
|
||||||
|
{'internal_sinks': {'panko': 'panko://'}})
|
||||||
|
|
||||||
|
def test_remote_sink_context_with_single_config(self):
|
||||||
|
self.relation_ids.return_value = []
|
||||||
|
self.os_release.return_value = 'mitaka'
|
||||||
|
self.test_config.set('remote-sink', 'http://foo')
|
||||||
|
self.assertEqual(contexts.RemoteSinksContext()(),
|
||||||
|
{'remote_sinks': ['http://foo']})
|
||||||
|
|
||||||
|
def test_remote_sink_context_with_multiple_config(self):
|
||||||
|
self.relation_ids.return_value = []
|
||||||
|
self.os_release.return_value = 'mitaka'
|
||||||
|
self.test_config.set('remote-sink', 'http://foo http://bar')
|
||||||
|
self.assertEqual(contexts.RemoteSinksContext()(),
|
||||||
|
{'remote_sinks': ['http://foo', 'http://bar']})
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ TO_PATCH = [
|
|||||||
'os_release',
|
'os_release',
|
||||||
'is_leader',
|
'is_leader',
|
||||||
'reset_os_release',
|
'reset_os_release',
|
||||||
|
'relation_ids',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -275,6 +276,7 @@ class CeilometerUtilsTest(CharmTestCase):
|
|||||||
def test_resolve_required_interfaces(self):
|
def test_resolve_required_interfaces(self):
|
||||||
self.os_release.side_effect = None
|
self.os_release.side_effect = None
|
||||||
self.os_release.return_value = 'icehouse'
|
self.os_release.return_value = 'icehouse'
|
||||||
|
self.relation_ids.return_value = None
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
utils.resolve_required_interfaces(),
|
utils.resolve_required_interfaces(),
|
||||||
{
|
{
|
||||||
@@ -287,6 +289,7 @@ class CeilometerUtilsTest(CharmTestCase):
|
|||||||
def test_resolve_required_interfaces_mitaka(self):
|
def test_resolve_required_interfaces_mitaka(self):
|
||||||
self.os_release.side_effect = None
|
self.os_release.side_effect = None
|
||||||
self.os_release.return_value = 'mitaka'
|
self.os_release.return_value = 'mitaka'
|
||||||
|
self.relation_ids.return_value = None
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
utils.resolve_required_interfaces(),
|
utils.resolve_required_interfaces(),
|
||||||
{
|
{
|
||||||
@@ -299,6 +302,7 @@ class CeilometerUtilsTest(CharmTestCase):
|
|||||||
def test_resolve_required_interfaces_queens(self):
|
def test_resolve_required_interfaces_queens(self):
|
||||||
self.os_release.side_effect = None
|
self.os_release.side_effect = None
|
||||||
self.os_release.return_value = 'queens'
|
self.os_release.return_value = 'queens'
|
||||||
|
self.relation_ids.return_value = None
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
utils.resolve_required_interfaces(),
|
utils.resolve_required_interfaces(),
|
||||||
{
|
{
|
||||||
@@ -308,6 +312,20 @@ class CeilometerUtilsTest(CharmTestCase):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_resolve_optional_interfaces(self):
|
||||||
|
self.os_release.side_effect = None
|
||||||
|
self.os_release.return_value = 'icehouse'
|
||||||
|
self.relation_ids.return_value = [0]
|
||||||
|
self.assertEqual(
|
||||||
|
utils.resolve_required_interfaces(),
|
||||||
|
{
|
||||||
|
'database': ['mongodb'],
|
||||||
|
'messaging': ['amqp'],
|
||||||
|
'identity': ['identity-service'],
|
||||||
|
'event-service': ['event-service'],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
@patch.object(utils, 'subprocess')
|
@patch.object(utils, 'subprocess')
|
||||||
def test_ceilometer_upgrade(self, mock_subprocess):
|
def test_ceilometer_upgrade(self, mock_subprocess):
|
||||||
self.is_leader.return_value = True
|
self.is_leader.return_value = True
|
||||||
|
|||||||
Reference in New Issue
Block a user