kiloeyes/kiloeyes/v2/elasticsearch/notificationmethods.py

293 lines
10 KiB
Python
Executable File

# Copyright 2015 Carnegie Mellon University
#
# 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 ast
import falcon
import json
from oslo_config import cfg
import re
from stevedore import driver
import uuid
from kiloeyes.common import es_conn
from kiloeyes.common import namespace
from kiloeyes.common import resource_api
from oslo_log import log
NOTIFICATION_METHOD_OPTS = [
cfg.StrOpt('doc_type', default='notificationmethods',
help='The doc type that notification_methods '
'will be saved into.'),
cfg.StrOpt('index_strategy', default='fixed',
help='The index strategy used to create index name.'),
cfg.StrOpt('index_prefix', default='data_',
help='The index prefix where metrics were saved to.'),
cfg.IntOpt('size', default=10000,
help=('The query result limit. Any result set more than '
'the limit will be discarded. To see all the matching '
'result, narrow your search by using a small time '
'window or strong matching name')),
]
cfg.CONF.register_opts(NOTIFICATION_METHOD_OPTS, group="notificationmethods")
LOG = log.getLogger(__name__)
class ParamUtil(object):
@staticmethod
def validateEmail(addr):
if len(addr) > 7:
if (re.match("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\."
"([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$", addr)
is not None):
return True
return False
@staticmethod
def name(req):
# parse name from request
name = req.get_param('name')
if name and name.strip():
return name
else:
return "DefaultNotificationMethods"
@staticmethod
def type_address(req):
# parse notification type from request
# Default is EMAIL
type = req.get_param('type')
address = req.get_param('address')
# Currently, notification method types of email,
# PagerDuty and webhooks are supported.
# In the case of email, the address is the email address.
# For PagerDuty, the address is the PagerDuty Service API Key.
# For webhook, the address is the URL of the webhook.
if (type and type.strip() == 'EMAIL'
and address and address.strip()
and ParamUtil.validateEmail(address.strip())):
return ("EMAIL", address.strip())
elif (type and type.strip() == 'PAGEDUTY'
and address and address.strip()):
return ("PAGEDUTY", address.strip())
elif (type and type.strip() == 'WEBHOOK'
and address and address.strip()):
return ("WEBHOOK", address.strip())
else:
return None
class NotificationMethodDispatcher(object):
def __init__(self, global_conf):
LOG.debug('initializing V2API in NotificationMethodDispatcher!')
super(NotificationMethodDispatcher, self).__init__()
self.doc_type = cfg.CONF.notificationmethods.doc_type
self.size = cfg.CONF.notificationmethods.size
# load index strategy
if cfg.CONF.notificationmethods.index_strategy:
self.index_strategy = driver.DriverManager(
namespace.STRATEGY_NS,
cfg.CONF.notificationmethods.index_strategy,
invoke_on_load=True,
invoke_kwds={}).driver
LOG.debug(dir(self.index_strategy))
else:
self.index_strategy = None
self.index_prefix = cfg.CONF.notificationmethods.index_prefix
self._es_conn = es_conn.ESConnection(
self.doc_type, self.index_strategy, self.index_prefix)
def post_data(self, req, res):
LOG.debug('In NotificationMethodDispatcher::post_data.')
msg = req.stream.read()
# convert msg to dict
dict_msg = ast.literal_eval(msg)
# random uuid used for store the methods in database
id = str(uuid.uuid4())
# add an id to store in elasticsearch
dict_msg["id"] = id
# add an item "request" in the msg to tell
# the receiver this is a POST request
# The final msg is something like:
# {"id":"c60ec47e-5038-4bf1-9f95-4046c6e9a759",
# "request":"POST",
# "name":"TheName",
# "type":"TheType",
# "Address":"TheAddress"}
dict_msg["request"] = "POST"
LOG.debug("post notification method: %s" % dict_msg)
code = self.handle_notification_msg(dict_msg)
res.status = getattr(falcon, 'HTTP_' + str(code))
def put_data(self, req, res, id):
LOG.debug('In NotificationMethodDispatcher::put_data.')
msg = req.stream.read()
dict_msg = ast.literal_eval(msg)
# specify the id to match in elasticsearch for update
dict_msg["id"] = id
# add an item "request" in the msg to tell the receiver this is a PUT
# request
dict_msg["request"] = "PUT"
LOG.debug("put notification method: %s" % dict_msg)
code = self.handle_notification_msg(dict_msg)
res.status = getattr(falcon, 'HTTP_' + str(code))
def del_data(self, req, res, id):
LOG.debug('In NotificationMethodDispatcher::del_data.')
dict_msg = {}
# specify the id to match in elasticsearch for deletion
dict_msg["id"] = id
# add an item "request" in the msg to tell the receiver this is a DEL
# request
dict_msg["request"] = "DEL"
LOG.debug("delete notification method: %s" % dict_msg)
code = self.handle_notification_msg(dict_msg)
res.status = getattr(falcon, 'HTTP_' + str(code))
def _get_notification_method_response(self, res):
if res and res.status_code == 200:
obj = res.json()
if obj:
return obj.get('hits')
return None
else:
return None
def handle_notification_msg(self, dict_msg):
# dict_msg's format is:
# {"id":"c60ec47e-5038-4bf1-9f95-4046c6e9a759",
# "request":"POST",
# "name":"TheName",
# "type":"TheType",
# "Address":"TheAddress"}
# We add the POS/PUT/DEL in the message to indicate the request
# type
# Get the notification id from the message,
# this id will be used as _id for elasticsearch,
# and also stored as id in the notification_methods document
# type
# convert to dict, pop request, and get id
# after request is removed, the dict can be converted to
# request body for elasticsearch
request_type = dict_msg.pop("request", None)
id = dict_msg["id"]
if request_type is not None and id is not None:
# post
if request_type == 'POST':
return self._es_conn.post_messages(json.dumps(dict_msg), id)
# put
if request_type == 'PUT':
return self._es_conn.put_messages(json.dumps(dict_msg), id)
# delete
if request_type == 'DEL':
return self._es_conn.del_messages(id)
@resource_api.Restify('/v2.0/notification-methods/', method='get')
def do_get_notification_methods(self, req, res):
LOG.debug("The notification_methods GET request is received!")
es_res = self._es_conn.get_messages({})
res.status = getattr(falcon, 'HTTP_%s' % es_res.status_code)
LOG.debug('Query to ElasticSearch returned: %s' % es_res.status_code)
es_res = self._get_notification_method_response(es_res)
LOG.debug('Query to ElasticSearch returned: %s' % es_res)
res_data = es_res["hits"]
if res_data:
def _make_body(elements):
yield '{"links": [{"rel": "self", "href":"'
yield req.uri + '"}],'
yield '"elements": ['
first = True
for element in elements:
if element['_source']:
if not first:
yield ','
else:
first = False
links = [{"rel": "self",
"href": req.uri + "/" +
element['_source']['id']}]
element['_source']['links'] = links
yield json.dumps(element['_source'])
yield ']}'
res.body = ''.join(_make_body(res_data))
else:
res.body = ''
res.content_type = 'application/json;charset=utf-8'
@resource_api.Restify('/v2.0/notification-methods/{id}', method='get')
def do_get_notification_method_by_id(self, req, res, id):
LOG.debug("The notification_methods GET by id request is received!")
es_res = self._es_conn.get_message_by_id(id)
res.status = getattr(falcon, 'HTTP_%s' % es_res.status_code)
LOG.debug('Query to ElasticSearch returned: %s' % es_res.status_code)
es_res = self._get_notification_method_response(es_res)
LOG.debug('Query to ElasticSearch returned: %s' % es_res)
if es_res and es_res.get('hits'):
res_data = es_res['hits'][0]
obj = res_data['_source']
obj['id'] = id
obj['links'] = [{"rel": "self",
"href": req.uri}]
res.body = json.dumps(obj)
res.content_type = 'application/json;charset=utf-8'
else:
res.body = ''
@resource_api.Restify('/v2.0/notification-methods/', method='post')
def do_post_notification_methods(self, req, res):
self.post_data(req, res)
@resource_api.Restify('/v2.0/notification-methods/{id}', method='put')
def do_put_notification_methods(self, req, res, id):
self.put_data(req, res, id)
@resource_api.Restify('/v2.0/notification-methods/{id}', method='delete')
def do_delete_notification_methods(self, req, res, id):
self.del_data(req, res, id)