diff --git a/monasca/api/alarm_definitions_api_v2.py b/monasca/api/alarm_definitions_api_v2.py index 66fdd9f3d..e9ab55546 100644 --- a/monasca/api/alarm_definitions_api_v2.py +++ b/monasca/api/alarm_definitions_api_v2.py @@ -20,6 +20,9 @@ LOG = log.getLogger(__name__) class AlarmDefinitionsV2API(object): def __init__(self, global_conf): + + super(AlarmDefinitionsV2API, self).__init__(global_conf) + LOG.debug('initializing AlarmDefinitionsV2API!') self.global_conf = global_conf diff --git a/monasca/api/alarms_api_v2.py b/monasca/api/alarms_api_v2.py index cd81c981c..d2f865c8f 100644 --- a/monasca/api/alarms_api_v2.py +++ b/monasca/api/alarms_api_v2.py @@ -20,6 +20,9 @@ LOG = log.getLogger(__name__) class AlarmsV2API(object): def __init__(self, global_conf): + + super(AlarmsV2API, self).__init__(global_conf) + LOG.debug('initializing AlarmsV2API!') self.global_conf = global_conf diff --git a/monasca/common/repositories/mysql/alarm_definitions_repository.py b/monasca/common/repositories/mysql/alarm_definitions_repository.py index d4a7519aa..51f571894 100644 --- a/monasca/common/repositories/mysql/alarm_definitions_repository.py +++ b/monasca/common/repositories/mysql/alarm_definitions_repository.py @@ -16,7 +16,7 @@ import datetime from monasca.common.repositories import alarm_definitions_repository from monasca.common.repositories.exceptions import DoesNotExistException from monasca.common.repositories.mysql.mysql_repository import MySQLRepository -from monasca.common.repositories.mysql.mysql_repository import try_catch_block +from monasca.common.repositories.mysql.mysql_repository import mysql_try_catch_block from monasca.openstack.common import log from monasca.openstack.common import uuidutils from monasca.common.repositories import exceptions @@ -56,7 +56,7 @@ class AlarmDefinitionsRepository(MySQLRepository, super(AlarmDefinitionsRepository, self).__init__() - @try_catch_block + @mysql_try_catch_block def get_alarm_definition(self, tenant_id, id): parms = [tenant_id, id] @@ -74,7 +74,7 @@ class AlarmDefinitionsRepository(MySQLRepository, else: raise DoesNotExistException - @try_catch_block + @mysql_try_catch_block def get_alarm_definitions(self, tenant_id, name, dimensions): parms = [tenant_id] @@ -114,7 +114,7 @@ class AlarmDefinitionsRepository(MySQLRepository, return self._execute_query(query, parms) - @try_catch_block + @mysql_try_catch_block def get_sub_alarms(self, tenant_id, alarm_definition_id): parms = [tenant_id, alarm_definition_id] @@ -131,7 +131,7 @@ class AlarmDefinitionsRepository(MySQLRepository, return self._execute_query(query, parms) - @try_catch_block + @mysql_try_catch_block def get_alarm_metrics(self, tenant_id, alarm_definition_id): parms = [tenant_id, alarm_definition_id] @@ -156,7 +156,7 @@ class AlarmDefinitionsRepository(MySQLRepository, return self._execute_query(query, parms) - @try_catch_block + @mysql_try_catch_block def delete_alarm_definition(self, tenant_id, alarm_definition_id): """Soft delete the alarm definition. @@ -187,7 +187,7 @@ class AlarmDefinitionsRepository(MySQLRepository, return True - @try_catch_block + @mysql_try_catch_block def get_sub_alarm_definitions(self, alarm_definition_id): parms = [alarm_definition_id] @@ -206,7 +206,7 @@ class AlarmDefinitionsRepository(MySQLRepository, return self._execute_query(query, parms) - @try_catch_block + @mysql_try_catch_block def create_alarm_definition(self, tenant_id, name, expression, sub_expr_list, description, severity, match_by, alarm_actions, undetermined_actions, diff --git a/monasca/common/repositories/mysql/alarms_repository.py b/monasca/common/repositories/mysql/alarms_repository.py index 57ef072f5..364e74222 100644 --- a/monasca/common/repositories/mysql/alarms_repository.py +++ b/monasca/common/repositories/mysql/alarms_repository.py @@ -15,7 +15,7 @@ from monasca.common.repositories.exceptions import DoesNotExistException from monasca.common.repositories import alarms_repository from monasca.common.repositories.mysql.mysql_repository import MySQLRepository -from monasca.common.repositories.mysql.mysql_repository import try_catch_block +from monasca.common.repositories.mysql.mysql_repository import mysql_try_catch_block from monasca.openstack.common import log @@ -47,7 +47,73 @@ class AlarmsRepository(MySQLRepository, alarms_repository.AlarmsRepository): super(AlarmsRepository, self).__init__() - @try_catch_block + @mysql_try_catch_block + def get_alarm_metrics(self, alarm_id): + + parms = [alarm_id] + + query = """select distinct a.id as alarm_id, md.name, + mdg.dimensions + from alarm as a + inner join alarm_metric as am on am.alarm_id = a.id + inner join metric_definition_dimensions as mdd + on mdd.id = am.metric_definition_dimensions_id + inner join metric_definition as md + on md.id = mdd.metric_definition_id + left join (select dimension_set_id, + group_concat(name, '=', value) as dimensions + from metric_dimension group by dimension_set_id) as mdg + on mdg.dimension_set_id = mdd.metric_dimension_set_id + where a.id = ? + order by a.id + """ + + return self._execute_query(query, parms) + + @mysql_try_catch_block + def get_sub_alarms(self, tenant_id, alarm_id): + + parms = [tenant_id, alarm_id] + + query = """select distinct sa.id as sub_alarm_id, sa.alarm_id, + sa.expression, ad.id as alarm_definition_id + from sub_alarm as sa + inner join alarm as a + on a.id = sa.alarm_id + inner join alarm_definition as ad + on ad.id = a.alarm_definition_id + where ad.tenant_id = ? and a.id = ? + """ + + return self._execute_query(query, parms) + + @mysql_try_catch_block + def delete_alarm(self, tenant_id, id): + + parms = [tenant_id, id] + + query = """ + delete alarm.* + from alarm + join + (select distinct a.id + from alarm as a + inner join alarm_definition as ad + on ad.id = a.alarm_definition_id + where ad.tenant_id = ? and a.id = ?) as b + on b.id = alarm.id + """ + + cnxn, cursor = self._get_cnxn_cursor_tuple() + + cursor.execute(query, parms) + + if cursor.rowcount < 1: + raise DoesNotExistException + + self._commit_close_cnxn(cnxn) + + @mysql_try_catch_block def get_alarm(self, tenant_id, id): parms = [tenant_id, id] @@ -66,7 +132,7 @@ class AlarmsRepository(MySQLRepository, alarms_repository.AlarmsRepository): else: return rows - @try_catch_block + @mysql_try_catch_block def get_alarms(self, tenant_id, query_parms): parms = [tenant_id] diff --git a/monasca/common/repositories/mysql/mysql_repository.py b/monasca/common/repositories/mysql/mysql_repository.py index 174548665..29286d7c5 100644 --- a/monasca/common/repositories/mysql/mysql_repository.py +++ b/monasca/common/repositories/mysql/mysql_repository.py @@ -79,7 +79,7 @@ class MySQLRepository(object): return rows -def try_catch_block(fun): +def mysql_try_catch_block(fun): def try_it(*args, **kwargs): diff --git a/monasca/v2/reference/alarm_definitions.py b/monasca/v2/reference/alarm_definitions.py index 76a91573f..de69efa31 100644 --- a/monasca/v2/reference/alarm_definitions.py +++ b/monasca/v2/reference/alarm_definitions.py @@ -13,8 +13,6 @@ # under the License. import json import re -from monasca.common.repositories.exceptions import DoesNotExistException - from pyparsing import ParseException import falcon from oslo.config import cfg @@ -25,17 +23,17 @@ from monasca.api.alarm_definitions_api_v2 import AlarmDefinitionsV2API from monasca.expression_parser.alarm_expr_parser import AlarmExprParser from monasca.openstack.common import log from monasca.v2.reference import helpers -from monasca.v2.common.schemas import \ - alarm_definition_request_body_schema as schema_alarms +from monasca.v2.common.schemas import alarm_definition_request_body_schema as schema_alarms from monasca.v2.common.schemas import exceptions as schemas_exceptions +from monasca.v2.reference.alarming import Alarming from monasca.v2.reference.helpers import read_json_msg_body -from monasca.common.messaging import exceptions as message_queue_exceptions +from monasca.v2.reference.resource import resource_try_catch_block LOG = log.getLogger(__name__) -class AlarmDefinitions(AlarmDefinitionsV2API): +class AlarmDefinitions(AlarmDefinitionsV2API, Alarming): def __init__(self, global_conf): @@ -53,11 +51,6 @@ class AlarmDefinitions(AlarmDefinitionsV2API): cfg.CONF.security.default_authorized_roles + \ cfg.CONF.security.agent_authorized_roles - self._message_queue \ - = resource_api.init_driver('monasca.messaging', - cfg.CONF.messaging.driver, - (['events'])) - self._alarm_definitions_repo = resource_api.init_driver( 'monasca.repositories', cfg.CONF.repositories.alarm_definitions_driver) @@ -138,14 +131,70 @@ class AlarmDefinitions(AlarmDefinitionsV2API): self._alarm_definition_delete(tenant_id, id) res.status = falcon.HTTP_204 - + @resource_try_catch_block def _alarm_definition_show(self, tenant_id, id): - try: + alarm_definition_row = \ + self._alarm_definitions_repo.get_alarm_definition( + tenant_id, id) - alarm_definition_row = \ - self._alarm_definitions_repo.get_alarm_definition( - tenant_id, id) + match_by = get_comma_separated_str_as_list( + alarm_definition_row.match_by) + + alarm_actions_list = get_comma_separated_str_as_list( + alarm_definition_row.alarm_actions) + + ok_actions_list = get_comma_separated_str_as_list( + alarm_definition_row.ok_actions) + + undetermined_actions_list = get_comma_separated_str_as_list( + alarm_definition_row.undetermined_actions) + + result = { + u'actions_enabled': alarm_definition_row.actions_enabled == 1, + u'alarm_actions': alarm_actions_list, + u'undetermined_actions': undetermined_actions_list, + u'ok_actions': ok_actions_list, + u'description': alarm_definition_row.description.decode( + 'utf8'), + u'expression': alarm_definition_row.expression.decode('utf8'), + u'id': alarm_definition_row.id.decode('utf8'), + u'match_by': match_by, + u'name': alarm_definition_row.name.decode('utf8'), + u'severity': alarm_definition_row.severity.decode('utf8')} + + return result + + @resource_try_catch_block + def _alarm_definition_delete(self, tenant_id, id): + + sub_alarm_definition_rows = \ + self._alarm_definitions_repo.get_sub_alarm_definitions( + id) + alarm_metric_rows = self._alarm_definitions_repo.get_alarm_metrics( + tenant_id, id) + sub_alarm_rows = self._alarm_definitions_repo.get_sub_alarms( + tenant_id, id) + + if not self._alarm_definitions_repo.delete_alarm_definition( + tenant_id, id): + raise falcon.HTTPNotFound + + self._send_alarm_definition_deleted_event(id, + sub_alarm_definition_rows) + + self._send_alarm_deleted_event(tenant_id, id, alarm_metric_rows, + sub_alarm_rows) + + @resource_try_catch_block + def _alarm_definition_list(self, tenant_id, name, dimensions, req_uri): + + alarm_definition_rows = \ + self._alarm_definitions_repo.get_alarm_definitions( + tenant_id, name, dimensions) + + result = [] + for alarm_definition_row in alarm_definition_rows: match_by = get_comma_separated_str_as_list( alarm_definition_row.match_by) @@ -159,113 +208,25 @@ class AlarmDefinitions(AlarmDefinitionsV2API): undetermined_actions_list = get_comma_separated_str_as_list( alarm_definition_row.undetermined_actions) - result = { - u'actions_enabled': alarm_definition_row.actions_enabled == 1, - u'alarm_actions': alarm_actions_list, - u'undetermined_actions': undetermined_actions_list, - u'ok_actions': ok_actions_list, - u'description': alarm_definition_row.description.decode( - 'utf8'), - u'expression': alarm_definition_row.expression.decode('utf8'), - u'id': alarm_definition_row.id.decode('utf8'), - u'match_by': match_by, - u'name': alarm_definition_row.name.decode('utf8'), - u'severity': alarm_definition_row.severity.decode('utf8')} + ad = {u'id': alarm_definition_row.id.decode('utf8'), + u'name': alarm_definition_row.name.decode("utf8"), + u'description': alarm_definition_row.description.decode( + 'utf8'), + u'expression': alarm_definition_row.expression.decode( + 'utf8'), u'match_by': match_by, + u'severity': alarm_definition_row.severity.decode( + 'utf8'), + u'actions_enabled': + alarm_definition_row.actions_enabled == 1, + u'alarm_actions': alarm_actions_list, + u'ok_actions': ok_actions_list, + u'undetermined_actions': undetermined_actions_list} - return result + helpers.add_links_to_resource(ad, req_uri) + result.append(ad) + return result - except DoesNotExistException: - raise falcon.HTTPNotFound() - except exceptions.RepositoryException as ex: - LOG.exception(ex) - msg = "".join(ex.message.args) - raise falcon.HTTPInternalServerError('Service unavailable', msg) - except Exception as ex: - LOG.exception(ex) - raise falcon.HTTPInternalServerError('Service unavailable', ex) - - def _alarm_definition_delete(self, tenant_id, id): - - try: - - sub_alarm_definition_rows = \ - self._alarm_definitions_repo.get_sub_alarm_definitions( - id) - alarm_metric_rows = self._alarm_definitions_repo.get_alarm_metrics( - tenant_id, id) - sub_alarm_rows = self._alarm_definitions_repo.get_sub_alarms( - tenant_id, id) - - if not self._alarm_definitions_repo.delete_alarm_definition( - tenant_id, id): - raise falcon.HTTPNotFound - - self._send_alarm_definition_deleted_event(id, - sub_alarm_definition_rows) - - self._send_alarm_deleted_event(tenant_id, id, alarm_metric_rows, - sub_alarm_rows) - - except exceptions.RepositoryException as ex: - LOG.exception(ex) - msg = "".join(ex.message.args) - raise falcon.HTTPInternalServerError('Service unavailable', msg) - except falcon.HTTPNotFound: - raise - except Exception as ex: - LOG.exception(ex) - raise falcon.HTTPInternalServerError('Service unavailable', ex) - - def _alarm_definition_list(self, tenant_id, name, dimensions, req_uri): - - try: - - alarm_definition_rows = \ - self._alarm_definitions_repo.get_alarm_definitions( - tenant_id, name, dimensions) - - result = [] - for alarm_definition_row in alarm_definition_rows: - - match_by = get_comma_separated_str_as_list( - alarm_definition_row.match_by) - - alarm_actions_list = get_comma_separated_str_as_list( - alarm_definition_row.alarm_actions) - - ok_actions_list = get_comma_separated_str_as_list( - alarm_definition_row.ok_actions) - - undetermined_actions_list = get_comma_separated_str_as_list( - alarm_definition_row.undetermined_actions) - - ad = {u'id': alarm_definition_row.id.decode('utf8'), - u'name': alarm_definition_row.name.decode("utf8"), - u'description': alarm_definition_row.description.decode( - 'utf8'), - u'expression': alarm_definition_row.expression.decode( - 'utf8'), u'match_by': match_by, - u'severity': alarm_definition_row.severity.decode( - 'utf8'), - u'actions_enabled': - alarm_definition_row.actions_enabled == 1, - u'alarm_actions': alarm_actions_list, - u'ok_actions': ok_actions_list, - u'undetermined_actions': undetermined_actions_list} - - helpers.add_links_to_resource(ad, req_uri) - result.append(ad) - - return result - - except exceptions.RepositoryException as ex: - LOG.exception(ex) - msg = "".join(ex.message.args) - raise falcon.HTTPInternalServerError('Service unavailable', msg) - except Exception as ex: - LOG.exception(ex) - raise falcon.HTTPInternalServerError('Service unavailable', ex) def _validate_alarm_definition(self, alarm_definition): @@ -275,125 +236,42 @@ class AlarmDefinitions(AlarmDefinitionsV2API): LOG.debug(ex) raise falcon.HTTPBadRequest('Bad request', ex.message) + @resource_try_catch_block def _alarm_definition_create(self, tenant_id, name, expression, description, severity, match_by, alarm_actions, undetermined_actions, ok_actions): try: + sub_expr_list = AlarmExprParser(expression).sub_expr_list - alarm_definition_id = \ - self._alarm_definitions_repo.create_alarm_definition( - tenant_id, name, expression, sub_expr_list, description, - severity, match_by, alarm_actions, undetermined_actions, - ok_actions) - - self._send_alarm_definition_created_event(tenant_id, - alarm_definition_id, - name, expression, - sub_expr_list, - description, match_by) - result = ( - {u'alarm_actions': alarm_actions, u'ok_actions': ok_actions, - u'description': description, u'match_by': match_by, - u'severity': severity.lower(), u'actions_enabled': u'true', - u'undetermined_actions': undetermined_actions, - u'expression': expression, u'id': alarm_definition_id, - u'name': name}) - - return result - except ParseException as ex: LOG.exception(ex) title = "Invalid alarm expression".encode('utf8') msg = "parser failed on expression '{}' at column {}".format( expression.encode('utf8'), str(ex.column).encode('utf')) raise falcon.HTTPBadRequest(title, msg) - except exceptions.RepositoryException as ex: - LOG.exception(ex) - msg = "".join(ex.message.args) - raise falcon.HTTPInternalServerError('Service unavailable', msg) - except Exception as ex: - LOG.exception(ex) - raise falcon.HTTPInternalServerError('Service unavailable', ex) - def _send_alarm_deleted_event(self, tenant_id, alarm_definition_id, - alarm_metric_rows, sub_alarm_rows): + alarm_definition_id = \ + self._alarm_definitions_repo.create_alarm_definition( + tenant_id, name, expression, sub_expr_list, description, + severity, match_by, alarm_actions, undetermined_actions, + ok_actions) - if not alarm_metric_rows: - return + self._send_alarm_definition_created_event(tenant_id, + alarm_definition_id, + name, expression, + sub_expr_list, + description, match_by) + result = ( + {u'alarm_actions': alarm_actions, u'ok_actions': ok_actions, + u'description': description, u'match_by': match_by, + u'severity': severity.lower(), u'actions_enabled': u'true', + u'undetermined_actions': undetermined_actions, + u'expression': expression, u'id': alarm_definition_id, + u'name': name}) - # Build a dict mapping alarm id -> list of sub alarms. - sub_alarm_dict = {} - for sub_alarm_row in sub_alarm_rows: - if sub_alarm_row.alarm_id in sub_alarm_dict: - sub_alarm_dict[sub_alarm_row.alarm_id] += [sub_alarm_row] - else: - sub_alarm_dict[sub_alarm_row.alarm_id] = [sub_alarm_row] - - prev_alarm_id = None - for alarm_metric_row in alarm_metric_rows: - if prev_alarm_id != alarm_metric_row.alarm_id: - if prev_alarm_id is not None: - sub_alarms_deleted_event_msg = \ - self._build_sub_alarm_deleted_event_msg( - sub_alarm_dict, prev_alarm_id) - alarm_deleted_event_msg[u'alarm-delete'][ - u'subAlarms': sub_alarms_deleted_event_msg] - self._send_event(alarm_deleted_event_msg) - - alarm_metrics_event_msg = [] - alarm_deleted_event_msg = { - u'alarm-deleted': {u'tenant_id': tenant_id, - u'alarmDefinitionId': - alarm_definition_id, - u'alarmId': alarm_metric_row.alarm_id, - u'alarmMetrics': - alarm_metrics_event_msg}} - - prev_alarm_id = alarm_metric_row.alarm_id - - dimensions = {} - metric = {u'name': alarm_metric_row.name, - u'dimensions': dimensions} - for dimension in alarm_metric_row.dimensions.split(','): - parsed_dimension = dimension.split('=') - dimensions[parsed_dimension[0]] = parsed_dimension[1] - - alarm_metrics_event_msg.append(metric) - - # Finish last alarm - sub_alarms_deleted_event_msg = self._build_sub_alarm_deleted_event_msg( - sub_alarm_dict, prev_alarm_id) - alarm_deleted_event_msg[u'alarm-deleted'][ - u'subAlarms'] = sub_alarms_deleted_event_msg - self._send_event(alarm_deleted_event_msg) - - def _build_sub_alarm_deleted_event_msg(self, sub_alarm_dict, alarm_id): - - sub_alarms_deleted_event_msg = {} - - if alarm_id not in sub_alarm_dict: - return sub_alarms_deleted_event_msg - - for sub_alarm in sub_alarm_dict[alarm_id]: - # There's only one expr in a sub alarm, so just take the first. - sub_expr = AlarmExprParser(sub_alarm.expression).sub_expr_list[0] - dimensions = {} - sub_alarms_deleted_event_msg[sub_alarm.sub_alarm_id] = { - u'function': sub_expr.normalized_func, - u'metricDefinition': {u'name': sub_expr.metric_name, - u'dimensions': dimensions}, - u'operator': sub_expr.normalized_operator, - u'threshold': sub_expr.threshold, u'period': sub_expr.period, - u'periods': sub_expr.periods, - u'expression': sub_expr.fmtd_sub_expr_str} - - for dimension in sub_expr.dimensions_as_list: - parsed_dimension = dimension.split('=') - dimensions[parsed_dimension[0]] = parsed_dimension[1] - - return sub_alarms_deleted_event_msg + return result def _send_alarm_definition_deleted_event(self, alarm_definition_id, sub_alarm_definition_rows): @@ -456,17 +334,6 @@ class AlarmDefinitions(AlarmDefinitionsV2API): self._send_event(alarm_definition_created_event_msg) - def _send_event(self, event_msg): - try: - self._message_queue.send_message( - json.dumps(event_msg, ensure_ascii=False).encode('utf8')) - except message_queue_exceptions.MessageQueueException as ex: - LOG.exception(ex) - raise falcon.HTTPInternalServerError( - 'Message queue service unavailable'.encode('utf8'), - ex.message.encode('utf8')) - - def get_query_alarm_definition_name(alarm_definition): try: if 'name' in alarm_definition: diff --git a/monasca/v2/reference/alarming.py b/monasca/v2/reference/alarming.py new file mode 100644 index 000000000..2d702298a --- /dev/null +++ b/monasca/v2/reference/alarming.py @@ -0,0 +1,127 @@ +# Copyright 2014 Hewlett-Packard +# +# 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 json +import falcon +from oslo.config import cfg +from monasca.common import resource_api +from monasca.expression_parser.alarm_expr_parser import AlarmExprParser +from monasca.openstack.common import log +from monasca.common.messaging import exceptions as message_queue_exceptions + +LOG = log.getLogger(__name__) + + +class Alarming(object): + """Super class for Alarms and AlarmDefinitions. + + Shared attributes and methods for classes Alarms and AlarmDefinitions. + + """ + + def __init__(self, global_conf): + + super(Alarming, self).__init__() + + self._message_queue \ + = resource_api.init_driver('monasca.messaging', + cfg.CONF.messaging.driver, + (['events'])) + + def _send_alarm_deleted_event(self, tenant_id, alarm_definition_id, + alarm_metric_rows, sub_alarm_rows): + + if not alarm_metric_rows: + return + + # Build a dict mapping alarm id -> list of sub alarms. + sub_alarm_dict = {} + for sub_alarm_row in sub_alarm_rows: + if sub_alarm_row.alarm_id in sub_alarm_dict: + sub_alarm_dict[sub_alarm_row.alarm_id] += [sub_alarm_row] + else: + sub_alarm_dict[sub_alarm_row.alarm_id] = [sub_alarm_row] + + prev_alarm_id = None + for alarm_metric_row in alarm_metric_rows: + if prev_alarm_id != alarm_metric_row.alarm_id: + if prev_alarm_id is not None: + sub_alarms_deleted_event_msg = \ + self._build_sub_alarm_deleted_event_msg( + sub_alarm_dict, prev_alarm_id) + alarm_deleted_event_msg[u'alarm-delete'][ + u'subAlarms': sub_alarms_deleted_event_msg] + self._send_event(alarm_deleted_event_msg) + + alarm_metrics_event_msg = [] + alarm_deleted_event_msg = { + u'alarm-deleted': {u'tenant_id': tenant_id, + u'alarmDefinitionId': + alarm_definition_id, + u'alarmId': alarm_metric_row.alarm_id, + u'alarmMetrics': + alarm_metrics_event_msg}} + + prev_alarm_id = alarm_metric_row.alarm_id + + dimensions = {} + metric = {u'name': alarm_metric_row.name, + u'dimensions': dimensions} + for dimension in alarm_metric_row.dimensions.split(','): + parsed_dimension = dimension.split('=') + dimensions[parsed_dimension[0]] = parsed_dimension[1] + + alarm_metrics_event_msg.append(metric) + + # Finish last alarm + sub_alarms_deleted_event_msg = self._build_sub_alarm_deleted_event_msg( + sub_alarm_dict, prev_alarm_id) + alarm_deleted_event_msg[u'alarm-deleted'][ + u'subAlarms'] = sub_alarms_deleted_event_msg + self._send_event(alarm_deleted_event_msg) + + def _build_sub_alarm_deleted_event_msg(self, sub_alarm_dict, alarm_id): + + sub_alarms_deleted_event_msg = {} + + if alarm_id not in sub_alarm_dict: + return sub_alarms_deleted_event_msg + + for sub_alarm in sub_alarm_dict[alarm_id]: + # There's only one expr in a sub alarm, so just take the first. + sub_expr = AlarmExprParser(sub_alarm.expression).sub_expr_list[0] + dimensions = {} + sub_alarms_deleted_event_msg[sub_alarm.sub_alarm_id] = { + u'function': sub_expr.normalized_func, + u'metricDefinition': {u'name': sub_expr.metric_name, + u'dimensions': dimensions}, + u'operator': sub_expr.normalized_operator, + u'threshold': sub_expr.threshold, u'period': sub_expr.period, + u'periods': sub_expr.periods, + u'expression': sub_expr.fmtd_sub_expr_str} + + for dimension in sub_expr.dimensions_as_list: + parsed_dimension = dimension.split('=') + dimensions[parsed_dimension[0]] = parsed_dimension[1] + + return sub_alarms_deleted_event_msg + + def _send_event(self, event_msg): + try: + self._message_queue.send_message( + json.dumps(event_msg, ensure_ascii=False).encode('utf8')) + except message_queue_exceptions.MessageQueueException as ex: + LOG.exception(ex) + raise falcon.HTTPInternalServerError( + 'Message queue service unavailable'.encode('utf8'), + ex.message.encode('utf8')) diff --git a/monasca/v2/reference/alarms.py b/monasca/v2/reference/alarms.py index ca17a4b1c..eb0b9449f 100644 --- a/monasca/v2/reference/alarms.py +++ b/monasca/v2/reference/alarms.py @@ -14,34 +14,26 @@ import json from falcon.util.uri import parse_query_string import re -from monasca.common.repositories.exceptions import DoesNotExistException - -from pyparsing import ParseException import falcon from oslo.config import cfg from monasca.api.alarms_api_v2 import AlarmsV2API - from monasca.common.repositories import exceptions from monasca.common import resource_api -from monasca.api.alarm_definitions_api_v2 import AlarmDefinitionsV2API -from monasca.expression_parser.alarm_expr_parser import AlarmExprParser from monasca.openstack.common import log from monasca.v2.reference import helpers -from monasca.v2.common.schemas import \ - alarm_definition_request_body_schema as schema_alarms -from monasca.v2.common.schemas import exceptions as schemas_exceptions -from monasca.v2.reference.helpers import read_json_msg_body -from monasca.common.messaging import exceptions as message_queue_exceptions +from monasca.v2.reference.alarming import Alarming +from monasca.v2.reference.resource import resource_try_catch_block LOG = log.getLogger(__name__) -class Alarms(AlarmsV2API): +class Alarms(AlarmsV2API, Alarming): def __init__(self, global_conf): try: + super(Alarms, self).__init__(global_conf) self._region = cfg.CONF.region @@ -54,11 +46,6 @@ class Alarms(AlarmsV2API): cfg.CONF.security.default_authorized_roles + \ cfg.CONF.security.agent_authorized_roles - self._message_queue \ - = resource_api.init_driver('monasca.messaging', - cfg.CONF.messaging.driver, - (['events'])) - self._alarms_repo = resource_api.init_driver( 'monasca.repositories', cfg.CONF.repositories.alarms_driver) @@ -92,10 +79,11 @@ class Alarms(AlarmsV2API): helpers.validate_authorization(req, self._default_authorized_roles) - result = '' - res.body = json.dumps(result, ensure_ascii=False).encode('utf8') - res.status = falcon.HTTP_200 - res.status = '501 Not Implemented' + tenant_id = helpers.get_tenant_id(req) + + self._alarm_delete(tenant_id, id) + + res.status = falcon.HTTP_204 @resource_api.Restify('/v2.0/alarms', method='get') def do_get_alarms(self, req, res): @@ -121,108 +109,102 @@ class Alarms(AlarmsV2API): res.body = json.dumps(result, ensure_ascii=False).encode('utf8') res.status = falcon.HTTP_200 + @resource_try_catch_block + def _alarm_delete(self, tenant_id, id): + + alarm_metric_rows = self._alarms_repo.get_alarm_metrics(id) + sub_alarm_rows = self._alarms_repo.get_sub_alarms(tenant_id, id) + + self._alarms_repo.delete_alarm(tenant_id, id) + + # alarm_definition_id is the same for all rows. + alarm_definition_id = sub_alarm_rows[0].alarm_definition_id + + self._send_alarm_deleted_event(tenant_id, alarm_definition_id, + alarm_metric_rows, sub_alarm_rows) + + @resource_try_catch_block def _alarm_show(self, req_uri, tenant_id, id): - try: + alarm_rows = self._alarms_repo.get_alarm(tenant_id, id) - alarm_rows = self._alarms_repo.get_alarm(tenant_id, id) + first_row = True + for alarm_row in alarm_rows: + if first_row: + ad = {u'id': alarm_row.alarm_definition_id, + u'name': alarm_row.alarm_definition_name, + u'severity': alarm_row.severity, } + helpers.add_links_to_resource(ad, + re.sub('alarms', + 'alarm-definitions', + req_uri), + rel=None) - first_row = True - for alarm_row in alarm_rows: - if first_row: - ad = {u'id': alarm_row.alarm_definition_id, - u'name': alarm_row.alarm_definition_name, - u'severity': alarm_row.severity, } - helpers.add_links_to_resource(ad, - re.sub('alarms', - 'alarm-definitions', - req_uri), - rel=None) + metrics = [] + alarm = {u'id': alarm_row.alarm_id, u'metrics': metrics, + u'state': alarm_row.state, + u'alarm_definition': ad} + helpers.add_links_to_resource(alarm, req_uri) - metrics = [] - alarm = {u'id': alarm_row.alarm_id, u'metrics': metrics, - u'state': alarm_row.state, - u'alarm_definition': ad} - helpers.add_links_to_resource(alarm, req_uri) + first_row = False - first_row = False + dimensions = {} + metric = {u'name': alarm_row.metric_name, + u'dimensions': dimensions} - 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] - 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) - metrics.append(metric) - - return alarm - - except DoesNotExistException: - raise falcon.HTTPNotFound() - except exceptions.RepositoryException as ex: - LOG.exception(ex) - msg = "".join(ex.message.args) - raise falcon.HTTPInternalServerError('Service unavailable', msg) - except Exception as ex: - LOG.exception(ex) - raise falcon.HTTPInternalServerError('Service unavailable', ex) + return alarm + @resource_try_catch_block def _alarm_list(self, req_uri, tenant_id, query_parms): - try: + alarm_rows = self._alarms_repo.get_alarms(tenant_id, query_parms) - alarm_rows = self._alarms_repo.get_alarms(tenant_id, query_parms) - - result = [] - - if not alarm_rows: - return result - - 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, } - helpers.add_links_to_resource(ad, - re.sub('alarms', - 'alarm-definitions', - req_uri), - rel=None) - - metrics = [] - alarm = {u'id': alarm_row.alarm_id, u'metrics': metrics, - u'state': alarm_row.state, - u'alarm_definition': ad} - helpers.add_links_to_resource(alarm, req_uri) - - 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) + result = [] + if not alarm_rows: return result - except exceptions.RepositoryException as ex: - LOG.exception(ex) - msg = "".join(ex.message.args) - raise falcon.HTTPInternalServerError('Service unavailable', msg) - except Exception as ex: - LOG.exception(ex) - raise falcon.HTTPInternalServerError('Service unavailable', ex) + 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, } + helpers.add_links_to_resource(ad, + re.sub('alarms', + 'alarm-definitions', + req_uri), + rel=None) + + metrics = [] + alarm = {u'id': alarm_row.alarm_id, u'metrics': metrics, + u'state': alarm_row.state, + u'alarm_definition': ad} + helpers.add_links_to_resource(alarm, req_uri) + + 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 diff --git a/monasca/v2/reference/resource.py b/monasca/v2/reference/resource.py new file mode 100644 index 000000000..458639862 --- /dev/null +++ b/monasca/v2/reference/resource.py @@ -0,0 +1,32 @@ +import falcon +from monasca.common.messaging import exceptions +from monasca.common.repositories.exceptions import DoesNotExistException +from monasca.openstack.common import log + + +LOG = log.getLogger(__name__) + + +def resource_try_catch_block(fun): + + def try_it(*args, **kwargs): + + try: + + return fun(*args, **kwargs) + + except falcon.HTTPNotFound: + raise + except DoesNotExistException: + raise falcon.HTTPNotFound + except falcon.HTTPBadRequest: + raise + except exceptions.RepositoryException as ex: + LOG.exception(ex) + msg = "".join(ex.message.args) + raise falcon.HTTPInternalServerError('Service unavailable', msg) + except Exception as ex: + LOG.exception(ex) + raise falcon.HTTPInternalServerError('Service unavailable', ex) + + return try_it