Add alarm history resource

Change-Id: Ic19c09cc0808c994cadfb7e3c7680dd58438852d
This commit is contained in:
Deklan Dieterly 2014-11-13 12:57:00 -07:00
parent 000056f174
commit 71c9acb1cc
55 changed files with 745 additions and 569 deletions

View File

@ -45,3 +45,11 @@ class AlarmsV2API(object):
@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'

View File

@ -45,13 +45,3 @@ class V2API(object):
@resource_api.Restify('/v2.0/metrics/statistics', method='get')
def do_get_statistics(self, req, res):
res.status = '501 Not Implemented'
@resource_api.Restify('/v2.0/alarms/state-history', method='get')
def do_get_alarms_state_history(self, req, res, id):
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'

View File

@ -14,14 +14,16 @@
# License for the specific language governing permissions and limitations
# under the License.
from stevedore import driver
import os
from monasca.common import resource_api
from monasca.openstack.common import log
from wsgiref import simple_server
from oslo.config import cfg
from oslo.config import types
from paste.deploy import loadapp
from wsgiref import simple_server
import paste.deploy
from monasca.common import resource_api
from monasca.openstack.common import log
METRICS_DISPATCHER_NAMESPACE = 'monasca.metrics_dispatcher'
ALARM_DEFINITIONS_DISPATCHER_NAMESPACE = 'monasca.alarm_definitions_dispatcher'
@ -183,6 +185,8 @@ def api_app(conf):
if __name__ == '__main__':
wsgi_app = loadapp('config:etc/monasca.ini', relative_to=os.getcwd())
wsgi_app = (
paste.deploy.loadapp('config:etc/monasca.ini',
relative_to=os.getcwd()))
httpd = simple_server.make_server('127.0.0.1', 9000, wsgi_app)
httpd.serve_forever()

View File

@ -11,13 +11,13 @@
# 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 time
from kafka import client
from kafka import common
from kafka import consumer
from kafka import producer
from oslo.config import cfg
import time
try:
import ujson as json

View File

@ -12,5 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
class MessageQueueException(Exception):
pass

View File

@ -11,16 +11,16 @@
# 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 time
from kafka import client
from kafka import common
from kafka import producer
from oslo.config import cfg
import time
from monasca.openstack.common import log
from monasca.common.messaging import publisher
from monasca.common.messaging import exceptions
from monasca.common.messaging import publisher
from monasca.openstack.common import log
LOG = log.getLogger(__name__)

View File

@ -12,5 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
def transform(events, tenant_id, region):
raise NotImplemented()

View File

@ -12,5 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
def transform(metrics, tenant_id, region):
raise NotImplemented()

View File

@ -13,15 +13,17 @@
# under the License.
from oslo.config import cfg
import monasca.common.messaging.message_formats.reference.events as reference_events
import monasca.common.messaging.message_formats.cadf.events as cadf_events
import monasca.common.messaging.message_formats.identity.events as identity_events
import monasca.common.messaging.message_formats.identity.events as ident_events
import monasca.common.messaging.message_formats.reference.events as ref_events
def create_events_transform():
message_format = cfg.CONF.messaging.events_message_format
if message_format == 'reference':
return reference_events.transform
return ref_events.transform
elif message_format == 'cadf':
return cadf_events.transform
else:
return identity_events.transform
return ident_events.transform

View File

@ -12,5 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
class TransformationException(Exception):
pass

View File

@ -12,7 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from datetime import datetime
def transform(events, tenant_id, region):
return events

View File

@ -12,7 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from datetime import datetime
def transform(metrics, tenant_id, region):
return metrics

View File

@ -13,15 +13,17 @@
# under the License.
from oslo.config import cfg
import monasca.common.messaging.message_formats.reference.metrics as reference_metrics
import monasca.common.messaging.message_formats.cadf.metrics as cadf_metrics
import monasca.common.messaging.message_formats.identity.metrics as identity_metrics
import monasca.common.messaging.message_formats.identity.metrics as id_metrics
import monasca.common.messaging.message_formats.reference.metrics as r_metrics
def create_metrics_transform():
metrics_message_format = cfg.CONF.messaging.metrics_message_format
if metrics_message_format == 'reference':
return reference_metrics.transform
return r_metrics.transform
elif metrics_message_format == 'cadf':
return cadf_metrics.transform
else:
return identity_metrics.transform
return id_metrics.transform

View File

@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from datetime import datetime
import datetime
def transform(event, tenant_id, region):

View File

@ -12,17 +12,13 @@
# License for the specific language governing permissions and limitations
# under the License.
from datetime import datetime
import datetime
def transform(metrics, tenant_id, region):
transformed_metric = {
'metric': {},
'meta': {
'tenantId': tenant_id,
'region': region
},
'creation_time': datetime.now()
}
transformed_metric = {'metric': {},
'meta': {'tenantId': tenant_id, 'region': region},
'creation_time': datetime.now()}
if isinstance(metrics, list):
transformed_metrics = []

View File

@ -13,16 +13,16 @@
# under the License.
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class Publisher(object):
@abc.abstractmethod
def send_message(self, message):
'''
Sends the message using the message queue.
"""Sends the message using the message queue.
:param message: Message to send.
'''
"""
return

View File

@ -12,20 +12,20 @@
# License for the specific language governing permissions and limitations
# under the License.
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class AlarmDefinitionsRepository(object):
def __init__(self):
super(AlarmDefinitionsRepository, self).__init__()
super(AlarmDefinitionsRepository, self).__init__()
@abc.abstractmethod
def create_alarm_definition(self, tenant_id, name,
expression, sub_expr_list, description, severity, match_by, alarm_actions,
undetermined_actions, ok_action):
def create_alarm_definition(self, tenant_id, name, expression,
sub_expr_list, description, severity, match_by,
alarm_actions, undetermined_actions,
ok_action):
pass
@abc.abstractmethod
@ -50,4 +50,4 @@ class AlarmDefinitionsRepository(object):
@abc.abstractmethod
def get_alarm_definitions(self, tenant_id, name, dimensions):
pass
pass

View File

@ -12,9 +12,33 @@
# License for the specific language governing permissions and limitations
# under the License.
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class AlarmsRepository(object):
pass
def __init__(self):
super(AlarmsRepository, self).__init__()
@abc.abstractmethod
def get_alarm_metrics(self, alarm_id):
pass
@abc.abstractmethod
def get_sub_alarms(self, tenant_id, alarm_id):
pass
@abc.abstractmethod
def delete_alarm(self, tenant_id, id):
pass
@abc.abstractmethod
def get_alarm(self, tenant_id, id):
pass
@abc.abstractmethod
def get_alarms(self, tenant_id, query_parms):
pass

View File

@ -13,6 +13,7 @@
# under the License.
import abc
import six

View File

@ -12,8 +12,10 @@
# License for the specific language governing permissions and limitations
# under the License.
class RepositoryException(Exception):
pass
class DoesNotExistException(RepositoryException):
pass
pass

View File

@ -12,13 +12,12 @@
# 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 re
import time
import urllib
from time import strftime
from time import gmtime
from influxdb import InfluxDBClient
from influxdb.client import InfluxDBClientError
from influxdb import client
from oslo.config import cfg
from monasca.common.repositories import exceptions
@ -30,11 +29,12 @@ LOG = log.getLogger(__name__)
class MetricsRepository(metrics_repository.MetricsRepository):
def __init__(self):
try:
self.conf = cfg.CONF
self.influxdb_client = InfluxDBClient(
self.influxdb_client = client.InfluxDBClient(
self.conf.influxdb.ip_address, self.conf.influxdb.port,
self.conf.influxdb.user, self.conf.influxdb.password,
self.conf.influxdb.database_name)
@ -144,8 +144,8 @@ class MetricsRepository(metrics_repository.MetricsRepository):
def _decode_influxdb_serie_name_list(self, series_names):
"""
Example series_names from InfluxDB.
"""Example series_names from InfluxDB.
[
{
"points": [
@ -178,11 +178,11 @@ class MetricsRepository(metrics_repository.MetricsRepository):
return json_metric_list
def _decode_influxdb_serie_name(self, serie_name):
"""
Decodes a serie name from InfluxDB. The raw serie name is
"""Decodes a serie name from InfluxDB.
The raw serie name is
formed by url encoding the name, tenant id, region, and dimensions,
and concatenating them into a quasi URL query string.
@ -227,11 +227,10 @@ class MetricsRepository(metrics_repository.MetricsRepository):
return metric
def measurement_list(self, tenant_id, name, dimensions, start_timestamp,
end_timestamp):
"""
Example result from InfluxDB.
"""Example result from InfluxDB.
[
{
"points": [
@ -285,9 +284,10 @@ class MetricsRepository(metrics_repository.MetricsRepository):
try:
result = self.influxdb_client.query(query, 's')
except InfluxDBClientError as ex:
if ex.code == 400 and ex.content == 'Couldn\'t look up ' \
'columns':
except client.InfluxDBClientError as ex:
# check for non-existent serie name.
msg = "Couldn't look up columns"
if ex.code == 400 and ex.content == (msg):
return json_measurement_list
else:
raise ex
@ -307,8 +307,9 @@ class MetricsRepository(metrics_repository.MetricsRepository):
columns]
# format the utc date in the points
fmtd_pts = [[strftime("%Y-%m-%dT%H:%M:%SZ", gmtime(point[0])),
point[1], point[2]] for point in serie['points']]
fmtd_pts = [[time.strftime("%Y-%m-%dT%H:%M:%SZ",
time.gmtime(point[0])), point[1],
point[2]] for point in serie['points']]
measurement = {"name": metric['name'],
"dimensions": metric['dimensions'],
@ -322,7 +323,6 @@ class MetricsRepository(metrics_repository.MetricsRepository):
LOG.exception(ex)
raise exceptions.RepositoryException(ex)
def metrics_statistics(self, tenant_id, name, dimensions, start_timestamp,
end_timestamp, statistics, period):
@ -336,9 +336,10 @@ class MetricsRepository(metrics_repository.MetricsRepository):
try:
result = self.influxdb_client.query(query, 's')
except InfluxDBClientError as ex:
if ex.code == 400 and ex.content == 'Couldn\'t look up ' \
'columns':
except client.InfluxDBClientError as ex:
# check for non-existent serie name.
msg = "Couldn't look up columns"
if ex.code == 400 and ex.content == (msg):
return json_statistics_list
else:
raise ex
@ -357,9 +358,10 @@ class MetricsRepository(metrics_repository.MetricsRepository):
columns = [column.replace('time', 'timestamp') for column in
columns]
fmtd_pts_list_list = [[strftime("%Y-%m-%dT%H:%M:%SZ",
gmtime(pts_list[0]))] + pts_list[1:]
for pts_list in serie['points']]
fmtd_pts_list_list = [[time.strftime("%Y-%m-%dT%H:%M:%SZ",
time.gmtime(pts_list[
0]))] + pts_list[1:]
for pts_list in serie['points']]
measurement = {"name": metric['name'],
"dimensions": metric['dimensions'],
@ -370,6 +372,121 @@ class MetricsRepository(metrics_repository.MetricsRepository):
return json_statistics_list
except Exception as ex:
LOG.exception(ex)
raise exceptions.RepositoryException(ex)
def alarm_history(self, tenant_id, alarm_id_list, start_timestamp=None,
end_timestamp=None):
"""Example result from Influxdb.
[
{
"points": [
[
1415894490,
272140001,
"6ac10841-d02f-4f7d-a191-ae0a3d9a25f2",
"[{\"name\": \"cpu.system_perc\", \"dimensions\": {
\"hostname\": \"mini-mon\", \"component\":
\"monasca-agent\", \"service\": \"monitoring\"}},
{\"name\": \"load.avg_1_min\", \"dimensions\": {
\"hostname\": \"mini-mon\", \"component\":
\"monasca-agent\", \"service\": \"monitoring\"}}]",
"ALARM",
"OK",
"Thresholds were exceeded for the sub-alarms: [max(
load.avg_1_min{hostname=mini-mon}) > 0.0,
max(cpu.system_perc) > 0.0]",
"{}"
],
],
"name": "alarm_state_history",
"columns": [
"time",
"sequence_number",
"alarm_id",
"metrics",
"new_state",
"old_state",
"reason",
"reason_data"
]
}
]
:param tenant_id:
:param alarm_id:
:return:
"""
try:
json_alarm_history_list = []
if not alarm_id_list:
return json_alarm_history_list
for alarm_id in alarm_id_list:
if '\'' in alarm_id or ';' in alarm_id:
raise Exception(
"Input from user contains single quote ['] or "
"semi-colon [;] characters[ {} ]".format(alarm_id))
query = """
select alarm_id, metrics, old_state, new_state,
reason, reason_data
from alarm_state_history
"""
where_clause = (
" where tenant_id = '{}' ".format(tenant_id.encode('utf8')))
alarm_id_where_clause_list = (
[" alarm_id = '{}' ".format(id.encode('utf8'))
for id in alarm_id_list])
alarm_id_where_clause = " or ".join(alarm_id_where_clause_list)
where_clause += ' and (' + alarm_id_where_clause + ')'
time_clause = ''
if start_timestamp:
# subtract 1 from timestamp to get >= semantics
time_clause += " and time > " + str(start_timestamp - 1) + "s"
if end_timestamp:
# add 1 to timestamp to get <= semantics
time_clause += " and time < " + str(end_timestamp + 1) + "s"
query += where_clause + time_clause
try:
result = self.influxdb_client.query(query, 's')
except client.InfluxDBClientError as ex:
# check for non-existent serie name. only happens
# if alarm_state_history serie does not exist.
msg = "Couldn't look up columns"
if ex.code == 400 and ex.content == (msg):
return json_alarm_history_list
else:
raise ex
if not result:
return json_alarm_history_list
# There's only one serie, alarm_state_history.
for point in result[0]['points']:
alarm_point = {u'alarm_id': point[2],
u'metrics': json.loads(point[3]),
u'old_state': point[4], u'new_state': point[5],
u'reason': point[6], u'reason_data': point[7],
u'timestamp': time.strftime(
"%Y-%m-%dT%H:%M:%SZ",
time.gmtime(point[0]))}
json_alarm_history_list.append(alarm_point)
return json_alarm_history_list
except Exception as ex:
LOG.exception(ex)
raise exceptions.RepositoryException(ex)

View File

@ -30,5 +30,5 @@ class MetricsRepository(object):
@abc.abstractmethod
def metrics_statistics(self, tenant_id, name, dimensions, start_timestamp,
end_timestamp, statistics):
end_timestamp, statistics, period):
pass

View File

@ -13,20 +13,19 @@
# under the License.
import datetime
from monasca.common.repositories import alarm_definitions_repository
from monasca.common.repositories.exceptions import DoesNotExistException
from monasca.common.repositories.mysql.mysql_repository import MySQLRepository
from monasca.common.repositories.mysql.mysql_repository import mysql_try_catch_block
from monasca.common.repositories import alarm_definitions_repository as adr
from monasca.common.repositories import exceptions
from monasca.common.repositories.mysql import mysql_repository
from monasca.openstack.common import log
from monasca.openstack.common import uuidutils
from monasca.common.repositories import exceptions
LOG = log.getLogger(__name__)
class AlarmDefinitionsRepository(MySQLRepository,
alarm_definitions_repository.AlarmDefinitionsRepository):
class AlarmDefinitionsRepository(mysql_repository.MySQLRepository,
adr.AlarmDefinitionsRepository):
base_query = """
select ad.id, ad.name, ad.description, ad.expression,
ad.match_by, ad.severity, ad.actions_enabled,
@ -56,7 +55,7 @@ class AlarmDefinitionsRepository(MySQLRepository,
super(AlarmDefinitionsRepository, self).__init__()
@mysql_try_catch_block
@mysql_repository.mysql_try_catch_block
def get_alarm_definition(self, tenant_id, id):
parms = [tenant_id, id]
@ -72,9 +71,9 @@ class AlarmDefinitionsRepository(MySQLRepository,
if rows:
return rows[0]
else:
raise DoesNotExistException
raise exceptions.DoesNotExistException
@mysql_try_catch_block
@mysql_repository.mysql_try_catch_block
def get_alarm_definitions(self, tenant_id, name, dimensions):
parms = [tenant_id]
@ -113,8 +112,7 @@ class AlarmDefinitionsRepository(MySQLRepository,
return self._execute_query(query, parms)
@mysql_try_catch_block
@mysql_repository.mysql_try_catch_block
def get_sub_alarms(self, tenant_id, alarm_definition_id):
parms = [tenant_id, alarm_definition_id]
@ -131,10 +129,10 @@ class AlarmDefinitionsRepository(MySQLRepository,
return self._execute_query(query, parms)
@mysql_try_catch_block
@mysql_repository.mysql_try_catch_block
def get_alarm_metrics(self, tenant_id, alarm_definition_id):
parms = [tenant_id, alarm_definition_id]
parms = [tenant_id, alarm_definition_id]
query = """select distinct a.id as alarm_id, md.name,
mdg.dimensions
@ -156,7 +154,7 @@ class AlarmDefinitionsRepository(MySQLRepository,
return self._execute_query(query, parms)
@mysql_try_catch_block
@mysql_repository.mysql_try_catch_block
def delete_alarm_definition(self, tenant_id, alarm_definition_id):
"""Soft delete the alarm definition.
@ -187,7 +185,7 @@ class AlarmDefinitionsRepository(MySQLRepository,
return True
@mysql_try_catch_block
@mysql_repository.mysql_try_catch_block
def get_sub_alarm_definitions(self, alarm_definition_id):
parms = [alarm_definition_id]
@ -206,7 +204,7 @@ class AlarmDefinitionsRepository(MySQLRepository,
return self._execute_query(query, parms)
@mysql_try_catch_block
@mysql_repository.mysql_try_catch_block
def create_alarm_definition(self, tenant_id, name, expression,
sub_expr_list, description, severity, match_by,
alarm_actions, undetermined_actions,
@ -275,7 +273,6 @@ class AlarmDefinitionsRepository(MySQLRepository,
return alarm_definition_id
def _insert_into_alarm_action(self, cursor, alarm_definition_id, actions,
alarm_state):
for action in actions:
@ -293,5 +290,3 @@ class AlarmDefinitionsRepository(MySQLRepository,
action_id)
values(?,?,?)""", alarm_definition_id,
alarm_state.encode('utf8'), action.encode('utf8'))

View File

@ -11,18 +11,18 @@
# 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.repositories.exceptions import DoesNotExistException
from monasca.common.repositories import alarms_repository
from monasca.common.repositories.mysql.mysql_repository import MySQLRepository
from monasca.common.repositories.mysql.mysql_repository import mysql_try_catch_block
from monasca.common.repositories import exceptions
from monasca.common.repositories.mysql import mysql_repository
from monasca.openstack.common import log
LOG = log.getLogger(__name__)
class AlarmsRepository(MySQLRepository, alarms_repository.AlarmsRepository):
class AlarmsRepository(mysql_repository.MySQLRepository,
alarms_repository.AlarmsRepository):
base_query = """
select distinct a.id as alarm_id, a.state,
@ -47,10 +47,10 @@ class AlarmsRepository(MySQLRepository, alarms_repository.AlarmsRepository):
super(AlarmsRepository, self).__init__()
@mysql_try_catch_block
@mysql_repository.mysql_try_catch_block
def get_alarm_metrics(self, alarm_id):
parms = [alarm_id]
parms = [alarm_id]
query = """select distinct a.id as alarm_id, md.name,
mdg.dimensions
@ -70,7 +70,7 @@ class AlarmsRepository(MySQLRepository, alarms_repository.AlarmsRepository):
return self._execute_query(query, parms)
@mysql_try_catch_block
@mysql_repository.mysql_try_catch_block
def get_sub_alarms(self, tenant_id, alarm_id):
parms = [tenant_id, alarm_id]
@ -87,20 +87,20 @@ class AlarmsRepository(MySQLRepository, alarms_repository.AlarmsRepository):
return self._execute_query(query, parms)
@mysql_try_catch_block
@mysql_repository.mysql_try_catch_block
def delete_alarm(self, tenant_id, id):
parms = [tenant_id, id]
query = """
delete alarm.*
from alarm
join
(select distinct a.id
from alarm as a
inner join alarm_definition as ad
delete alarm.*
from alarm
join
(select distinct a.id
from alarm as a
inner join alarm_definition as ad
on ad.id = a.alarm_definition_id
where ad.tenant_id = ? and a.id = ?) as b
where ad.tenant_id = ? and a.id = ?) as b
on b.id = alarm.id
"""
@ -109,11 +109,11 @@ class AlarmsRepository(MySQLRepository, alarms_repository.AlarmsRepository):
cursor.execute(query, parms)
if cursor.rowcount < 1:
raise DoesNotExistException
raise exceptions.DoesNotExistException
self._commit_close_cnxn(cnxn)
@mysql_try_catch_block
@mysql_repository.mysql_try_catch_block
def get_alarm(self, tenant_id, id):
parms = [tenant_id, id]
@ -128,11 +128,11 @@ class AlarmsRepository(MySQLRepository, alarms_repository.AlarmsRepository):
rows = self._execute_query(query, parms)
if not rows:
raise DoesNotExistException
raise exceptions.DoesNotExistException
else:
return rows
@mysql_try_catch_block
@mysql_repository.mysql_try_catch_block
def get_alarms(self, tenant_id, query_parms):
parms = [tenant_id]

View File

@ -12,10 +12,12 @@
# License for the specific language governing permissions and limitations
# under the License.
from monasca.openstack.common import log
from oslo.config import cfg
import peewee
from monasca.openstack.common import log
LOG = log.getLogger(__name__)
db = peewee.MySQLDatabase(cfg.CONF.mysql.database_name,

View File

@ -11,27 +11,22 @@
# 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 pyodbc
from monasca.common.repositories.exceptions import DoesNotExistException
from oslo.config import cfg
import pyodbc
from monasca.common.repositories import alarms_repository
from monasca.openstack.common import log
from monasca.openstack.common import uuidutils
from monasca.common.repositories import exceptions
from monasca.openstack.common import log
LOG = log.getLogger(__name__)
class MySQLRepository(object):
database_driver = 'MySQL ODBC 5.3 ANSI Driver'
database_cnxn_template = 'DRIVER={' \
'%s};Server=%s;CHARSET=UTF8;Database=%s;Uid=%s' \
';Pwd=%s'
database_cnxn_template = ('DRIVER={'
'%s};Server=%s;CHARSET=UTF8;Database=%s;Uid=%s'
';Pwd=%s')
def __init__(self):
@ -65,7 +60,6 @@ class MySQLRepository(object):
cnxn.commit()
cnxn.close()
def _execute_query(self, query, parms):
cnxn, cursor = self._get_cnxn_cursor_tuple()
@ -87,10 +81,10 @@ def mysql_try_catch_block(fun):
return fun(*args, **kwargs)
except DoesNotExistException:
except exceptions.DoesNotExistException:
raise
except Exception as ex:
LOG.exception(ex)
raise exceptions.RepositoryException(ex)
return try_it
return try_it

View File

@ -13,11 +13,14 @@
# under the License.
import datetime
from monasca.common.repositories import notifications_repository
from monasca.common.repositories import exceptions
from monasca.openstack.common import log
import peewee
import model
import peewee
from monasca.common.repositories import exceptions
from monasca.common.repositories import notifications_repository
from monasca.openstack.common import log
LOG = log.getLogger(__name__)
@ -55,7 +58,7 @@ class NotificationsRepository(
self, id, tenant_id, name, notification_type, address):
try:
now = datetime.datetime.utcnow()
q = Notification_Method.create(
Notification_Method.create(
id=id,
tenant_id=tenant_id,
name=name,
@ -73,7 +76,6 @@ class NotificationsRepository(
Notification_Method.tenant_id == tenant_id)
results = q.execute()
notifications = []
notifications = [
self.notification_from_result(result) for result in results]
return notifications
@ -82,7 +84,6 @@ class NotificationsRepository(
raise exceptions.RepositoryException(ex)
def delete_notification(self, tenant_id, notification_id):
num_rows_deleted = 0
try:
q = Notification_Method.delete().where(
@ -113,7 +114,6 @@ class NotificationsRepository(
def update_notification(
self, id, tenant_id, name, notification_type, address):
now = datetime.datetime.utcnow()
num_rows_updated = 0
try:
q = Notification_Method.update(
name=name,

View File

@ -4,7 +4,7 @@
# 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
# 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
@ -12,11 +12,13 @@
# License for the specific language governing permissions and limitations
# under the License.
from monasca.common.repositories import transforms_repository
from monasca.common.repositories import exceptions
from monasca.openstack.common import log
import peewee
import model
import peewee
from monasca.common.repositories import exceptions
from monasca.common.repositories import transforms_repository
from monasca.openstack.common import log
LOG = log.getLogger(__name__)
@ -32,11 +34,14 @@ class Transform(model.Model):
updated_at = peewee.DateTimeField()
deleted_at = peewee.DateTimeField()
class TransformsRepository(transforms_repository.TransformsRepository):
def create_transforms(self, id, tenant_id, name, description, specification, enabled):
class TransformsRepository(transforms_repository.TransformsRepository):
def create_transforms(self, id, tenant_id, name, description,
specification, enabled):
try:
q = Transform.create(id=id, tenant_id=tenant_id, name=name, description=description, specification=specification, enabled=enabled)
q = Transform.create(id=id, tenant_id=tenant_id, name=name,
description=description,
specification=specification, enabled=enabled)
q.save()
except Exception as ex:
LOG.exception(str(ex))
@ -49,13 +54,10 @@ class TransformsRepository(transforms_repository.TransformsRepository):
transforms = []
for result in results:
transform = {
'id': result.id,
'name': result.name,
'description': result.description,
'specification': result.specification,
'enabled': result.enabled
}
transform = {'id': result.id, 'name': result.name,
'description': result.description,
'specification': result.specification,
'enabled': result.enabled}
transforms.append(transform)
return transforms
except Exception as ex:
@ -66,7 +68,8 @@ class TransformsRepository(transforms_repository.TransformsRepository):
num_rows_deleted = 0
try:
q = Transform.delete().where((Transform.tenant_id == tenant_id) & (Transform.id == transform_id))
q = Transform.delete().where((Transform.tenant_id == tenant_id) & (
Transform.id == transform_id))
num_rows_deleted = q.execute()
except Exception as ex:
LOG.exception(str(ex))
@ -75,4 +78,4 @@ class TransformsRepository(transforms_repository.TransformsRepository):
if num_rows_deleted < 1:
raise exceptions.DoesNotExistException()
return
return

View File

@ -13,6 +13,7 @@
# under the License.
import abc
import six

View File

@ -4,7 +4,7 @@
# 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
# 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
@ -13,14 +13,15 @@
# under the License.
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class TransformsRepository(object):
@abc.abstractmethod
def create_transforms(self, id, tenant_id, name, description, specification, enabled):
def create_transforms(self, id, tenant_id, name, description,
specification, enabled):
return
@abc.abstractmethod
@ -29,4 +30,4 @@ class TransformsRepository(object):
@abc.abstractmethod
def delete_transform(self, tenant_id, transform_id):
return
return

View File

@ -16,9 +16,9 @@
import falcon
from falcon import api_helpers
from stevedore import driver
from monasca.openstack.common import log
from stevedore import driver
RESOURCE_METHOD_FLAG = 'fab05a04-b861-4651-bd0c-9cb3eb9a6088'
@ -27,21 +27,19 @@ LOG = log.getLogger(__name__)
def init_driver(namespace, driver_name, drv_invoke_args=()):
"""Initialize the resource driver and returns it.
:param namespace: the resource namespace (in setup.cfg).
:param driver_name: the driver name (in monasca.conf)
:param invoke_args: args to pass to the driver (a tuple)
"""
mgr = driver.DriverManager(
namespace = namespace,
name = driver_name,
invoke_on_load = True,
invoke_args = drv_invoke_args
)
mgr = driver.DriverManager(namespace=namespace, name=driver_name,
invoke_on_load=True,
invoke_args=drv_invoke_args)
return mgr.driver
class Restify(object):
def __init__(self, path='', method='GET'):
if not path:
raise Exception('Path has to be specified.')
@ -130,11 +128,11 @@ class ResourceAPI(falcon.API):
except Exception:
LOG.exception('Error occurred while adding the resource')
LOG.debug(self._routes)
def add_resource(self, resource_name, namespace, driver_name,
def add_resource(self, resource_name, namespace, driver_name,
invoke_args=(), uri=None):
"""Loads the resource driver, and adds it to the routes.
:param resource_name: the name of the resource.
:param namespace: the resource namespace (in setup.cfg).
:param driver_name: the driver name (in monasca.conf)
@ -142,8 +140,8 @@ class ResourceAPI(falcon.API):
:param uri: the uri to associate with the resource
"""
resource_driver = init_driver(namespace, driver_name, invoke_args)
LOG.debug('%s dispatcher driver %s is loaded.' %
LOG.debug('%s dispatcher driver %s is loaded.' %
(resource_name, driver_name))
self.add_route(uri, resource_driver)
LOG.debug('%s dispatcher driver has been added to the routes!' %
LOG.debug('%s dispatcher driver has been added to the routes!' %
(resource_name))

View File

@ -15,6 +15,7 @@
# under the License.
import abc
import six

View File

@ -16,22 +16,13 @@
import itertools
import sys
from pyparsing import CaselessLiteral
from pyparsing import alphanums
from pyparsing import delimitedList
from pyparsing import Forward
from pyparsing import Group
from pyparsing import Literal
from pyparsing import nums
from pyparsing import opAssoc
from pyparsing import operatorPrecedence
from pyparsing import Optional
from pyparsing import stringEnd
from pyparsing import Word
import pyparsing
class SubExpr(object):
def __init__(self, tokens):
self._sub_expr = tokens
self._func = tokens.func
self._metric_name = tokens.metric_name
@ -173,67 +164,70 @@ class BinaryOp(object):
class AndSubExpr(BinaryOp):
""" Expand later as needed.
"""
"""Expand later as needed."""
pass
class OrSubExpr(BinaryOp):
"""Expand later as needed.
"""
"""Expand later as needed."""
pass
COMMA = Literal(",")
LPAREN = Literal("(")
RPAREN = Literal(")")
EQUAL = Literal("=")
LBRACE = Literal("{")
RBRACE = Literal("}")
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 + alphanums + ".-_#!$%&'*+/:;?@["
"\\]^`|~")
valid_identifier_chars = (
(unicode_printables + pyparsing.alphanums + ".-_#!$%&'*+/:;?@[\\]^`|~"))
metric_name = Word(valid_identifier_chars, min=1, max=255)("metric_name")
dimension_name = Word(valid_identifier_chars, min=1, max=255)
dimension_value = Word(valid_identifier_chars, min=1, max=255)
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 = Word(nums)
decimal_number = Word(nums + ".")
integer_number = pyparsing.Word(pyparsing.nums)
decimal_number = pyparsing.Word(pyparsing.nums + ".")
max = CaselessLiteral("max")
min = CaselessLiteral("min")
avg = CaselessLiteral("avg")
count = CaselessLiteral("count")
sum = CaselessLiteral("sum")
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 = (CaselessLiteral("<") | CaselessLiteral("lt"))
less_than_eq_op = (CaselessLiteral("<=") | CaselessLiteral("lte"))
greater_than_op = (CaselessLiteral(">") | CaselessLiteral("gt"))
greater_than_eq_op = (CaselessLiteral(">=") | CaselessLiteral("gte"))
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 = CaselessLiteral("and") | CaselessLiteral("&&")
OR = CaselessLiteral("or") | CaselessLiteral("||")
AND = pyparsing.CaselessLiteral("and") | pyparsing.CaselessLiteral("&&")
OR = pyparsing.CaselessLiteral("or") | pyparsing.CaselessLiteral("||")
logical_op = (AND | OR)("logical_op")
times = CaselessLiteral("times")
times = pyparsing.CaselessLiteral("times")
dimension = Group(dimension_name + EQUAL + dimension_value)
dimension = pyparsing.Group(dimension_name + EQUAL + dimension_value)
# Cannot have any whitespace after the comma delimiter.
dimension_list = Group(Optional(
LBRACE + delimitedList(dimension, delim=',', combine=True)(
dimension_list = pyparsing.Group(pyparsing.Optional(
LBRACE + pyparsing.delimitedList(dimension, delim=',', combine=True)(
"dimensions_list") + RBRACE))
metric = metric_name + dimension_list("dimensions")
@ -241,17 +235,18 @@ period = integer_number("period")
threshold = decimal_number("threshold")
periods = integer_number("periods")
expression = Forward()
expression = pyparsing.Forward()
sub_expression = (func + LPAREN + metric + Optional(
COMMA + period) + RPAREN + relational_op + threshold + Optional(
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 = operatorPrecedence(sub_expression,
[(AND, 2, opAssoc.LEFT, AndSubExpr),
(OR, 2, opAssoc.LEFT, OrSubExpr)])
expression = (
pyparsing.operatorPrecedence(sub_expression,
[(AND, 2, pyparsing.opAssoc.LEFT, AndSubExpr),
(OR, 2, pyparsing.opAssoc.LEFT, OrSubExpr)]))
class AlarmExprParser(object):
@ -262,37 +257,37 @@ class AlarmExprParser(object):
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 + stringEnd).parseString(
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. """
"""Used for development and testing."""
expr0 = "max(-_.千幸福的笑脸{घोड़ा=馬, dn2=dv2}, 60) gte 100 times 3 && " \
"(min(ເຮືອນ{dn3=dv3,家=дом}) < 10 or sum(biz{dn5=dv5}) > 99 and " \
"count(fizzle) lt 0 or count(baz) > 1)".decode('utf8')
expr0 = ("max(-_.千幸福的笑脸{घोड़ा=馬, dn2=dv2}, 60) 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')
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'))
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
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__":

View File

@ -14,11 +14,13 @@
class MockAuthFilter(object):
'''
"""Authorization filter.
This authorization filter doesn't do any authentication, it just copies the
auth token to the tenant ID and supplies the 'admin' role and is meant for
testing purposes only.
'''
"""
def __init__(self, app, conf):
self.app = app
self.conf = conf

View File

@ -1,87 +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 logging
import os
import tempfile
import fixtures
import testtools
_TRUE_VALUES = ('True', 'true', '1', 'yes')
_LOG_FORMAT = "%(levelname)8s [%(name)s] %(message)s"
class BaseTestCase(testtools.TestCase):
def setUp(self):
super(BaseTestCase, self).setUp()
self._set_timeout()
self._fake_output()
self._fake_logs()
self.useFixture(fixtures.NestedTempfile())
self.useFixture(fixtures.TempHomeDir())
self.tempdirs = []
def _set_timeout(self):
test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
try:
test_timeout = int(test_timeout)
except ValueError:
# If timeout value is invalid do not set a timeout.
test_timeout = 0
if test_timeout > 0:
self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
def _fake_output(self):
if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES:
stdout = self.useFixture(fixtures.StringStream('stdout')).stream
self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES:
stderr = self.useFixture(fixtures.StringStream('stderr')).stream
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
def _fake_logs(self):
if os.environ.get('OS_DEBUG') in _TRUE_VALUES:
level = logging.DEBUG
else:
level = logging.INFO
capture_logs = os.environ.get('OS_LOG_CAPTURE') in _TRUE_VALUES
if capture_logs:
self.useFixture(
fixtures.FakeLogger(
format=_LOG_FORMAT,
level=level,
nuke_handlers=capture_logs,
)
)
else:
logging.basicConfig(format=_LOG_FORMAT, level=level)
def create_tempfiles(self, files, ext='.conf'):
tempfiles = []
for (basename, contents) in files:
if not os.path.isabs(basename):
(fd, path) = tempfile.mkstemp(prefix=basename, suffix=ext)
else:
path = basename + ext
fd = os.open(path, os.O_CREAT | os.O_WRONLY)
tempfiles.append(path)
try:
os.write(fd, contents)
finally:
os.close(fd)
return tempfiles

View File

@ -0,0 +1,21 @@
# 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 unittest
class Test_first(unittest.TestCase):
def test_first(self):
assert 1 == 1

View File

@ -12,8 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from voluptuous import Schema, Length, Optional
from voluptuous import Required, Any, All
import voluptuous
from monasca.openstack.common import log
from monasca.v2.common.schemas import exceptions
@ -22,20 +21,26 @@ from monasca.v2.common.schemas import exceptions
LOG = log.getLogger(__name__)
alarm_definition_schema = {
Required('name'): All(Any(str, unicode), Length(max=250)),
Required('expression'): All(Any(str, unicode), Length(max=4096)),
Optional('description'): All(Any(str, unicode), Length(max=250)),
Optional('severity'): All(
Any('low', 'medium', 'high', 'critical', 'LOW', "MEDIUM", 'HIGH',
'CRITICAL')),
Optional('match_by'): All(Any([unicode], [str]), Length(max=255)),
Optional('ok_actions'): All(Any([str], [unicode]), Length(max=400)),
Optional('alarm_actions'): All(Any([str], [unicode]), Length(max=400)),
Optional('undetermined_actions'): All(Any([str], [unicode]),
Length(max=400))}
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))}
request_body_schema = Schema(alarm_definition_schema, required=True,
extra=True)
request_body_schema = voluptuous.Schema(alarm_definition_schema, required=True,
extra=True)
def validate(msg):

View File

@ -4,7 +4,7 @@
# 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
# 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
@ -12,15 +12,17 @@
# License for the specific language governing permissions and limitations
# under the License.
from voluptuous import Schema
from voluptuous import Any, All, Length
import voluptuous
from monasca.openstack.common import log
from monasca.v2.common.schemas import exceptions
LOG = log.getLogger(__name__)
dimensions_schema = Schema({All(Any(str, unicode), Length(max=255)):
All(Any(str, unicode), Length(max=255))})
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):

View File

@ -4,7 +4,7 @@
# 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
# 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
@ -12,17 +12,17 @@
# License for the specific language governing permissions and limitations
# under the License.
from voluptuous import Schema
from voluptuous import Any, All, Length
import voluptuous
from monasca.openstack.common import log
from monasca.v2.common.schemas import exceptions
LOG = log.getLogger(__name__)
# TODO: Add regex to validate key/values don't use any excluded characters.
event_schema_request_body = Schema({All(Any(str, unicode), Length(max=255)):
All(Any(None, str, unicode, bool, int,
float, dict, []))})
event_schema_request_body = voluptuous.Schema({
voluptuous.All(voluptuous.Any(str, unicode),
voluptuous.Length(max=255)): voluptuous.All(
voluptuous.Any(None, str, unicode, bool, int, float, dict, []))})
def validate(body):

View File

@ -4,7 +4,7 @@
# 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
# 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
@ -12,14 +12,15 @@
# License for the specific language governing permissions and limitations
# under the License.
from voluptuous import Schema
from voluptuous import Any, All, Length
import voluptuous
from monasca.openstack.common import log
from monasca.v2.common.schemas import exceptions
LOG = log.getLogger(__name__)
metric_name_schema = Schema(All(Any(str, unicode), Length(max=64)))
metric_name_schema = voluptuous.Schema(
voluptuous.All(voluptuous.Any(str, unicode), voluptuous.Length(max=64)))
def validate(name):

View File

@ -4,7 +4,7 @@
# 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
# 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
@ -12,23 +12,24 @@
# License for the specific language governing permissions and limitations
# under the License.
from voluptuous import Schema
from voluptuous import Required, Any, All, Range, Optional
import voluptuous
from monasca.openstack.common import log
from monasca.v2.common.schemas import metric_name_schema
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 = {
Required('name'): metric_name_schema.metric_name_schema,
Optional('dimensions'): dimensions_schema.dimensions_schema,
Required('timestamp'): All(Any(int, float), Range(min=0)),
Required('value'): Any(int, float)
}
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 = Schema(Any(metric_schema, [metric_schema]))
request_body_schema = voluptuous.Schema(
voluptuous.Any(metric_schema, [metric_schema]))
def validate(msg):

View File

@ -4,7 +4,7 @@
# 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
# 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
@ -12,20 +12,24 @@
# License for the specific language governing permissions and limitations
# under the License.
from voluptuous import Schema
from voluptuous import Optional, Required, Any, All, Length
import voluptuous
from monasca.openstack.common import log
from monasca.v2.common.schemas import exceptions
LOG = log.getLogger(__name__)
notification_schema = {
Required('name'): Schema(All(Any(str, unicode), Length(max=250))),
Required('type'): Schema(Any("EMAIL", "email")),
Required('address'): Schema(All(Any(str, unicode), Length(max=100)))
}
voluptuous.Required('name'): voluptuous.Schema(
voluptuous.All(voluptuous.Any(str, unicode),
voluptuous.Length(max=250))),
voluptuous.Required('type'): voluptuous.Schema(
voluptuous.Any("EMAIL", "email")),
voluptuous.Required('address'): voluptuous.Schema(
voluptuous.All(voluptuous.Any(str, unicode),
voluptuous.Length(max=100)))}
request_body_schema = Schema(Any(notification_schema))
request_body_schema = voluptuous.Schema(voluptuous.Any(notification_schema))
def validate(msg):

View File

@ -4,7 +4,7 @@
# 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
# 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
@ -12,22 +12,26 @@
# License for the specific language governing permissions and limitations
# under the License.
from voluptuous import Schema
from voluptuous import Optional, Required, Any, All, Length
import voluptuous
from monasca.openstack.common import log
from monasca.v2.common.schemas import exceptions
LOG = log.getLogger(__name__)
transform_schema = {
Required('name'): Schema(All(Any(str, unicode), Length(max=64))),
Required('description'): Schema(All(Any(str, unicode), Length(max=250))),
Required('specification'):
Schema(All(Any(str, unicode), Length(max=64536))),
Optional('enabled'): bool
}
voluptuous.Required('name'): voluptuous.Schema(
voluptuous.All(voluptuous.Any(str, unicode),
voluptuous.Length(max=64))),
voluptuous.Required('description'): voluptuous.Schema(
voluptuous.All(voluptuous.Any(str, unicode),
voluptuous.Length(max=250))),
voluptuous.Required('specification'): voluptuous.Schema(
voluptuous.All(voluptuous.Any(str, unicode),
voluptuous.Length(max=64536))),
voluptuous.Optional('enabled'): bool}
request_body_schema = Schema(Any(transform_schema))
request_body_schema = voluptuous.Schema(voluptuous.Any(transform_schema))
def validate(msg):

View File

@ -11,21 +11,24 @@
# 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 re
from pyparsing import ParseException
import falcon
from oslo.config import cfg
import pyparsing
from monasca.api.alarm_definitions_api_v2 import AlarmDefinitionsV2API
from monasca.common.repositories import exceptions
from monasca.common import resource_api
from monasca.api.alarm_definitions_api_v2 import AlarmDefinitionsV2API
from monasca.expression_parser.alarm_expr_parser import AlarmExprParser
import monasca.expression_parser.alarm_expr_parser
from monasca.openstack.common import log
from monasca.v2.reference import helpers
from monasca.v2.common.schemas import alarm_definition_request_body_schema as schema_alarms
from monasca.v2.common.schemas import (alarm_definition_request_body_schema
as schema_alarms)
from monasca.v2.common.schemas import exceptions as schemas_exceptions
from monasca.v2.reference.alarming import Alarming
from monasca.v2.reference import helpers
from monasca.v2.reference.helpers import read_json_msg_body
from monasca.v2.reference.resource import resource_try_catch_block
@ -43,13 +46,13 @@ class AlarmDefinitions(AlarmDefinitionsV2API, Alarming):
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._alarm_definitions_repo = resource_api.init_driver(
'monasca.repositories',
@ -134,9 +137,8 @@ class AlarmDefinitions(AlarmDefinitionsV2API, Alarming):
@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)
alarm_definition_row = (
self._alarm_definitions_repo.get_alarm_definition(tenant_id, id))
match_by = get_comma_separated_str_as_list(
alarm_definition_row.match_by)
@ -168,9 +170,8 @@ class AlarmDefinitions(AlarmDefinitionsV2API, Alarming):
@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)
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(
@ -189,9 +190,9 @@ class AlarmDefinitions(AlarmDefinitionsV2API, Alarming):
@resource_try_catch_block
def _alarm_definition_list(self, tenant_id, name, dimensions, req_uri):
alarm_definition_rows = \
self._alarm_definitions_repo.get_alarm_definitions(
tenant_id, name, dimensions)
alarm_definition_rows = (
self._alarm_definitions_repo.get_alarm_definitions(tenant_id, name,
dimensions))
result = []
for alarm_definition_row in alarm_definition_rows:
@ -227,7 +228,6 @@ class AlarmDefinitions(AlarmDefinitionsV2API, Alarming):
return result
def _validate_alarm_definition(self, alarm_definition):
try:
@ -243,20 +243,29 @@ class AlarmDefinitions(AlarmDefinitionsV2API, Alarming):
ok_actions):
try:
sub_expr_list = AlarmExprParser(expression).sub_expr_list
sub_expr_list = (
monasca.expression_parser.alarm_expr_parser.
AlarmExprParser(expression).sub_expr_list)
except ParseException as ex:
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('utf'))
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)
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,
@ -334,6 +343,7 @@ class AlarmDefinitions(AlarmDefinitionsV2API, Alarming):
self._send_event(alarm_definition_created_event_msg)
def get_query_alarm_definition_name(alarm_definition):
try:
if 'name' in alarm_definition:
@ -407,6 +417,7 @@ def get_query_ok_actions(alarm_definition):
else:
return []
def get_comma_separated_str_as_list(comma_separated_str):
if not comma_separated_str:

View File

@ -12,12 +12,14 @@
# License for the specific language governing permissions and limitations
# under the License.
import json
import falcon
from oslo.config import cfg
from monasca.common import resource_api
from monasca.expression_parser.alarm_expr_parser import AlarmExprParser
from monasca.openstack.common import log
from monasca.common.messaging import exceptions as message_queue_exceptions
from monasca.common import resource_api
import monasca.expression_parser.alarm_expr_parser
from monasca.openstack.common import log
LOG = log.getLogger(__name__)
@ -33,10 +35,9 @@ class Alarming(object):
super(Alarming, self).__init__()
self._message_queue \
= resource_api.init_driver('monasca.messaging',
cfg.CONF.messaging.driver,
(['events']))
self._message_queue = (
resource_api.init_driver('monasca.messaging',
cfg.CONF.messaging.driver, (['events'])))
def _send_alarm_deleted_event(self, tenant_id, alarm_definition_id,
alarm_metric_rows, sub_alarm_rows):
@ -52,15 +53,17 @@ class Alarming(object):
else:
sub_alarm_dict[sub_alarm_row.alarm_id] = [sub_alarm_row]
# Forward declaration.
alarm_deleted_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_deleted_event_msg = \
self._build_sub_alarm_deleted_event_msg(
sub_alarm_dict, prev_alarm_id)
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]
u'subAlarms': sub_alarms_deleted_event_msg]
self._send_event(alarm_deleted_event_msg)
alarm_metrics_event_msg = []
@ -99,7 +102,8 @@ class Alarming(object):
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]
sub_expr = (monasca.expression_parser.alarm_expr_parser.
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,

View File

@ -11,17 +11,19 @@
# 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
from falcon.util.uri import parse_query_string
import re
import falcon
from oslo.config import cfg
from monasca.api.alarms_api_v2 import AlarmsV2API
from monasca.common.repositories import exceptions
from monasca.common import resource_api
from monasca.openstack.common import log
from monasca.v2.reference import helpers
from monasca.v2.reference.alarming import Alarming
from monasca.v2.reference import helpers
from monasca.v2.reference.resource import resource_try_catch_block
@ -38,17 +40,20 @@ class Alarms(AlarmsV2API, Alarming):
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._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)
@ -91,7 +96,7 @@ class Alarms(AlarmsV2API, Alarming):
helpers.validate_authorization(req, self._default_authorized_roles)
tenant_id = helpers.get_tenant_id(req)
query_parms = parse_query_string(req.query_string)
query_parms = falcon.uri.parse_query_string(req.query_string)
result = self._alarm_list(req.uri, tenant_id, query_parms)
@ -101,6 +106,11 @@ class Alarms(AlarmsV2API, Alarming):
@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)
@ -109,6 +119,53 @@ class Alarms(AlarmsV2API, Alarming):
res.body = json.dumps(result, ensure_ascii=False).encode('utf8')
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)
result = self._alarm_history_list(tenant_id, start_timestamp,
end_timestamp, query_parms)
res.body = json.dumps(result, ensure_ascii=False).encode('utf8')
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)
result = self._alarm_history(tenant_id, [id])
res.body = json.dumps(result, ensure_ascii=False).encode('utf8')
res.status = falcon.HTTP_200
@resource_try_catch_block
def _alarm_history_list(self, tenant_id, start_timestamp,
end_timestamp, query_parms):
# 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]
return self._metrics_repo.alarm_history(tenant_id, alarm_id_list,
start_timestamp, end_timestamp)
@resource_try_catch_block
def _alarm_history(self, tenant_id, alarm_id):
return self._metrics_repo.alarm_history(tenant_id, alarm_id)
@resource_try_catch_block
def _alarm_delete(self, tenant_id, id):
@ -171,6 +228,8 @@ class Alarms(AlarmsV2API, Alarming):
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:

View File

@ -17,41 +17,44 @@ import json
import falcon
from oslo.config import cfg
from monasca.openstack.common import log
from monasca.api import monasca_events_api_v2
from monasca.common import resource_api
from monasca.common.messaging import exceptions as message_queue_exceptions
from monasca.common.messaging.message_formats import events_transform_factory
from monasca.v2.common import utils
from monasca.common import resource_api
from monasca.openstack.common import log
from monasca.v2.common.schemas import (
events_request_body_schema as schemas_event)
from monasca.v2.common.schemas import exceptions as schemas_exceptions
from monasca.v2.common.schemas import \
events_request_body_schema as schemas_event
from monasca.v2.common import utils
from monasca.v2.reference import helpers
LOG = log.getLogger(__name__)
class Events(monasca_events_api_v2.EventsV2API):
def __init__(self, global_conf):
super(Events, 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_events_authorized_roles = \
cfg.CONF.security.default_authorized_roles + \
cfg.CONF.security.agent_authorized_roles
self._event_transform = \
events_transform_factory.create_events_transform()
self._message_queue = \
self._default_authorized_roles = (
cfg.CONF.security.default_authorized_roles)
self._delegate_authorized_roles = (
cfg.CONF.security.delegate_authorized_roles)
self._post_events_authorized_roles = (
cfg.CONF.security.default_authorized_roles +
cfg.CONF.security.agent_authorized_roles)
self._event_transform = (
events_transform_factory.create_events_transform())
self._message_queue = (
resource_api.init_driver('monasca.messaging',
cfg.CONF.messaging.driver,
['raw-events'])
['raw-events']))
def _validate_event(self, event):
"""Validates the event
:param event: An event object.
:raises falcon.HTTPBadRequest
"""
@ -63,7 +66,7 @@ class Events(monasca_events_api_v2.EventsV2API):
def _send_event(self, event):
"""Send the event using the message queue.
:param metrics: An event object.
:raises: falcon.HTTPServiceUnavailable
"""

View File

@ -11,25 +11,25 @@
# 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 json
from urlparse import urlparse
import urlparse
import falcon
from falcon.util.uri import parse_query_string
import simplejson
from monasca.openstack.common import log
from monasca.v2.common.schemas import dimensions_schema
from monasca.v2.common.schemas import exceptions as schemas_exceptions
from monasca.v2.common.schemas import metric_name_schema
from monasca.v2.common.schemas import dimensions_schema
import simplejson
LOG = log.getLogger(__name__)
def read_json_msg_body(req):
"""
Read the json_msg from the http request body and return them as JSON.
"""Read the json_msg from the http request body and return them as JSON.
:param req: HTTP request object.
:return: Returns the metrics as a JSON object.
:raises falcon.HTTPBadRequest:
@ -51,9 +51,8 @@ def validate_json_content_type(req):
def is_in_role(req, authorized_roles):
"""Determines if one or more of the X-ROLES is in the supplied
authorized_roles.
"""Is one or more of the X-ROLES in the supplied authorized_roles.
:param req: HTTP request object. Must contain "X-ROLES" in the HTTP
request header.
:param authorized_roles: List of authorized roles to check against.
@ -71,7 +70,7 @@ def is_in_role(req, authorized_roles):
def validate_authorization(req, authorized_roles):
"""Validates whether one or more X-ROLES in the HTTP header is authorized.
:param req: HTTP request object. Must contain "X-ROLES" in the HTTP
request header.
:param authorized_roles: List of authorized roles to check against.
@ -92,7 +91,7 @@ def validate_authorization(req, authorized_roles):
def get_tenant_id(req):
"""Returns the tenant ID in the HTTP request header.
:param req: HTTP request object.
"""
return req.get_header('X-TENANT-ID')
@ -100,14 +99,14 @@ def get_tenant_id(req):
def get_x_tenant_or_tenant_id(req, delegate_authorized_roles):
"""Evaluates whether the tenant ID or cross tenant ID should be returned.
:param req: HTTP request object.
:param delegate_authorized_roles: List of authorized roles that have
delegate privileges.
:returns: Returns the cross tenant or tenant ID.
"""
if is_in_role(req, delegate_authorized_roles):
params = parse_query_string(req.query_string)
params = falcon.uri.parse_query_string(req.query_string)
if 'tenant_id' in params:
tenant_id = params['tenant_id']
return tenant_id
@ -115,12 +114,12 @@ def get_x_tenant_or_tenant_id(req, delegate_authorized_roles):
def get_query_name(req, name_required=False):
"""
Returns the query param "name" if supplied.
"""Returns the query param "name" if supplied.
:param req: HTTP request object.
"""
try:
params = parse_query_string(req.query_string)
params = falcon.uri.parse_query_string(req.query_string)
if 'name' in params:
name = params['name']
return name
@ -136,13 +135,13 @@ def get_query_name(req, name_required=False):
def get_query_dimensions(req):
"""Gets and parses the query param dimensions.
:param req: HTTP request object.
:return: Returns the dimensions as a JSON object
:raises falcon.HTTPBadRequest: If dimensions are malformed.
"""
try:
params = parse_query_string(req.query_string)
params = falcon.uri.parse_query_string(req.query_string)
dimensions = {}
if 'dimensions' in params:
dimensions_str = params['dimensions']
@ -160,25 +159,31 @@ def get_query_dimensions(req):
raise falcon.HTTPBadRequest('Bad request', ex.message)
def get_query_starttime_timestamp(req):
def get_query_starttime_timestamp(req, required=True):
try:
params = parse_query_string(req.query_string)
params = falcon.uri.parse_query_string(req.query_string)
if 'start_time' in params:
return _convert_time_string(params['start_time'])
else:
raise Exception("Missing start time")
if required:
raise Exception("Missing start time")
else:
return None
except Exception as ex:
LOG.debug(ex)
raise falcon.HTTPBadRequest('Bad request', ex.message)
def get_query_endtime_timestamp(req):
def get_query_endtime_timestamp(req, required=True):
try:
params = parse_query_string(req.query_string)
params = falcon.uri.parse_query_string(req.query_string)
if 'end_time' in params:
return _convert_time_string(params['end_time'])
else:
return None
if required:
raise Exception("Missing end time")
else:
return None
except Exception as ex:
LOG.debug(ex)
raise falcon.HTTPBadRequest('Bad request', ex.message)
@ -192,7 +197,7 @@ def _convert_time_string(date_time_string):
def get_query_statistics(req):
try:
params = parse_query_string(req.query_string)
params = falcon.uri.parse_query_string(req.query_string)
if 'statistics' in params:
statistics = params['statistics'].split(',')
statistics = [statistic.lower() for statistic in statistics]
@ -209,7 +214,7 @@ def get_query_statistics(req):
def get_query_period(req):
try:
params = parse_query_string(req.query_string)
params = falcon.uri.parse_query_string(req.query_string)
if 'period' in params:
return params['period']
else:
@ -221,7 +226,7 @@ def get_query_period(req):
def validate_query_name(name):
"""Validates the query param name.
:param name: Query param name.
:raises falcon.HTTPBadRequest: If name is not valid.
"""
@ -234,7 +239,7 @@ def validate_query_name(name):
def validate_query_dimensions(dimensions):
"""Validates the query param dimensions.
:param dimensions: Query param dimensions.
:raises falcon.HTTPBadRequest: If dimensions are not valid.
"""
@ -247,7 +252,7 @@ def validate_query_dimensions(dimensions):
def get_link(uri, resource_id, rel='self'):
"""Returns a link dictionary containing href, and rel.
:param uri: the http request.uri.
:param resource_id: the id of the resource
"""
@ -265,7 +270,7 @@ def get_link(uri, resource_id, rel='self'):
def add_links_to_resource(resource, uri, rel='self'):
"""Adds links to the given resource dictionary.
:param resource: the resource dictionary you wish to add links.
:param uri: the http request.uri.
"""
@ -275,7 +280,7 @@ def add_links_to_resource(resource, uri, rel='self'):
def add_links_to_resource_list(resourcelist, uri):
"""Adds links to the given resource dictionary list.
:param resourcelist: the list of resources you wish to add links.
:param uri: the http request.uri.
"""
@ -286,7 +291,7 @@ def add_links_to_resource_list(resourcelist, uri):
def read_http_resource(req):
"""Read from http request and return json.
:param req: the http request.
"""
try:
@ -302,7 +307,7 @@ def read_http_resource(req):
def raise_not_found_exception(resource_name, resource_id, tenant_id):
"""Provides exception for not found requests (update, delete, list).
:param resource_name: the name of the resource.
:param resource_id: id of the resource.
:param tenant_id: id of the tenant

View File

@ -16,22 +16,17 @@ import json
import falcon
from oslo.config import cfg
from stevedore import driver
from monasca.openstack.common import log
from monasca.api import monasca_api_v2
from monasca.common import resource_api
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.common.schemas import exceptions as schemas_exceptions
from monasca.v2.common.schemas import \
metrics_request_body_schema as schemas_metrics
from monasca.common.repositories import exceptions
from monasca.v2.reference import helpers
from monasca.v2.reference.helpers import read_json_msg_body
LOG = log.getLogger(__name__)
@ -41,21 +36,23 @@ 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._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)
@ -66,7 +63,7 @@ class Metrics(monasca_api_v2.V2API):
def _validate_metrics(self, metrics):
"""Validates the metrics
:param metrics: A metric object or array of metrics objects.
:raises falcon.HTTPBadRequest
"""
@ -78,7 +75,7 @@ class Metrics(monasca_api_v2.V2API):
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
"""
@ -90,7 +87,7 @@ class Metrics(monasca_api_v2.V2API):
except message_queue_exceptions.MessageQueueException as ex:
LOG.exception(ex)
raise falcon.HTTPServiceUnavailable('Service unavailable',
ex.message)
ex.message, 60)
if isinstance(metrics, list):
for metric in metrics:
@ -100,7 +97,7 @@ class Metrics(monasca_api_v2.V2API):
def _list_metrics(self, tenant_id, name, dimensions):
"""Query the metric repo for the metrics, format them and return them.
:param tenant_id:
:param name:
:param dimensions:
@ -112,7 +109,7 @@ class Metrics(monasca_api_v2.V2API):
except Exception as ex:
LOG.exception(ex)
raise falcon.HTTPServiceUnavailable('Service unavailable',
ex.message)
ex.message, 60)
def _measurement_list(self, tenant_id, name, dimensions, start_timestamp,
end_timestamp):
@ -124,7 +121,7 @@ class Metrics(monasca_api_v2.V2API):
except Exception as ex:
LOG.exception(ex)
raise falcon.HTTPServiceUnavailable('Service unavailable',
ex.message)
ex.message, 60)
def _metric_statistics(self, tenant_id, name, dimensions, start_timestamp,
end_timestamp, statistics, period):
@ -137,7 +134,7 @@ class Metrics(monasca_api_v2.V2API):
except Exception as ex:
LOG.exception(ex)
raise falcon.HTTPServiceUnavailable('Service unavailable',
ex.message)
ex.message, 60)
@resource_api.Restify('/v2.0/metrics/', method='post')
def do_post_metrics(self, req, res):
@ -146,9 +143,9 @@ class Metrics(monasca_api_v2.V2API):
self._post_metrics_authorized_roles)
metrics = helpers.read_http_resource(req)
self._validate_metrics(metrics)
tenant_id = \
tenant_id = (
helpers.get_x_tenant_or_tenant_id(req,
self._delegate_authorized_roles)
self._delegate_authorized_roles))
transformed_metrics = self._metrics_transform(metrics, tenant_id,
self._region)
self._send_metrics(transformed_metrics)
@ -175,7 +172,7 @@ class Metrics(monasca_api_v2.V2API):
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)
end_timestamp = helpers.get_query_endtime_timestamp(req, False)
result = self._measurement_list(tenant_id, name, dimensions,
start_timestamp, end_timestamp)
res.body = json.dumps(result, ensure_ascii=False).encode('utf8')
@ -190,7 +187,7 @@ class Metrics(monasca_api_v2.V2API):
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)
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,

View File

@ -12,22 +12,19 @@
# License for the specific language governing permissions and limitations
# under the License.
# TODO: Used simplejson to read the yaml as simplejson transforms to "str"
# not "unicode"
import json
import falcon
from oslo.config import cfg
from monasca.api import monasca_notifications_api_v2
from monasca.common.repositories import exceptions as repository_exceptions
from monasca.common import resource_api
from monasca.openstack.common import log
from monasca.openstack.common import uuidutils
from monasca.api import monasca_notifications_api_v2
from monasca.common import resource_api
from monasca.common.repositories import exceptions as repository_exceptions
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.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
LOG = log.getLogger(__name__)
@ -37,14 +34,14 @@ 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._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
"""
@ -56,7 +53,7 @@ class Notifications(monasca_notifications_api_v2.NotificationsV2API):
def _create_notification(self, id, tenant_id, notification):
"""Store the notification using the repository.
:param notification: A notification object.
:raises: falcon.HTTPServiceUnavailable,falcon.HTTPConflict
"""
@ -66,8 +63,9 @@ class Notifications(monasca_notifications_api_v2.NotificationsV2API):
address = notification['address']
if self._notifications_repo.exists(tenant_id, name):
raise falcon.HTTPConflict('Conflict', (
'Notification Method already exists: tenant_id=%s name=%s' % (
tenant_id, name)), code=409)
'Notification Method already exists: tenant_id=%s '
'name=%s' % (
tenant_id, name)), code=409)
self._notifications_repo.create_notification(id, tenant_id, name,
notification_type,
address)
@ -78,7 +76,7 @@ class Notifications(monasca_notifications_api_v2.NotificationsV2API):
def _update_notification(self, id, tenant_id, notification):
"""Update the notification using the repository.
:param notification: A notification object.
:raises: falcon.HTTPServiceUnavailable,falcon.HTTPError (404)
"""
@ -107,7 +105,7 @@ class Notifications(monasca_notifications_api_v2.NotificationsV2API):
def _list_notifications(self, tenant_id, uri):
"""Lists all notifications for this tenant id.
:param tenant_id: The tenant id.
:raises: falcon.HTTPServiceUnavailable
"""
@ -123,7 +121,7 @@ class Notifications(monasca_notifications_api_v2.NotificationsV2API):
def _list_notification(self, tenant_id, notification_id, uri):
"""Lists the notification by id.
:param tenant_id: The tenant id.
:param notification_id: The notification id
:raises: falcon.HTTPServiceUnavailable,falcon.HTTPError (404):
@ -143,7 +141,7 @@ class Notifications(monasca_notifications_api_v2.NotificationsV2API):
def _delete_notification(self, tenant_id, notification_id):
"""Deletes the notification using the repository.
:param tenant_id: The tenant id.
:param notification_id: The notification id
:raises: falcon.HTTPServiceUnavailable,falcon.HTTPError (404)

View File

@ -1,6 +1,19 @@
# 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 monasca.common.messaging import exceptions
from monasca.common.repositories.exceptions import DoesNotExistException
from monasca.common.repositories import exceptions
from monasca.openstack.common import log
@ -17,7 +30,7 @@ def resource_try_catch_block(fun):
except falcon.HTTPNotFound:
raise
except DoesNotExistException:
except exceptions.DoesNotExistException:
raise falcon.HTTPNotFound
except falcon.HTTPBadRequest:
raise

View File

@ -12,40 +12,38 @@
# License for the specific language governing permissions and limitations
# under the License.
# TODO: Used simplejson to read the yaml as simplejson transforms to "str"
# not "unicode"
import json
import simplejson
import falcon
from oslo.config import cfg
from monasca.api import monasca_transforms_api_v2
from monasca.common.repositories import exceptions as repository_exceptions
from monasca.common import resource_api
from monasca.openstack.common import log
from monasca.openstack.common import uuidutils
from monasca.api import monasca_transforms_api_v2
from monasca.common import resource_api
from monasca.common.repositories import exceptions as repository_exceptions
from monasca.v2.common.schemas import exceptions as schemas_exceptions
from monasca.v2.common.schemas import \
transforms_request_body_schema as schemas_transforms
from monasca.v2.common.schemas import (exceptions as schemas_exceptions)
from monasca.v2.common.schemas import (
transforms_request_body_schema as schemas_transforms)
from monasca.v2.reference import helpers
LOG = log.getLogger(__name__)
class Transforms(monasca_transforms_api_v2.TransformsV2API):
def __init__(self, global_conf):
super(Transforms, self).__init__(global_conf)
self._region = cfg.CONF.region
self._default_authorized_roles = \
cfg.CONF.security.default_authorized_roles
self._default_authorized_roles = (
cfg.CONF.security.default_authorized_roles)
self._transforms_repo = resource_api.init_driver(
'monasca.repositories', cfg.CONF.repositories.transforms_driver)
def _validate_transform(self, transform):
"""Validates the transform
:param transform: An event object.
:raises falcon.HTTPBadRequest
"""
@ -57,7 +55,7 @@ class Transforms(monasca_transforms_api_v2.TransformsV2API):
def _create_transform(self, id, tenant_id, transform):
"""Store the transform using the repository.
:param transform: A transform object.
:raises: falcon.HTTPServiceUnavailable
"""

View File

@ -8,4 +8,4 @@ gunicorn>=19.1.0
oslo.config>=1.2.1
ujson>=1.33
Pyparsing>=2.0.3
pyodbc>=3.0.7
influxdb>=0.1.12

View File

@ -9,6 +9,7 @@ pep8<=1.5.6
httplib2>=0.7.5
mock>=1.0
mox>=0.5.3
nose
# Docs Requirements
oslosphinx
oslotest

12
tox.ini
View File

@ -4,14 +4,12 @@ skipsdist = True
envlist = py27,py33,pep8
[testenv]
setenv = VIRTUAL_ENV={envdir}
usedevelop = True
install_command = pip install -U {opts} {packages}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
install_command = pip install -U --allow-external pytidylib --allow-insecure pytidylib --allow-external netifaces --allow-insecure netifaces {opts} {packages}
usedevelop = True
setenv = VIRTUAL_ENV={envdir}
commands = python setup.py testr --slowest --testr-args='--concurrency 1 {posargs}'
downloadcache = {toxworkdir}/_download
whitelist_externals = bash
commands = nosetests
[testenv:cover]
setenv = NOSE_WITH_COVERAGE=1
@ -32,8 +30,6 @@ commands = python setup.py build_sphinx
commands = {posargs}
[flake8]
# H305 imports not grouped correctly
ignore = H305
builtins = _
exclude=.venv,.git,.tox,dist,doc,./monasca/openstack/common,*lib/python*,*egg,tools,build
show-source = True