# # Copyright 2013 Red Hat, Inc # # 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 warnings from ceilometerclient.common import base from ceilometerclient.common import utils from ceilometerclient import exc from ceilometerclient.v2 import options UPDATABLE_ATTRIBUTES = [ 'name', 'description', 'type', 'state', 'severity', 'enabled', 'alarm_actions', 'ok_actions', 'insufficient_data_actions', 'repeat_actions', 'project_id', 'user_id' ] CREATION_ATTRIBUTES = UPDATABLE_ATTRIBUTES + ['time_constraints'] class Alarm(base.Resource): def __repr__(self): return "" % self._info def __getattr__(self, k): # Alias to have the Alarm client object # that look like the Alarm storage object if k == 'rule': k = '%s_rule' % self.type if k == 'id': return self.alarm_id return super(Alarm, self).__getattr__(k) def delete(self): return self.manager.delete(self.alarm_id) def get_state(self): state = self.manager.get_state(self.alarm_id) return state.get('alarm') class AlarmChange(base.Resource): def __repr__(self): return "" % self._info def __getattr__(self, k): return super(AlarmChange, self).__getattr__(k) class AlarmManager(base.Manager): resource_class = Alarm def __init__(self, api, aodh_enabled=False): self.aodh_enabled = aodh_enabled super(AlarmManager, self).__init__(api) def _path(self, id=None): # TODO(liusheng) if aodh will only have v1 api, we need to change # following path if aodh enabled (self.aodh_enabled) return '/v2/alarms/%s' % id if id else '/v2/alarms' def list(self, q=None): return self._list(options.build_url(self._path(), q)) def get(self, alarm_id): try: return self._list(self._path(alarm_id), expect_single=True)[0] except IndexError: return None except exc.HTTPNotFound: # When we try to get a deleted alarm, or # when an alarm doesn't exist, HTTPNotFound exception occurs. # Since scenario tests at the time of cleanUp() will not know # how to handle it, we only return None. return None @classmethod def _compat_legacy_alarm_kwargs(cls, kwargs, create=False): cls._compat_counter_rename_kwargs(kwargs, create) cls._compat_alarm_before_rule_type_kwargs(kwargs, create) @staticmethod def _compat_counter_rename_kwargs(kwargs, create=False): # NOTE(jd) Compatibility with Havana-2 API if 'counter_name' in kwargs: warnings.warn("counter_name has been renamed to meter_name", DeprecationWarning) kwargs['meter_name'] = kwargs['counter_name'] @staticmethod def _compat_alarm_before_rule_type_kwargs(kwargs, create=False): # NOTE(sileht) Compatibility with Havana-3 API if create and 'type' not in kwargs: warnings.warn("alarm without type set is deprecated", DeprecationWarning) kwargs['type'] = 'threshold' for field in ['period', 'evaluation_periods', 'threshold', 'statistic', 'comparison_operator', 'meter_name']: if field in kwargs: kwargs.setdefault('threshold_rule', {})[field] = kwargs[field] del kwargs[field] if 'matching_metadata' in kwargs: query = [] for key in kwargs['matching_metadata']: query.append({'field': key, 'op': 'eq', 'value': kwargs['matching_metadata'][key]}) del kwargs['matching_metadata'] kwargs['threshold_rule']['query'] = query @staticmethod def _merge_time_constraints(existing_tcs, kwargs): new_tcs = kwargs.get('time_constraints', []) if not existing_tcs: updated_tcs = new_tcs else: updated_tcs = [dict(tc) for tc in existing_tcs] for tc in new_tcs: for i, old_tc in enumerate(updated_tcs): # if names match, merge if old_tc['name'] == tc.get('name'): utils.merge_nested_dict(updated_tcs[i], tc) break else: updated_tcs.append(tc) tcs_to_remove = kwargs.get('remove_time_constraints', []) for tc in updated_tcs: if tc.get('name') in tcs_to_remove: updated_tcs.remove(tc) return updated_tcs def create(self, **kwargs): self._compat_legacy_alarm_kwargs(kwargs, create=True) new = dict((key, value) for (key, value) in kwargs.items() if (key in CREATION_ATTRIBUTES or key.endswith('_rule'))) return self._create(self._path(), new) def update(self, alarm_id, **kwargs): self._compat_legacy_alarm_kwargs(kwargs) alarm = self.get(alarm_id) if alarm is None: raise exc.CommandError('Alarm not found: %s' % alarm_id) updated = alarm.to_dict() updated['time_constraints'] = self._merge_time_constraints( updated.get('time_constraints', []), kwargs) kwargs = dict((k, v) for k, v in kwargs.items() if k in updated and (k in UPDATABLE_ATTRIBUTES or k.endswith('_rule'))) utils.merge_nested_dict(updated, kwargs, depth=1) return self._update(self._path(alarm_id), updated) def delete(self, alarm_id): return self._delete(self._path(alarm_id)) def set_state(self, alarm_id, state): body = self.api.put("%s/state" % self._path(alarm_id), json=state).json() return body def get_state(self, alarm_id): body = self.api.get("%s/state" % self._path(alarm_id)).json() return body def get_history(self, alarm_id, q=None): path = '%s/history' % self._path(alarm_id) url = options.build_url(path, q) return self._list(url, obj_class=AlarmChange)