Package: ceilometer Followup-For: Bug #732367 The attached patch fixes this bug by cherry-picking a fix from upstream. This makes ceilometer work with the version of python-wsme which is currently in unstable and testing. The same bug was fixed in upstream havana by pining wsme to 0.5b5, but this version is not available in Debian.
Gaudenz -- System Information: Debian Release: jessie/sid APT prefers testing APT policy: (800, 'testing'), (700, 'unstable'), (50, 'experimental') Architecture: amd64 (x86_64) Foreign Architectures: i386 Kernel: Linux 3.11-2-amd64 (SMP w/2 CPU cores) Locale: LANG=de_CH.UTF-8, LC_CTYPE=de_CH.UTF-8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/dash
>From 9e9ab905c55bcfa769144557f266c8c5f5101ccb Mon Sep 17 00:00:00 2001 From: Gaudenz Steinlin <gaud...@debian.org> Date: Tue, 17 Dec 2013 10:03:03 +0100 Subject: [PATCH] Fix for compatibility with WSME>=0.5b6 Add fix for compatibility with WSME>=0.5b6 taken from upstream commit e4a1a4fcefd4718e057cf8128c9a6c6b7c98ef59. --- debian/changelog | 7 + debian/patches/WSME-0.5b6-compatibility.patch | 317 ++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 325 insertions(+) create mode 100644 debian/patches/WSME-0.5b6-compatibility.patch diff --git a/debian/changelog b/debian/changelog index 4685714..32f7da9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +ceilometer (2013.2.1-3) UNRELEASED; urgency=medium + + * Add fix for compatibility with WSME>=0.5b6 taken from upstream commit + e4a1a4fcefd4718e057cf8128c9a6c6b7c98ef59. + + -- Gaudenz Steinlin <gaud...@debian.org> Tue, 17 Dec 2013 09:59:52 +0100 + ceilometer (2013.2.1-2) unstable; urgency=medium * Added missing version depends for python-iso8601 (needs >= 0.1.8). diff --git a/debian/patches/WSME-0.5b6-compatibility.patch b/debian/patches/WSME-0.5b6-compatibility.patch new file mode 100644 index 0000000..e34fcfe --- /dev/null +++ b/debian/patches/WSME-0.5b6-compatibility.patch @@ -0,0 +1,317 @@ +commit e4a1a4fcefd4718e057cf8128c9a6c6b7c98ef59 +Author: Julien Danjou <jul...@danjou.info> +Date: Mon Sep 30 16:43:59 2013 +0200 + + api: update for WSME 0.5b6 compliance + + This makes use of the mandatory option of WSME, that now works, to + remove some of our custom validation code. This is needed for new + versions of WSME that do more validation on their own. + + Fixes-Bug: #1240741 + + Change-Id: Icb66d17066b515bebf3f3a326d84e18cbfce01ef + Signed-off-by: Julien Danjou <jul...@danjou.info> + +--- a/ceilometer/api/controllers/v2.py ++++ b/ceilometer/api/controllers/v2.py +@@ -516,19 +516,19 @@ + source = wtypes.text + "The ID of the source that identifies where the sample comes from" + +- counter_name = wtypes.text ++ counter_name = wsme.wsattr(wtypes.text, mandatory=True) + "The name of the meter" + # FIXME(dhellmann): Make this meter_name? + +- counter_type = wtypes.text ++ counter_type = wsme.wsattr(wtypes.text, mandatory=True) + "The type of the meter (see :ref:`measurements`)" + # FIXME(dhellmann): Make this meter_type? + +- counter_unit = wtypes.text ++ counter_unit = wsme.wsattr(wtypes.text, mandatory=True) + "The unit of measure for the value in counter_volume" + # FIXME(dhellmann): Make this meter_unit? + +- counter_volume = float ++ counter_volume = wsme.wsattr(float, mandatory=True) + "The actual measured value" + + user_id = wtypes.text +@@ -537,7 +537,7 @@ + project_id = wtypes.text + "The ID of the project or tenant that owns the resource" + +- resource_id = wtypes.text ++ resource_id = wsme.wsattr(wtypes.text, mandatory=True) + "The ID of the :class:`Resource` for which the measurements are taken" + + timestamp = datetime.datetime +@@ -561,11 +561,6 @@ + super(Sample, self).__init__(counter_volume=counter_volume, + resource_metadata=resource_metadata, + timestamp=timestamp, **kwds) +- # Seems the mandatory option doesn't work so do it manually +- for m in ('counter_volume', 'counter_unit', +- 'counter_name', 'counter_type', 'resource_id'): +- if getattr(self, m) in (wsme.Unset, None): +- raise wsme.exc.MissingArgument(m) + + if self.resource_metadata in (wtypes.Unset, None): + self.resource_metadata = {} +@@ -709,20 +704,12 @@ + for e in pecan.request.storage_conn.get_samples(f, limit=limit) + ] + +- @wsme.validate([Sample]) + @wsme_pecan.wsexpose([Sample], body=[Sample]) +- def post(self, body): ++ def post(self, samples): + """Post a list of new Samples to Ceilometer. + +- :param body: a list of samples within the request body. ++ :param samples: a list of samples within the request body. + """ +- # Note: +- # 1) the above validate decorator seems to do nothing. LP#1220678 +- # 2) the mandatory options seems to also do nothing. LP#1227004 +- # 3) the body should already be in a list of Sample's LP#1233219 +- +- samples = [Sample(**b) for b in body] +- + now = timeutils.utcnow() + auth_project = acl.get_limited_to_project(pecan.request.headers) + def_source = pecan.request.cfg.sample_source +@@ -1002,14 +989,6 @@ + + @staticmethod + def validate(threshold_rule): +- #note(sileht): wsme mandatory doesn't work as expected +- #workaround for https://bugs.launchpad.net/wsme/+bug/1227004 +- for field in ['meter_name', 'threshold']: +- if not getattr(threshold_rule, field): +- error = _("threshold_rule/%s is mandatory") % field +- pecan.response.translatable_error = error +- raise wsme.exc.ClientSideError(unicode(error)) +- + #note(sileht): wsme default doesn't work in some case + #workaround for https://bugs.launchpad.net/wsme/+bug/1227039 + if not threshold_rule.query: +@@ -1074,17 +1053,6 @@ + alarm_ids=['739e99cb-c2ec-4718-b900-332502355f38', + '153462d0-a9b8-4b5b-8175-9e4b05e9b856']) + +- @staticmethod +- def validate(combination_rule): +- #note(sileht): wsme mandatory doesn't works as expected +- #workaround for https://bugs.launchpad.net/wsme/+bug/1227004 +- if not combination_rule.alarm_ids: +- error = _("combination_rule/alarm_ids is mandatory") +- pecan.response.translatable_error = error +- raise wsme.exc.ClientSideError(unicode(error)) +- +- return combination_rule +- + + class Alarm(_Base): + """Representation of an alarm. +@@ -1165,13 +1133,18 @@ + + @staticmethod + def validate(alarm): +- #note(sileht): wsme mandatory doesn't work as expected +- #workaround for https://bugs.launchpad.net/wsme/+bug/1227004 +- for field in ['name', 'type']: +- if not getattr(alarm, field): +- error = _("%s is mandatory") % field +- pecan.response.translatable_error = error +- raise wsme.exc.ClientSideError(unicode(error)) ++ if (alarm.threshold_rule == wtypes.Unset ++ and alarm.combination_rule == wtypes.Unset): ++ error = _("either threshold_rule or combination_rule " ++ "must be set") ++ pecan.response.translatable_error = error ++ raise wsme.exc.ClientSideError(unicode(error)) ++ ++ if alarm.threshold_rule and alarm.combination_rule: ++ error = _("threshold_rule and combination_rule " ++ "cannot be set at the same time") ++ pecan.response.translatable_error = error ++ raise wsme.exc.ClientSideError(unicode(error)) + + if alarm.threshold_rule: + # ensure an implicit constraint on project_id is added to +@@ -1182,20 +1155,17 @@ + on_behalf_of=alarm.project_id + ) + elif alarm.combination_rule: +- auth_project = _get_auth_project(alarm.project_id) ++ project = _get_auth_project(alarm.project_id ++ if alarm.project_id != wtypes.Unset ++ else None) + for id in alarm.combination_rule.alarm_ids: + alarms = list(pecan.request.storage_conn.get_alarms( +- alarm_id=id, project=auth_project)) ++ alarm_id=id, project=project)) + if not alarms: + error = _("Alarm %s doesn't exist") % id + pecan.response.translatable_error = error + raise wsme.exc.ClientSideError(unicode(error)) + +- if alarm.threshold_rule and alarm.combination_rule: +- error = _("threshold_rule and combination_rule " +- "cannot be set at the same time") +- pecan.response.translatable_error = error +- raise wsme.exc.ClientSideError(unicode(error)) + return alarm + + @classmethod +@@ -1318,7 +1288,6 @@ + """Return this alarm.""" + return Alarm.from_db_model(self._alarm()) + +- @wsme.validate(Alarm) + @wsme_pecan.wsexpose(Alarm, wtypes.text, body=Alarm) + def put(self, data): + """Modify this alarm.""" +@@ -1329,18 +1298,20 @@ + + data.alarm_id = self._id + user, project = acl.get_limited_to(pecan.request.headers) +- data.user_id = user or data.user_id or alarm_in.user_id +- data.project_id = project or data.project_id or alarm_in.project_id ++ if user: ++ data.user_id = user ++ elif data.user_id == wtypes.Unset: ++ data.user_id = alarm_in.user_id ++ if project: ++ data.project_id = project ++ elif data.project_id == wtypes.Unset: ++ data.project_id = alarm_in.project_id + data.timestamp = now + if alarm_in.state != data.state: + data.state_timestamp = now + else: + data.state_timestamp = alarm_in.state_timestamp + +- #note(sileht): workaround for +- #https://bugs.launchpad.net/wsme/+bug/1220678 +- Alarm.validate(data) +- + old_alarm = Alarm.from_db_model(alarm_in).as_dict(storage.models.Alarm) + updated_alarm = data.as_dict(storage.models.Alarm) + try: +@@ -1440,7 +1411,6 @@ + except NotImplementedError: + pass + +- @wsme.validate(Alarm) + @wsme_pecan.wsexpose(Alarm, body=Alarm, status_code=201) + def post(self, data): + """Create a new alarm.""" +@@ -1449,17 +1419,17 @@ + + data.alarm_id = str(uuid.uuid4()) + user, project = acl.get_limited_to(pecan.request.headers) +- data.user_id = (user or data.user_id or +- pecan.request.headers.get('X-User-Id')) +- data.project_id = (project or data.project_id or +- pecan.request.headers.get('X-Project-Id')) ++ if user: ++ data.user_id = user ++ elif data.user_id == wtypes.Unset: ++ data.user_id = pecan.request.headers.get('X-User-Id') ++ if project: ++ data.project_id = project ++ elif data.project_id == wtypes.Unset: ++ data.project_id = pecan.request.headers.get('X-Project-Id') + data.timestamp = now + data.state_timestamp = now + +- #note(sileht): workaround for +- #https://bugs.launchpad.net/wsme/+bug/1220678 +- Alarm.validate(data) +- + change = data.as_dict(storage.models.Alarm) + + # make sure alarms are unique by name per project. +--- a/ceilometer/tests/api.py ++++ b/ceilometer/tests/api.py +@@ -104,6 +104,9 @@ + 'template_path': '%s/ceilometer/api/templates' % root_dir, + 'enable_acl': enable_acl, + }, ++ 'wsme': { ++ 'debug': True, ++ }, + } + + return pecan.testing.load_test_app(self.config) +--- a/tests/api/v2/test_alarm_scenarios.py ++++ b/tests/api/v2/test_alarm_scenarios.py +@@ -278,7 +278,9 @@ + status=400, headers=self.auth_headers) + self.assertEqual( + resp.json['error_message']['faultstring'], +- '%s is mandatory' % field) ++ "Invalid input for field/attribute %s." ++ " Value: \'None\'. Mandatory field missing." ++ % field.split('/', 1)[-1]) + alarms = list(self.conn.get_alarms()) + self.assertEqual(4, len(alarms)) + +--- a/tests/api/v2/test_app.py ++++ b/tests/api/v2/test_app.py +@@ -134,7 +134,8 @@ + # Ensure translated messages get placed properly into json faults + self.stubs.Set(gettextutils, 'get_localized_message', + self._fake_get_localized_message) +- response = self.post_json('/alarms', params={}, ++ response = self.post_json('/alarms', params={'name': 'foobar', ++ 'type': 'threshold'}, + expect_errors=True, + headers={"Accept": + "application/json"} +@@ -169,7 +170,8 @@ + self.stubs.Set(gettextutils, 'get_localized_message', + self._fake_get_localized_message) + +- response = self.post_json('/alarms', params={}, ++ response = self.post_json('/alarms', params={'name': 'foobar', ++ 'type': 'threshold'}, + expect_errors=True, + headers={"Accept": + "application/xml,*/*"} +@@ -186,7 +188,8 @@ + self.stubs.Set(gettextutils, 'get_localized_message', + self._fake_get_localized_message) + +- response = self.post_json('/alarms', params={}, ++ response = self.post_json('/alarms', params={'name': 'foobar', ++ 'type': 'threshold'}, + expect_errors=True, + headers={"Accept": + "application/xml,*/*", +--- a/tests/api/v2/test_post_samples_scenarios.py ++++ b/tests/api/v2/test_post_samples_scenarios.py +@@ -198,7 +198,7 @@ + s_broke = copy.copy(s1) + del s_broke[0][m] + print('posting without %s' % m) +- data = self.post_json('/meters/my_counter_name/', s_broke, ++ data = self.post_json('/meters/my_counter_name', s_broke, + expect_errors=True) + self.assertEqual(data.status_int, 400) + +--- a/requirements.txt ++++ b/requirements.txt +@@ -22,7 +22,7 @@ + lxml>=2.3 + requests>=1.1 + six>=1.4.1 +-WSME>=0.5b5,<0.5b6 ++WSME>=0.5b6 + PyYAML>=3.1.0 + oslo.config>=1.2.0 + happybase>=0.4 diff --git a/debian/patches/series b/debian/patches/series index 31b8fb8..dc4e9d0 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1 +1,2 @@ removes-sqlalchemy-restriction.patch +WSME-0.5b6-compatibility.patch -- 1.8.5.1