From fcaf0f8a8eff245558d1c46f8bb0723bd6fac77a Mon Sep 17 00:00:00 2001 From: Deklan Dieterly Date: Tue, 11 Nov 2014 13:18:29 -0700 Subject: [PATCH] Add alarm list resource Change-Id: Ic0906f1b498644116f43c0fa18608ccc38239619 --- monasca/api/alarms_api_v2.py | 2 +- monasca/api/server.py | 2 +- .../repositories/mysql/alarms_repository.py | 102 +++++++++++++- monasca/v2/reference/alarm_definitions.py | 8 +- monasca/v2/reference/alarms.py | 124 ++++++++++++++++-- setup.cfg | 3 + 6 files changed, 219 insertions(+), 22 deletions(-) diff --git a/monasca/api/alarms_api_v2.py b/monasca/api/alarms_api_v2.py index 10b824111..cd81c981c 100644 --- a/monasca/api/alarms_api_v2.py +++ b/monasca/api/alarms_api_v2.py @@ -36,7 +36,7 @@ class AlarmsV2API(object): res.status = '501 Not Implemented' @resource_api.Restify('/v2.0/alarms/', method='get') - def do_get_alarms(self, req, res, id): + def do_get_alarms(self, req, res): res.status = '501 Not Implemented' @resource_api.Restify('/v2.0/alarms/{id}', method='get') diff --git a/monasca/api/server.py b/monasca/api/server.py index 7bb664c78..313db0cec 100644 --- a/monasca/api/server.py +++ b/monasca/api/server.py @@ -176,7 +176,7 @@ def api_app(conf): # load the alarm definitions resource app.add_resource('alarms', - ALARM_DEFINITIONS_DISPATCHER_NAMESPACE, + ALARMS_DISPATCHER_NAMESPACE, cfg.CONF.dispatcher.driver, [conf]) return app diff --git a/monasca/common/repositories/mysql/alarms_repository.py b/monasca/common/repositories/mysql/alarms_repository.py index 478e0a5d8..e698f8343 100644 --- a/monasca/common/repositories/mysql/alarms_repository.py +++ b/monasca/common/repositories/mysql/alarms_repository.py @@ -14,8 +14,7 @@ import datetime import pyodbc -from oslo.config import cfg - +from monasca.common.repositories import exceptions from monasca.common.repositories import alarms_repository from monasca.common.repositories.mysql.mysql_repository import MySQLRepository from monasca.openstack.common import log @@ -25,9 +24,104 @@ LOG = log.getLogger(__name__) class AlarmsRepository(MySQLRepository, alarms_repository.AlarmsRepository): - def __init__(self): - super(AlarmsRepository, self).__init__() + def get_alarms(self, tenant_id, query_parms): + + try: + + parms = [tenant_id] + + select_clause = """ + select distinct a.id as alarm_id, a.state, + ad.id as alarm_definition_id, ad.name as alarm_definition_name, + ad.severity, + md.name as metric_name, mdg.dimensions as metric_dimensions + from alarm as a + inner join alarm_definition as ad + on ad.id = a.alarm_definition_id + 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, name, value, + 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 + """ + + order_by_clause = " order by a.id " + + where_clause = " where ad.tenant_id = ? " + + if 'alarm_definition_id' in query_parms: + parms.append(query_parms['alarm_definition_id']) + where_clause += " and ad.id = ? " + + if 'metric_name' in query_parms: + sub_select_clause = """ + and a.id in (select distinct a.id 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 (select distinct id from + metric_definition + where name = ?) as md + on md.id = mdd.metric_definition_id) + """ + + parms.append(query_parms['metric_name'].encode('utf8')) + where_clause += sub_select_clause + + if 'state' in query_parms: + parms.append(query_parms['state'].encode('utf8')) + where_clause += " and a.state = ? " + + if 'metric_dimensions' in query_parms: + sub_select_clause = """ + and a.id in (select distinct a.id 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 + """ + sub_select_parms = [] + i = 0 + for metric_dimension in query_parms['metric_dimensions'].split( + ','): + parsed_dimension = metric_dimension.split(':') + sub_select_clause += """ + inner join (select distinct dimension_set_id + from metric_dimension + where name = ? and value = ?) as md{} + on md{}.dimension_set_id = mdd.metric_dimension_set_id + """.format(i, i) + i += 1 + sub_select_parms += [parsed_dimension[0].encode('utf8'), + parsed_dimension[1].encode('utf8')] + + sub_select_clause += ")" + parms += sub_select_parms + where_clause += sub_select_clause + + query = select_clause + where_clause + order_by_clause + + cnxn, cursor = self._get_cnxn_cursor_tuple() + + cursor.execute(query, parms) + + rows = cursor.fetchall() + + self._commit_close_cnxn(cnxn) + + return rows + + except Exception as ex: + LOG.exception(ex) + raise exceptions.RepositoryException(ex) \ No newline at end of file diff --git a/monasca/v2/reference/alarm_definitions.py b/monasca/v2/reference/alarm_definitions.py index 3385c62ef..76a91573f 100644 --- a/monasca/v2/reference/alarm_definitions.py +++ b/monasca/v2/reference/alarm_definitions.py @@ -36,6 +36,7 @@ LOG = log.getLogger(__name__) class AlarmDefinitions(AlarmDefinitionsV2API): + def __init__(self, global_conf): try: @@ -52,9 +53,10 @@ 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._message_queue \ + = resource_api.init_driver('monasca.messaging', + cfg.CONF.messaging.driver, + (['events'])) self._alarm_definitions_repo = resource_api.init_driver( 'monasca.repositories', diff --git a/monasca/v2/reference/alarms.py b/monasca/v2/reference/alarms.py index 80f9c9155..f85abdcc9 100644 --- a/monasca/v2/reference/alarms.py +++ b/monasca/v2/reference/alarms.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. import json +from falcon.util.uri import parse_query_string from pyparsing import ParseException import falcon @@ -24,7 +25,8 @@ 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.helpers import read_json_msg_body from monasca.common.messaging import exceptions as message_queue_exceptions @@ -34,45 +36,141 @@ LOG = log.getLogger(__name__) class Alarms(AlarmsV2API): + def __init__(self, global_conf): + try: super(Alarms, self).__init__(global_conf) self._region = cfg.CONF.region - self._default_authorized_roles = cfg.CONF.security.default_authorized_roles - self._delegate_authorized_roles = cfg.CONF.security.delegate_authorized_roles - self._post_metrics_authorized_roles = cfg.CONF.security.default_authorized_roles + cfg.CONF.security.agent_authorized_roles + self._default_authorized_roles = \ + cfg.CONF.security.default_authorized_roles + self._delegate_authorized_roles = \ + cfg.CONF.security.delegate_authorized_roles + self._post_metrics_authorized_roles = \ + 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._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) + 'monasca.repositories', cfg.CONF.repositories.alarms_driver) except Exception as ex: LOG.exception(ex) raise exceptions.RepositoryException(ex) - @resource_api.Restify('/v2.0/alarms/{id}', method='put') def do_put_alarms(self, req, res, id): + + 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' @resource_api.Restify('/v2.0/alarms/{id}', method='patch') def do_patch_alarms(self, req, res, id): + + 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' @resource_api.Restify('/v2.0/alarms/{id}', method='delete') def do_delete_alarms(self, req, res, id): + + 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' - @resource_api.Restify('/v2.0/alarms/', method='get') - def do_get_alarms(self, req, res, id): - res.status = '501 Not Implemented' + @resource_api.Restify('/v2.0/alarms', method='get') + def do_get_alarms(self, req, res): + + helpers.validate_authorization(req, self._default_authorized_roles) + tenant_id = helpers.get_tenant_id(req) + + query_parms = parse_query_string(req.query_string) + + result = self._alarm_list(req.uri, tenant_id, query_parms) + + res.body = json.dumps(result, ensure_ascii=False).encode('utf8') + res.status = falcon.HTTP_200 @resource_api.Restify('/v2.0/alarms/{id}', method='get') def do_get_alarm_by_id(self, req, res, id): + + 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' + + def _alarm_list(self, req_uri, tenant_id, query_parms): + + try: + + 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(a) + + 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, req_uri) + + metrics = [] + a = { + u'id': alarm_row.alarm_id, + u'metrics': metrics, + u'state': alarm_row.state, + u'alarm_definition': ad + } + helpers.add_links_to_resource(a, 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(a) + + 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) diff --git a/setup.cfg b/setup.cfg index 5f607871f..76f6e2943 100644 --- a/setup.cfg +++ b/setup.cfg @@ -40,6 +40,9 @@ monasca.metrics_dispatcher = monasca.alarm_definitions_dispatcher = v2_reference = monasca.v2.reference.alarm_definitions:AlarmDefinitions +monasca.alarms_dispatcher = + v2_reference = monasca.v2.reference.alarms:Alarms + monasca.events_dispatcher = v2_reference = monasca.v2.reference.events:Events