diff --git a/ceilometer/middleware.py b/ceilometer/middleware.py new file mode 100644 index 000000000..a13e2d167 --- /dev/null +++ b/ceilometer/middleware.py @@ -0,0 +1,74 @@ +# -*- encoding: utf-8 -*- +# +# Copyright © 2013 eNovance +# +# Author: Julien Danjou +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo.config import cfg + +from ceilometer import plugin +from ceilometer import sample + +cfg.CONF.import_opt('nova_control_exchange', + 'ceilometer.compute.notifications') +cfg.CONF.import_opt('glance_control_exchange', + 'ceilometer.image.notifications') +cfg.CONF.import_opt('neutron_control_exchange', + 'ceilometer.network.notifications') +cfg.CONF.import_opt('cinder_control_exchange', + 'ceilometer.volume.notifications') + +OPTS = [ + cfg.MultiStrOpt('http_control_exchanges', + default=[cfg.CONF.nova_control_exchange, + cfg.CONF.glance_control_exchange, + cfg.CONF.neutron_control_exchange, + cfg.CONF.cinder_control_exchange], + help="Exchanges name to listen for notifications"), +] + +cfg.CONF.register_opts(OPTS) + + +class HTTPRequest(plugin.NotificationBase): + event_types = ['http.request'] + + @staticmethod + def get_exchange_topics(conf): + """Return a sequence of ExchangeTopics defining the exchange and + topics to be connected for this plugin. + """ + return [ + plugin.ExchangeTopics( + exchange=exchange, + topics=set(topic + ".info" + for topic in conf.notification_topics)) + for exchange in conf.http_control_exchanges + ] + + def process_notification(self, message): + yield sample.Sample.from_notification( + name=message['event_type'], + type=sample.TYPE_DELTA, + volume=1, + unit=message['event_type'].split('.')[1], + user_id=message['payload']['request'].get('HTTP_X_USER_ID'), + project_id=message['payload']['request'].get('HTTP_X_PROJECT_ID'), + resource_id=None, + message=message) + + +class HTTPResponse(HTTPRequest): + event_types = ['http.response'] diff --git a/etc/ceilometer/ceilometer.conf.sample b/etc/ceilometer/ceilometer.conf.sample index 3159870f6..dbb853589 100644 --- a/etc/ceilometer/ceilometer.conf.sample +++ b/etc/ceilometer/ceilometer.conf.sample @@ -1,5 +1,16 @@ [DEFAULT] +# +# Options defined in ceilometer.middleware +# + +# Exchanges name to listen for notifications (multi valued) +#http_control_exchanges=nova +#http_control_exchanges=glance +#http_control_exchanges=neutron +#http_control_exchanges=cinder + + # # Options defined in ceilometer.pipeline # diff --git a/setup.cfg b/setup.cfg index e9fdc0ca5..9c9f9c2a7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -49,6 +49,8 @@ ceilometer.collector = port = ceilometer.network.notifications:Port router = ceilometer.network.notifications:Router floatingip = ceilometer.network.notifications:FloatingIP + http.request = ceilometer.middleware:HTTPRequest + http.response = ceilometer.middleware:HTTPResponse ceilometer.poll.compute = disk.read.requests = ceilometer.compute.pollsters.disk:ReadRequestsPollster diff --git a/tests/test_middleware.py b/tests/test_middleware.py new file mode 100644 index 000000000..7b12ebbd3 --- /dev/null +++ b/tests/test_middleware.py @@ -0,0 +1,88 @@ +# -*- encoding: utf-8 -*- +# +# Copyright © 2013 eNovance +# +# Author: Julien Danjou +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from oslo.config import cfg + +from ceilometer.tests import base +from ceilometer import middleware + + +HTTP_REQUEST = { + u'_context_auth_token': u'3d8b13de1b7d499587dfc69b77dc09c2', + u'_context_is_admin': True, + u'_context_project_id': u'7c150a59fe714e6f9263774af9688f0e', + u'_context_quota_class': None, + u'_context_read_deleted': u'no', + u'_context_remote_address': u'10.0.2.15', + u'_context_request_id': u'req-d68b36e0-9233-467f-9afb-d81435d64d66', + u'_context_roles': [u'admin'], + u'_context_timestamp': u'2012-05-08T20:23:41.425105', + u'_context_user_id': u'1e3ce043029547f1a61c1996d1a531a2', + u'event_type': u'http.request', + u'message_id': u'dae6f69c-00e0-41c0-b371-41ec3b7f4451', + u'payload': {u'request': {'HTTP_X_FOOBAR': 'foobaz', + 'HTTP_X_USER_ID': 'jd-x32'}}, + u'priority': u'INFO', + u'publisher_id': u'compute.vagrant-precise', + u'timestamp': u'2012-05-08 20:23:48.028195', +} + +HTTP_RESPONSE = { + u'_context_auth_token': u'3d8b13de1b7d499587dfc69b77dc09c2', + u'_context_is_admin': True, + u'_context_project_id': u'7c150a59fe714e6f9263774af9688f0e', + u'_context_quota_class': None, + u'_context_read_deleted': u'no', + u'_context_remote_address': u'10.0.2.15', + u'_context_request_id': u'req-d68b36e0-9233-467f-9afb-d81435d64d66', + u'_context_roles': [u'admin'], + u'_context_timestamp': u'2012-05-08T20:23:41.425105', + u'_context_user_id': u'1e3ce043029547f1a61c1996d1a531a2', + u'event_type': u'http.response', + u'message_id': u'dae6f69c-00e0-41c0-b371-41ec3b7f4451', + u'payload': {u'request': {'HTTP_X_FOOBAR': 'foobaz', + 'HTTP_X_USER_ID': 'jd-x32'}}, + u'priority': u'INFO', + u'publisher_id': u'compute.vagrant-precise', + u'timestamp': u'2012-05-08 20:23:48.028195', +} + + +class TestNotifications(base.TestCase): + def test_process_request_notification(self): + sample = list(middleware.HTTPRequest().process_notification( + HTTP_REQUEST + ))[0] + self.assertEqual(sample.user_id, + HTTP_REQUEST['payload']['request']['HTTP_X_USER_ID']) + self.assertEqual(sample.project_id, None) + self.assertEqual(sample.resource_id, None) + self.assertEqual(sample.volume, 1) + + def test_process_response_notification(self): + sample = list(middleware.HTTPResponse().process_notification( + HTTP_RESPONSE + ))[0] + self.assertEqual(sample.user_id, + HTTP_REQUEST['payload']['request']['HTTP_X_USER_ID']) + self.assertEqual(sample.project_id, None) + self.assertEqual(sample.resource_id, None) + self.assertEqual(sample.volume, 1) + + def test_exchanges(self): + topics = middleware.HTTPRequest().get_exchange_topics(cfg.CONF) + self.assertEqual(len(topics), 4)