From abdec8ad374aa321cec4f41e6c4704909c40c3be Mon Sep 17 00:00:00 2001 From: gordon chung Date: Tue, 20 Jan 2015 18:14:51 -0500 Subject: [PATCH] initial checkin --- AUTHORS | 1 + CONTRIBUTING.rst | 16 + ChangeLog | 4 + HACKING.rst | 4 + LICENSE | 30 +- MANIFEST.in | 6 + README.rst | 15 + babel.cfg | 2 + ceilometermiddleware.egg-info/PKG-INFO | 38 ++ ceilometermiddleware.egg-info/SOURCES.txt | 35 ++ .../dependency_links.txt | 1 + ceilometermiddleware.egg-info/not-zip-safe | 1 + ceilometermiddleware.egg-info/pbr.json | 1 + ceilometermiddleware.egg-info/requires.txt | 8 + ceilometermiddleware.egg-info/top_level.txt | 1 + ceilometermiddleware/__init__.py | 19 + ceilometermiddleware/__init__.pyc | Bin 0 -> 276 bytes ceilometermiddleware/swift.py | 225 ++++++++++++ ceilometermiddleware/swift.pyc | Bin 0 -> 7443 bytes ceilometermiddleware/tests/__init__.py | 0 ceilometermiddleware/tests/__init__.pyc | Bin 0 -> 125 bytes ceilometermiddleware/tests/base.py | 23 ++ ceilometermiddleware/tests/base.pyc | Bin 0 -> 437 bytes ceilometermiddleware/tests/test_swift.py | 330 ++++++++++++++++++ ceilometermiddleware/tests/test_swift.pyc | Bin 0 -> 14967 bytes doc/source/conf.py | 75 ++++ doc/source/contributing.rst | 4 + doc/source/index.rst | 25 ++ doc/source/installation.rst | 12 + doc/source/readme.rst | 1 + doc/source/usage.rst | 7 + openstack-common.conf | 6 + requirements.txt | 12 + setup.cfg | 47 +++ setup.py | 22 ++ test-requirements.txt | 15 + tox.ini | 37 ++ 37 files changed, 995 insertions(+), 28 deletions(-) create mode 100644 AUTHORS create mode 100644 CONTRIBUTING.rst create mode 100644 ChangeLog create mode 100644 HACKING.rst create mode 100644 MANIFEST.in create mode 100644 README.rst create mode 100644 babel.cfg create mode 100644 ceilometermiddleware.egg-info/PKG-INFO create mode 100644 ceilometermiddleware.egg-info/SOURCES.txt create mode 100644 ceilometermiddleware.egg-info/dependency_links.txt create mode 100644 ceilometermiddleware.egg-info/not-zip-safe create mode 100644 ceilometermiddleware.egg-info/pbr.json create mode 100644 ceilometermiddleware.egg-info/requires.txt create mode 100644 ceilometermiddleware.egg-info/top_level.txt create mode 100644 ceilometermiddleware/__init__.py create mode 100644 ceilometermiddleware/__init__.pyc create mode 100644 ceilometermiddleware/swift.py create mode 100644 ceilometermiddleware/swift.pyc create mode 100644 ceilometermiddleware/tests/__init__.py create mode 100644 ceilometermiddleware/tests/__init__.pyc create mode 100644 ceilometermiddleware/tests/base.py create mode 100644 ceilometermiddleware/tests/base.pyc create mode 100644 ceilometermiddleware/tests/test_swift.py create mode 100644 ceilometermiddleware/tests/test_swift.pyc create mode 100755 doc/source/conf.py create mode 100644 doc/source/contributing.rst create mode 100644 doc/source/index.rst create mode 100644 doc/source/installation.rst create mode 100644 doc/source/readme.rst create mode 100644 doc/source/usage.rst create mode 100644 openstack-common.conf create mode 100644 requirements.txt create mode 100644 setup.cfg create mode 100755 setup.py create mode 100644 test-requirements.txt create mode 100644 tox.ini diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..2cf6b2e --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +gordon chung diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..72e28be --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,16 @@ +If you would like to contribute to the development of OpenStack, +you must follow the steps in this page: + + http://docs.openstack.org/infra/manual/developers.html + +Once those steps have been completed, changes to OpenStack +should be submitted for review via the Gerrit tool, following +the workflow documented at: + + http://docs.openstack.org/infra/manual/developers.html#development-workflow + +Pull requests submitted through GitHub will be ignored. + +Bugs should be filed on Launchpad, not GitHub: + + https://bugs.launchpad.net/ceilometermiddleware diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..efe1d67 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,4 @@ +CHANGES +======= + +* Initial Cookiecutter Commit diff --git a/HACKING.rst b/HACKING.rst new file mode 100644 index 0000000..7f9f3a8 --- /dev/null +++ b/HACKING.rst @@ -0,0 +1,4 @@ +ceilometermiddleware Style Commandments +=============================================== + +Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/ diff --git a/LICENSE b/LICENSE index e06d208..68c771a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,5 @@ -Apache License + + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -173,30 +174,3 @@ Apache License incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. - diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..c978a52 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,6 @@ +include AUTHORS +include ChangeLog +exclude .gitignore +exclude .gitreview + +global-exclude *.pyc diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..dd27982 --- /dev/null +++ b/README.rst @@ -0,0 +1,15 @@ +=============================== +ceilometermiddleware +=============================== + +OpenStack Telemetry middleware for generating metrics + +* Free software: Apache license +* Documentation: http://docs.openstack.org/developer/ceilometermiddleware +* Source: http://git.openstack.org/cgit/openstack/ceilometermiddleware +* Bugs: http://bugs.launchpad.net/ceilometer + +Features +-------- + +* TODO diff --git a/babel.cfg b/babel.cfg new file mode 100644 index 0000000..15cd6cb --- /dev/null +++ b/babel.cfg @@ -0,0 +1,2 @@ +[python: **.py] + diff --git a/ceilometermiddleware.egg-info/PKG-INFO b/ceilometermiddleware.egg-info/PKG-INFO new file mode 100644 index 0000000..22e18b0 --- /dev/null +++ b/ceilometermiddleware.egg-info/PKG-INFO @@ -0,0 +1,38 @@ +Metadata-Version: 1.1 +Name: ceilometermiddleware +Version: 0.0.0.post1 +Summary: OpenStack Telemetry middleware for generating metrics +Home-page: http://www.openstack.org/ +Author: OpenStack +Author-email: openstack-dev@lists.openstack.org +License: UNKNOWN +Description: =============================== + ceilometermiddleware + =============================== + + OpenStack Telemetry middleware for generating metrics + + * Free software: Apache license + * Documentation: http://docs.openstack.org/developer/ceilometermiddleware + * Source: http://git.openstack.org/cgit/openstack/ceilometermiddleware + * Bugs: http://bugs.launchpad.net/ceilometer + + Features + -------- + + * TODO + + +Platform: UNKNOWN +Classifier: Environment :: OpenStack +Classifier: Intended Audience :: Information Technology +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: POSIX :: Linux +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 diff --git a/ceilometermiddleware.egg-info/SOURCES.txt b/ceilometermiddleware.egg-info/SOURCES.txt new file mode 100644 index 0000000..3f1d14b --- /dev/null +++ b/ceilometermiddleware.egg-info/SOURCES.txt @@ -0,0 +1,35 @@ +.coveragerc +.mailmap +.testr.conf +AUTHORS +CONTRIBUTING.rst +ChangeLog +HACKING.rst +LICENSE +MANIFEST.in +README.rst +babel.cfg +openstack-common.conf +requirements.txt +setup.cfg +setup.py +test-requirements.txt +tox.ini +ceilometermiddleware/__init__.py +ceilometermiddleware/swift.py +ceilometermiddleware.egg-info/PKG-INFO +ceilometermiddleware.egg-info/SOURCES.txt +ceilometermiddleware.egg-info/dependency_links.txt +ceilometermiddleware.egg-info/not-zip-safe +ceilometermiddleware.egg-info/pbr.json +ceilometermiddleware.egg-info/requires.txt +ceilometermiddleware.egg-info/top_level.txt +ceilometermiddleware/tests/__init__.py +ceilometermiddleware/tests/base.py +ceilometermiddleware/tests/test_swift.py +doc/source/conf.py +doc/source/contributing.rst +doc/source/index.rst +doc/source/installation.rst +doc/source/readme.rst +doc/source/usage.rst \ No newline at end of file diff --git a/ceilometermiddleware.egg-info/dependency_links.txt b/ceilometermiddleware.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/ceilometermiddleware.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/ceilometermiddleware.egg-info/not-zip-safe b/ceilometermiddleware.egg-info/not-zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/ceilometermiddleware.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/ceilometermiddleware.egg-info/pbr.json b/ceilometermiddleware.egg-info/pbr.json new file mode 100644 index 0000000..e36cd2e --- /dev/null +++ b/ceilometermiddleware.egg-info/pbr.json @@ -0,0 +1 @@ +{"is_release": false, "git_version": "e8cd146"} \ No newline at end of file diff --git a/ceilometermiddleware.egg-info/requires.txt b/ceilometermiddleware.egg-info/requires.txt new file mode 100644 index 0000000..c25640b --- /dev/null +++ b/ceilometermiddleware.egg-info/requires.txt @@ -0,0 +1,8 @@ +oslo.config>=1.6.0 +oslo.context>=0.1.0 +oslo.messaging>=1.4.0,!=1.5.0 +oslo.utils>=1.2.0 +pbr>=0.6,!=0.7,<1.0 +pycadf>=0.6.0 +six>=1.7.0 +Babel>=1.3 diff --git a/ceilometermiddleware.egg-info/top_level.txt b/ceilometermiddleware.egg-info/top_level.txt new file mode 100644 index 0000000..afdc788 --- /dev/null +++ b/ceilometermiddleware.egg-info/top_level.txt @@ -0,0 +1 @@ +ceilometermiddleware diff --git a/ceilometermiddleware/__init__.py b/ceilometermiddleware/__init__.py new file mode 100644 index 0000000..f5cdc8c --- /dev/null +++ b/ceilometermiddleware/__init__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +# 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. + +import pbr.version + + +__version__ = pbr.version.VersionInfo( + 'ceilometermiddleware').version_string() diff --git a/ceilometermiddleware/__init__.pyc b/ceilometermiddleware/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c4892f38407155cd4e73ceac7b80da95e460a3c GIT binary patch literal 276 zcmZurIS#@=40ICiv*7`hslo>kqNSk$QP5bxE@-11XxD(K`3m3QRXhMD9HPPUj>n$y z%E7(exNdLr4Sm8#9pkwWppYIVL`#pfN3lM&g7XCi|D0_`kfV?#ao|b+gv(7jh$GN?Oxw7e-@aF kTj>X`kM`dU17?#MVUG4^slhZ%b9G4cs0;2&fL2iVp735oivR!s literal 0 HcmV?d00001 diff --git a/ceilometermiddleware/swift.py b/ceilometermiddleware/swift.py new file mode 100644 index 0000000..8f27a1c --- /dev/null +++ b/ceilometermiddleware/swift.py @@ -0,0 +1,225 @@ +# +# Copyright 2012 eNovance +# +# 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. + +""" +Telemetry Middleware for Swift Proxy + +Configuration: +In /etc/swift/proxy-server.conf on the main pipeline add "ceilometer" just +before "proxy-server" and add the following filter in the file: +.. code-block:: python + [filter:ceilometer] + paste.filter_factory = ceilometermiddleware.swift:R + # Some optional configuration this allow to publish additional metadata + metadata_headers = X-TEST + # Set reseller prefix (defaults to "AUTH_" if not set) + reseller_prefix = AUTH_ +""" +import functools +import logging + +import oslo.messaging +from oslo_config import cfg +from oslo_context import context +from oslo_utils import timeutils +from pycadf import event as cadf_event +from pycadf import measurement as cadf_measurement +from pycadf import metric as cadf_metric +from pycadf import resource as cadf_resource +import six +import six.moves.urllib.parse as urlparse + +_LOG = logging.getLogger(__name__) + + +def _log_and_ignore_error(fn): + @functools.wraps(fn) + def wrapper(*args, **kwargs): + try: + return fn(*args, **kwargs) + except Exception as e: + _LOG.exception('An exception occurred processing ' + 'the API call: %s ', e) + return wrapper + + +class InputProxy(object): + """File-like object that counts bytes read. + + To be swapped in for wsgi.input for accounting purposes. + Borrowed from swift.common.utils. Duplicated here to avoid + dependency on swift package. + """ + def __init__(self, wsgi_input): + self.wsgi_input = wsgi_input + self.bytes_received = 0 + + def read(self, *args, **kwargs): + """Pass read request to the underlying file-like object + + Add bytes read to total. + """ + chunk = self.wsgi_input.read(*args, **kwargs) + self.bytes_received += len(chunk) + return chunk + + def readline(self, *args, **kwargs): + """Pass readline request to the underlying file-like object + + Add bytes read to total. + """ + line = self.wsgi_input.readline(*args, **kwargs) + self.bytes_received += len(line) + return line + + +class Swift(object): + """Swift middleware used for counting requests.""" + + def __init__(self, app, conf): + self._app = app + + self._notifier = oslo.messaging.Notifier( + oslo.messaging.get_transport(cfg.CONF), + publisher_id='ceilometermiddleware') + + self.metadata_headers = [h.strip().replace('-', '_').lower() + for h in conf.get( + "metadata_headers", + "").split(",") if h.strip()] + + self.reseller_prefix = conf.get('reseller_prefix', 'AUTH_') + if self.reseller_prefix and self.reseller_prefix[-1] != '_': + self.reseller_prefix += '_' + + def __call__(self, env, start_response): + start_response_args = [None] + input_proxy = InputProxy(env['wsgi.input']) + env['wsgi.input'] = input_proxy + + def my_start_response(status, headers, exc_info=None): + start_response_args[0] = (status, list(headers), exc_info) + + def iter_response(iterable): + iterator = iter(iterable) + try: + chunk = next(iterator) + while not chunk: + chunk = next(iterator) + except StopIteration: + chunk = '' + + if start_response_args[0]: + start_response(*start_response_args[0]) + bytes_sent = 0 + try: + while chunk: + bytes_sent += len(chunk) + yield chunk + chunk = next(iterator) + finally: + self.emit_event(env, input_proxy.bytes_received, bytes_sent) + + try: + iterable = self._app(env, my_start_response) + except Exception: + self.emit_event(env, input_proxy.bytes_received, 0, 'failure') + raise + else: + return iter_response(iterable) + + @_log_and_ignore_error + def emit_event(self, env, bytes_received, bytes_sent, outcome='success'): + path = urlparse.quote(env['PATH_INFO']) + method = env['REQUEST_METHOD'] + headers = {} + for header in env: + if header.startswith('HTTP_') and env[header]: + key = header[5:] + if isinstance(env[header], six.text_type): + headers[key] = env[header].encode('utf-8') + else: + headers[key] = str(env[header]) + + try: + container = obj = None + version, account, remainder = path.replace( + '/', '', 1).split('/', 2) + if not version or not account: + raise ValueError('Invalid path: %s' % path) + if remainder: + if '/' in remainder: + container, obj = remainder.split('/', 1) + else: + container = remainder + except ValueError: + return + + now = timeutils.utcnow().isoformat() + + resource_metadata = { + "path": path, + "version": version, + "container": container, + "object": obj, + } + + for header in self.metadata_headers: + if header.upper() in headers: + resource_metadata['http_header_%s' % header] = headers.get( + header.upper()) + + # build object store details + target = cadf_resource.Resource( + typeURI='service/storage/object', + id=account.partition(self.reseller_prefix)[2]) + target.metadata = resource_metadata + target.action = method.lower() + + # build user details + initiator = cadf_resource.Resource( + typeURI='service/security/account/user', + id=env.get('HTTP_X_USER_ID')) + initiator.project_id = env.get('HTTP_X_TENANT_ID') + + # build notification body + event = cadf_event.Event(eventTime=now, outcome=outcome, + initiator=initiator, target=target, + observer=cadf_resource.Resource(id='target')) + + # measurements + if bytes_received: + event.add_measurement(cadf_measurement.Measurement( + result=bytes_received, + metric=cadf_metric.Metric( + name='storage.objects.incoming.bytes', unit='B'))) + if bytes_sent: + event.add_measurement(cadf_measurement.Measurement( + result=bytes_sent, + metric=cadf_metric.Metric( + name='storage.objects.outgoing.bytes', unit='B'))) + + self._notifier.info(context.get_admin_context().to_dict(), + 'objectstore.http.request', event.as_dict()) + + +def filter_factory(global_conf, **local_conf): + conf = global_conf.copy() + conf.update(local_conf) + + def filter(app): + return Swift(app, conf) + return filter diff --git a/ceilometermiddleware/swift.pyc b/ceilometermiddleware/swift.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c8ce92e330d6ffd60bdb52f2d5efa584fc7895db GIT binary patch literal 7443 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 A2><{9 literal 0 HcmV?d00001 diff --git a/ceilometermiddleware/tests/__init__.py b/ceilometermiddleware/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ceilometermiddleware/tests/__init__.pyc b/ceilometermiddleware/tests/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ce97fdfcb47cb3c6784ed7d665d4417a7403d8c GIT binary patch literal 125 zcmZSn%*(YTZEr|20~9a}EE^eCwpWGi|jdL_DVE3y+> z)_Y(KUx-?uN)y0?VV4e3tQ@CXG=Dz%%L2(K07qFKZmpzdS?FAES*im~Wg*ph!>sx^ zD&xJ{tcRo$ba__isf&-+YZIK-X^I!EL$n*8+k?{D*lC@GX$O3PwsV7z!6i3_!CLR0 z&FJPud!C(VPcKa#_#DX+urKfGykGH*>|_4h;DOQ+U;)nW#)Y`EE7d +# +# 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. + +import mock +from oslo_config import cfg +from oslo_utils import timeutils +import six + +from ceilometermiddleware import swift +from ceilometermiddleware.tests import base as tests_base + + +class FakeApp(object): + def __init__(self, body=None): + self.body = body or ['This string is 28 bytes long'] + + def __call__(self, env, start_response): + yield + start_response('200 OK', [ + ('Content-Type', 'text/plain'), + ('Content-Length', str(sum(map(len, self.body)))) + ]) + while env['wsgi.input'].read(5): + pass + for line in self.body: + yield line + + +class FakeRequest(object): + """A bare bones request object + + The middleware will inspect this for request method, + wsgi.input and headers. + """ + + def __init__(self, path, environ=None, headers=None): + environ = environ or {} + headers = headers or {} + + environ['PATH_INFO'] = path + + if 'wsgi.input' not in environ: + environ['wsgi.input'] = six.moves.cStringIO('') + + for header, value in headers.iteritems(): + environ['HTTP_%s' % header.upper()] = value + self.environ = environ + + +@mock.patch('oslo.messaging.get_transport', mock.MagicMock()) +class TestSwift(tests_base.TestCase): + + def setUp(self): + super(TestSwift, self).setUp() + cfg.CONF([], project='ceilometermiddleware') + self.addCleanup(cfg.CONF.reset) + + @staticmethod + def start_response(*args): + pass + + def test_get(self): + app = swift.Swift(FakeApp(), {}) + req = FakeRequest('/1.0/account/container/obj', + environ={'REQUEST_METHOD': 'GET'}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + resp = app(req.environ, self.start_response) + self.assertEqual(["This string is 28 bytes long"], list(resp)) + self.assertEqual(1, len(notify.call_args_list)) + data = notify.call_args_list[0][0] + self.assertEqual('objectstore.http.request', data[1]) + self.assertEqual(28, data[2]['measurements'][0]['result']) + self.assertEqual('storage.objects.outgoing.bytes', + data[2]['measurements'][0]['metric']['name']) + metadata = data[2]['target']['metadata'] + self.assertEqual('1.0', metadata['version']) + self.assertEqual('container', metadata['container']) + self.assertEqual('obj', metadata['object']) + self.assertEqual('get', data[2]['target']['action']) + + def test_put(self): + app = swift.Swift(FakeApp(body=['']), {}) + req = FakeRequest( + '/1.0/account/container/obj', + environ={'REQUEST_METHOD': 'PUT', + 'wsgi.input': + six.moves.cStringIO('some stuff')}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(1, len(notify.call_args_list)) + data = notify.call_args_list[0][0] + self.assertEqual('objectstore.http.request', data[1]) + self.assertEqual(10, data[2]['measurements'][0]['result']) + self.assertEqual('storage.objects.incoming.bytes', + data[2]['measurements'][0]['metric']['name']) + metadata = data[2]['target']['metadata'] + self.assertEqual('1.0', metadata['version']) + self.assertEqual('container', metadata['container']) + self.assertEqual('obj', metadata['object']) + self.assertEqual('put', data[2]['target']['action']) + + def test_post(self): + app = swift.Swift(FakeApp(body=['']), {}) + req = FakeRequest( + '/1.0/account/container/obj', + environ={'REQUEST_METHOD': 'POST', + 'wsgi.input': six.moves.cStringIO('some other stuff')}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(1, len(notify.call_args_list)) + data = notify.call_args_list[0][0] + self.assertEqual('objectstore.http.request', data[1]) + self.assertEqual(16, data[2]['measurements'][0]['result']) + self.assertEqual('storage.objects.incoming.bytes', + data[2]['measurements'][0]['metric']['name']) + metadata = data[2]['target']['metadata'] + self.assertEqual('1.0', metadata['version']) + self.assertEqual('container', metadata['container']) + self.assertEqual('obj', metadata['object']) + self.assertEqual('post', data[2]['target']['action']) + + def test_head(self): + app = swift.Swift(FakeApp(body=['']), {}) + req = FakeRequest('/1.0/account/container/obj', + environ={'REQUEST_METHOD': 'HEAD'}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(1, len(notify.call_args_list)) + data = notify.call_args_list[0][0] + self.assertEqual('objectstore.http.request', data[1]) + self.assertIsNone(data[2].get('measurements')) + metadata = data[2]['target']['metadata'] + self.assertEqual('1.0', metadata['version']) + self.assertEqual('container', metadata['container']) + self.assertEqual('obj', metadata['object']) + self.assertEqual('head', data[2]['target']['action']) + + def test_bogus_request(self): + """Test even for arbitrary request method, this will still work.""" + app = swift.Swift(FakeApp(body=['']), {}) + req = FakeRequest('/1.0/account/container/obj', + environ={'REQUEST_METHOD': 'BOGUS'}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(1, len(notify.call_args_list)) + data = notify.call_args_list[0][0] + self.assertEqual('objectstore.http.request', data[1]) + self.assertIsNone(data[2].get('measurements')) + metadata = data[2]['target']['metadata'] + self.assertEqual('1.0', metadata['version']) + self.assertEqual('container', metadata['container']) + self.assertEqual('obj', metadata['object']) + self.assertEqual('bogus', data[2]['target']['action']) + + def test_get_container(self): + app = swift.Swift(FakeApp(), {}) + req = FakeRequest('/1.0/account/container', + environ={'REQUEST_METHOD': 'GET'}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(1, len(notify.call_args_list)) + data = notify.call_args_list[0][0] + self.assertEqual('objectstore.http.request', data[1]) + self.assertEqual(28, data[2]['measurements'][0]['result']) + self.assertEqual('storage.objects.outgoing.bytes', + data[2]['measurements'][0]['metric']['name']) + metadata = data[2]['target']['metadata'] + self.assertEqual('1.0', metadata['version']) + self.assertEqual('container', metadata['container']) + self.assertIsNone(metadata['object']) + self.assertEqual('get', data[2]['target']['action']) + + def test_no_metadata_headers(self): + app = swift.Swift(FakeApp(), {}) + req = FakeRequest('/1.0/account/container', + environ={'REQUEST_METHOD': 'GET'}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(1, len(notify.call_args_list)) + data = notify.call_args_list[0][0] + self.assertEqual('objectstore.http.request', data[1]) + metadata = data[2]['target']['metadata'] + self.assertEqual('1.0', metadata['version']) + self.assertEqual('container', metadata['container']) + self.assertIsNone(metadata['object']) + self.assertEqual('get', data[2]['target']['action']) + http_headers = [k for k in metadata.keys() + if k.startswith('http_header_')] + self.assertEqual(0, len(http_headers)) + + def test_metadata_headers(self): + app = swift.Swift(FakeApp(), { + 'metadata_headers': 'X_VAR1, x-var2, x-var3, token' + }) + req = FakeRequest('/1.0/account/container', + environ={'REQUEST_METHOD': 'GET'}, + headers={'X_VAR1': 'value1', + 'X_VAR2': 'value2', + 'TOKEN': 'token'}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(1, len(notify.call_args_list)) + data = notify.call_args_list[0][0] + self.assertEqual('objectstore.http.request', data[1]) + metadata = data[2]['target']['metadata'] + self.assertEqual('1.0', metadata['version']) + self.assertEqual('container', metadata['container']) + self.assertIsNone(metadata['object']) + self.assertEqual('get', data[2]['target']['action']) + http_headers = [k for k in metadata.keys() + if k.startswith('http_header_')] + self.assertEqual(3, len(http_headers)) + self.assertEqual('value1', metadata['http_header_x_var1']) + self.assertEqual('value2', metadata['http_header_x_var2']) + self.assertEqual('token', metadata['http_header_token']) + self.assertFalse('http_header_x_var3' in metadata) + + def test_metadata_headers_unicode(self): + app = swift.Swift(FakeApp(), { + 'metadata_headers': 'unicode' + }) + uni = u'\xef\xbd\xa1\xef\xbd\xa5' + req = FakeRequest('/1.0/account/container', + environ={'REQUEST_METHOD': 'GET'}, + headers={'UNICODE': uni}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(1, len(notify.call_args_list)) + data = notify.call_args_list[0][0] + self.assertEqual('objectstore.http.request', data[1]) + metadata = data[2]['target']['metadata'] + self.assertEqual('1.0', metadata['version']) + self.assertEqual('container', metadata['container']) + self.assertIsNone(metadata['object']) + self.assertEqual('get', data[2]['target']['action']) + http_headers = [k for k in metadata.keys() + if k.startswith('http_header_')] + self.assertEqual(1, len(http_headers)) + self.assertEqual(uni.encode('utf-8'), + metadata['http_header_unicode']) + + def test_metadata_headers_on_not_existing_header(self): + app = swift.Swift(FakeApp(), { + 'metadata_headers': 'x-var3' + }) + req = FakeRequest('/1.0/account/container', + environ={'REQUEST_METHOD': 'GET'}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(1, len(notify.call_args_list)) + data = notify.call_args_list[0][0] + self.assertEqual('objectstore.http.request', data[1]) + metadata = data[2]['target']['metadata'] + self.assertEqual('1.0', metadata['version']) + self.assertEqual('container', metadata['container']) + self.assertIsNone(metadata['object']) + self.assertEqual('get', data[2]['target']['action']) + http_headers = [k for k in metadata.keys() + if k.startswith('http_header_')] + self.assertEqual(0, len(http_headers)) + + def test_bogus_path(self): + app = swift.Swift(FakeApp(), {}) + req = FakeRequest('/5.0//', + environ={'REQUEST_METHOD': 'GET'}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(0, len(notify.call_args_list)) + + def test_missing_resource_id(self): + app = swift.Swift(FakeApp(), {}) + req = FakeRequest('/v1/', environ={'REQUEST_METHOD': 'GET'}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(0, len(notify.call_args_list)) + + @mock.patch.object(timeutils, 'utcnow') + def test_emit_event_fail(self, mocked_time): + mocked_time.side_effect = Exception("a exception") + app = swift.Swift(FakeApp(body=["test"]), {}) + req = FakeRequest('/1.0/account/container', + environ={'REQUEST_METHOD': 'GET'}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + resp = list(app(req.environ, self.start_response)) + self.assertEqual(0, len(notify.call_args_list)) + self.assertEqual(["test"], resp) + + def test_reseller_prefix(self): + app = swift.Swift(FakeApp(), {}) + req = FakeRequest('/1.0/AUTH_account/container/obj', + environ={'REQUEST_METHOD': 'GET'}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(1, len(notify.call_args_list)) + data = notify.call_args_list[0][0] + self.assertEqual("account", data[2]['target']['id']) + + def test_custom_prefix(self): + app = swift.Swift(FakeApp(), {'reseller_prefix': 'CUSTOM_'}) + req = FakeRequest('/1.0/CUSTOM_account/container/obj', + environ={'REQUEST_METHOD': 'GET'}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(1, len(notify.call_args_list)) + data = notify.call_args_list[0][0] + self.assertEqual("account", data[2]['target']['id']) + + def test_invalid_reseller_prefix(self): + # Custom reseller prefix set, but without trailing underscore + app = swift.Swift( + FakeApp(), {'reseller_prefix': 'CUSTOM'}) + req = FakeRequest('/1.0/CUSTOM_account/container/obj', + environ={'REQUEST_METHOD': 'GET'}) + with mock.patch('oslo.messaging.Notifier.info') as notify: + list(app(req.environ, self.start_response)) + self.assertEqual(1, len(notify.call_args_list)) + data = notify.call_args_list[0][0] + self.assertEqual("account", data[2]['target']['id']) diff --git a/ceilometermiddleware/tests/test_swift.pyc b/ceilometermiddleware/tests/test_swift.pyc new file mode 100644 index 0000000000000000000000000000000000000000..101abb6e5d7b3c8a7ae72d52a379e43bebe52740 GIT binary patch literal 14967 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 k25t^AEK=G<{j;*4(4r7Q7=h=1.6.0 +oslo.context>=0.1.0 +oslo.messaging>=1.4.0,!=1.5.0 +oslo.utils>=1.2.0 +pbr>=0.6,!=0.7,<1.0 +pycadf>=0.6.0 +six>=1.7.0 +Babel>=1.3 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..abcf0dc --- /dev/null +++ b/setup.cfg @@ -0,0 +1,47 @@ +[metadata] +name = ceilometermiddleware +summary = OpenStack Telemetry middleware for generating metrics +description-file = + README.rst +author = OpenStack +author-email = openstack-dev@lists.openstack.org +home-page = http://www.openstack.org/ +classifier = + Environment :: OpenStack + Intended Audience :: Information Technology + Intended Audience :: System Administrators + License :: OSI Approved :: Apache Software License + Operating System :: POSIX :: Linux + Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 2.6 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.3 + Programming Language :: Python :: 3.4 + +[files] +packages = + ceilometermiddleware + +[build_sphinx] +source-dir = doc/source +build-dir = doc/build +all_files = 1 + +[upload_sphinx] +upload-dir = doc/build/html + +[compile_catalog] +directory = ceilometermiddleware/locale +domain = ceilometermiddleware + +[update_catalog] +domain = ceilometermiddleware +output_dir = ceilometermiddleware/locale +input_file = ceilometermiddleware/locale/ceilometermiddleware.pot + +[extract_messages] +keywords = _ gettext ngettext l_ lazy_gettext +mapping_file = babel.cfg +output_file = ceilometermiddleware/locale/ceilometermiddleware.pot diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..70c2b3f --- /dev/null +++ b/setup.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. +# +# 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. + +# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT +import setuptools + +setuptools.setup( + setup_requires=['pbr'], + pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..1803eb5 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,15 @@ +# The order of packages is significant, because pip processes them in the order +# of appearance. Changing the order has an impact on the overall integration +# process, which may cause wedges in the gate later. + +hacking>=0.10.0,<0.11 + +coverage>=3.6 +discover +python-subunit +sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 +oslosphinx>=2.2.0 +oslotest>=1.2.0 +testrepository>=0.0.18 +testscenarios>=0.4 +testtools>=0.9.36,!=1.2.0 diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..7363931 --- /dev/null +++ b/tox.ini @@ -0,0 +1,37 @@ +[tox] +minversion = 1.6 +envlist = py33,py34,py26,py27,pypy,pep8 +skipsdist = True + +[testenv] +usedevelop = True +install_command = pip install -U {opts} {packages} +setenv = + VIRTUAL_ENV={envdir} +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +commands = python setup.py testr --slowest --testr-args='{posargs}' + +[testenv:pep8] +commands = flake8 + +[testenv:venv] +commands = {posargs} + +[testenv:cover] +commands = python setup.py testr --coverage --testr-args='{posargs}' + +[testenv:docs] +commands = python setup.py build_sphinx + +[testenv:debug] +commands = oslo_debug_helper {posargs} + +[flake8] +# H803 skipped on purpose per list discussion. +# E123, E125 skipped as they are invalid PEP-8. + +show-source = True +ignore = E123,E125,H803 +builtins = _ +exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build