Implements complex query functionality for alarm history
New API resource /query/alarms/history has been added. Implements: blueprint complex-filter-expressions-in-api-queries Change-Id: I8a98b4edc2dc7afac9261bc0cd83f69d7cadc4b9
This commit is contained in:
		@@ -2052,9 +2052,29 @@ class QuerySamplesController(rest.RestController):
 | 
				
			|||||||
                                            query.limit)]
 | 
					                                            query.limit)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class QueryAlarmHistoryController(rest.RestController):
 | 
				
			||||||
 | 
					    """Provides complex query possibilites for alarm history
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    @wsme_pecan.wsexpose([AlarmChange], body=ComplexQuery)
 | 
				
			||||||
 | 
					    def post(self, body):
 | 
				
			||||||
 | 
					        """Define query for retrieving AlarmChange data.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param body: Query rules for the alarm history to be returned.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        query = ValidatedComplexQuery(body)
 | 
				
			||||||
 | 
					        query.validate(visibility_field="on_behalf_of")
 | 
				
			||||||
 | 
					        conn = pecan.request.storage_conn
 | 
				
			||||||
 | 
					        return [AlarmChange.from_db_model(s)
 | 
				
			||||||
 | 
					                for s in conn.query_alarm_history(query.filter_expr,
 | 
				
			||||||
 | 
					                                                  query.orderby,
 | 
				
			||||||
 | 
					                                                  query.limit)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class QueryAlarmsController(rest.RestController):
 | 
					class QueryAlarmsController(rest.RestController):
 | 
				
			||||||
    """Provides complex query possibilities for alarms
 | 
					    """Provides complex query possibilities for alarms
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					    history = QueryAlarmHistoryController()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @wsme_pecan.wsexpose([Alarm], body=ComplexQuery)
 | 
					    @wsme_pecan.wsexpose([Alarm], body=ComplexQuery)
 | 
				
			||||||
    def post(self, body):
 | 
					    def post(self, body):
 | 
				
			||||||
        """Define query for retrieving Alarm data.
 | 
					        """Define query for retrieving Alarm data.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -343,3 +343,15 @@ class Connection(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        raise NotImplementedError(_('Complex query for alarms \
 | 
					        raise NotImplementedError(_('Complex query for alarms \
 | 
				
			||||||
            is not implemented.'))
 | 
					            is not implemented.'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def query_alarm_history(filter_expr=None, orderby=None, limit=None):
 | 
				
			||||||
 | 
					        """Return an iterable of model.AlarmChange objects.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param filter_expr: Filter expression for query.
 | 
				
			||||||
 | 
					        :param orderby: List of field name and direction pairs for order by.
 | 
				
			||||||
 | 
					        :param limit: Maximum number of results to return.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        raise NotImplementedError(_('Complex query for alarms \
 | 
				
			||||||
 | 
					            history is not implemented.'))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -791,7 +791,8 @@ class Connection(base.Connection):
 | 
				
			|||||||
                filter_expr)
 | 
					                filter_expr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        retrieve = {models.Meter: self._retrieve_samples,
 | 
					        retrieve = {models.Meter: self._retrieve_samples,
 | 
				
			||||||
                    models.Alarm: self._retrieve_alarms}
 | 
					                    models.Alarm: self._retrieve_alarms,
 | 
				
			||||||
 | 
					                    models.AlarmChange: self._retrieve_alarm_changes}
 | 
				
			||||||
        return retrieve[model](query_filter, orderby_filter, limit)
 | 
					        return retrieve[model](query_filter, orderby_filter, limit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def query_samples(self, filter_expr=None, orderby=None, limit=None):
 | 
					    def query_samples(self, filter_expr=None, orderby=None, limit=None):
 | 
				
			||||||
@@ -1014,6 +1015,21 @@ class Connection(base.Connection):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        self.db.alarm.remove({'alarm_id': alarm_id})
 | 
					        self.db.alarm.remove({'alarm_id': alarm_id})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _retrieve_alarm_changes(self, query_filter, orderby, limit):
 | 
				
			||||||
 | 
					        if limit is not None:
 | 
				
			||||||
 | 
					            alarms_history = self.db.alarm_history.find(query_filter,
 | 
				
			||||||
 | 
					                                                        limit=limit,
 | 
				
			||||||
 | 
					                                                        sort=orderby)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            alarms_history = self.db.alarm_history.find(
 | 
				
			||||||
 | 
					                query_filter, sort=orderby)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for alarm_history in alarms_history:
 | 
				
			||||||
 | 
					            ah = {}
 | 
				
			||||||
 | 
					            ah.update(alarm_history)
 | 
				
			||||||
 | 
					            del ah['_id']
 | 
				
			||||||
 | 
					            yield models.AlarmChange(**ah)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_alarm_changes(self, alarm_id, on_behalf_of,
 | 
					    def get_alarm_changes(self, alarm_id, on_behalf_of,
 | 
				
			||||||
                          user=None, project=None, type=None,
 | 
					                          user=None, project=None, type=None,
 | 
				
			||||||
                          start_timestamp=None, start_timestamp_op=None,
 | 
					                          start_timestamp=None, start_timestamp_op=None,
 | 
				
			||||||
@@ -1057,12 +1073,10 @@ class Connection(base.Connection):
 | 
				
			|||||||
            if ts_range:
 | 
					            if ts_range:
 | 
				
			||||||
                q['timestamp'] = ts_range
 | 
					                q['timestamp'] = ts_range
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sort = [("timestamp", pymongo.DESCENDING)]
 | 
					        return self._retrieve_alarm_changes(q,
 | 
				
			||||||
        for alarm_change in self.db.alarm_history.find(q, sort=sort):
 | 
					                                            [("timestamp",
 | 
				
			||||||
            ac = {}
 | 
					                                              pymongo.DESCENDING)],
 | 
				
			||||||
            ac.update(alarm_change)
 | 
					                                            None)
 | 
				
			||||||
            del ac['_id']
 | 
					 | 
				
			||||||
            yield models.AlarmChange(**ac)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def record_alarm_change(self, alarm_change):
 | 
					    def record_alarm_change(self, alarm_change):
 | 
				
			||||||
        """Record alarm change event.
 | 
					        """Record alarm change event.
 | 
				
			||||||
@@ -1073,3 +1087,11 @@ class Connection(base.Connection):
 | 
				
			|||||||
        """Return an iterable of model.Alarm objects.
 | 
					        """Return an iterable of model.Alarm objects.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return self._retrieve_data(filter_expr, orderby, limit, models.Alarm)
 | 
					        return self._retrieve_data(filter_expr, orderby, limit, models.Alarm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def query_alarm_history(self, filter_expr=None, orderby=None, limit=None):
 | 
				
			||||||
 | 
					        """Return an iterable of model.AlarmChange objects.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return self._retrieve_data(filter_expr,
 | 
				
			||||||
 | 
					                                   orderby,
 | 
				
			||||||
 | 
					                                   limit,
 | 
				
			||||||
 | 
					                                   models.AlarmChange)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -602,7 +602,8 @@ class Connection(base.Connection):
 | 
				
			|||||||
                                    table)
 | 
					                                    table)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        retrieve = {models.Meter: self._retrieve_samples,
 | 
					        retrieve = {models.Meter: self._retrieve_samples,
 | 
				
			||||||
                    models.Alarm: self._retrieve_alarms}
 | 
					                    models.Alarm: self._retrieve_alarms,
 | 
				
			||||||
 | 
					                    models.AlarmChange: self._retrieve_alarm_history}
 | 
				
			||||||
        return retrieve[table](query)
 | 
					        return retrieve[table](query)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def query_samples(self, filter_expr=None, orderby=None, limit=None):
 | 
					    def query_samples(self, filter_expr=None, orderby=None, limit=None):
 | 
				
			||||||
@@ -840,6 +841,17 @@ class Connection(base.Connection):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        return self._retrieve_data(filter_expr, orderby, limit, models.Alarm)
 | 
					        return self._retrieve_data(filter_expr, orderby, limit, models.Alarm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _retrieve_alarm_history(self, query):
 | 
				
			||||||
 | 
					        return (self._row_to_alarm_change_model(x) for x in query.all())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def query_alarm_history(self, filter_expr=None, orderby=None, limit=None):
 | 
				
			||||||
 | 
					        """Return an iterable of model.AlarmChange objects.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return self._retrieve_data(filter_expr,
 | 
				
			||||||
 | 
					                                   orderby,
 | 
				
			||||||
 | 
					                                   limit,
 | 
				
			||||||
 | 
					                                   models.AlarmChange)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_alarm_changes(self, alarm_id, on_behalf_of,
 | 
					    def get_alarm_changes(self, alarm_id, on_behalf_of,
 | 
				
			||||||
                          user=None, project=None, type=None,
 | 
					                          user=None, project=None, type=None,
 | 
				
			||||||
                          start_timestamp=None, start_timestamp_op=None,
 | 
					                          start_timestamp=None, start_timestamp_op=None,
 | 
				
			||||||
@@ -896,7 +908,7 @@ class Connection(base.Connection):
 | 
				
			|||||||
                    models.AlarmChange.timestamp < end_timestamp)
 | 
					                    models.AlarmChange.timestamp < end_timestamp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        query = query.order_by(desc(models.AlarmChange.timestamp))
 | 
					        query = query.order_by(desc(models.AlarmChange.timestamp))
 | 
				
			||||||
        return (self._row_to_alarm_change_model(x) for x in query.all())
 | 
					        return self._retrieve_alarm_history(query)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def record_alarm_change(self, alarm_change):
 | 
					    def record_alarm_change(self, alarm_change):
 | 
				
			||||||
        """Record alarm change event.
 | 
					        """Record alarm change event.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -340,3 +340,108 @@ class TestQueryAlarmsController(tests_api.FunctionalTest,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(400, data.status_int)
 | 
					        self.assertEqual(400, data.status_int)
 | 
				
			||||||
        self.assertIn("Limit should be positive", data.body)
 | 
					        self.assertIn("Limit should be positive", data.body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestQueryAlarmsHistoryController(
 | 
				
			||||||
 | 
					        tests_api.FunctionalTest, tests_db.MixinTestsWithBackendScenarios):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        super(TestQueryAlarmsHistoryController, self).setUp()
 | 
				
			||||||
 | 
					        self.url = '/query/alarms/history'
 | 
				
			||||||
 | 
					        for id in [1, 2]:
 | 
				
			||||||
 | 
					            for type in ["creation", "state transition"]:
 | 
				
			||||||
 | 
					                for date in [datetime.datetime(2013, 1, 1),
 | 
				
			||||||
 | 
					                             datetime.datetime(2013, 2, 2)]:
 | 
				
			||||||
 | 
					                    event_id = "-".join([str(id), type, date.isoformat()])
 | 
				
			||||||
 | 
					                    alarm_change = {"event_id": event_id,
 | 
				
			||||||
 | 
					                                    "alarm_id": "alarm-id%d" % id,
 | 
				
			||||||
 | 
					                                    "type": type,
 | 
				
			||||||
 | 
					                                    "detail": "",
 | 
				
			||||||
 | 
					                                    "user_id": "user-id%d" % id,
 | 
				
			||||||
 | 
					                                    "project_id": "project-id%d" % id,
 | 
				
			||||||
 | 
					                                    "on_behalf_of": "project-id%d" % id,
 | 
				
			||||||
 | 
					                                    "timestamp": date}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    self.conn.record_alarm_change(alarm_change)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_query_all(self):
 | 
				
			||||||
 | 
					        data = self.post_json(self.url,
 | 
				
			||||||
 | 
					                              params={})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(8, len(data.json))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_filter_with_isotime(self):
 | 
				
			||||||
 | 
					        date_time = datetime.datetime(2013, 1, 1)
 | 
				
			||||||
 | 
					        isotime = date_time.isoformat()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = self.post_json(self.url,
 | 
				
			||||||
 | 
					                              params={"filter":
 | 
				
			||||||
 | 
					                                      '{">": {"timestamp":"'
 | 
				
			||||||
 | 
					                                      + isotime + '"}}'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(4, len(data.json))
 | 
				
			||||||
 | 
					        for history in data.json:
 | 
				
			||||||
 | 
					            result_time = timeutils.parse_isotime(history['timestamp'])
 | 
				
			||||||
 | 
					            result_time = result_time.replace(tzinfo=None)
 | 
				
			||||||
 | 
					            self.assertTrue(result_time > date_time)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_non_admin_tenant_sees_only_its_own_project(self):
 | 
				
			||||||
 | 
					        data = self.post_json(self.url,
 | 
				
			||||||
 | 
					                              params={},
 | 
				
			||||||
 | 
					                              headers=non_admin_header)
 | 
				
			||||||
 | 
					        for history in data.json:
 | 
				
			||||||
 | 
					            self.assertEqual("project-id1", history['on_behalf_of'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_non_admin_tenant_cannot_query_others_project(self):
 | 
				
			||||||
 | 
					        data = self.post_json(self.url,
 | 
				
			||||||
 | 
					                              params={"filter":
 | 
				
			||||||
 | 
					                                      '{"=": {"on_behalf_of":'
 | 
				
			||||||
 | 
					                                      + ' "project-id2"}}'},
 | 
				
			||||||
 | 
					                              expect_errors=True,
 | 
				
			||||||
 | 
					                              headers=non_admin_header)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(401, data.status_int)
 | 
				
			||||||
 | 
					        self.assertIn("Not Authorized to access project project-id2",
 | 
				
			||||||
 | 
					                      data.body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_non_admin_tenant_can_explicitly_filter_for_own_project(self):
 | 
				
			||||||
 | 
					        data = self.post_json(self.url,
 | 
				
			||||||
 | 
					                              params={"filter":
 | 
				
			||||||
 | 
					                                      '{"=": {"on_behalf_of":'
 | 
				
			||||||
 | 
					                                      + ' "project-id1"}}'},
 | 
				
			||||||
 | 
					                              headers=non_admin_header)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for history in data.json:
 | 
				
			||||||
 | 
					            self.assertEqual("project-id1", history['on_behalf_of'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_admin_tenant_sees_every_project(self):
 | 
				
			||||||
 | 
					        data = self.post_json(self.url,
 | 
				
			||||||
 | 
					                              params={},
 | 
				
			||||||
 | 
					                              headers=admin_header)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(8, len(data.json))
 | 
				
			||||||
 | 
					        for history in data.json:
 | 
				
			||||||
 | 
					            self.assertIn(history['on_behalf_of'],
 | 
				
			||||||
 | 
					                          (["project-id1", "project-id2"]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_query_with_filter_orderby_and_limit(self):
 | 
				
			||||||
 | 
					        data = self.post_json(self.url,
 | 
				
			||||||
 | 
					                              params={"filter": '{"=": {"type": "creation"}}',
 | 
				
			||||||
 | 
					                                      "orderby": '[{"timestamp": "DESC"}]',
 | 
				
			||||||
 | 
					                                      "limit": 3})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(3, len(data.json))
 | 
				
			||||||
 | 
					        self.assertEqual(["2013-02-02T00:00:00",
 | 
				
			||||||
 | 
					                          "2013-02-02T00:00:00",
 | 
				
			||||||
 | 
					                          "2013-01-01T00:00:00"],
 | 
				
			||||||
 | 
					                         [h["timestamp"] for h in data.json])
 | 
				
			||||||
 | 
					        for history in data.json:
 | 
				
			||||||
 | 
					            self.assertEqual("creation", history["type"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_limit_should_be_positive(self):
 | 
				
			||||||
 | 
					        data = self.post_json(self.url,
 | 
				
			||||||
 | 
					                              params={"limit": 0},
 | 
				
			||||||
 | 
					                              expect_errors=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(400, data.status_int)
 | 
				
			||||||
 | 
					        self.assertIn("Limit should be positive", data.body)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2331,6 +2331,131 @@ class ComplexAlarmQueryTest(AlarmTestBase,
 | 
				
			|||||||
            self.assertTrue(a.enabled)
 | 
					            self.assertTrue(a.enabled)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ComplexAlarmHistoryQueryTest(AlarmTestBase,
 | 
				
			||||||
 | 
					                                   tests_db.MixinTestsWithBackendScenarios):
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        super(DBTestBase, self).setUp()
 | 
				
			||||||
 | 
					        self.filter_expr = {"and":
 | 
				
			||||||
 | 
					                            [{"or":
 | 
				
			||||||
 | 
					                              [{"=": {"type": "rule change"}},
 | 
				
			||||||
 | 
					                               {"=": {"type": "state transition"}}]},
 | 
				
			||||||
 | 
					                             {"=": {"alarm_id": "0r4ng3"}}]}
 | 
				
			||||||
 | 
					        self.add_some_alarms()
 | 
				
			||||||
 | 
					        self.prepare_alarm_history()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def prepare_alarm_history(self):
 | 
				
			||||||
 | 
					        alarms = list(self.conn.get_alarms())
 | 
				
			||||||
 | 
					        for alarm in alarms:
 | 
				
			||||||
 | 
					            i = alarms.index(alarm)
 | 
				
			||||||
 | 
					            alarm_change = dict(event_id=
 | 
				
			||||||
 | 
					                                "16fd2706-8baf-433b-82eb-8c7fada847c%s" % i,
 | 
				
			||||||
 | 
					                                alarm_id=alarm.alarm_id,
 | 
				
			||||||
 | 
					                                type=models.AlarmChange.CREATION,
 | 
				
			||||||
 | 
					                                detail="detail %s" % alarm.name,
 | 
				
			||||||
 | 
					                                user_id=alarm.user_id,
 | 
				
			||||||
 | 
					                                project_id=alarm.project_id,
 | 
				
			||||||
 | 
					                                on_behalf_of=alarm.project_id,
 | 
				
			||||||
 | 
					                                timestamp=datetime.datetime(2012, 9, 24,
 | 
				
			||||||
 | 
					                                                            7 + i,
 | 
				
			||||||
 | 
					                                                            30 + i))
 | 
				
			||||||
 | 
					            self.conn.record_alarm_change(alarm_change=alarm_change)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            alarm_change2 = dict(event_id=
 | 
				
			||||||
 | 
					                                 "16fd2706-8baf-433b-82eb-8c7fada847d%s" % i,
 | 
				
			||||||
 | 
					                                 alarm_id=alarm.alarm_id,
 | 
				
			||||||
 | 
					                                 type=models.AlarmChange.RULE_CHANGE,
 | 
				
			||||||
 | 
					                                 detail="detail %s" % i,
 | 
				
			||||||
 | 
					                                 user_id=alarm.user_id,
 | 
				
			||||||
 | 
					                                 project_id=alarm.project_id,
 | 
				
			||||||
 | 
					                                 on_behalf_of=alarm.project_id,
 | 
				
			||||||
 | 
					                                 timestamp=datetime.datetime(2012, 9, 25,
 | 
				
			||||||
 | 
					                                                             10 + i,
 | 
				
			||||||
 | 
					                                                             30 + i))
 | 
				
			||||||
 | 
					            self.conn.record_alarm_change(alarm_change=alarm_change2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            alarm_change3 = dict(event_id=
 | 
				
			||||||
 | 
					                                 "16fd2706-8baf-433b-82eb-8c7fada847e%s"
 | 
				
			||||||
 | 
					                                 % i,
 | 
				
			||||||
 | 
					                                 alarm_id=alarm.alarm_id,
 | 
				
			||||||
 | 
					                                 type=models.AlarmChange.STATE_TRANSITION,
 | 
				
			||||||
 | 
					                                 detail="detail %s" % (i + 1),
 | 
				
			||||||
 | 
					                                 user_id=alarm.user_id,
 | 
				
			||||||
 | 
					                                 project_id=alarm.project_id,
 | 
				
			||||||
 | 
					                                 on_behalf_of=alarm.project_id,
 | 
				
			||||||
 | 
					                                 timestamp=datetime.datetime(2012, 9, 26,
 | 
				
			||||||
 | 
					                                                             10 + i,
 | 
				
			||||||
 | 
					                                                             30 + i))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if alarm.name == "red-alert":
 | 
				
			||||||
 | 
					                alarm_change3['on_behalf_of'] = 'and-da-girls'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.conn.record_alarm_change(alarm_change=alarm_change3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if alarm.name in ["red-alert", "yellow-alert"]:
 | 
				
			||||||
 | 
					                alarm_change4 = dict(event_id=
 | 
				
			||||||
 | 
					                                     "16fd2706-8baf-433b-82eb-8c7fada847f%s"
 | 
				
			||||||
 | 
					                                     % i,
 | 
				
			||||||
 | 
					                                     alarm_id=alarm.alarm_id,
 | 
				
			||||||
 | 
					                                     type=models.AlarmChange.DELETION,
 | 
				
			||||||
 | 
					                                     detail="detail %s" % (i + 2),
 | 
				
			||||||
 | 
					                                     user_id=alarm.user_id,
 | 
				
			||||||
 | 
					                                     project_id=alarm.project_id,
 | 
				
			||||||
 | 
					                                     on_behalf_of=alarm.project_id,
 | 
				
			||||||
 | 
					                                     timestamp=datetime.datetime(2012, 9, 27,
 | 
				
			||||||
 | 
					                                                                 10 + i,
 | 
				
			||||||
 | 
					                                                                 30 + i))
 | 
				
			||||||
 | 
					                self.conn.record_alarm_change(alarm_change=alarm_change4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_alarm_history_with_no_filter(self):
 | 
				
			||||||
 | 
					        history = list(self.conn.query_alarm_history())
 | 
				
			||||||
 | 
					        self.assertEqual(11, len(history))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_alarm_history_with_no_filter_and_limit(self):
 | 
				
			||||||
 | 
					        history = list(self.conn.query_alarm_history(limit=3))
 | 
				
			||||||
 | 
					        self.assertEqual(3, len(history))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_alarm_history_with_filter(self):
 | 
				
			||||||
 | 
					        history = list(
 | 
				
			||||||
 | 
					            self.conn.query_alarm_history(filter_expr=self.filter_expr))
 | 
				
			||||||
 | 
					        self.assertEqual(2, len(history))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_alarm_history_with_filter_and_orderby(self):
 | 
				
			||||||
 | 
					        history = list(
 | 
				
			||||||
 | 
					            self.conn.query_alarm_history(filter_expr=self.filter_expr,
 | 
				
			||||||
 | 
					                                          orderby=[{"timestamp":
 | 
				
			||||||
 | 
					                                                   "asc"}]))
 | 
				
			||||||
 | 
					        self.assertEqual([models.AlarmChange.RULE_CHANGE,
 | 
				
			||||||
 | 
					                          models.AlarmChange.STATE_TRANSITION],
 | 
				
			||||||
 | 
					                         [h.type for h in history])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_alarm_history_with_filter_and_orderby_and_limit(self):
 | 
				
			||||||
 | 
					        history = list(
 | 
				
			||||||
 | 
					            self.conn.query_alarm_history(filter_expr=self.filter_expr,
 | 
				
			||||||
 | 
					                                          orderby=[{"timestamp":
 | 
				
			||||||
 | 
					                                                    "asc"}],
 | 
				
			||||||
 | 
					                                          limit=1))
 | 
				
			||||||
 | 
					        self.assertEqual(models.AlarmChange.RULE_CHANGE, history[0].type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_alarm_history_with_on_behalf_of_filter(self):
 | 
				
			||||||
 | 
					        filter_expr = {"=": {"on_behalf_of": "and-da-girls"}}
 | 
				
			||||||
 | 
					        history = list(self.conn.query_alarm_history(filter_expr=filter_expr))
 | 
				
			||||||
 | 
					        self.assertEqual(1, len(history))
 | 
				
			||||||
 | 
					        self.assertEqual("16fd2706-8baf-433b-82eb-8c7fada847e0",
 | 
				
			||||||
 | 
					                         history[0].event_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_alarm_history_with_alarm_id_as_filter(self):
 | 
				
			||||||
 | 
					        filter_expr = {"=": {"alarm_id": "r3d"}}
 | 
				
			||||||
 | 
					        history = list(self.conn.query_alarm_history(filter_expr=filter_expr,
 | 
				
			||||||
 | 
					                                                     orderby=[{"timestamp":
 | 
				
			||||||
 | 
					                                                               "asc"}]))
 | 
				
			||||||
 | 
					        self.assertEqual(4, len(history))
 | 
				
			||||||
 | 
					        self.assertEqual([models.AlarmChange.CREATION,
 | 
				
			||||||
 | 
					                          models.AlarmChange.RULE_CHANGE,
 | 
				
			||||||
 | 
					                          models.AlarmChange.STATE_TRANSITION,
 | 
				
			||||||
 | 
					                          models.AlarmChange.DELETION],
 | 
				
			||||||
 | 
					                         [h.type for h in history])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EventTestBase(tests_db.TestBase,
 | 
					class EventTestBase(tests_db.TestBase,
 | 
				
			||||||
                    tests_db.MixinTestsWithBackendScenarios):
 | 
					                    tests_db.MixinTestsWithBackendScenarios):
 | 
				
			||||||
    """Separate test base class because we don't want to
 | 
					    """Separate test base class because we don't want to
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,9 +94,9 @@ field of *Sample*).
 | 
				
			|||||||
Complex Query
 | 
					Complex Query
 | 
				
			||||||
+++++++++++++
 | 
					+++++++++++++
 | 
				
			||||||
The filter expressions of the Complex Query feature operate on the fields
 | 
					The filter expressions of the Complex Query feature operate on the fields
 | 
				
			||||||
of *Sample* and *Alarm*. The following comparison operators are supported: *=*,
 | 
					of *Sample*, *Alarm* and *AlarmChange*. The following comparison operators are
 | 
				
			||||||
*!=*, *<*, *<=*, *>* and *>=*; and the following logical operators can be
 | 
					supported: *=*, *!=*, *<*, *<=*, *>* and *>=*; and the following logical operators
 | 
				
			||||||
used: *and* and *or*.
 | 
					can be used: *and* and *or*.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Complex Query supports defining the list of orderby expressions in the form
 | 
					Complex Query supports defining the list of orderby expressions in the form
 | 
				
			||||||
of [{"field_name": "asc"}, {"field_name2": "desc"}, ...].
 | 
					of [{"field_name": "asc"}, {"field_name2": "desc"}, ...].
 | 
				
			||||||
@@ -111,6 +111,9 @@ The *filter*, *orderby* and *limit* are all optional fields in a query.
 | 
				
			|||||||
.. rest-controller:: ceilometer.api.controllers.v2:QueryAlarmsController
 | 
					.. rest-controller:: ceilometer.api.controllers.v2:QueryAlarmsController
 | 
				
			||||||
   :webprefix: /v2/query/alarms
 | 
					   :webprefix: /v2/query/alarms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. rest-controller:: ceilometer.api.controllers.v2:QueryAlarmHistoryController
 | 
				
			||||||
 | 
					   :webprefix: /v2/query/alarms/history
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. autotype:: ceilometer.api.controllers.v2.ComplexQuery
 | 
					.. autotype:: ceilometer.api.controllers.v2.ComplexQuery
 | 
				
			||||||
   :members:
 | 
					   :members:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user