From d398cdff364ba07a7f18f59417bb4f3af5cccbc3 Mon Sep 17 00:00:00 2001
From: Edward Hope-Morley <edward.hope-morley@canonical.com>
Date: Mon, 28 Jan 2019 17:21:18 +0000
Subject: [PATCH] Make event_sink publisher configurable

The charm currently configures events to be published to
rabbit on both the config.event_topic (event.sample queue)
and alarm topic but as of Queens Ceilometer no longer
consumes event.sample. This patch makes the event_sink
publishers configurable and defaults to publishing to
aodh to retain backwards compatibility.

Change-Id: I5b55f31adcf2b069ff51e387a416f9f1ac4099f8
Partial-Bug: #1676586
---
 config.yaml                            | 10 ++++
 lib/ceilometer_contexts.py             | 25 +++++++++
 templates/queens/event_pipeline.yaml   | 38 +++++++++++++
 unit_tests/test_ceilometer_contexts.py | 75 ++++++++++++++++++++++----
 4 files changed, 137 insertions(+), 11 deletions(-)
 create mode 100644 templates/queens/event_pipeline.yaml

diff --git a/config.yaml b/config.yaml
index 5cf2da3..8a4876c 100644
--- a/config.yaml
+++ b/config.yaml
@@ -239,6 +239,16 @@ options:
       OpenStack mostly defaults to using public endpoints for
       internal communication between services. If set to True this option
       will configure services to use internal endpoints where possible.
+  events-publisher:
+    type: string
+    default: aodh
+    description: |
+      As of the Queens release ceilometer no longer consumes events on the
+      event.sample queue. Valid options here include "aodh" or "gnocchi" and
+      if you want to disable publishing events you can specify an empty string
+      "". The default is set to aodh for backwards compatibility. Note this
+      setting has no impact on the remote-sink option and is ignored prior to
+      Queens.
   remote-sink:
     type: string
     default:
diff --git a/lib/ceilometer_contexts.py b/lib/ceilometer_contexts.py
index 01f73bd..80799f2 100644
--- a/lib/ceilometer_contexts.py
+++ b/lib/ceilometer_contexts.py
@@ -17,9 +17,13 @@ from charmhelpers.core.hookenv import (
     relation_get,
     related_units,
     config,
+    log,
+    INFO,
+    WARNING,
 )
 
 from charmhelpers.contrib.openstack.utils import (
+    get_os_codename_package,
     os_release,
     CompareOpenStackReleases,
 )
@@ -168,6 +172,27 @@ class RemoteSinksContext(OSContextGenerator):
                     if not ctxt.get('internal_sinks'):
                         ctxt['internal_sinks'] = {}
                     ctxt['internal_sinks'][unit.split('/')[0]] = publisher
+
+        release = get_os_codename_package('ceilometer-common', fatal=False)
+        ctxt['event_sink_publisher'] = None
+        if CompareOpenStackReleases(release) >= 'queens':
+            # NOTE: see bug LP 1676586
+            if config('events-publisher') == "aodh":
+                ctxt['event_sink_publisher'] = 'notifier://?topic=alarm.all'
+            elif config('events-publisher') == "gnocchi":
+                if relation_ids('metric-service'):
+                    ctxt['event_sink_publisher'] = 'gnocchi://'
+                else:
+                    log("Unable to configure event publisher '{}' since "
+                        "no gnocchi relation found".
+                        format(config('events-publisher')), level=INFO)
+            elif config('events-publisher') == "":
+                log("Not configuring any event publishers", level=INFO)
+            else:
+                log("Invalid event publisher config provided '{}'. Not "
+                    "configuring any event publishers".
+                    format(config('events-publisher')), level=WARNING)
+
         return ctxt
 
 
diff --git a/templates/queens/event_pipeline.yaml b/templates/queens/event_pipeline.yaml
new file mode 100644
index 0000000..8ad62b4
--- /dev/null
+++ b/templates/queens/event_pipeline.yaml
@@ -0,0 +1,38 @@
+---
+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:
+{%- if event_sink_publisher %}
+     - {{ event_sink_publisher }}
+{% endif %}
diff --git a/unit_tests/test_ceilometer_contexts.py b/unit_tests/test_ceilometer_contexts.py
index e70a65e..40154ff 100644
--- a/unit_tests/test_ceilometer_contexts.py
+++ b/unit_tests/test_ceilometer_contexts.py
@@ -226,13 +226,23 @@ class CeilometerContextsTest(CharmTestCase):
         }
         self.assertEqual(contexts.HAProxyContext()(), expected)
 
-    def test_remote_sink_context_no_config(self):
+    @patch.object(contexts, 'get_os_codename_package')
+    def test_remote_sink_context_no_config(self, mock_get_rel):
+        mock_get_rel.return_value = 'mitaka'
         self.relation_ids.return_value = []
         self.os_release.return_value = 'mitaka'
-        self.assertEqual(contexts.RemoteSinksContext()(), {})
+        self.assertEqual(contexts.RemoteSinksContext()(), {
+            'event_sink_publisher': None})
 
-    def test_remote_sink_context_event_service_relation(self):
-        self.relation_ids.return_value = ['event-service:0']
+        mock_get_rel.return_value = 'queens'
+        self.assertEqual(contexts.RemoteSinksContext()(),
+                         {'event_sink_publisher':
+                          'notifier://?topic=alarm.all'})
+
+    @patch.object(contexts, 'get_os_codename_package')
+    def test_remote_sink_context_event_service_relation(self, mock_get_rel):
+        mock_get_rel.return_value = 'mitaka'
+        self.relation_ids.return_value = ['event-service:0', 'meter-service:0']
         self.related_units.return_value = ['panko/0']
         self.os_release.return_value = 'mitaka'
         data = {
@@ -240,21 +250,64 @@ class CeilometerContextsTest(CharmTestCase):
         }
         self.test_relation.set(data)
         self.assertEqual(contexts.RemoteSinksContext()(),
-                         {'internal_sinks': {'panko': 'panko://'}})
+                         {'internal_sinks': {'panko': 'panko://'},
+                          'event_sink_publisher': None})
 
-    def test_remote_sink_context_with_single_config(self):
-        self.relation_ids.return_value = []
+        mock_get_rel.return_value = 'queens'
+        self.assertEqual(contexts.RemoteSinksContext()(),
+                         {'internal_sinks': {'panko': 'panko://'},
+                          'event_sink_publisher':
+                          'notifier://?topic=alarm.all'})
+
+        self.test_config.set('events-publisher', 'gnocchi')
+        self.assertEqual(contexts.RemoteSinksContext()(),
+                         {'internal_sinks': {'panko': 'panko://'},
+                          'event_sink_publisher':
+                          'gnocchi://'})
+
+    @patch.object(contexts, 'get_os_codename_package')
+    def test_remote_sink_context_with_single_config(self, mock_get_rel):
+        mock_get_rel.return_value = 'mitaka'
+        self.relation_ids.return_value = ['meter-service:0']
         self.os_release.return_value = 'mitaka'
         self.test_config.set('remote-sink', 'http://foo')
         self.assertEqual(contexts.RemoteSinksContext()(),
-                         {'remote_sinks': ['http://foo']})
+                         {'remote_sinks': ['http://foo'],
+                          'event_sink_publisher': None})
 
-    def test_remote_sink_context_with_multiple_config(self):
-        self.relation_ids.return_value = []
+        mock_get_rel.return_value = 'queens'
+        self.assertEqual(contexts.RemoteSinksContext()(),
+                         {'remote_sinks': ['http://foo'],
+                          'event_sink_publisher':
+                          'notifier://?topic=alarm.all'})
+
+        self.test_config.set('events-publisher', 'gnocchi')
+        self.assertEqual(contexts.RemoteSinksContext()(),
+                         {'remote_sinks': ['http://foo'],
+                          'event_sink_publisher':
+                          'gnocchi://'})
+
+    @patch.object(contexts, 'get_os_codename_package')
+    def test_remote_sink_context_with_multiple_config(self, mock_get_rel):
+        mock_get_rel.return_value = 'mitaka'
+        self.relation_ids.return_value = ['meter-service:0']
         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']})
+                         {'remote_sinks': ['http://foo', 'http://bar'],
+                          'event_sink_publisher': None})
+
+        mock_get_rel.return_value = 'queens'
+        self.assertEqual(contexts.RemoteSinksContext()(),
+                         {'remote_sinks': ['http://foo', 'http://bar'],
+                          'event_sink_publisher':
+                          'notifier://?topic=alarm.all'})
+
+        self.test_config.set('events-publisher', 'gnocchi')
+        self.assertEqual(contexts.RemoteSinksContext()(),
+                         {'remote_sinks': ['http://foo', 'http://bar'],
+                          'event_sink_publisher':
+                          'gnocchi://'})
 
     @patch.object(contexts, 'AMQPContext')
     def test_AMQPListenersContext(self, mock_AMQPContext):
-- 
GitLab