diff --git a/aodh/storage/base.py b/aodh/storage/base.py index 477d4f3a..2d3839b9 100644 --- a/aodh/storage/base.py +++ b/aodh/storage/base.py @@ -75,7 +75,7 @@ class Connection(object): @staticmethod def get_alarms(name=None, user=None, state=None, meter=None, project=None, enabled=None, alarm_id=None, - alarm_type=None, severity=None): + alarm_type=None, severity=None, exclude=None): """Yields a lists of alarms that match filters. :param name: Optional name for alarm. @@ -86,7 +86,8 @@ class Connection(object): :param enabled: Optional boolean to list disable alarm. :param alarm_id: Optional alarm_id to return one alarm. :param alarm_type: Optional alarm type. - :parmr severity: Optional alarm severity + :param severity: Optional alarm severity. + :param exclude: Optional dict for inequality constraint. """ raise aodh.NotImplementedError('Alarms not implemented') diff --git a/aodh/storage/hbase/inmemory.py b/aodh/storage/hbase/inmemory.py index 9d4293c2..47477c9b 100644 --- a/aodh/storage/hbase/inmemory.py +++ b/aodh/storage/hbase/inmemory.py @@ -187,6 +187,8 @@ class MTable(object): """ op = args[0] value = args[1] + if value.startswith('binary:'): + value = value[len('binary:'):] if value.startswith('regexstring:'): value = value[len('regexstring:'):] r = {} diff --git a/aodh/storage/hbase/utils.py b/aodh/storage/hbase/utils.py index 85a34f2a..9b01923a 100644 --- a/aodh/storage/hbase/utils.py +++ b/aodh/storage/hbase/utils.py @@ -117,6 +117,11 @@ def make_query(**kwargs): q.append("ColumnPrefixFilter('%s')" % value) elif key == 'event_id': q.append("RowFilter ( = , 'regexstring:\d*:%s')" % value) + elif key == 'exclude': + for k, v in six.iteritems(value): + q.append("SingleColumnValueFilter " + "('f', '%(k)s', !=, 'binary:%(v)s', true, true)" % + {'k': quote(k), 'v': dump(v)}) else: q.append("SingleColumnValueFilter " "('f', '%s', =, 'binary:%s', true, true)" % diff --git a/aodh/storage/impl_hbase.py b/aodh/storage/impl_hbase.py index 5cdcb028..226ba59f 100644 --- a/aodh/storage/impl_hbase.py +++ b/aodh/storage/impl_hbase.py @@ -122,7 +122,7 @@ class Connection(hbase_base.Connection, base.Connection): def get_alarms(self, name=None, user=None, state=None, meter=None, project=None, enabled=None, alarm_id=None, - alarm_type=None, severity=None): + alarm_type=None, severity=None, exclude=None): if meter: raise aodh.NotImplementedError( @@ -131,7 +131,8 @@ class Connection(hbase_base.Connection, base.Connection): q = hbase_utils.make_query(alarm_id=alarm_id, name=name, enabled=enabled, user_id=user, project_id=project, state=state, - type=alarm_type, severity=severity) + type=alarm_type, severity=severity, + exclude=exclude) with self.conn_pool.connection() as conn: alarm_table = conn.table(self.ALARM_TABLE) diff --git a/aodh/storage/impl_log.py b/aodh/storage/impl_log.py index 1f1ed431..c53afc75 100644 --- a/aodh/storage/impl_log.py +++ b/aodh/storage/impl_log.py @@ -37,7 +37,7 @@ class Connection(base.Connection): @staticmethod def get_alarms(name=None, user=None, state=None, meter=None, project=None, enabled=None, alarm_id=None, - alarm_type=None, severity=None): + alarm_type=None, severity=None, exclude=None): """Yields a lists of alarms that match filters.""" return [] diff --git a/aodh/storage/impl_sqlalchemy.py b/aodh/storage/impl_sqlalchemy.py index 3ab7e3fa..aa1998ee 100644 --- a/aodh/storage/impl_sqlalchemy.py +++ b/aodh/storage/impl_sqlalchemy.py @@ -22,6 +22,7 @@ from alembic import migration from oslo_db.sqlalchemy import session as db_session from oslo_log import log from oslo_utils import timeutils +import six from sqlalchemy import desc from aodh.i18n import _LI @@ -141,7 +142,7 @@ class Connection(base.Connection): def get_alarms(self, name=None, user=None, state=None, meter=None, project=None, enabled=None, alarm_id=None, - alarm_type=None, severity=None): + alarm_type=None, severity=None, exclude=None): """Yields a lists of alarms that match filters. :param name: Optional name for alarm. @@ -152,7 +153,8 @@ class Connection(base.Connection): :param enabled: Optional boolean to list disable alarm. :param alarm_id: Optional alarm_id to return one alarm. :param alarm_type: Optional alarm type. - :param severity: Optional alarm severity + :param severity: Optional alarm severity. + :param exclude: Optional dict for inequality constraint. """ session = self._engine_facade.get_session() @@ -173,6 +175,9 @@ class Connection(base.Connection): query = query.filter(models.Alarm.type == alarm_type) if severity is not None: query = query.filter(models.Alarm.severity == severity) + if exclude is not None: + for key, value in six.iteritems(exclude): + query = query.filter(getattr(models.Alarm, key) != value) query = query.order_by(desc(models.Alarm.timestamp)) alarms = self._retrieve_alarms(query) diff --git a/aodh/storage/pymongo_base.py b/aodh/storage/pymongo_base.py index f172434e..5fc7fbf6 100644 --- a/aodh/storage/pymongo_base.py +++ b/aodh/storage/pymongo_base.py @@ -20,6 +20,7 @@ from oslo_log import log import pymongo +import six from aodh.storage import base from aodh.storage import models @@ -87,7 +88,7 @@ class Connection(base.Connection): def get_alarms(self, name=None, user=None, state=None, meter=None, project=None, enabled=None, alarm_id=None, - alarm_type=None, severity=None): + alarm_type=None, severity=None, exclude=None): """Yields a lists of alarms that match filters. :param name: Optional name for alarm. @@ -99,6 +100,7 @@ class Connection(base.Connection): :param alarm_id: Optional alarm_id to return one alarm. :param alarm_type: Optional alarm type. :param severity: Optional alarm severity. + :param exclude: Optional dict for inequality constraint. """ q = {} if user is not None: @@ -119,6 +121,9 @@ class Connection(base.Connection): q['type'] = alarm_type if severity is not None: q['severity'] = severity + if exclude is not None: + for key, value in six.iteritems(exclude): + q[key] = {'$ne': value} return self._retrieve_alarms(q, [("timestamp", diff --git a/aodh/tests/api/v2/test_query.py b/aodh/tests/api/v2/test_query.py index 386b2d9c..9472857a 100644 --- a/aodh/tests/api/v2/test_query.py +++ b/aodh/tests/api/v2/test_query.py @@ -325,7 +325,7 @@ class TestQueryToKwArgs(tests_base.BaseTestCase): wsme.exc.UnknownArgument, utils.query_to_kwargs, q, alarm_storage_base.Connection.get_alarms) - valid_keys = ['alarm_id', 'enabled', 'meter', 'name', + valid_keys = ['alarm_id', 'enabled', 'exclude', 'meter', 'name', 'project', 'severity', 'state', 'type', 'user'] msg = ("unrecognized field in query: %s, " "valid keys: %s") % (q, valid_keys) diff --git a/aodh/tests/storage/test_storage_scenarios.py b/aodh/tests/storage/test_storage_scenarios.py index 40419ac8..01d169eb 100644 --- a/aodh/tests/storage/test_storage_scenarios.py +++ b/aodh/tests/storage/test_storage_scenarios.py @@ -177,6 +177,14 @@ class AlarmTest(AlarmTestBase, alarms = list(self.alarm_conn.get_alarms(alarm_type='combination')) self.assertEqual(0, len(alarms)) + def test_list_excluded_by_name(self): + self.add_some_alarms() + exclude = {'name': 'yellow-alert'} + alarms = list(self.alarm_conn.get_alarms(exclude=exclude)) + self.assertEqual(2, len(alarms)) + alarm_names = sorted([a.name for a in alarms]) + self.assertEqual(['orange-alert', 'red-alert'], alarm_names) + def test_add(self): self.add_some_alarms() alarms = list(self.alarm_conn.get_alarms())