Add alarm definition delete resource

Change-Id: If9a1b2c7b068056f352b41787922e6f7caa8b638
This commit is contained in:
Deklan Dieterly 2014-11-05 09:22:10 -07:00
parent a17aad2c8e
commit 30a411cff9
4 changed files with 331 additions and 59 deletions

View File

@ -23,3 +23,19 @@ class AlarmDefinitionsRepository(object):
expression, sub_expr_list, description, severity, match_by, alarm_actions,
undetermined_actions, ok_action):
pass
@abc.abstractmethod
def get_sub_alarms(self, tenant_id, alarm_definition_id):
pass
@abc.abstractmethod
def get_alarm_metrics(self, tenant_id, alarm_definition_id):
pass
@abc.abstractmethod
def delete_alarm_definition(self, tenant_id, alarm_definition_id):
pass
@abc.abstractmethod
def get_sub_alarm_definitions(self, alarm_definition_id):
pass

View File

@ -26,8 +26,8 @@ LOG = log.getLogger(__name__)
class AlarmDefinitionsRepository(
alarm_definitions_repository.AlarmDefinitionsRepository):
database_driver = 'MySQL ODBC 5.3 Unicode Driver'
alarm_definitions_repository.AlarmDefinitionsRepository):
database_driver = 'MySQL ODBC 5.3 ANSI Driver'
database_cnxn_template = 'DRIVER={' \
'%s};Server=%s;CHARSET=UTF8;Database=%s;Uid=%s' \
';Pwd=%s'
@ -50,6 +50,138 @@ class AlarmDefinitionsRepository(
LOG.exception(ex)
raise exceptions.RepositoryException(ex)
def _get_cnxn_cursor_tuple(self):
cnxn = pyodbc.connect(self._cnxn_string)
cursor = cnxn.cursor()
return cnxn, cursor
def _commit_close_cnxn(self, cnxn):
cnxn.commit()
cnxn.close()
def get_sub_alarms(self, tenant_id, alarm_definition_id):
try:
cnxn, cursor = self._get_cnxn_cursor_tuple()
cursor.execute(
"""select distinct sa.id as sub_alarm_id, sa.alarm_id,
sa.expression
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 ad.id = ?
""", [tenant_id, alarm_definition_id])
rows = cursor.fetchall()
self._commit_close_cnxn(cnxn)
return rows
except Exception as ex:
LOG.exception(ex)
raise exceptions.RepositoryException(ex)
def get_alarm_metrics(self, tenant_id, alarm_definition_id):
try:
cnxn, cursor = self._get_cnxn_cursor_tuple()
cursor.execute(
"""select distinct a.id as alarm_id, md.name, mdg.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,
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 ad.tenant_id = ? and ad.id = ?
order by a.id
""", [tenant_id, alarm_definition_id])
rows = cursor.fetchall()
self._commit_close_cnxn(cnxn)
return rows
except Exception as ex:
LOG.exception(ex)
raise exceptions.RepositoryException(ex)
def delete_alarm_definition(self, tenant_id, alarm_definition_id):
"""Soft delete the alarm definition.
Soft delete the alarm definition and hard delete any associated
alarms.
:param tenant_id:
:param alarm_definition_id:
:returns True: -- if alarm definition exists and was deleted.
:returns False: -- if the alarm definition does not exists.
:raises RepositoryException:
"""
try:
cnxn, cursor = self._get_cnxn_cursor_tuple()
cursor.execute(
"""update alarm_definition
set deleted_at = NOW()
where tenant_id = ? and id = ? and deleted_at is NULL""",
[tenant_id, alarm_definition_id]
)
if cursor.rowcount < 1:
self._commit_close_cnxn(cnxn)
return False
cursor.execute(
"""delete from alarm where alarm_definition_id = ?""",
[alarm_definition_id]
)
self._commit_close_cnxn(cnxn)
return True
except Exception as ex:
LOG.exception(ex)
raise exceptions.RepositoryException(ex)
def get_sub_alarm_definitions(self, alarm_definition_id):
try:
cnxn, cursor = self._get_cnxn_cursor_tuple()
cursor.execute(
"""select sad.*, sadd.dimensions
from sub_alarm_definition as sad
left join (select sub_alarm_definition_id,
group_concat(dimension_name, '=', value)
as dimensions
from sub_alarm_definition_dimension
group by sub_alarm_definition_id)
as sadd
on sadd.sub_alarm_definition_id = sad.id
where sad.alarm_definition_id = ?""",
[alarm_definition_id])
rows = cursor.fetchall()
self._commit_close_cnxn(cnxn)
return rows
except Exception as ex:
LOG.exception(ex)
raise exceptions.RepositoryException(ex)
def create_alarm_definition(self, tenant_id, name, expression,
sub_expr_list, description, severity, match_by,
@ -57,22 +189,21 @@ class AlarmDefinitionsRepository(
ok_actions):
try:
cnxn = pyodbc.connect(self._cnxn_string)
cursor = cnxn.cursor()
cnxn, cursor = self._get_cnxn_cursor_tuple()
now = datetime.datetime.utcnow()
alarm_definition_id = uuidutils.generate_uuid()
cursor.execute("insert into alarm_definition("
"id, "
"tenant_id, "
"name, "
"description, "
"expression, "
"severity, "
"match_by,"
"actions_enabled, "
"created_at, "
"updated_at) "
"values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
cursor.execute("""insert into alarm_definition(
id,
tenant_id,
name,
description,
expression,
severity,
match_by,
actions_enabled,
created_at,
updated_at)
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
alarm_definition_id, tenant_id, name.encode('utf8'),
description.encode('utf8'),
expression.encode('utf8'),
@ -82,18 +213,18 @@ class AlarmDefinitionsRepository(
for sub_expr in sub_expr_list:
sub_alarm_definition_id = uuidutils.generate_uuid()
sub_expr.id = sub_alarm_definition_id
cursor.execute("insert into sub_alarm_definition("
"id, "
"alarm_definition_id,"
"function, "
"metric_name, "
"operator, "
"threshold,"
"period, "
"periods, "
"created_at, "
"updated_at)"
" values(?,?,?,?,?,?,?,?,?,?)",
cursor.execute("""insert into sub_alarm_definition(
id,
alarm_definition_id,
function,
metric_name,
operator,
threshold,
period,
periods,
created_at,
updated_at)
values(?,?,?,?,?,?,?,?,?,?)""",
sub_alarm_definition_id, alarm_definition_id,
sub_expr.normalized_func.encode('utf8'),
sub_expr.normalized_metric_name.encode(
@ -107,11 +238,11 @@ class AlarmDefinitionsRepository(
for dimension in sub_expr.dimensions_as_list:
parsed_dimension = dimension.split('=')
cursor.execute(
"insert into sub_alarm_definition_dimension("
"sub_alarm_definition_id,"
"dimension_name,"
"value)"
"values(?,?,?)", sub_alarm_definition_id,
"""insert into sub_alarm_definition_dimension(
sub_alarm_definition_id,
dimension_name,
value)
values(?,?,?)""", sub_alarm_definition_id,
parsed_dimension[0].encode('utf8'),
parsed_dimension[1].encode('utf8'))
@ -123,8 +254,7 @@ class AlarmDefinitionsRepository(
self._insert_into_alarm_action(cursor, alarm_definition_id,
ok_actions, u"OK")
cnxn.commit()
cnxn.close()
self._commit_close_cnxn(cnxn)
return alarm_definition_id
@ -132,7 +262,6 @@ class AlarmDefinitionsRepository(
LOG.exception(ex)
raise exceptions.RepositoryException(ex)
def _insert_into_alarm_action(self, cursor, alarm_definition_id, actions,
alarm_state):
for action in actions:
@ -144,9 +273,11 @@ class AlarmDefinitionsRepository(
"Non-existent notification id {} submitted for {} "
"notification action".format(action.encode('utf8'),
alarm_state.encode('utf8')))
cursor.execute("insert into alarm_action("
"alarm_definition_id,"
"alarm_state,"
"action_id)"
"values(?,?,?)", alarm_definition_id,
cursor.execute("""insert into alarm_action(
alarm_definition_id,
alarm_state,
action_id)
values(?,?,?)""", alarm_definition_id,
alarm_state.encode('utf8'), action.encode('utf8'))

View File

@ -23,8 +23,7 @@ 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
@ -40,13 +39,9 @@ class AlarmDefinitions(AlarmDefinitionsV2API):
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,
@ -108,7 +103,41 @@ class AlarmDefinitions(AlarmDefinitionsV2API):
@resource_api.Restify('/v2.0/alarm-definitions/{id}', method='delete')
def do_delete_alarm_definitions(self, req, res, id):
res.status = '501 Not Implemented'
helpers.validate_authorization(req, self._default_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
self._alarm_definition_delete(tenant_id, id)
res.status = falcon.HTTP_204
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 _validate_alarm_definition(self, alarm_definition):
@ -116,7 +145,7 @@ class AlarmDefinitions(AlarmDefinitionsV2API):
schema_alarms.validate(alarm_definition)
except schemas_exceptions.ValidationException as ex:
LOG.debug(ex)
raise falcon.HTTPBadRequest('Bad reqeust', ex.message)
raise falcon.HTTPBadRequest('Bad request', ex.message)
def _alarm_definition_create(self, tenant_id, name, expression,
description, severity, match_by,
@ -125,8 +154,7 @@ class AlarmDefinitions(AlarmDefinitionsV2API):
try:
sub_expr_list = AlarmExprParser(expression).sub_expr_list
alarm_definition_id = \
self._alarm_definitions_repo.create_alarm_definition(
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)
@ -156,6 +184,103 @@ class AlarmDefinitions(AlarmDefinitionsV2API):
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):
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_alarm_definition_deleted_event(self, alarm_definition_id,
sub_alarm_definition_rows):
sub_alarm_definition_deleted_event_msg = {}
alarm_definition_deleted_event_msg = {u"alarm-definition-deleted": {
u"alarmDefinitionId": alarm_definition_id,
u'subAlarmMetricDefinitions': sub_alarm_definition_deleted_event_msg}}
for sub_alarm_definition in sub_alarm_definition_rows:
sub_alarm_definition_deleted_event_msg[sub_alarm_definition.id] = {
u'name': sub_alarm_definition.metric_name}
dimensions = {}
sub_alarm_definition_deleted_event_msg[sub_alarm_definition.id][
u'dimensions'] = dimensions
if sub_alarm_definition.dimensions:
for dimension in sub_alarm_definition.dimensions.split(','):
parsed_dimension = dimension.split('=')
dimensions[parsed_dimension[0]] = parsed_dimension[1]
self._send_event(alarm_definition_deleted_event_msg)
def _send_alarm_definition_created_event(self, tenant_id,
alarm_definition_id, name,
@ -164,8 +289,7 @@ class AlarmDefinitions(AlarmDefinitionsV2API):
alarm_definition_created_event_msg = {
u'alarm-definition-created': {u'tenantId': tenant_id,
u'alarmDefinitionId':
alarm_definition_id,
u'alarmDefinitionId': alarm_definition_id,
u'alarmName': name,
u'alarmDescription': description,
u'alarmExpression': expression,
@ -185,8 +309,7 @@ class AlarmDefinitions(AlarmDefinitionsV2API):
metric_definition[u'dimensions'] = dimensions
sub_expr_event_msg[sub_expr.id][
u'operator'] = sub_expr.normalized_operator
sub_expr_event_msg[sub_expr.id][
u'threshold'] = sub_expr.threshold
sub_expr_event_msg[sub_expr.id][u'threshold'] = sub_expr.threshold
sub_expr_event_msg[sub_expr.id][u'period'] = sub_expr.period
sub_expr_event_msg[sub_expr.id][u'periods'] = sub_expr.periods
sub_expr_event_msg[sub_expr.id][
@ -244,7 +367,7 @@ def get_query_alarm_definition_severity(alarm_definition):
severity = alarm_definition['severity']
severity = severity.decode('utf8').lower()
if severity not in ['low', 'medium', 'high', 'critical']:
raise falcon.HTTPBadRequest('Bad request, Invalid severity')
raise falcon.HTTPBadRequest('Bad request', 'Invalid severity')
return severity
else:
return ''

View File

@ -7,3 +7,5 @@ stevedore>=0.14
gunicorn>=19.1.0
oslo.config>=1.2.1
ujson>=1.33
Pyparsing>=2.0.3
pyodbc>=3.0.7