parent
d8270168e9
commit
9e688a1169
|
@ -1,51 +0,0 @@
|
|||
# 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.
|
||||
from monasca.common import resource_api
|
||||
from monasca.openstack.common import log
|
||||
|
||||
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
|
||||
|
||||
@resource_api.Restify('/v2.0/alarm-definitions', method='post')
|
||||
def do_post_alarm_definitions(self, req, res):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/alarm-definitions/{id}', method='get')
|
||||
def do_get_alarm_definition(self, req, res, id):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/alarm-definitions/{id}', method='put')
|
||||
def do_put_alarm_definitions(self, req, res, id):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/alarm-definitions', method='get')
|
||||
def do_get_alarm_definitions(self, req, res):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/alarm-definitions/{id}', method='patch')
|
||||
def do_patch_alarm_definitions(self, req, res, id):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/alarm-definitions/{id}', method='delete')
|
||||
def do_delete_alarm_definitions(self, req, res, id):
|
||||
res.status = '501 Not Implemented'
|
|
@ -1,55 +0,0 @@
|
|||
# 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.
|
||||
from monasca.common import resource_api
|
||||
from monasca.openstack.common import log
|
||||
|
||||
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
|
||||
|
||||
@resource_api.Restify('/v2.0/alarms/{id}', method='put')
|
||||
def do_put_alarms(self, req, res, id):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/alarms/{id}', method='patch')
|
||||
def do_patch_alarms(self, req, res, id):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/alarms/{id}', method='delete')
|
||||
def do_delete_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):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/alarms/{id}', method='get')
|
||||
def do_get_alarm_by_id(self, req, res, id):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/alarms/x', method='get')
|
||||
def do_get_alarms_state_history(self, req, res):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/alarms/{id}/state-history', method='get')
|
||||
def do_get_alarm_state_history(self, req, res, id):
|
||||
res.status = '501 Not Implemented'
|
|
@ -1,47 +0,0 @@
|
|||
# Copyright 2013 IBM Corp
|
||||
#
|
||||
# Author: Tong Li <litong01@us.ibm.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
from monasca.common import resource_api
|
||||
from monasca.openstack.common import log
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class V2API(object):
|
||||
def __init__(self, global_conf):
|
||||
LOG.debug('initializing V2API!')
|
||||
self.global_conf = global_conf
|
||||
|
||||
@resource_api.Restify('/v2.0/metrics', method='get')
|
||||
def do_get_metrics(self, req, res):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/metrics/', method='post')
|
||||
def do_post_metrics(self, req, res):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/{version_id}', method='get')
|
||||
def do_get_version(self, req, res, version_id):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/metrics/measurements', method='get')
|
||||
def do_get_measurements(self, req, res):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/metrics/statistics', method='get')
|
||||
def do_get_statistics(self, req, res):
|
||||
res.status = '501 Not Implemented'
|
|
@ -1,46 +0,0 @@
|
|||
# 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.
|
||||
|
||||
from monasca.common import resource_api
|
||||
from monasca.openstack.common import log
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class NotificationsV2API(object):
|
||||
|
||||
def __init__(self, global_conf):
|
||||
LOG.debug('initializing V2API!')
|
||||
self.global_conf = global_conf
|
||||
|
||||
@resource_api.Restify('/v2.0/notification-methods', method='post')
|
||||
def do_post_notification_methods(self, req, res):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/notification-methods/{id}', method='delete')
|
||||
def do_delete_notification_methods(self, req, res, id):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/notification-methods', method='get')
|
||||
def do_get_notification_methods(self, req, res):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/notification-methods/{id}', method='get')
|
||||
def do_get_notification_method(self, req, res, id):
|
||||
res.status = '501 Not Implemented'
|
||||
|
||||
@resource_api.Restify('/v2.0/notification-methods/{id}', method='put')
|
||||
def do_put_notification_methods(self, req, res, id):
|
||||
res.status = '501 Not Implemented'
|
|
@ -1,297 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# 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 itertools
|
||||
import sys
|
||||
|
||||
import pyparsing
|
||||
|
||||
|
||||
class SubExpr(object):
|
||||
|
||||
def __init__(self, tokens):
|
||||
|
||||
self._sub_expr = tokens
|
||||
self._func = tokens.func
|
||||
self._metric_name = tokens.metric_name
|
||||
self._dimensions = tokens.dimensions.dimensions_list
|
||||
self._operator = tokens.relational_op
|
||||
self._threshold = tokens.threshold
|
||||
self._period = tokens.period
|
||||
self._periods = tokens.periods
|
||||
self._id = None
|
||||
|
||||
@property
|
||||
def sub_expr_str(self):
|
||||
"""Get the entire sub expression as a string with no spaces."""
|
||||
return "".join(list(itertools.chain(*self._sub_expr)))
|
||||
|
||||
@property
|
||||
def fmtd_sub_expr_str(self):
|
||||
"""Get the entire sub expressions as a string with spaces."""
|
||||
result = "{}({}".format(self._func.encode('utf8'),
|
||||
self._metric_name.encode('utf8'))
|
||||
|
||||
if self._dimensions:
|
||||
result += "{{{}}}".format(self._dimensions.encode('utf8'))
|
||||
|
||||
if self._period:
|
||||
result += ", {}".format(self._period.encode('utf8'))
|
||||
|
||||
result += ")"
|
||||
|
||||
result += " {} {}".format(self._operator.encode('utf8'),
|
||||
self._threshold.encode('utf8'))
|
||||
|
||||
if self._periods:
|
||||
result += " times {}".format(self._periods.encode('utf8'))
|
||||
|
||||
return result.decode('utf8')
|
||||
|
||||
@property
|
||||
def dimensions_str(self):
|
||||
"""Get all the dimensions as a single comma delimited string."""
|
||||
return self._dimensions
|
||||
|
||||
@property
|
||||
def operands_list(self):
|
||||
"""Get this sub expression as a list."""
|
||||
return [self]
|
||||
|
||||
@property
|
||||
def func(self):
|
||||
"""Get the function as it appears in the orig expression."""
|
||||
return self._func
|
||||
|
||||
@property
|
||||
def normalized_func(self):
|
||||
"""Get the function upper-cased."""
|
||||
return self._func.upper()
|
||||
|
||||
@property
|
||||
def metric_name(self):
|
||||
"""Get the metric name as it appears in the orig expression."""
|
||||
return self._metric_name
|
||||
|
||||
@property
|
||||
def normalized_metric_name(self):
|
||||
"""Get the metric name lower-cased."""
|
||||
return self._metric_name.lower()
|
||||
|
||||
@property
|
||||
def dimensions(self):
|
||||
"""Get the dimensions."""
|
||||
return self._dimensions
|
||||
|
||||
@property
|
||||
def dimensions_as_list(self):
|
||||
"""Get the dimensions as a list."""
|
||||
if self._dimensions:
|
||||
return self._dimensions.split(",")
|
||||
else:
|
||||
return []
|
||||
|
||||
@property
|
||||
def operator(self):
|
||||
"""Get the operator."""
|
||||
return self._operator
|
||||
|
||||
@property
|
||||
def threshold(self):
|
||||
"""Get the threshold value."""
|
||||
return self._threshold
|
||||
|
||||
@property
|
||||
def period(self):
|
||||
"""Get the period. Default is 60 seconds."""
|
||||
if self._period:
|
||||
return self._period
|
||||
else:
|
||||
return u'60'
|
||||
|
||||
@property
|
||||
def periods(self):
|
||||
"""Get the periods. Default is 1."""
|
||||
if self._periods:
|
||||
return self._periods
|
||||
else:
|
||||
return u'1'
|
||||
|
||||
@property
|
||||
def normalized_operator(self):
|
||||
"""Get the operator as one of LT, GT, LTE, or GTE."""
|
||||
if self._operator.lower() == "lt" or self._operator == "<":
|
||||
return u"LT"
|
||||
elif self._operator.lower() == "gt" or self._operator == ">":
|
||||
return u"GT"
|
||||
elif self._operator.lower() == "lte" or self._operator == "<=":
|
||||
return u"LTE"
|
||||
elif self._operator.lower() == "gte" or self._operator == ">=":
|
||||
return u"GTE"
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
"""Get the id used to identify this sub expression in the repo."""
|
||||
return self._id
|
||||
|
||||
@id.setter
|
||||
def id(self, id):
|
||||
"""Set the d used to identify this sub expression in the repo."""
|
||||
self._id = id
|
||||
|
||||
|
||||
class BinaryOp(object):
|
||||
def __init__(self, tokens):
|
||||
self.op = tokens[0][1]
|
||||
self.operands = tokens[0][0::2]
|
||||
|
||||
@property
|
||||
def operands_list(self):
|
||||
return ([sub_operand for operand in self.operands for sub_operand in
|
||||
operand.operands_list])
|
||||
|
||||
|
||||
class AndSubExpr(BinaryOp):
|
||||
"""Expand later as needed."""
|
||||
pass
|
||||
|
||||
|
||||
class OrSubExpr(BinaryOp):
|
||||
"""Expand later as needed."""
|
||||
pass
|
||||
|
||||
|
||||
COMMA = pyparsing.Literal(",")
|
||||
LPAREN = pyparsing.Literal("(")
|
||||
RPAREN = pyparsing.Literal(")")
|
||||
EQUAL = pyparsing.Literal("=")
|
||||
LBRACE = pyparsing.Literal("{")
|
||||
RBRACE = pyparsing.Literal("}")
|
||||
|
||||
# Initialize non-ascii unicode code points in the Basic Multilingual Plane.
|
||||
unicode_printables = u''.join(
|
||||
unichr(c) for c in xrange(128, 65536) if not unichr(c).isspace())
|
||||
|
||||
# Does not like comma. No Literals from above allowed.
|
||||
valid_identifier_chars = (
|
||||
(unicode_printables + pyparsing.alphanums + ".-_#!$%&'*+/:;?@[\\]^`|~"))
|
||||
|
||||
metric_name = (
|
||||
pyparsing.Word(valid_identifier_chars, min=1, max=255)("metric_name"))
|
||||
dimension_name = pyparsing.Word(valid_identifier_chars, min=1, max=255)
|
||||
dimension_value = pyparsing.Word(valid_identifier_chars, min=1, max=255)
|
||||
|
||||
integer_number = pyparsing.Word(pyparsing.nums)
|
||||
decimal_number = pyparsing.Word(pyparsing.nums + ".")
|
||||
|
||||
max = pyparsing.CaselessLiteral("max")
|
||||
min = pyparsing.CaselessLiteral("min")
|
||||
avg = pyparsing.CaselessLiteral("avg")
|
||||
count = pyparsing.CaselessLiteral("count")
|
||||
sum = pyparsing.CaselessLiteral("sum")
|
||||
func = (max | min | avg | count | sum)("func")
|
||||
|
||||
less_than_op = (
|
||||
(pyparsing.CaselessLiteral("<") | pyparsing.CaselessLiteral("lt")))
|
||||
less_than_eq_op = (
|
||||
(pyparsing.CaselessLiteral("<=") | pyparsing.CaselessLiteral("lte")))
|
||||
greater_than_op = (
|
||||
(pyparsing.CaselessLiteral(">") | pyparsing.CaselessLiteral("gt")))
|
||||
greater_than_eq_op = (
|
||||
(pyparsing.CaselessLiteral(">=") | pyparsing.CaselessLiteral("gte")))
|
||||
|
||||
# Order is important. Put longer prefix first.
|
||||
relational_op = (
|
||||
less_than_eq_op | less_than_op | greater_than_eq_op | greater_than_op)(
|
||||
"relational_op")
|
||||
|
||||
AND = pyparsing.CaselessLiteral("and") | pyparsing.CaselessLiteral("&&")
|
||||
OR = pyparsing.CaselessLiteral("or") | pyparsing.CaselessLiteral("||")
|
||||
logical_op = (AND | OR)("logical_op")
|
||||
|
||||
times = pyparsing.CaselessLiteral("times")
|
||||
|
||||
dimension = pyparsing.Group(dimension_name + EQUAL + dimension_value)
|
||||
|
||||
# Cannot have any whitespace after the comma delimiter.
|
||||
dimension_list = pyparsing.Group(pyparsing.Optional(
|
||||
LBRACE + pyparsing.delimitedList(dimension, delim=',', combine=True)(
|
||||
"dimensions_list") + RBRACE))
|
||||
|
||||
metric = metric_name + dimension_list("dimensions")
|
||||
period = integer_number("period")
|
||||
threshold = decimal_number("threshold")
|
||||
periods = integer_number("periods")
|
||||
|
||||
expression = pyparsing.Forward()
|
||||
|
||||
sub_expression = (func + LPAREN + metric + pyparsing.Optional(
|
||||
COMMA + period) + RPAREN + relational_op + threshold + pyparsing.Optional(
|
||||
times + periods) | LPAREN + expression + RPAREN)
|
||||
|
||||
sub_expression.setParseAction(SubExpr)
|
||||
|
||||
expression = (
|
||||
pyparsing.operatorPrecedence(sub_expression,
|
||||
[(AND, 2, pyparsing.opAssoc.LEFT, AndSubExpr),
|
||||
(OR, 2, pyparsing.opAssoc.LEFT, OrSubExpr)]))
|
||||
|
||||
|
||||
class AlarmExprParser(object):
|
||||
def __init__(self, expr):
|
||||
self._expr = expr
|
||||
|
||||
@property
|
||||
def sub_expr_list(self):
|
||||
# Remove all spaces before parsing. Simple, quick fix for whitespace
|
||||
# issue with dimension list not allowing whitespace after comma.
|
||||
parseResult = (expression + pyparsing.stringEnd).parseString(
|
||||
self._expr.replace(' ', ''))
|
||||
sub_expr_list = parseResult[0].operands_list
|
||||
return sub_expr_list
|
||||
|
||||
|
||||
def main():
|
||||
"""Used for development and testing."""
|
||||
|
||||
expr0 = (
|
||||
"max(-_.千幸福的笑脸{घोड़ा=馬, "
|
||||
"dn2=dv2,千幸福的笑脸घ=千幸福的笑脸घ}) gte 100 "
|
||||
"times 3 && "
|
||||
"(min(ເຮືອນ{dn3=dv3,家=дом}) < 10 or sum(biz{dn5=dv5}) >9 9and "
|
||||
"count(fizzle) lt 0 or count(baz) > 1)".decode('utf8'))
|
||||
|
||||
expr1 = ("max(foo{hostname=mini-mon,千=千}, 120) > 100 and (max(bar)>100 "
|
||||
" or max(biz)>100)".decode('utf8'))
|
||||
|
||||
expr2 = "max(foo)>=100"
|
||||
|
||||
for expr in (expr0, expr1, expr2):
|
||||
print ('orig expr: {}'.format(expr.encode('utf8')))
|
||||
alarmExprParser = AlarmExprParser(expr)
|
||||
sub_expr = alarmExprParser.sub_expr_list
|
||||
for sub_expression in sub_expr:
|
||||
print ('sub expr: {}'.format(
|
||||
sub_expression.sub_expr_str.encode('utf8')))
|
||||
print ('fmtd sub expr: {}'.format(
|
||||
sub_expression.fmtd_sub_expr_str.encode('utf8')))
|
||||
print ('sub_expr dimensions: {}'.format(
|
||||
sub_expression.dimensions_str.encode('utf8')))
|
||||
print ()
|
||||
print ()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
|
@ -1,125 +0,0 @@
|
|||
# Copyright 2013 IBM Corp
|
||||
#
|
||||
# Author: Tong Li <litong01@us.ibm.com>
|
||||
#
|
||||
# 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 StringIO
|
||||
try:
|
||||
import ujson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
|
||||
class MetricValidator(object):
|
||||
"""middleware that validate the metric input stream.
|
||||
|
||||
This middleware checks if the input stream actually follows metric spec
|
||||
and all the messages in the request has valid metric data. If the body
|
||||
is valid json and compliant with the spec, then the request will forward
|
||||
the request to the next in the pipeline, otherwise, it will reject the
|
||||
request with response code of 400 or 406.
|
||||
"""
|
||||
def __init__(self, app, conf):
|
||||
self.app = app
|
||||
self.conf = conf
|
||||
|
||||
def _is_valid_metric(self, metric):
|
||||
"""Validate a message
|
||||
|
||||
The external message format is
|
||||
{
|
||||
"name":"name1",
|
||||
"dimensions":{
|
||||
"key1":"value1",
|
||||
"key2":"value2"
|
||||
},
|
||||
"timestamp":1405630174,
|
||||
"value":1.0
|
||||
}
|
||||
|
||||
Once this is validated, the message needs to be transformed into
|
||||
the following internal format:
|
||||
|
||||
The current valid message format is as follows (interna):
|
||||
{
|
||||
"metric": {"something": "The metric as a JSON object"},
|
||||
"meta": {
|
||||
"tenantId": "the tenant ID acquired",
|
||||
"region": "the region that the metric was submitted under",
|
||||
},
|
||||
"creation_time": "the time when the API received the metric",
|
||||
}
|
||||
"""
|
||||
if (metric.get('name') and metric.get('dimensions') and
|
||||
metric.get('timestamp') and metric.get('value')):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __call__(self, env, start_response):
|
||||
# if request starts with /datapoints/, then let it go on.
|
||||
# this login middle
|
||||
if (env.get('PATH_INFO', '').startswith('/v2.0/metrics') and
|
||||
env.get('REQUEST_METHOD', '') == 'POST'):
|
||||
# We only check the requests which are posting against metrics
|
||||
# endpoint
|
||||
try:
|
||||
body = env['wsgi.input'].read()
|
||||
metrics = json.loads(body)
|
||||
# Do business logic validation here.
|
||||
is_valid = True
|
||||
if isinstance(metrics, list):
|
||||
for metric in metrics:
|
||||
if not self._is_valid_metric(metric):
|
||||
is_valid = False
|
||||
break
|
||||
else:
|
||||
is_valid = self._is_valid_metric(metrics)
|
||||
|
||||
if is_valid:
|
||||
# If the message is valid, then wrap it into this internal
|
||||
# format. The tenantId should be available from the
|
||||
# request since this should have been authenticated.
|
||||
# ideally this transformation should be done somewhere
|
||||
# else. For the sake of simplicity, do the simple one
|
||||
# here to make the life a bit easier.
|
||||
|
||||
# TODO(HP) Add logic to get region id from request header
|
||||
# HTTP_X_SERVICE_CATALOG, then find endpoints, then region
|
||||
region_id = None
|
||||
msg = {'metric': metrics,
|
||||
'meta': {'tenantId': env.get('HTTP_X_PROJECT_ID'),
|
||||
'region': region_id},
|
||||
'creation_time': datetime.datetime.now()}
|
||||
env['wsgi.input'] = StringIO.StringIO(json.dumps(msg))
|
||||
return self.app(env, start_response)
|
||||
except Exception:
|
||||
pass
|
||||
# It is either invalid or exceptioned out while parsing json
|
||||
# we will send the request back with 400.
|
||||
start_response("400 Bad Request", [], '')
|
||||
return []
|
||||
else:
|
||||
# not a metric post request, move on.
|
||||
return self.app(env, start_response)
|
||||
|
||||
|
||||
def filter_factory(global_conf, **local_conf):
|
||||
|
||||
def validator_filter(app):
|
||||
return MetricValidator(app, local_conf)
|
||||
|
||||
return validator_filter
|
|
@ -1,52 +0,0 @@
|
|||
# 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 voluptuous
|
||||
|
||||
from monasca.openstack.common import log
|
||||
from monasca.v2.common.schemas import exceptions
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
alarm_definition_schema = {
|
||||
voluptuous.Required('name'): voluptuous.All(voluptuous.Any(str, unicode),
|
||||
voluptuous.Length(max=250)),
|
||||
voluptuous.Required('expression'): voluptuous.All(
|
||||
voluptuous.Any(str, unicode), voluptuous.Length(max=4096)),
|
||||
voluptuous.Optional('description'): voluptuous.All(
|
||||
voluptuous.Any(str, unicode), voluptuous.Length(max=250)),
|
||||
voluptuous.Optional('severity'): voluptuous.All(
|
||||
voluptuous.Any('low', 'medium', 'high', 'critical', 'LOW', "MEDIUM",
|
||||
'HIGH', 'CRITICAL')),
|
||||
voluptuous.Optional('match_by'): voluptuous.All(
|
||||
voluptuous.Any([unicode], [str]), voluptuous.Length(max=255)),
|
||||
voluptuous.Optional('ok_actions'): voluptuous.All(
|
||||
voluptuous.Any([str], [unicode]), voluptuous.Length(max=400)),
|
||||
voluptuous.Optional('alarm_actions'): voluptuous.All(
|
||||
voluptuous.Any([str], [unicode]), voluptuous.Length(max=400)),
|
||||
voluptuous.Optional('undetermined_actions'): voluptuous.All(
|
||||
voluptuous.Any([str], [unicode]), voluptuous.Length(max=400)),
|
||||
voluptuous.Optional('actions_enabled'): bool}
|
||||
|
||||
request_body_schema = voluptuous.Schema(alarm_definition_schema, required=True,
|
||||
extra=True)
|
||||
|
||||
|
||||
def validate(msg):
|
||||
try:
|
||||
request_body_schema(msg)
|
||||
except Exception as ex:
|
||||
LOG.debug(ex)
|
||||
raise exceptions.ValidationException(str(ex))
|
|
@ -1,33 +0,0 @@
|
|||
# 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 voluptuous
|
||||
|
||||
from monasca.openstack.common import log
|
||||
from monasca.v2.common.schemas import exceptions
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
dimensions_schema = voluptuous.Schema({
|
||||
voluptuous.All(voluptuous.Any(str, unicode),
|
||||
voluptuous.Length(max=255)): voluptuous.All(
|
||||
voluptuous.Any(str, unicode), voluptuous.Length(max=255))})
|
||||
|
||||
|
||||
def validate(dimensions):
|
||||
try:
|
||||
dimensions_schema(dimensions)
|
||||
except Exception as ex:
|
||||
LOG.debug(ex)
|
||||
raise exceptions.ValidationException(str(ex))
|
|
@ -1,31 +0,0 @@
|
|||
# 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 voluptuous
|
||||
|
||||
from monasca.openstack.common import log
|
||||
from monasca.v2.common.schemas import exceptions
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
metric_name_schema = voluptuous.Schema(
|
||||
voluptuous.All(voluptuous.Any(str, unicode), voluptuous.Length(max=64)))
|
||||
|
||||
|
||||
def validate(name):
|
||||
try:
|
||||
metric_name_schema(name)
|
||||
except Exception as ex:
|
||||
LOG.debug(ex)
|
||||
raise exceptions.ValidationException(str(ex))
|
|
@ -1,40 +0,0 @@
|
|||
# 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 voluptuous
|
||||
|
||||
from monasca.openstack.common import log
|
||||
from monasca.v2.common.schemas import dimensions_schema
|
||||
from monasca.v2.common.schemas import exceptions
|
||||
from monasca.v2.common.schemas import metric_name_schema
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
metric_schema = {
|
||||
voluptuous.Required('name'): metric_name_schema.metric_name_schema,
|
||||
voluptuous.Optional('dimensions'): dimensions_schema.dimensions_schema,
|
||||
voluptuous.Required('timestamp'): voluptuous.All(
|
||||
voluptuous.Any(int, float), voluptuous.Range(min=0)),
|
||||
voluptuous.Required('value'): voluptuous.Any(int, float)}
|
||||
|
||||
request_body_schema = voluptuous.Schema(
|
||||
voluptuous.Any(metric_schema, [metric_schema]))
|
||||
|
||||
|
||||
def validate(msg):
|
||||
try:
|
||||
request_body_schema(msg)
|
||||
except Exception as ex:
|
||||
LOG.debug(ex)
|
||||
raise exceptions.ValidationException(str(ex))
|
|
@ -1,41 +0,0 @@
|
|||
# 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 voluptuous
|
||||
|
||||
from monasca.openstack.common import log
|
||||
from monasca.v2.common.schemas import exceptions
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
notification_schema = {
|
||||
voluptuous.Required('name'): voluptuous.Schema(
|
||||
voluptuous.All(voluptuous.Any(str, unicode),
|
||||
voluptuous.Length(max=250))),
|
||||
voluptuous.Required('type'): voluptuous.Schema(
|
||||
voluptuous.Any("EMAIL", "email", "WEBHOOK", "webhook",
|
||||
"PAGERDUTY", "pagerduty")),
|
||||
voluptuous.Required('address'): voluptuous.Schema(
|
||||
voluptuous.All(voluptuous.Any(str, unicode),
|
||||
voluptuous.Length(max=100)))}
|
||||
|
||||
request_body_schema = voluptuous.Schema(voluptuous.Any(notification_schema))
|
||||
|
||||
|
||||
def validate(msg):
|
||||
try:
|
||||
request_body_schema(msg)
|
||||
except Exception as ex:
|
||||
LOG.debug(ex)
|
||||
raise exceptions.ValidationException(str(ex))
|
|
@ -1,687 +0,0 @@
|
|||
# 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 re
|
||||
|
||||
import falcon
|
||||
from oslo.config import cfg
|
||||
import pyparsing
|
||||
|
||||
from monasca.api import alarm_definitions_api_v2
|
||||
from monasca.common.repositories import exceptions
|
||||
from monasca.common import resource_api
|
||||
import monasca.expression_parser.alarm_expr_parser
|
||||
from monasca.openstack.common import log
|
||||
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 import alarming
|
||||
from monasca.v2.reference import helpers
|
||||
from monasca.v2.reference import resource
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class AlarmDefinitions(alarm_definitions_api_v2.AlarmDefinitionsV2API,
|
||||
alarming.Alarming):
|
||||
def __init__(self, global_conf):
|
||||
|
||||
try:
|
||||
|
||||
super(AlarmDefinitions, 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._alarm_definitions_repo = resource_api.init_driver(
|
||||
'monasca.repositories',
|
||||
cfg.CONF.repositories.alarm_definitions_driver)
|
||||
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
raise exceptions.RepositoryException(ex)
|
||||
|
||||
@resource_api.Restify('/v2.0/alarm-definitions', method='post')
|
||||
def do_post_alarm_definitions(self, req, res):
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
|
||||
alarm_definition = helpers.read_json_msg_body(req)
|
||||
|
||||
self._validate_alarm_definition(alarm_definition)
|
||||
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
name = get_query_alarm_definition_name(alarm_definition)
|
||||
expression = get_query_alarm_definition_expression(alarm_definition)
|
||||
description = get_query_alarm_definition_description(alarm_definition)
|
||||
severity = get_query_alarm_definition_severity(alarm_definition)
|
||||
match_by = get_query_alarm_definition_match_by(alarm_definition)
|
||||
alarm_actions = get_query_alarm_definition_alarm_actions(
|
||||
alarm_definition)
|
||||
undetermined_actions = get_query_alarm_definition_undetermined_actions(
|
||||
alarm_definition)
|
||||
ok_actions = get_query_ok_actions(alarm_definition)
|
||||
|
||||
result = self._alarm_definition_create(tenant_id, name, expression,
|
||||
description, severity, match_by,
|
||||
alarm_actions,
|
||||
undetermined_actions,
|
||||
ok_actions)
|
||||
|
||||
helpers.add_links_to_resource(result, req.uri)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_201
|
||||
|
||||
@resource_api.Restify('/v2.0/alarm-definitions/{id}', method='get')
|
||||
def do_get_alarm_definition(self, req, res, id):
|
||||
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
|
||||
result = self._alarm_definition_show(tenant_id, id)
|
||||
|
||||
helpers.add_links_to_resource(result, re.sub('/' + id, '', req.uri))
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
@resource_api.Restify('/v2.0/alarm-definitions/{id}', method='put')
|
||||
def do_put_alarm_definitions(self, req, res, id):
|
||||
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
|
||||
alarm_definition = helpers.read_json_msg_body(req)
|
||||
|
||||
self._validate_alarm_definition(alarm_definition)
|
||||
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
|
||||
# Mandatory positional args
|
||||
name = get_query_alarm_definition_name(alarm_definition)
|
||||
expression = get_query_alarm_definition_expression(alarm_definition)
|
||||
actions_enabled = (
|
||||
get_query_alarm_definition_actions_enabled(alarm_definition,
|
||||
required=True))
|
||||
|
||||
# Validator makes actions_enabled optional. So, check it here.
|
||||
if not actions_enabled:
|
||||
raise falcon.HTTPBadRequest('Bad request', 'Missing '
|
||||
'actions_enabled')
|
||||
|
||||
# Optional args
|
||||
description = get_query_alarm_definition_description(alarm_definition,
|
||||
return_none=True)
|
||||
alarm_actions = get_query_alarm_definition_alarm_actions(
|
||||
alarm_definition, return_none=True)
|
||||
ok_actions = get_query_ok_actions(alarm_definition, return_none=True)
|
||||
undetermined_actions = get_query_alarm_definition_undetermined_actions(
|
||||
alarm_definition, return_none=True)
|
||||
match_by = get_query_alarm_definition_match_by(alarm_definition,
|
||||
return_none=True)
|
||||
severity = get_query_alarm_definition_severity(alarm_definition,
|
||||
return_none=True)
|
||||
|
||||
result = self._alarm_definition_update_or_patch(tenant_id,
|
||||
id,
|
||||
name,
|
||||
expression,
|
||||
actions_enabled,
|
||||
description,
|
||||
alarm_actions,
|
||||
ok_actions,
|
||||
undetermined_actions,
|
||||
match_by,
|
||||
severity,
|
||||
patch=False)
|
||||
|
||||
helpers.add_links_to_resource(result, req.uri)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
@resource_api.Restify('/v2.0/alarm-definitions', method='get')
|
||||
def do_get_alarm_definitions(self, req, res):
|
||||
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
name = helpers.get_query_name(req)
|
||||
dimensions = helpers.get_query_dimensions(req)
|
||||
offset = helpers.normalize_offset(helpers.get_query_param(req,
|
||||
'offset'))
|
||||
|
||||
result = self._alarm_definition_list(tenant_id, name, dimensions,
|
||||
req.uri, offset)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
@resource_api.Restify('/v2.0/alarm-definitions/{id}', method='patch')
|
||||
def do_patch_alarm_definitions(self, req, res, id):
|
||||
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
|
||||
alarm_definition = helpers.read_json_msg_body(req)
|
||||
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
|
||||
# Optional args
|
||||
name = get_query_alarm_definition_name(alarm_definition,
|
||||
return_none=True)
|
||||
expression = get_query_alarm_definition_expression(alarm_definition,
|
||||
return_none=True)
|
||||
actions_enabled = (
|
||||
get_query_alarm_definition_actions_enabled(alarm_definition,
|
||||
return_none=True))
|
||||
|
||||
description = get_query_alarm_definition_description(alarm_definition,
|
||||
return_none=True)
|
||||
alarm_actions = get_query_alarm_definition_alarm_actions(
|
||||
alarm_definition, return_none=True)
|
||||
ok_actions = get_query_ok_actions(alarm_definition, return_none=True)
|
||||
undetermined_actions = get_query_alarm_definition_undetermined_actions(
|
||||
alarm_definition, return_none=True)
|
||||
match_by = get_query_alarm_definition_match_by(alarm_definition,
|
||||
return_none=True)
|
||||
severity = get_query_alarm_definition_severity(alarm_definition,
|
||||
return_none=True)
|
||||
|
||||
result = self._alarm_definition_update_or_patch(tenant_id,
|
||||
id,
|
||||
name,
|
||||
expression,
|
||||
actions_enabled,
|
||||
description,
|
||||
alarm_actions,
|
||||
ok_actions,
|
||||
undetermined_actions,
|
||||
match_by,
|
||||
severity,
|
||||
patch=True)
|
||||
|
||||
helpers.add_links_to_resource(result, req.uri)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
@resource_api.Restify('/v2.0/alarm-definitions/{id}', method='delete')
|
||||
def do_delete_alarm_definitions(self, req, res, id):
|
||||
|
||||
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
|
||||
|
||||
@resource.resource_try_catch_block
|
||||
def _alarm_definition_show(self, tenant_id, id):
|
||||
|
||||
alarm_definition_row = (
|
||||
self._alarm_definitions_repo.get_alarm_definition(tenant_id, id))
|
||||
|
||||
return self._build_alarm_definition_show_result(alarm_definition_row)
|
||||
|
||||
def _build_alarm_definition_show_result(self, alarm_definition_row):
|
||||
|
||||
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').upper()}
|
||||
|
||||
return result
|
||||
|
||||
@resource.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_event(u'alarm-deleted', tenant_id, id,
|
||||
alarm_metric_rows, sub_alarm_rows)
|
||||
|
||||
@resource.resource_try_catch_block
|
||||
def _alarm_definition_list(self, tenant_id, name, dimensions, req_uri,
|
||||
offset):
|
||||
|
||||
alarm_definition_rows = (
|
||||
self._alarm_definitions_repo.get_alarm_definitions(tenant_id, name,
|
||||
dimensions,
|
||||
offset))
|
||||
|
||||
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') if alarm_definition_row['description'] else u'',
|
||||
u'expression': alarm_definition_row['expression'].decode(
|
||||
'utf8'), u'match_by': match_by,
|
||||
u'severity': alarm_definition_row['severity'].decode(
|
||||
'utf8').upper(),
|
||||
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)
|
||||
|
||||
result = helpers.paginate(result, req_uri, offset)
|
||||
|
||||
return result
|
||||
|
||||
def _validate_alarm_definition(self, alarm_definition):
|
||||
|
||||
try:
|
||||
schema_alarms.validate(alarm_definition)
|
||||
except schemas_exceptions.ValidationException as ex:
|
||||
LOG.debug(ex)
|
||||
raise falcon.HTTPBadRequest('Bad request', ex.message)
|
||||
|
||||
@resource.resource_try_catch_block
|
||||
def _alarm_definition_update_or_patch(self, tenant_id,
|
||||
id,
|
||||
name,
|
||||
expression,
|
||||
actions_enabled,
|
||||
description,
|
||||
alarm_actions,
|
||||
ok_actions,
|
||||
undetermined_actions,
|
||||
match_by,
|
||||
severity,
|
||||
patch):
|
||||
|
||||
if expression:
|
||||
try:
|
||||
sub_expr_list = (
|
||||
monasca.expression_parser.alarm_expr_parser.
|
||||
AlarmExprParser(expression).sub_expr_list)
|
||||
|
||||
except pyparsing.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('utf8'))
|
||||
raise falcon.HTTPBadRequest(title, msg)
|
||||
else:
|
||||
sub_expr_list = None
|
||||
|
||||
alarm_def_row, sub_alarm_def_dicts = (
|
||||
self._alarm_definitions_repo.update_or_patch_alarm_definition(
|
||||
tenant_id,
|
||||
id,
|
||||
name,
|
||||
expression,
|
||||
sub_expr_list,
|
||||
actions_enabled,
|
||||
description,
|
||||
alarm_actions,
|
||||
ok_actions,
|
||||
undetermined_actions,
|
||||
match_by,
|
||||
severity,
|
||||
patch))
|
||||
|
||||
old_sub_alarm_def_event_dict = (
|
||||
self._build_sub_alarm_def_update_dict(
|
||||
sub_alarm_def_dicts['old']))
|
||||
|
||||
new_sub_alarm_def_event_dict = (
|
||||
self._build_sub_alarm_def_update_dict(sub_alarm_def_dicts[
|
||||
'new']))
|
||||
|
||||
changed_sub_alarm_def_event_dict = (
|
||||
self._build_sub_alarm_def_update_dict(sub_alarm_def_dicts[
|
||||
'changed']))
|
||||
|
||||
unchanged_sub_alarm_def_event_dict = (
|
||||
self._build_sub_alarm_def_update_dict(sub_alarm_def_dicts[
|
||||
'unchanged']))
|
||||
|
||||
alarm_def_event_dict = (
|
||||
{u'tenantId': tenant_id,
|
||||
u'alarmDefinitionId': id,
|
||||
u'alarmName': name,
|
||||
u'alarmDescription': description,
|
||||
u'alarmExpression': expression,
|
||||
u'severity': severity,
|
||||
u'matchBy': match_by,
|
||||
u'alarmActionsEnabled': actions_enabled,
|
||||
u'oldAlarmSubExpressions': old_sub_alarm_def_event_dict,
|
||||
u'changedSubExpressions': changed_sub_alarm_def_event_dict,
|
||||
u'unchangedSubExpressions': unchanged_sub_alarm_def_event_dict,
|
||||
u'newAlarmSubExpressions': new_sub_alarm_def_event_dict})
|
||||
|
||||
alarm_definition_updated_event = (
|
||||
{u'alarm-definition-updated': alarm_def_event_dict})
|
||||
|
||||
self.send_event(self.events_message_queue,
|
||||
alarm_definition_updated_event)
|
||||
|
||||
result = self._build_alarm_definition_show_result(alarm_def_row)
|
||||
|
||||
return result
|
||||
|
||||
def _build_sub_alarm_def_update_dict(self, sub_alarm_def_dict):
|
||||
|
||||
sub_alarm_def_update_dict = {}
|
||||
for id, sub_alarm_def in sub_alarm_def_dict.items():
|
||||
dimensions = {}
|
||||
for name, value in sub_alarm_def.dimensions.items():
|
||||
dimensions[u'uname'] = value
|
||||
sub_alarm_def_update_dict[sub_alarm_def.id] = {}
|
||||
sub_alarm_def_update_dict[sub_alarm_def.id][u'function'] = (
|
||||
sub_alarm_def.function)
|
||||
sub_alarm_def_update_dict[sub_alarm_def.id][
|
||||
u'metricDefinition'] = (
|
||||
{u'name': sub_alarm_def.metric_name,
|
||||
u'dimensions': dimensions})
|
||||
sub_alarm_def_update_dict[sub_alarm_def.id][u'operator'] = (
|
||||
sub_alarm_def.operator)
|
||||
sub_alarm_def_update_dict[sub_alarm_def.id][u'threshold'] = (
|
||||
sub_alarm_def.threshold)
|
||||
sub_alarm_def_update_dict[sub_alarm_def.id][u'period'] = (
|
||||
sub_alarm_def.period)
|
||||
sub_alarm_def_update_dict[sub_alarm_def.id][u'periods'] = (
|
||||
sub_alarm_def.periods)
|
||||
sub_alarm_def_update_dict[sub_alarm_def.id][u'expression'] = (
|
||||
sub_alarm_def.expression)
|
||||
|
||||
return sub_alarm_def_update_dict
|
||||
|
||||
@resource.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 = (
|
||||
monasca.expression_parser.alarm_expr_parser.
|
||||
AlarmExprParser(expression).sub_expr_list)
|
||||
|
||||
except pyparsing.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('utf8'))
|
||||
raise falcon.HTTPBadRequest(title, msg)
|
||||
|
||||
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, u'actions_enabled': u'true',
|
||||
u'undetermined_actions': undetermined_actions,
|
||||
u'expression': expression, u'id': alarm_definition_id,
|
||||
u'name': name})
|
||||
|
||||
return result
|
||||
|
||||
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(self.events_message_queue,
|
||||
alarm_definition_deleted_event_msg)
|
||||
|
||||
def _send_alarm_definition_created_event(self, tenant_id,
|
||||
alarm_definition_id, name,
|
||||
expression, sub_expr_list,
|
||||
description, match_by):
|
||||
|
||||
alarm_definition_created_event_msg = {
|
||||
u'alarm-definition-created': {u'tenantId': tenant_id,
|
||||
u'alarmDefinitionId':
|
||||
alarm_definition_id,
|
||||
u'alarmName': name,
|
||||
u'alarmDescription': description,
|
||||
u'alarmExpression': expression,
|
||||
u'matchBy': match_by}}
|
||||
|
||||
sub_expr_event_msg = {}
|
||||
for sub_expr in sub_expr_list:
|
||||
sub_expr_event_msg[sub_expr.id] = {
|
||||
u'function': sub_expr.normalized_func}
|
||||
metric_definition = {u'name': sub_expr.normalized_metric_name}
|
||||
sub_expr_event_msg[sub_expr.id][
|
||||
u'metricDefinition'] = metric_definition
|
||||
dimensions = {}
|
||||
for dimension in sub_expr.dimensions_as_list:
|
||||
parsed_dimension = dimension.split("=")
|
||||
dimensions[parsed_dimension[0]] = parsed_dimension[1]
|
||||
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'period'] = sub_expr.period
|
||||
sub_expr_event_msg[sub_expr.id][u'periods'] = sub_expr.periods
|
||||
sub_expr_event_msg[sub_expr.id][
|
||||
u'expression'] = sub_expr.fmtd_sub_expr_str
|
||||
|
||||
alarm_definition_created_event_msg[u'alarm-definition-created'][
|
||||
u'alarmSubExpressions'] = sub_expr_event_msg
|
||||
|
||||
self.send_event(self.events_message_queue,
|
||||
alarm_definition_created_event_msg)
|
||||
|
||||
|
||||
def get_query_alarm_definition_name(alarm_definition, return_none=False):
|
||||
try:
|
||||
if 'name' in alarm_definition:
|
||||
name = alarm_definition['name']
|
||||
return name
|
||||
else:
|
||||
if return_none:
|
||||
return None
|
||||
else:
|
||||
raise Exception("Missing name")
|
||||
except Exception as ex:
|
||||
LOG.debug(ex)
|
||||
raise falcon.HTTPBadRequest('Bad request', ex.message)
|
||||
|
||||
|
||||
def get_query_alarm_definition_expression(alarm_definition,
|
||||
return_none=False):
|
||||
try:
|
||||
if 'expression' in alarm_definition:
|
||||
expression = alarm_definition['expression']
|
||||
return expression
|
||||
else:
|
||||
if return_none:
|
||||
return None
|
||||
else:
|
||||
raise Exception("Missing expression")
|
||||
except Exception as ex:
|
||||
LOG.debug(ex)
|
||||
raise falcon.HTTPBadRequest('Bad request', ex.message)
|
||||
|
||||
|
||||
def get_query_alarm_definition_description(alarm_definition,
|
||||
return_none=False):
|
||||
if 'description' in alarm_definition:
|
||||
return alarm_definition['description']
|
||||
else:
|
||||
if return_none:
|
||||
return None
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
def get_query_alarm_definition_severity(alarm_definition, return_none=False):
|
||||
if 'severity' in alarm_definition:
|
||||
severity = alarm_definition['severity']
|
||||
severity = severity.decode('utf8').upper()
|
||||
if severity not in ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL']:
|
||||
raise falcon.HTTPBadRequest('Bad request', 'Invalid severity')
|
||||
return severity
|
||||
else:
|
||||
if return_none:
|
||||
return None
|
||||
else:
|
||||
return 'LOW'
|
||||
|
||||
|
||||
def get_query_alarm_definition_match_by(alarm_definition, return_none=False):
|
||||
if 'match_by' in alarm_definition:
|
||||
match_by = alarm_definition['match_by']
|
||||
return match_by
|
||||
else:
|
||||
if return_none:
|
||||
return None
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def get_query_alarm_definition_alarm_actions(alarm_definition,
|
||||
return_none=False):
|
||||
if 'alarm_actions' in alarm_definition:
|
||||
alarm_actions = alarm_definition['alarm_actions']
|
||||
return alarm_actions
|
||||
else:
|
||||
if return_none:
|
||||
return None
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def get_query_alarm_definition_undetermined_actions(alarm_definition,
|
||||
return_none=False):
|
||||
if 'undetermined_actions' in alarm_definition:
|
||||
undetermined_actions = alarm_definition['undetermined_actions']
|
||||
return undetermined_actions
|
||||
else:
|
||||
if return_none:
|
||||
return None
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def get_query_ok_actions(alarm_definition, return_none=False):
|
||||
if 'ok_actions' in alarm_definition:
|
||||
ok_actions = alarm_definition['ok_actions']
|
||||
return ok_actions
|
||||
else:
|
||||
if return_none:
|
||||
return None
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def get_query_alarm_definition_actions_enabled(alarm_definition,
|
||||
required=False,
|
||||
return_none=False):
|
||||
try:
|
||||
if 'actions_enabled' in alarm_definition:
|
||||
enabled_actions = alarm_definition['actions_enabled']
|
||||
return enabled_actions
|
||||
else:
|
||||
if return_none:
|
||||
return None
|
||||
elif required:
|
||||
raise Exception("Missing actions-enabled")
|
||||
else:
|
||||
return ''
|
||||
except Exception as ex:
|
||||
LOG.debug(ex)
|
||||
raise falcon.HTTPBadRequest('Bad request', ex.message)
|
||||
|
||||
|
||||
def get_comma_separated_str_as_list(comma_separated_str):
|
||||
if not comma_separated_str:
|
||||
return []
|
||||
else:
|
||||
return comma_separated_str.decode('utf8').split(',')
|
|
@ -1,176 +0,0 @@
|
|||
# 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 falcon
|
||||
from oslo.config import cfg
|
||||
|
||||
from monasca.common.messaging import exceptions as message_queue_exceptions
|
||||
from monasca.common import resource_api
|
||||
import monasca.expression_parser.alarm_expr_parser
|
||||
from monasca.openstack.common import log
|
||||
from monasca.v2.reference import helpers
|
||||
|
||||
|
||||
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.events_message_queue = (
|
||||
resource_api.init_driver('monasca.messaging',
|
||||
cfg.CONF.messaging.driver,
|
||||
(['events'])))
|
||||
|
||||
self.alarm_state_transitions_message_queue = (
|
||||
resource_api.init_driver('monasca.messaging',
|
||||
cfg.CONF.messaging.driver,
|
||||
(['alarm-state-transitions'])))
|
||||
|
||||
def _send_alarm_transitioned_event(self, tenant_id, alarm_id,
|
||||
alarm_definition_row,
|
||||
alarm_metric_rows,
|
||||
old_state, new_state):
|
||||
|
||||
metrics = []
|
||||
alarm_transitioned_event_msg = {u'alarm-transitioned': {
|
||||
u'tenantId': tenant_id,
|
||||
u'alarmId': alarm_id,
|
||||
u'alarmDefinitionId': alarm_definition_row['id'],
|
||||
u'alarmName': alarm_definition_row['name'],
|
||||
u'alarmDescription': alarm_definition_row['description'],
|
||||
u'actionsEnabled': alarm_definition_row['actions_enabled'] == 1,
|
||||
u'stateChangeReason': 'Alarm state updated via API',
|
||||
u'severity': alarm_definition_row['severity'],
|
||||
u'oldState': old_state,
|
||||
u'newState': new_state,
|
||||
u'metrics': metrics}
|
||||
}
|
||||
|
||||
for alarm_metric_row in alarm_metric_rows:
|
||||
metric = self._build_metric(alarm_metric_row)
|
||||
metrics.append(metric)
|
||||
|
||||
self.send_event(self.alarm_state_transitions_message_queue,
|
||||
alarm_transitioned_event_msg)
|
||||
|
||||
def _build_metric(self, alarm_metric_row):
|
||||
|
||||
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]
|
||||
|
||||
return metric
|
||||
|
||||
def _send_alarm_event(self, event_type, tenant_id, alarm_definition_id,
|
||||
alarm_metric_rows, sub_alarm_rows, extra_info=None):
|
||||
|
||||
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]
|
||||
|
||||
# Forward declaration.
|
||||
alarm_event_msg = {}
|
||||
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_event_msg = (
|
||||
self._build_sub_alarm_event_msg(sub_alarm_dict,
|
||||
prev_alarm_id))
|
||||
alarm_event_msg[event_type][
|
||||
u'subAlarms': sub_alarms_event_msg]
|
||||
self.send_event(self.events_message_queue,
|
||||
alarm_event_msg)
|
||||
|
||||
alarm_metrics_event_msg = []
|
||||
alarm_event_msg = {event_type: {u'tenant_id': tenant_id,
|
||||
u'alarmDefinitionId':
|
||||
alarm_definition_id,
|
||||
u'alarmId': alarm_metric_row[
|
||||
'alarm_id'],
|
||||
u'alarmMetrics':
|
||||
alarm_metrics_event_msg}}
|
||||
if extra_info:
|
||||
alarm_event_msg[event_type].update(extra_info)
|
||||
|
||||
prev_alarm_id = alarm_metric_row['alarm_id']
|
||||
|
||||
metric = self._build_metric(alarm_metric_row)
|
||||
alarm_metrics_event_msg.append(metric)
|
||||
|
||||
# Finish last alarm
|
||||
sub_alarms_event_msg = self._build_sub_alarm_event_msg(sub_alarm_dict,
|
||||
prev_alarm_id)
|
||||
alarm_event_msg[event_type][u'subAlarms'] = sub_alarms_event_msg
|
||||
|
||||
self.send_event(self.events_message_queue,
|
||||
alarm_event_msg)
|
||||
|
||||
def _build_sub_alarm_event_msg(self, sub_alarm_dict, alarm_id):
|
||||
|
||||
sub_alarms_event_msg = {}
|
||||
|
||||
if alarm_id not in sub_alarm_dict:
|
||||
return sub_alarms_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 = (
|
||||
monasca.expression_parser.alarm_expr_parser.AlarmExprParser(
|
||||
sub_alarm['expression']).sub_expr_list[0])
|
||||
dimensions = {}
|
||||
sub_alarms_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_event_msg
|
||||
|
||||
def send_event(self, message_queue, event_msg):
|
||||
try:
|
||||
message_queue.send_message(
|
||||
helpers.dumpit_utf8(event_msg))
|
||||
except message_queue_exceptions.MessageQueueException as ex:
|
||||
LOG.exception(ex)
|
||||
raise falcon.HTTPInternalServerError(
|
||||
'Message queue service unavailable'.encode('utf8'),
|
||||
ex.message.encode('utf8'))
|
|
@ -1,328 +0,0 @@
|
|||
# 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 re
|
||||
|
||||
import falcon
|
||||
from oslo.config import cfg
|
||||
|
||||
from monasca.api import alarms_api_v2
|
||||
from monasca.common.repositories import exceptions
|
||||
from monasca.common import resource_api
|
||||
from monasca.openstack.common import log
|
||||
from monasca.v2.reference.alarming import Alarming
|
||||
from monasca.v2.reference import helpers
|
||||
from monasca.v2.reference import resource
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class Alarms(alarms_api_v2.AlarmsV2API, Alarming):
|
||||
|
||||
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._alarms_repo = resource_api.init_driver(
|
||||
'monasca.repositories', cfg.CONF.repositories.alarms_driver)
|
||||
|
||||
self._metrics_repo = resource_api.init_driver(
|
||||
'monasca.repositories', cfg.CONF.repositories.metrics_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)
|
||||
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
|
||||
state = self._get_alarm_state(req)
|
||||
|
||||
self._alarm_update(tenant_id, id, state)
|
||||
|
||||
result = self._alarm_show(req.uri, tenant_id, id)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
@resource_api.Restify('/v2.0/alarms/{id}', method='patch')
|
||||
def do_patch_alarms(self, req, res, id):
|
||||
|
||||
# Same logic as alarm_update
|
||||
return self.do_put_alarms(req, res, id)
|
||||
|
||||
@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)
|
||||
|
||||
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):
|
||||
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
|
||||
query_parms = falcon.uri.parse_query_string(req.query_string)
|
||||
|
||||
offset = helpers.normalize_offset(helpers.get_query_param(req,
|
||||
'offset'))
|
||||
|
||||
result = self._alarm_list(req.uri, tenant_id, query_parms, offset)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
@resource_api.Restify('/v2.0/alarms/{id}', method='get')
|
||||
def do_get_alarm_by_id(self, req, res, id):
|
||||
|
||||
# Necessary because falcon interprets 'state-history' as an
|
||||
# alarm id, and this url masks '/v2.0/alarms/state-history'.
|
||||
if id.lower() == 'state-history':
|
||||
return self.do_get_alarms_state_history(req, res)
|
||||
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
|
||||
result = self._alarm_show(req.uri, tenant_id, id)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
@resource_api.Restify('/v2.0/alarms/state-history', method='get')
|
||||
def do_get_alarms_state_history(self, req, res):
|
||||
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
start_timestamp = helpers.get_query_starttime_timestamp(req, False)
|
||||
end_timestamp = helpers.get_query_endtime_timestamp(req, False)
|
||||
query_parms = falcon.uri.parse_query_string(req.query_string)
|
||||
offset = helpers.normalize_offset(helpers.get_query_param(req,
|
||||
'offset'))
|
||||
|
||||
result = self._alarm_history_list(tenant_id, start_timestamp,
|
||||
end_timestamp, query_parms,
|
||||
req.uri, offset)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
@resource_api.Restify('/v2.0/alarms/{id}/state-history', method='get')
|
||||
def do_get_alarm_state_history(self, req, res, id):
|
||||
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
offset = helpers.normalize_offset(helpers.get_query_param(req,
|
||||
'offset'))
|
||||
|
||||
result = self._alarm_history(tenant_id, [id], req.uri, offset)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
@resource.resource_try_catch_block
|
||||
def _alarm_update(self, tenant_id, id, new_state):
|
||||
|
||||
alarm_metric_rows = self._alarms_repo.get_alarm_metrics(id)
|
||||
sub_alarm_rows = self._alarms_repo.get_sub_alarms(tenant_id, id)
|
||||
|
||||
old_state = self._alarms_repo.update_alarm(tenant_id, id, new_state)
|
||||
|
||||
# alarm_definition_id is the same for all rows.
|
||||
alarm_definition_id = sub_alarm_rows[0]['alarm_definition_id']
|
||||
|
||||
state_info = {u'alarmState': new_state, u'oldAlarmState': old_state}
|
||||
|
||||
self._send_alarm_event(u'alarm-updated', tenant_id,
|
||||
alarm_definition_id, alarm_metric_rows,
|
||||
sub_alarm_rows, state_info)
|
||||
|
||||
if old_state != new_state:
|
||||
try:
|
||||
alarm_definition_row = self._alarms_repo.get_alarm_definition(
|
||||
tenant_id, id)
|
||||
except exceptions.DoesNotExistException:
|
||||
# Alarm definition does not exist. May have been deleted
|
||||
# in another transaction. In that case, all associated
|
||||
# alarms were also deleted, so don't send transition events.
|
||||
pass
|
||||
else:
|
||||
self._send_alarm_transitioned_event(tenant_id, id,
|
||||
alarm_definition_row,
|
||||
alarm_metric_rows,
|
||||
old_state, new_state)
|
||||
|
||||
@resource.resource_try_catch_block
|
||||
def _alarm_history_list(self, tenant_id, start_timestamp,
|
||||
end_timestamp, query_parms, req_uri, offset):
|
||||
|
||||
# get_alarms expects 'metric_dimensions' for dimensions key.
|
||||
if 'dimensions' in query_parms:
|
||||
new_query_parms = {'metric_dimensions': query_parms['dimensions']}
|
||||
else:
|
||||
new_query_parms = {}
|
||||
|
||||
alarm_rows = self._alarms_repo.get_alarms(tenant_id, new_query_parms)
|
||||
alarm_id_list = [alarm_row['alarm_id'] for alarm_row in alarm_rows]
|
||||
|
||||
result = self._metrics_repo.alarm_history(tenant_id, alarm_id_list,
|
||||
offset,
|
||||
start_timestamp,
|
||||
end_timestamp)
|
||||
|
||||
return helpers.paginate(result, req_uri, offset)
|
||||
|
||||
@resource.resource_try_catch_block
|
||||
def _alarm_history(self, tenant_id, alarm_id, req_uri, offset):
|
||||
|
||||
result = self._metrics_repo.alarm_history(tenant_id, alarm_id, offset)
|
||||
|
||||
return helpers.paginate(result, req_uri, offset)
|
||||
|
||||
@resource.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_event(u'alarm-deleted', tenant_id,
|
||||
alarm_definition_id, alarm_metric_rows,
|
||||
sub_alarm_rows)
|
||||
|
||||
@resource.resource_try_catch_block
|
||||
def _alarm_show(self, req_uri, 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)
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
return alarm
|
||||
|
||||
@resource.resource_try_catch_block
|
||||
def _alarm_list(self, req_uri, tenant_id, query_parms, offset):
|
||||
|
||||
alarm_rows = self._alarms_repo.get_alarms(tenant_id, query_parms,
|
||||
offset)
|
||||
|
||||
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'], }
|
||||
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 helpers.paginate(result, req_uri, offset)
|
||||
|
||||
def _get_alarm_state(self, req):
|
||||
|
||||
json_msg = helpers.read_http_resource(req)
|
||||
if 'state' in json_msg:
|
||||
state = json_msg['state'].upper()
|
||||
if state not in ['OK', 'ALARM', 'UNDETERMINED']:
|
||||
raise falcon.HTTPBadRequest('Bad request', 'Invalid state')
|
||||
return state
|
||||
else:
|
||||
raise falcon.HTTPBadRequest('Bad request', 'Missing state')
|
|
@ -1,230 +0,0 @@
|
|||
# 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.api import monasca_api_v2
|
||||
from monasca.common.messaging import exceptions as message_queue_exceptions
|
||||
from monasca.common.messaging.message_formats import metrics_transform_factory
|
||||
from monasca.common import resource_api
|
||||
from monasca.openstack.common import log
|
||||
from monasca.v2.common.schemas import (exceptions as schemas_exceptions)
|
||||
from monasca.v2.common.schemas import (
|
||||
metrics_request_body_schema as schemas_metrics)
|
||||
from monasca.v2.common import utils
|
||||
from monasca.v2.reference import helpers
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class Metrics(monasca_api_v2.V2API):
|
||||
def __init__(self, global_conf):
|
||||
|
||||
try:
|
||||
|
||||
super(Metrics, 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._metrics_transform = (
|
||||
metrics_transform_factory.create_metrics_transform())
|
||||
self._message_queue = (
|
||||
resource_api.init_driver('monasca.messaging',
|
||||
cfg.CONF.messaging.driver,
|
||||
['metrics']))
|
||||
self._metrics_repo = resource_api.init_driver(
|
||||
'monasca.repositories', cfg.CONF.repositories.metrics_driver)
|
||||
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
raise falcon.HTTPInternalServerError('Service unavailable',
|
||||
ex.message)
|
||||
|
||||
def _validate_metrics(self, metrics):
|
||||
"""Validates the metrics
|
||||
|
||||
:param metrics: A metric object or array of metrics objects.
|
||||
:raises falcon.HTTPBadRequest
|
||||
"""
|
||||
try:
|
||||
schemas_metrics.validate(metrics)
|
||||
except schemas_exceptions.ValidationException as ex:
|
||||
LOG.debug(ex)
|
||||
raise falcon.HTTPBadRequest('Bad request', ex.message)
|
||||
|
||||
def _send_metrics(self, metrics):
|
||||
"""Send the metrics using the message queue.
|
||||
|
||||
:param metrics: A metric object or array of metrics objects.
|
||||
:raises: falcon.HTTPServiceUnavailable
|
||||
"""
|
||||
|
||||
def _send_metric(metric):
|
||||
try:
|
||||
str_msg = json.dumps(metric, default=utils.date_handler)
|
||||
self._message_queue.send_message(str_msg)
|
||||
except message_queue_exceptions.MessageQueueException as ex:
|
||||
LOG.exception(ex)
|
||||
raise falcon.HTTPServiceUnavailable('Service unavailable',
|
||||
ex.message, 60)
|
||||
|
||||
if isinstance(metrics, list):
|
||||
for metric in metrics:
|
||||
_send_metric(metric)
|
||||
else:
|
||||
_send_metric(metrics)
|
||||
|
||||
def _list_metrics(self, tenant_id, name, dimensions, req_uri, offset):
|
||||
"""Query the metric repo for the metrics, format them and return them.
|
||||
|
||||
:param tenant_id:
|
||||
:param name:
|
||||
:param dimensions:
|
||||
:raises falcon.HTTPServiceUnavailable
|
||||
"""
|
||||
|
||||
try:
|
||||
result = self._metrics_repo.list_metrics(tenant_id,
|
||||
self._region,
|
||||
name,
|
||||
dimensions, offset)
|
||||
|
||||
return helpers.paginate(result, req_uri, offset)
|
||||
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
raise falcon.HTTPServiceUnavailable('Service unavailable',
|
||||
ex.message, 60)
|
||||
|
||||
def _measurement_list(self, tenant_id, name, dimensions, start_timestamp,
|
||||
end_timestamp, req_uri, offset):
|
||||
try:
|
||||
result = self._metrics_repo.measurement_list(tenant_id,
|
||||
self._region,
|
||||
name,
|
||||
dimensions,
|
||||
start_timestamp,
|
||||
end_timestamp,
|
||||
offset)
|
||||
|
||||
if offset is not None:
|
||||
|
||||
paginated_result = []
|
||||
for measurement in result:
|
||||
paginated_result.append(
|
||||
helpers.paginate_measurement(measurement,
|
||||
req_uri, offset))
|
||||
|
||||
result = {u'links': [{u'rel': u'self',
|
||||
u'href': req_uri.decode('utf8')}],
|
||||
u'elements': paginated_result}
|
||||
|
||||
return result
|
||||
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
raise falcon.HTTPServiceUnavailable('Service unavailable',
|
||||
ex.message, 60)
|
||||
|
||||
def _metric_statistics(self, tenant_id, name, dimensions, start_timestamp,
|
||||
end_timestamp, statistics, period):
|
||||
try:
|
||||
return self._metrics_repo.metrics_statistics(tenant_id,
|
||||
self._region,
|
||||
name,
|
||||
dimensions,
|
||||
start_timestamp,
|
||||
end_timestamp,
|
||||
statistics, period)
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
raise falcon.HTTPServiceUnavailable('Service unavailable',
|
||||
ex.message, 60)
|
||||
|
||||
@resource_api.Restify('/v2.0/metrics/', method='post')
|
||||
def do_post_metrics(self, req, res):
|
||||
helpers.validate_json_content_type(req)
|
||||
helpers.validate_authorization(req,
|
||||
self._post_metrics_authorized_roles)
|
||||
metrics = helpers.read_http_resource(req)
|
||||
self._validate_metrics(metrics)
|
||||
tenant_id = (
|
||||
helpers.get_x_tenant_or_tenant_id(req,
|
||||
self._delegate_authorized_roles))
|
||||
transformed_metrics = self._metrics_transform(metrics, tenant_id,
|
||||
self._region)
|
||||
self._send_metrics(transformed_metrics)
|
||||
res.status = falcon.HTTP_204
|
||||
|
||||
@resource_api.Restify('/v2.0/metrics/', method='get')
|
||||
def do_get_metrics(self, req, res):
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
name = helpers.get_query_name(req)
|
||||
helpers.validate_query_name(name)
|
||||
dimensions = helpers.get_query_dimensions(req)
|
||||
helpers.validate_query_dimensions(dimensions)
|
||||
offset = helpers.normalize_offset(helpers.get_query_param(req,
|
||||
'offset'))
|
||||
result = self._list_metrics(tenant_id, name, dimensions,
|
||||
req.uri, offset)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
@resource_api.Restify('/v2.0/metrics/measurements', method='get')
|
||||
def do_get_measurements(self, req, res):
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
name = helpers.get_query_name(req)
|
||||
helpers.validate_query_name(name)
|
||||
dimensions = helpers.get_query_dimensions(req)
|
||||
helpers.validate_query_dimensions(dimensions)
|
||||
start_timestamp = helpers.get_query_starttime_timestamp(req)
|
||||
end_timestamp = helpers.get_query_endtime_timestamp(req, False)
|
||||
offset = helpers.normalize_offset(helpers.get_query_param(req,
|
||||
'offset'))
|
||||
result = self._measurement_list(tenant_id, name, dimensions,
|
||||
start_timestamp, end_timestamp,
|
||||
req.uri, offset)
|
||||
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
@resource_api.Restify('/v2.0/metrics/statistics', method='get')
|
||||
def do_get_statistics(self, req, res):
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
name = helpers.get_query_name(req)
|
||||
helpers.validate_query_name(name)
|
||||
dimensions = helpers.get_query_dimensions(req)
|
||||
helpers.validate_query_dimensions(dimensions)
|
||||
start_timestamp = helpers.get_query_starttime_timestamp(req)
|
||||
end_timestamp = helpers.get_query_endtime_timestamp(req, False)
|
||||
statistics = helpers.get_query_statistics(req)
|
||||
period = helpers.get_query_period(req)
|
||||
result = self._metric_statistics(tenant_id, name, dimensions,
|
||||
start_timestamp, end_timestamp,
|
||||
statistics, period)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
|
@ -1,185 +0,0 @@
|
|||
# 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 falcon
|
||||
from oslo.config import cfg
|
||||
|
||||
from monasca.api import monasca_notifications_api_v2
|
||||
from monasca.common import resource_api
|
||||
from monasca.openstack.common import log
|
||||
from monasca.v2.common.schemas import (exceptions as schemas_exceptions)
|
||||
from monasca.v2.common.schemas import (
|
||||
notifications_request_body_schema as schemas_notifications)
|
||||
from monasca.v2.reference import helpers
|
||||
from monasca.v2.reference import resource
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class Notifications(monasca_notifications_api_v2.NotificationsV2API):
|
||||
|
||||
def __init__(self, global_conf):
|
||||
|
||||
super(Notifications, self).__init__(global_conf)
|
||||
|
||||
self._region = cfg.CONF.region
|
||||
self._default_authorized_roles = (
|
||||
cfg.CONF.security.default_authorized_roles)
|
||||
self._notifications_repo = resource_api.init_driver(
|
||||
'monasca.repositories', cfg.CONF.repositories.notifications_driver)
|
||||
|
||||
def _validate_notification(self, notification):
|
||||
"""Validates the notification
|
||||
|
||||
:param notification: An event object.
|
||||
:raises falcon.HTTPBadRequest
|
||||
"""
|
||||
try:
|
||||
schemas_notifications.validate(notification)
|
||||
except schemas_exceptions.ValidationException as ex:
|
||||
LOG.debug(ex)
|
||||
raise falcon.HTTPBadRequest('Bad request', ex.message)
|
||||
|
||||
@resource.resource_try_catch_block
|
||||
def _create_notification(self, tenant_id, notification, uri):
|
||||
|
||||
name = notification['name'].decode('utf8')
|
||||
notification_type = notification['type'].upper().decode('utf8')
|
||||
address = notification['address'].decode('utf8')
|
||||
notification_id = self._notifications_repo.create_notification(
|
||||
tenant_id,
|
||||
name,
|
||||
notification_type,
|
||||
address)
|
||||
|
||||
return self._create_notification_response(notification_id,
|
||||
name,
|
||||
notification_type,
|
||||
address,
|
||||
uri)
|
||||
|
||||
@resource.resource_try_catch_block
|
||||
def _update_notification(self, id, tenant_id, notification, uri):
|
||||
|
||||
name = notification['name'].decode('utf8')
|
||||
notification_type = notification['type'].upper().decode('utf8')
|
||||
address = notification['address'].decode('utf8')
|
||||
self._notifications_repo.update_notification(id, tenant_id, name,
|
||||
notification_type,
|
||||
address)
|
||||
|
||||
return self._create_notification_response(id,
|
||||
name,
|
||||
notification_type,
|
||||
address,
|
||||
uri)
|
||||
|
||||
def _create_notification_response(self, id, name, type,
|
||||
address, uri):
|
||||
|
||||
response = {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'type': type,
|
||||
'address': address
|
||||
}
|
||||
|
||||
return helpers.add_links_to_resource(response, uri)
|
||||
|
||||
@resource.resource_try_catch_block
|
||||
def _list_notifications(self, tenant_id, uri, offset):
|
||||
|
||||
rows = self._notifications_repo.list_notifications(tenant_id, offset)
|
||||
|
||||
result = [self._build_notification_result(row,
|
||||
uri) for row in rows]
|
||||
|
||||
return helpers.paginate(result, uri, offset)
|
||||
|
||||
@resource.resource_try_catch_block
|
||||
def _list_notification(self, tenant_id, notification_id, uri):
|
||||
|
||||
row = self._notifications_repo.list_notification(
|
||||
tenant_id,
|
||||
notification_id)
|
||||
|
||||
return self._build_notification_result(row, uri)
|
||||
|
||||
def _build_notification_result(self, notification_row, uri):
|
||||
|
||||
result = {
|
||||
u'id': notification_row['id'],
|
||||
u'name': notification_row['name'],
|
||||
u'type': notification_row['type'],
|
||||
u'address': notification_row['address']
|
||||
}
|
||||
|
||||
helpers.add_links_to_resource(result, uri)
|
||||
|
||||
return result
|
||||
|
||||
@resource.resource_try_catch_block
|
||||
def _delete_notification(self, tenant_id, notification_id):
|
||||
|
||||
self._notifications_repo.delete_notification(tenant_id,
|
||||
notification_id)
|
||||
|
||||
@resource_api.Restify('/v2.0/notification-methods', method='post')
|
||||
def do_post_notification_methods(self, req, res):
|
||||
helpers.validate_json_content_type(req)
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
notification = helpers.read_http_resource(req)
|
||||
self._validate_notification(notification)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
result = self._create_notification(tenant_id, notification, req.uri)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_201
|
||||
|
||||
@resource_api.Restify('/v2.0/notification-methods', method='get')
|
||||
def do_get_notification_methods(self, req, res):
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
offset = helpers.normalize_offset(helpers.get_query_param(req,
|
||||
'offset'))
|
||||
result = self._list_notifications(tenant_id, req.uri, offset)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
@resource_api.Restify('/v2.0/notification-methods/{id}', method='delete')
|
||||
def do_delete_notification_methods(self, req, res, id):
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
self._delete_notification(tenant_id, id)
|
||||
res.status = falcon.HTTP_204
|
||||
|
||||
@resource_api.Restify('/v2.0/notification-methods/{id}', method='get')
|
||||
def do_get_notification_method(self, req, res, id):
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
result = self._list_notification(tenant_id, id, req.uri)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
||||
|
||||
@resource_api.Restify('/v2.0/notification-methods/{id}', method='put')
|
||||
def do_put_notification_methods(self, req, res, id):
|
||||
helpers.validate_json_content_type(req)
|
||||
helpers.validate_authorization(req, self._default_authorized_roles)
|
||||
notification = helpers.read_http_resource(req)
|
||||
self._validate_notification(notification)
|
||||
tenant_id = helpers.get_tenant_id(req)
|
||||
result = self._update_notification(id, tenant_id, notification,
|
||||
req.uri)
|
||||
res.body = helpers.dumpit_utf8(result)
|
||||
res.status = falcon.HTTP_200
|
Loading…
Reference in New Issue