# Copyright 2015 Cray
# Copyright 2016 FUJITSU LIMITED
# Copyright 2017 Hewlett Packard Enterprise Development LP
#
# 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 datetime
import time

import fixtures
from oslo_config import cfg
from oslo_db.sqlalchemy.engines import create_engine
from sqlalchemy import delete, MetaData, insert, bindparam

from monasca_api.common.repositories.sqla import models
from monasca_api.tests import base

CONF = cfg.CONF


class TestAlarmRepoDB(base.BaseTestCase):

    @classmethod
    def setUpClass(cls):
        engine = create_engine('sqlite://')

        qry = open('monasca_api/tests/sqlite_alarm.sql', 'r').read()
        sconn = engine.raw_connection()
        c = sconn.cursor()
        c.executescript(qry)
        sconn.commit()
        c.close()
        cls.engine = engine

        def _fake_engine_from_config(*args, **kw):
            return cls.engine
        cls.fixture = fixtures.MonkeyPatch(
            'sqlalchemy.create_engine', _fake_engine_from_config)
        cls.fixture.setUp()

        metadata = MetaData()

        cls.aa = models.create_aa_model(metadata)
        cls._delete_aa_query = delete(cls.aa)
        cls._insert_aa_query = (insert(cls.aa)
                                .values(
                                    alarm_definition_id=bindparam('alarm_definition_id'),
                                    alarm_state=bindparam('alarm_state'),
                                    action_id=bindparam('action_id')))

        cls.ad = models.create_ad_model(metadata)
        cls._delete_ad_query = delete(cls.ad)
        cls._insert_ad_query = (insert(cls.ad)
                                .values(
                                    id=bindparam('id'),
                                    tenant_id=bindparam('tenant_id'),
                                    name=bindparam('name'),
                                    severity=bindparam('severity'),
                                    expression=bindparam('expression'),
                                    match_by=bindparam('match_by'),
                                    actions_enabled=bindparam('actions_enabled'),
                                    created_at=bindparam('created_at'),
                                    updated_at=bindparam('updated_at'),
                                    deleted_at=bindparam('deleted_at')))
        cls.sad = models.create_sad_model(metadata)
        cls._delete_sad_query = delete(cls.sad)
        cls._insert_sad_query = (insert(cls.sad)
                                 .values(
                                     id=bindparam('id'),
                                     alarm_definition_id=bindparam('alarm_definition_id'),
                                     function=bindparam('function'),
                                     metric_name=bindparam('metric_name'),
                                     operator=bindparam('operator'),
                                     threshold=bindparam('threshold'),
                                     period=bindparam('period'),
                                     periods=bindparam('periods'),
                                     created_at=bindparam('created_at'),
                                     updated_at=bindparam('updated_at')))

        cls.sadd = models.create_sadd_model(metadata)
        cls._delete_sadd_query = delete(cls.sadd)
        cls._insert_sadd_query = (insert(cls.sadd)
                                  .values(
                                      sub_alarm_definition_id=bindparam('sub_alarm_definition_id'),
                                      dimension_name=bindparam('dimension_name'),
                                      value=bindparam('value')))

        cls.nm = models.create_nm_model(metadata)
        cls._delete_nm_query = delete(cls.nm)
        cls._insert_nm_query = (insert(cls.nm)
                                .values(
                                    id=bindparam('id'),
                                    tenant_id=bindparam('tenant_id'),
                                    name=bindparam('name'),
                                    type=bindparam('type'),
                                    address=bindparam('address'),
                                    created_at=bindparam('created_at'),
                                    updated_at=bindparam('updated_at')))

        cls.a = models.create_a_model(metadata)
        cls._delete_a_query = delete(cls.a)
        cls._insert_a_query = (insert(cls.a)
                               .values(
                                   id=bindparam('id'),
                                   alarm_definition_id=bindparam('alarm_definition_id'),
                                   state=bindparam('state'),
                                   lifecycle_state=bindparam('lifecycle_state'),
                                   link=bindparam('link'),
                                   created_at=bindparam('created_at'),
                                   updated_at=bindparam('updated_at'),
                                   state_updated_at=bindparam('state_updated_at')))

        cls.sa = models.create_sa_model(metadata)
        cls._delete_sa_query = delete(cls.sa)
        cls._insert_sa_query = (insert(cls.sa)
                                .values(
                                id=bindparam('id'),
                                    sub_expression_id=bindparam('sub_expression_id'),
                                    alarm_id=bindparam('alarm_id'),
                                    expression=bindparam('expression'),
                                    created_at=bindparam('created_at'),
                                    updated_at=bindparam('updated_at')))

        cls.am = models.create_am_model(metadata)
        cls._delete_am_query = delete(cls.am)
        cls._insert_am_query = (insert(cls.am)
                                .values(
                                    alarm_id=bindparam('alarm_id'),
                                    metric_definition_dimensions_id=bindparam(
                                        'metric_definition_dimensions_id')))

        cls.md = models.create_md_model(metadata)
        cls._delete_md_query = delete(cls.md)
        cls._insert_md_query = (insert(cls.md)
                                .values(
                                    dimension_set_id=bindparam('dimension_set_id'),
                                    name=bindparam('name'),
                                    value=bindparam('value')))

        cls.mdd = models.create_mdd_model(metadata)
        cls._delete_mdd_query = delete(cls.mdd)
        cls._insert_mdd_query = (insert(cls.mdd)
                                 .values(
                                     id=bindparam('id'),
                                     metric_definition_id=bindparam('metric_definition_id'),
                                     metric_dimension_set_id=bindparam('metric_dimension_set_id')))

        cls.mde = models.create_mde_model(metadata)
        cls._delete_mde_query = delete(cls.mde)
        cls._insert_mde_query = (insert(cls.mde)
                                 .values(
                                     id=bindparam('id'),
                                     name=bindparam('name'),
                                     tenant_id=bindparam('tenant_id'),
                                     region=bindparam('region')))

    @classmethod
    def tearDownClass(cls):
        cls.fixture.cleanUp()
        if hasattr(CONF, 'sql_engine'):
            delattr(CONF, 'sql_engine')

    def setUp(self):
        super(TestAlarmRepoDB, self).setUp()

        self.conf_override(connection='sqlite://', group='database')

        from monasca_api.common.repositories.sqla import alarms_repository as ar
        self.repo = ar.AlarmsRepository()

        timestamp1 = datetime.datetime(2015, 3, 14, 9, 26, 53)
        timestamp2 = datetime.datetime(2015, 3, 14, 9, 26, 54)
        timestamp3 = datetime.datetime(2015, 3, 14, 9, 26, 55)
        timestamp4 = datetime.datetime(2015, 3, 15, 9, 26, 53)

        self.default_as = [{'id': '1',
                            'alarm_definition_id': '1',
                            'state': 'OK',
                            'lifecycle_state': 'OPEN',
                            'link': 'http://somesite.com/this-alarm-info',
                            'created_at': timestamp1,
                            'updated_at': timestamp1,
                            'state_updated_at': timestamp1},
                           {'id': '2',
                            'alarm_definition_id': '1',
                            'state': 'UNDETERMINED',
                            'lifecycle_state': 'OPEN',
                            'link': 'http://somesite.com/this-alarm-info',
                            'created_at': timestamp2,
                            'updated_at': timestamp2,
                            'state_updated_at': timestamp2},
                           {'id': '3',
                            'alarm_definition_id': '1',
                            'state': 'ALARM',
                            'lifecycle_state': None,
                            'link': 'http://somesite.com/this-alarm-info',
                            'created_at': timestamp3,
                            'updated_at': timestamp3,
                            'state_updated_at': timestamp3},
                           {'id': '234111',
                            'alarm_definition_id': '234',
                            'state': 'UNDETERMINED',
                            'lifecycle_state': None,
                            'link': None,
                            'created_at': timestamp4,
                            'updated_at': timestamp4,
                            'state_updated_at': timestamp4}]

        self.default_ads = [{'id': '1',
                             'tenant_id': 'bob',
                             'name': '90% CPU',
                             'severity': 'LOW',
                             'expression': 'AVG(cpu.idle_perc{flavor_id=777,'
                             ' image_id=888, device=1}) > 10',
                             'match_by': 'flavor_id,image_id',
                             'actions_enabled': False,
                             'created_at': datetime.datetime.now(),
                             'updated_at': datetime.datetime.now(),
                             'deleted_at': None},
                            {'id': '234',
                             'tenant_id': 'bob',
                             'name': '50% CPU',
                             'severity': 'CRITICAL',
                             'expression': 'AVG(cpu.sys_mem'
                             '{service=monitoring})'
                             ' > 20 and AVG(cpu.idle_perc'
                             '{service=monitoring}) < 10',
                             'match_by': 'hostname,region',
                             'actions_enabled': False,
                             'created_at': datetime.datetime.now(),
                             'updated_at': datetime.datetime.now(),
                             'deleted_at': None}]

        self.default_sadds = [{'sub_alarm_definition_id': '111',
                               'dimension_name': 'flavor_id',
                               'value': '777'},
                              {'sub_alarm_definition_id': '111',
                               'dimension_name': 'image_id',
                               'value': '888'},
                              {'sub_alarm_definition_id': '111',
                               'dimension_name': 'metric_name',
                               'value': 'cpu'},
                              {'sub_alarm_definition_id': '111',
                               'dimension_name': 'device',
                               'value': '1'},
                              {'sub_alarm_definition_id': '222',
                               'dimension_name': 'flavor_id',
                               'value': '777'},
                              {'sub_alarm_definition_id': '222',
                               'dimension_name': 'image_id',
                               'value': '888'},
                              {'sub_alarm_definition_id': '222',
                               'dimension_name': 'metric_name',
                               'value': 'mem'}]

        self.default_nms = [{'id': '29387234',
                             'tenant_id': 'alarm-test',
                             'name': 'MyEmail',
                             'type': 'EMAIL',
                             'address': 'a@b',
                             'created_at': datetime.datetime.now(),
                             'updated_at': datetime.datetime.now()},
                            {'id': '77778687',
                             'tenant_id': 'alarm-test',
                             'name': 'OtherEmail',
                             'type': 'EMAIL',
                             'address': 'a@b',
                             'created_at': datetime.datetime.now(),
                             'updated_at': datetime.datetime.now()}]

        self.default_aas = [{'alarm_definition_id': '123',
                             'alarm_state': 'ALARM',
                             'action_id': '29387234'},
                            {'alarm_definition_id': '123',
                             'alarm_state': 'ALARM',
                             'action_id': '77778687'},
                            {'alarm_definition_id': '234',
                             'alarm_state': 'ALARM',
                             'action_id': '29387234'},
                            {'alarm_definition_id': '234',
                             'alarm_state': 'ALARM',
                             'action_id': '77778687'}]

        self.default_sads = [{'id': '43',
                              'alarm_definition_id': '234',
                              'function': 'f_43',
                              'metric_name': 'm_43',
                              'operator': 'GT',
                              'threshold': 0,
                              'period': 1,
                              'periods': 2,
                              'created_at': datetime.datetime.now(),
                              'updated_at': datetime.datetime.now()},
                             {'id': '45',
                              'alarm_definition_id': '234',
                              'function': 'f_45',
                              'metric_name': 'm_45',
                              'operator': 'GT',
                              'threshold': 0,
                              'period': 1,
                              'periods': 2,
                              'created_at': datetime.datetime.now(),
                              'updated_at': datetime.datetime.now()},
                             {'id': '47',
                              'alarm_definition_id': '234',
                              'function': 'f_47',
                              'metric_name': 'm_47',
                              'operator': 'GT',
                              'threshold': 0,
                              'period': 1,
                              'periods': 2,
                              'created_at': datetime.datetime.now(),
                              'updated_at': datetime.datetime.now()},
                             {'id': '8484',
                              'alarm_definition_id': '234',
                              'function': 'f_49',
                              'metric_name': 'm_49',
                              'operator': 'GT',
                              'threshold': 0,
                              'period': 1,
                              'periods': 2,
                              'created_at': datetime.datetime.now(),
                              'updated_at': datetime.datetime.now()},
                             {'id': '8686',
                              'alarm_definition_id': '234',
                              'function': 'f_51',
                              'metric_name': 'm_51',
                              'operator': 'GT',
                              'threshold': 0,
                              'period': 1,
                              'periods': 2,
                              'created_at': datetime.datetime.now(),
                              'updated_at': datetime.datetime.now()}]

        self.default_sas = [{'sub_expression_id': '43',
                             'id': '42',
                             'alarm_id': '1',
                             'expression': 'avg(cpu.idle_perc{flavor_id=777,'
                             ' image_id=888, device=1}) > 10',
                             'created_at': datetime.datetime.now(),
                             'updated_at': datetime.datetime.now()},
                            {'sub_expression_id': '45',
                             'id': '43',
                             'alarm_id': '2',
                             'expression': 'avg(cpu.idle_perc{flavor_id=777,'
                             ' image_id=888, device=1}) > 10',
                             'created_at': datetime.datetime.now(),
                             'updated_at': datetime.datetime.now()},
                            {'sub_expression_id': '47',
                             'id': '44',
                             'alarm_id': '3',
                             'expression': 'avg(cpu.idle_perc{flavor_id=777,'
                             ' image_id=888, device=1}) > 10',
                             'created_at': datetime.datetime.now(),
                             'updated_at': datetime.datetime.now()}]

        self.default_ams = [{'alarm_id': '1',
                             'metric_definition_dimensions_id': b'11'},
                            {'alarm_id': '1',
                             'metric_definition_dimensions_id': b'22'},
                            {'alarm_id': '2',
                             'metric_definition_dimensions_id': b'11'},
                            {'alarm_id': '3',
                             'metric_definition_dimensions_id': b'22'},
                            {'alarm_id': '234111',
                             'metric_definition_dimensions_id': b'31'},
                            {'alarm_id': '234111',
                             'metric_definition_dimensions_id': b'32'}]

        self.default_mdes = [{'id': b'1',
                              'name': 'cpu.idle_perc',
                              'tenant_id': 'bob',
                              'region': 'west'},
                             {'id': b'111',
                              'name': 'cpu.sys_mem',
                              'tenant_id': 'bob',
                              'region': 'west'},
                             {'id': b'112',
                              'name': 'cpu.idle_perc',
                              'tenant_id': 'bob',
                              'region': 'west'}]

        self.default_mdds = [{'id': b'11',
                              'metric_definition_id': b'1',
                              'metric_dimension_set_id': b'1'},
                             {'id': b'22',
                              'metric_definition_id': b'1',
                              'metric_dimension_set_id': b'2'},
                             {'id': b'31',
                              'metric_definition_id': b'111',
                              'metric_dimension_set_id': b'21'},
                             {'id': b'32',
                              'metric_definition_id': b'112',
                              'metric_dimension_set_id': b'22'}]

        self.default_mds = [{'dimension_set_id': b'1',
                             'name': 'instance_id',
                             'value': '123'},
                            {'dimension_set_id': b'1',
                             'name': 'service',
                             'value': 'monitoring'},
                            {'dimension_set_id': b'2',
                             'name': 'flavor_id',
                             'value': '222'},
                            {'dimension_set_id': b'22',
                             'name': 'flavor_id',
                             'value': '333'},
                            {'dimension_set_id': b'21',
                             'name': 'service',
                             'value': 'monitoring'},
                            {'dimension_set_id': b'22',
                             'name': 'service',
                             'value': 'monitoring'},
                            {'dimension_set_id': b'21',
                             'name': 'hostname',
                             'value': 'roland'},
                            {'dimension_set_id': b'22',
                             'name': 'hostname',
                             'value': 'roland'},
                            {'dimension_set_id': b'21',
                             'name': 'region',
                             'value': 'colorado'},
                            {'dimension_set_id': b'22',
                             'name': 'region',
                             'value': 'colorado'},
                            {'dimension_set_id': b'22',
                             'name': 'extra',
                             'value': 'vivi'}]

        self.alarm1 = {'alarm_definition': {'id': '1',
                                            'name': '90% CPU',
                                            'severity': 'LOW'},
                       'created_timestamp': '2015-03-14T09:26:53Z',
                       'id': '1',
                       'lifecycle_state': 'OPEN',
                       'link': 'http://somesite.com/this-alarm-info',
                       'metrics': [{'dimensions': {'instance_id': '123',
                                                   'service': 'monitoring'},
                                    'name': 'cpu.idle_perc'},
                                   {'dimensions': {'flavor_id': '222'},
                                    'name': 'cpu.idle_perc'}],
                       'state': 'OK',
                       'state_updated_timestamp': '2015-03-14T09:26:53Z',
                       'updated_timestamp': '2015-03-14T09:26:53Z'}
        self.alarm2 = {'alarm_definition': {'id': '1',
                                            'name': '90% CPU',
                                            'severity': 'LOW'},
                       'created_timestamp': '2015-03-14T09:26:54Z',
                       'id': '2',
                       'lifecycle_state': 'OPEN',
                       'link': 'http://somesite.com/this-alarm-info',
                       'metrics': [{'dimensions': {'instance_id': '123',
                                                   'service': 'monitoring'},
                                    'name': 'cpu.idle_perc'}],
                       'state': 'UNDETERMINED',
                       'state_updated_timestamp': '2015-03-14T09:26:54Z',
                       'updated_timestamp': '2015-03-14T09:26:54Z'}
        self.alarm_compound = {'alarm_definition': {'id': '234',
                                                    'name': '50% CPU',
                                                    'severity': 'CRITICAL'},
                               'created_timestamp': '2015-03-15T09:26:53Z',
                               'id': '234111',
                               'lifecycle_state': None,
                               'link': None,
                               'metrics': [
                                   {'dimensions': {'hostname': 'roland',
                                                   'region': 'colorado',
                                                   'service': 'monitoring'},
                                    'name': 'cpu.sys_mem'},
                                   {'dimensions': {'extra': 'vivi',
                                                   'flavor_id': '333',
                                                   'hostname': 'roland',
                                                   'region': 'colorado',
                                                   'service': 'monitoring'},
                                    'name': 'cpu.idle_perc'}],
                               'state': 'UNDETERMINED',
                               'state_updated_timestamp':
                               '2015-03-15T09:26:53Z',
                               'updated_timestamp': '2015-03-15T09:26:53Z'}
        self.alarm3 = {'alarm_definition': {'id': '1',
                                            'name': '90% CPU',
                                            'severity': 'LOW'},
                       'created_timestamp': '2015-03-14T09:26:55Z',
                       'id': '3',
                       'lifecycle_state': None,
                       'link': 'http://somesite.com/this-alarm-info',
                       'metrics': [{'dimensions': {'flavor_id': '222'},
                                    'name': 'cpu.idle_perc'}],
                       'state': 'ALARM',
                       'state_updated_timestamp': '2015-03-14T09:26:55Z',
                       'updated_timestamp': '2015-03-14T09:26:55Z'}

        with self.engine.begin() as conn:
            conn.execute(self._delete_am_query)
            conn.execute(self._insert_am_query, self.default_ams)
            conn.execute(self._delete_md_query)
            conn.execute(self._insert_md_query, self.default_mds)
            conn.execute(self._delete_mdd_query)
            conn.execute(self._insert_mdd_query, self.default_mdds)
            conn.execute(self._delete_a_query)
            conn.execute(self._insert_a_query, self.default_as)
            conn.execute(self._delete_sa_query)
            conn.execute(self._insert_sa_query, self.default_sas)
            conn.execute(self._delete_mde_query)
            conn.execute(self._insert_mde_query, self.default_mdes)
            conn.execute(self._delete_ad_query)
            conn.execute(self._insert_ad_query, self.default_ads)
            conn.execute(self._delete_sad_query)
            conn.execute(self._insert_sad_query, self.default_sads)
            conn.execute(self._delete_sadd_query)
            conn.execute(self._insert_sadd_query, self.default_sadds)
            conn.execute(self._delete_nm_query)
            conn.execute(self._insert_nm_query, self.default_nms)
            conn.execute(self._delete_aa_query)
            conn.execute(self._insert_aa_query, self.default_aas)

    def helper_builder_result(self, alarm_rows):
        result = []

        if not alarm_rows:
            return result

        # Forward declaration
        alarm = {}
        prev_alarm_id = None
        for alarm_row in alarm_rows:
            if prev_alarm_id != alarm_row['alarm_id']:
                if prev_alarm_id is not None:
                    result.append(alarm)

                ad = {u'id': alarm_row['alarm_definition_id'],
                      u'name': alarm_row['alarm_definition_name'],
                      u'severity': alarm_row['severity'], }

                metrics = []
                alarm = {u'id': alarm_row['alarm_id'], u'metrics': metrics,
                         u'state': alarm_row['state'],
                         u'lifecycle_state': alarm_row['lifecycle_state'],
                         u'link': alarm_row['link'],
                         u'state_updated_timestamp':
                             alarm_row['state_updated_timestamp'].isoformat() +
                             'Z',
                         u'updated_timestamp':
                             alarm_row['updated_timestamp'].isoformat() + 'Z',
                         u'created_timestamp':
                             alarm_row['created_timestamp'].isoformat() + 'Z',
                         u'alarm_definition': ad}

                prev_alarm_id = alarm_row['alarm_id']

            dimensions = {}
            metric = {u'name': alarm_row['metric_name'],
                      u'dimensions': dimensions}

            if alarm_row['metric_dimensions']:
                for dimension in alarm_row['metric_dimensions'].split(','):
                    parsed_dimension = dimension.split('=')
                    dimensions[parsed_dimension[0]] = parsed_dimension[1]

            metrics.append(metric)

        result.append(alarm)
        return result

    def test_should_delete(self):
        tenant_id = 'bob'
        alarm_id = '1'

        alarm1 = self.repo.get_alarm(tenant_id, alarm_id)
        alarm1 = self.helper_builder_result(alarm1)
        self.assertEqual(alarm1[0], self.alarm1)
        self.repo.delete_alarm(tenant_id, alarm_id)
        from monasca_api.common.repositories import exceptions
        self.assertRaises(exceptions.DoesNotExistException,
                          self.repo.get_alarm, tenant_id, alarm_id)

    def test_should_throw_exception_on_delete(self):
        tenant_id = 'bob'
        from monasca_api.common.repositories import exceptions
        self.assertRaises(exceptions.DoesNotExistException,
                          self.repo.delete_alarm, tenant_id, 'Not an alarm ID')

    def test_should_find_alarm_def(self):
        tenant_id = 'bob'
        alarm_id = '1'

        expected = {'actions_enabled': False,
                    'deleted_at': None,
                    'description': None,
                    'expression': 'AVG(cpu.idle_perc{flavor_id=777,'
                    ' image_id=888, device=1}) > 10',
                    'id': '1',
                    'match_by': 'flavor_id,image_id',
                    'name': '90% CPU',
                    'severity': 'LOW',
                    'tenant_id': 'bob'}
        alarm_def = self.repo.get_alarm_definition(tenant_id, alarm_id)
        expected['created_at'] = alarm_def['created_at']
        expected['updated_at'] = alarm_def['updated_at']
        self.assertEqual(alarm_def, expected)
        from monasca_api.common.repositories import exceptions
        self.assertRaises(exceptions.DoesNotExistException,
                          self.repo.get_alarm_definition,
                          tenant_id, 'Not an alarm ID')

    def test_should_find(self):
        res = self.repo.get_alarms(tenant_id='Not a tenant id', limit=1)
        self.assertEqual(res, [])

        tenant_id = 'bob'
        res = self.repo.get_alarms(tenant_id=tenant_id, limit=1000)
        res = self.helper_builder_result(res)

        expected = [self.alarm1,
                    self.alarm2,
                    self.alarm_compound,
                    self.alarm3]

        self.assertEqual(res, expected)

        alarm_def_id = self.alarm_compound['alarm_definition']['id']
        query_parms = {'alarm_definition_id': alarm_def_id}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm_compound]

        self.assertEqual(res, expected)

        query_parms = {'metric_name': 'cpu.sys_mem'}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm_compound]

        self.assertEqual(res, expected)

        query_parms = {'metric_name': 'cpu.idle_perc'}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm1,
                    self.alarm2,
                    self.alarm_compound,
                    self.alarm3]

        self.assertEqual(res, expected)

        query_parms = {'metric_name': 'cpu.idle_perc',
                       'metric_dimensions': {'flavor_id': '222'}}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm1,
                    self.alarm3]

        self.assertEqual(res, expected)

        query_parms = {'metric_dimensions': {'flavor_id': '333'}}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm_compound]

        self.assertEqual(res, expected)

        query_parms = {'metric_dimensions': {'flavor_id': '222|333'}}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm1,
                    self.alarm_compound,
                    self.alarm3]

        self.assertEqual(res, expected)

        query_parms = {'metric_dimensions': {'flavor_id': ''}}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm1,
                    self.alarm_compound,
                    self.alarm3]

        self.assertEqual(res, expected)

        query_parms = {'metric_name': 'cpu.idle_perc',
                       'metric_dimensions': {'service': 'monitoring',
                                             'hostname': 'roland'}}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm_compound]

        self.assertEqual(res, expected)

        query_parms = {'state': 'UNDETERMINED'}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm2,
                    self.alarm_compound]

        self.assertEqual(res, expected)

        alarm_def_id = self.alarm1['alarm_definition']['id']
        query_parms = {'metric_name': 'cpu.idle_perc',
                       'metric_dimensions': {'service': 'monitoring'},
                       'alarm_definition_id': alarm_def_id}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm1,
                    self.alarm2]

        self.assertEqual(res, expected)

        alarm_def_id = self.alarm1['alarm_definition']['id']
        query_parms = {'metric_name': 'cpu.idle_perc',
                       'alarm_definition_id': alarm_def_id}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm1,
                    self.alarm2,
                    self.alarm3]

        self.assertEqual(res, expected)

        alarm_def_id = self.alarm_compound['alarm_definition']['id']
        query_parms = {'alarm_definition_id': alarm_def_id,
                       'state': 'UNDETERMINED'}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm_compound]

        self.assertEqual(res, expected)

        query_parms = {'metric_name': 'cpu.sys_mem',
                       'state': 'UNDETERMINED'}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm_compound]

        self.assertEqual(res, expected)

        query_parms = {'metric_name': 'cpu.idle_perc',
                       'metric_dimensions': {'service': 'monitoring'},
                       'state': 'UNDETERMINED'}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm2,
                    self.alarm_compound]

        self.assertEqual(res, expected)

        time_now = datetime.datetime.now().isoformat() + 'Z'
        query_parms = {'metric_name': 'cpu.idle_perc',
                       'metric_dimensions': {'service': 'monitoring'},
                       'state': 'UNDETERMINED',
                       'state_updated_start_time': time_now}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = []

        self.assertEqual(res, expected)

        time_now = '2015-03-15T00:00:00.0Z'
        query_parms = {'state_updated_start_time': time_now}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm_compound]

        self.assertEqual(res, expected)

        time_now = '2015-03-14T00:00:00.0Z'
        query_parms = {'state_updated_start_time': time_now}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm1,
                    self.alarm2,
                    self.alarm_compound,
                    self.alarm3]

        self.assertEqual(res, expected)

        query_parms = {'state_updated_start_time': time_now,
                       'link': 'http://google.com',
                       'lifecycle_state': 'OPEN'}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=None,
                                   offset='10')
        res = self.helper_builder_result(res)
        expected = []

        self.assertEqual(res, expected)

        query_parms = {'severity': 'LOW'}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm1,
                    self.alarm2,
                    self.alarm3]
        self.assertEqual(expected, res)

        query_parms = {'severity': 'CRITICAL'}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm_compound]
        self.assertEqual(expected, res)

        query_parms = {'severity': 'LOW|CRITICAL'}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm1,
                    self.alarm2,
                    self.alarm_compound,
                    self.alarm3]
        self.assertEqual(expected, res)

    def test_should_count(self):
        tenant_id = 'bob'

        res = self.repo.get_alarms_count(tenant_id=tenant_id)
        self.assertEqual([{'count': 4}], res)

        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         limit=1000)
        self.assertEqual([{'count': 4}], res)

        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         limit=1000,
                                         offset=10)
        self.assertEqual([], res)

        alarm_def_id = self.alarm_compound['alarm_definition']['id']
        query_parms = {'alarm_definition_id': alarm_def_id}
        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         query_parms=query_parms,
                                         limit=1000)
        self.assertEqual([{'count': 1}], res)

        query_parms = {'metric_name': 'cpu.sys_mem'}
        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         query_parms=query_parms,
                                         limit=1000)
        self.assertEqual([{'count': 1}], res)

        query_parms = {'state': 'UNDETERMINED'}
        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         query_parms=query_parms,
                                         limit=1000)
        self.assertEqual([{'count': 2}], res)

        time_now = '2015-03-15T00:00:00.0Z'
        query_parms = {'state_updated_start_time': time_now}
        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         query_parms=query_parms,
                                         limit=1000)
        self.assertEqual([{'count': 1}], res)

        query_parms = {'severity': 'LOW'}
        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         query_parms=query_parms,
                                         limit=1000)
        self.assertEqual([{'count': 3}], res)

        query_parms = {'lifecycle_state': 'OPEN'}
        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         query_parms=query_parms,
                                         limit=1000)
        self.assertEqual([{'count': 2}], res)

        query_parms = {'link': 'http://somesite.com/this-alarm-info'}
        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         query_parms=query_parms,
                                         limit=1000)
        self.assertEqual([{'count': 3}], res)

        query_parms = {'metric_dimensions': {'flavor_id': '222'}}
        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         query_parms=query_parms,
                                         limit=1000)
        self.assertEqual([{'count': 2}], res)

        query_parms = {'group_by': ['metric_name']}
        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         query_parms=query_parms,
                                         limit=1000)
        expected = [{'count': 4, 'metric_name': 'cpu.idle_perc'},
                    {'count': 1, 'metric_name': 'cpu.sys_mem'}]
        self.assertEqual(expected, res)

        query_parms = {'group_by': ['dimension_name']}
        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         query_parms=query_parms,
                                         limit=1000)
        expected = [{'count': 1, 'dimension_name': 'extra'},
                    {'count': 3, 'dimension_name': 'flavor_id'},
                    {'count': 1, 'dimension_name': 'hostname'},
                    {'count': 2, 'dimension_name': 'instance_id'},
                    {'count': 1, 'dimension_name': 'region'},
                    {'count': 3, 'dimension_name': 'service'}]
        self.assertEqual(expected, res)

        query_parms = {'group_by': ['dimension_value']}
        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         query_parms=query_parms,
                                         limit=1000)
        expected = [{'count': 2, 'dimension_value': '123'},
                    {'count': 2, 'dimension_value': '222'},
                    {'count': 1, 'dimension_value': '333'},
                    {'count': 1, 'dimension_value': 'colorado'},
                    {'count': 3, 'dimension_value': 'monitoring'},
                    {'count': 1, 'dimension_value': 'roland'},
                    {'count': 1, 'dimension_value': 'vivi'}]
        self.assertEqual(expected, res)

        query_parms = {'group_by': []}
        res = self.repo.get_alarms_count(tenant_id=tenant_id,
                                         query_parms=query_parms,
                                         limit=1000)
        self.assertEqual([{'count': 4}], res)

    def test_should_sort_and_find(self):
        tenant_id = 'bob'
        query_parms = {'metric_name': 'cpu.idle_perc',
                       'sort_by': ['alarm_id']}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm1,
                    self.alarm2,
                    self.alarm_compound,
                    self.alarm3]
        self.assertEqual(expected, res)

        query_parms = {'metric_name': 'cpu.idle_perc',
                       'sort_by': ['alarm_definition_id']}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm1,
                    self.alarm2,
                    self.alarm3,
                    self.alarm_compound]
        self.assertEqual(expected, res)

        query_parms = {'metric_name': 'cpu.idle_perc',
                       'sort_by': ['alarm_definition_name']}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        expected = [self.alarm_compound,
                    self.alarm1,
                    self.alarm2,
                    self.alarm3]
        res = self.helper_builder_result(res)
        self.assertEqual(expected, res)

        query_parms = {'metric_name': 'cpu.idle_perc',
                       'sort_by': ['severity']}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        expected = [self.alarm1,
                    self.alarm2,
                    self.alarm3,
                    self.alarm_compound]
        res = self.helper_builder_result(res)
        self.assertEqual(expected, res)

        query_parms = {'metric_name': 'cpu.idle_perc',
                       'sort_by': ['state']}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        expected = [self.alarm1,
                    self.alarm2,
                    self.alarm_compound,
                    self.alarm3]
        res = self.helper_builder_result(res)
        self.assertEqual(expected, res)

        query_parms = {'metric_name': 'cpu.idle_perc',
                       'sort_by': ['alarm_id asc']}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm1,
                    self.alarm2,
                    self.alarm_compound,
                    self.alarm3]
        self.assertEqual(expected, res)

        query_parms = {'metric_name': 'cpu.idle_perc',
                       'sort_by': ['alarm_id desc']}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm3,
                    self.alarm_compound,
                    self.alarm2,
                    self.alarm1]
        self.assertEqual(expected, res)

        query_parms = {'metric_name': 'cpu.idle_perc',
                       'sort_by': ['alarm_id nfl']}
        res = self.repo.get_alarms(tenant_id=tenant_id,
                                   query_parms=query_parms,
                                   limit=1000)
        res = self.helper_builder_result(res)
        expected = [self.alarm1,
                    self.alarm2,
                    self.alarm_compound,
                    self.alarm3]
        self.assertEqual(expected, res)

    def test_should_update(self):
        tenant_id = 'bob'
        alarm_id = '2'

        alarm = self.repo.get_alarm(tenant_id, alarm_id)
        alarm = self.helper_builder_result(alarm)[0]
        original_state_updated_date = alarm['state_updated_timestamp']
        original_updated_timestamp = alarm['updated_timestamp']
        self.assertEqual(alarm['state'], 'UNDETERMINED')

        prev_state, _ = self.repo.update_alarm(tenant_id, alarm_id, 'OK', None, None)
        alarm_new = self.repo.get_alarm(tenant_id, alarm_id)
        alarm_new = self.helper_builder_result(alarm_new)[0]
        new_state_updated_date = alarm_new['state_updated_timestamp']
        new_updated_timestamp = alarm_new['updated_timestamp']
        self.assertNotEqual(original_updated_timestamp,
                            new_updated_timestamp,
                            'updated_at did not change')
        self.assertNotEqual(original_state_updated_date,
                            new_state_updated_date,
                            'state_updated_at did not change')
        alarm_tmp = tuple(alarm[k] for k in ('state', 'link', 'lifecycle_state'))
        self.assertEqual(alarm_tmp, prev_state)
        alarm['state_updated_timestamp'] = alarm_new['state_updated_timestamp']
        alarm['updated_timestamp'] = alarm_new['updated_timestamp']
        alarm['state'] = alarm_new['state']
        alarm['link'] = alarm_new['link']
        alarm['lifecycle_state'] = alarm_new['lifecycle_state']

        self.assertEqual(alarm, alarm_new)

        time.sleep(1)
        prev_state, _ = self.repo.update_alarm(tenant_id, alarm_id, 'OK', None, None)
        alarm_unchanged = self.repo.get_alarm(tenant_id, alarm_id)
        alarm_unchanged = self.helper_builder_result(alarm_unchanged)[0]
        unchanged_state_updated_date = alarm_unchanged['state_updated_timestamp']
        unchanged_updated_timestamp = alarm_unchanged['updated_timestamp']
        self.assertNotEqual(unchanged_updated_timestamp,
                            new_updated_timestamp,
                            'updated_at did not change')
        self.assertEqual(unchanged_state_updated_date,
                         new_state_updated_date,
                         'state_updated_at did change')
        alarm_new_tmp = tuple(alarm_new[k] for k in ('state', 'link', 'lifecycle_state'))
        self.assertEqual(alarm_new_tmp, prev_state)

    def test_should_throw_exception_on_update(self):
        tenant_id = 'bob'
        alarm_id = 'Not real alarm id'
        from monasca_api.common.repositories import exceptions

        self.assertRaises(exceptions.DoesNotExistException,
                          self.repo.update_alarm,
                          tenant_id,
                          alarm_id,
                          'UNDETERMINED',
                          None,
                          None)

    def test_get_alarm_metrics(self):
        alarm_id = '2'
        alarm_metrics = self.repo.get_alarm_metrics(alarm_id)

        expected = [{'alarm_id': '2',
                     'dimensions': 'instance_id=123,service=monitoring',
                     'name': 'cpu.idle_perc'}]

        self.assertEqual(alarm_metrics, expected)

    def test_get_subalarms(self):
        tenant_id = 'bob'
        alarm_id = '2'

        sub_alarms = self.repo.get_sub_alarms(tenant_id, alarm_id)
        expected = [{'alarm_definition_id': '1',
                     'alarm_id': '2',
                     'expression': 'avg(cpu.idle_perc{flavor_id=777, image_id=888, device=1}) > 10',
                     'sub_alarm_id': '43'}]
        self.assertEqual(sub_alarms, expected)
