Closes #216
This commit is contained in:
parent
b5a4b293a9
commit
62d03b0d41
|
@ -11,6 +11,7 @@
|
||||||
from __future__ import absolute_import, division, print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
from lemur import factory
|
from lemur import factory
|
||||||
|
from lemur.extensions import metrics
|
||||||
|
|
||||||
from lemur.users.views import mod as users_bp
|
from lemur.users.views import mod as users_bp
|
||||||
from lemur.roles.views import mod as roles_bp
|
from lemur.roles.views import mod as roles_bp
|
||||||
|
@ -70,8 +71,17 @@ def configure_hook(app):
|
||||||
def after(response):
|
def after(response):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@app.errorhandler(500)
|
||||||
|
def internal_error(error):
|
||||||
|
metrics.send('500_status_code', 'counter', 1)
|
||||||
|
|
||||||
|
@app.errorhandler(400)
|
||||||
|
def response_error(error):
|
||||||
|
metrics.send('400_status_code', 'counter', 1)
|
||||||
|
|
||||||
@app.errorhandler(PermissionDenied)
|
@app.errorhandler(PermissionDenied)
|
||||||
def handle_invalid_usage(error):
|
def permission_denied_error(error):
|
||||||
|
metrics.send('403_status_code', 'counter', 1)
|
||||||
response = {'message': 'You are not allow to access this resource'}
|
response = {'message': 'You are not allow to access this resource'}
|
||||||
response.status_code = 403
|
response.status_code = 403
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -14,6 +14,7 @@ from flask import Blueprint, current_app
|
||||||
from flask.ext.restful import reqparse, Resource, Api
|
from flask.ext.restful import reqparse, Resource, Api
|
||||||
from flask.ext.principal import Identity, identity_changed
|
from flask.ext.principal import Identity, identity_changed
|
||||||
|
|
||||||
|
from lemur.extensions import metrics
|
||||||
from lemur.common.utils import get_psuedo_random_string
|
from lemur.common.utils import get_psuedo_random_string
|
||||||
|
|
||||||
from lemur.users import service as user_service
|
from lemur.users import service as user_service
|
||||||
|
@ -96,8 +97,10 @@ class Login(Resource):
|
||||||
# Tell Flask-Principal the identity changed
|
# Tell Flask-Principal the identity changed
|
||||||
identity_changed.send(current_app._get_current_object(),
|
identity_changed.send(current_app._get_current_object(),
|
||||||
identity=Identity(user.id))
|
identity=Identity(user.id))
|
||||||
|
metrics.send('successful_login', 'counter', 1)
|
||||||
return dict(token=create_token(user))
|
return dict(token=create_token(user))
|
||||||
|
|
||||||
|
metrics.send('invalid_login', 'counter', 1)
|
||||||
return dict(message='The supplied credentials are invalid'), 401
|
return dict(message='The supplied credentials are invalid'), 401
|
||||||
|
|
||||||
|
|
||||||
|
@ -176,6 +179,7 @@ class Ping(Resource):
|
||||||
profile = r.json()
|
profile = r.json()
|
||||||
|
|
||||||
user = user_service.get_by_email(profile['email'])
|
user = user_service.get_by_email(profile['email'])
|
||||||
|
metrics.send('successful_login', 'counter', 1)
|
||||||
|
|
||||||
# update their google 'roles'
|
# update their google 'roles'
|
||||||
roles = []
|
roles = []
|
||||||
|
@ -263,6 +267,7 @@ class Google(Resource):
|
||||||
user = user_service.get_by_email(profile['email'])
|
user = user_service.get_by_email(profile['email'])
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
|
metrics.send('successful_login', 'counter', 1)
|
||||||
return dict(token=create_token(user))
|
return dict(token=create_token(user))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ from sqlalchemy import func, or_
|
||||||
from flask import g, current_app
|
from flask import g, current_app
|
||||||
|
|
||||||
from lemur import database
|
from lemur import database
|
||||||
|
from lemur.extensions import metrics
|
||||||
from lemur.plugins.base import plugins
|
from lemur.plugins.base import plugins
|
||||||
from lemur.certificates.models import Certificate
|
from lemur.certificates.models import Certificate
|
||||||
|
|
||||||
|
@ -271,6 +272,7 @@ def create(**kwargs):
|
||||||
cert.notifications = notifications
|
cert.notifications = notifications
|
||||||
|
|
||||||
database.update(cert)
|
database.update(cert)
|
||||||
|
metrics.send('certificate_issued', 'counter', 1, metric_tags=dict(owner=cert.owner, issuer=cert.issuer))
|
||||||
return cert
|
return cert
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,3 +17,6 @@ principal = Principal()
|
||||||
|
|
||||||
from flask_mail import Mail
|
from flask_mail import Mail
|
||||||
smtp_mail = Mail()
|
smtp_mail = Mail()
|
||||||
|
|
||||||
|
from lemur.metrics import Metrics
|
||||||
|
metrics = Metrics()
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.metrics
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
from flask import current_app
|
||||||
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
|
|
||||||
|
class Metrics(object):
|
||||||
|
"""
|
||||||
|
:param app: The Flask application object. Defaults to None.
|
||||||
|
"""
|
||||||
|
_providers = []
|
||||||
|
|
||||||
|
def __init__(self, app=None):
|
||||||
|
if app is not None:
|
||||||
|
self.init_app(app)
|
||||||
|
|
||||||
|
def init_app(self, app):
|
||||||
|
"""Initializes the application with the extension.
|
||||||
|
|
||||||
|
:param app: The Flask application object.
|
||||||
|
"""
|
||||||
|
self._providers = app.config.get('METRIC_PROVIDERS', [])
|
||||||
|
|
||||||
|
def send(self, metric_name, metric_type, metric_value, *args, **kwargs):
|
||||||
|
for provider in self._providers:
|
||||||
|
current_app.logger.debug(
|
||||||
|
"Sending metric '{metric}' to the {provider} provider.".format(metric=metric_name, provider=provider))
|
||||||
|
p = plugins.get(provider)
|
||||||
|
p.submit(metric_name, metric_type, metric_value, *args, **kwargs)
|
|
@ -0,0 +1,16 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.bases.metric
|
||||||
|
:platform: Unix
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
|
||||||
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
"""
|
||||||
|
from lemur.plugins.base import Plugin
|
||||||
|
|
||||||
|
|
||||||
|
class MetricPlugin(Plugin):
|
||||||
|
type = 'metric'
|
||||||
|
|
||||||
|
def submit(self, *args, **kwargs):
|
||||||
|
raise NotImplemented
|
|
@ -0,0 +1,5 @@
|
||||||
|
try:
|
||||||
|
VERSION = __import__('pkg_resources') \
|
||||||
|
.get_distribution(__name__).version
|
||||||
|
except Exception as e:
|
||||||
|
VERSION = 'unknown'
|
|
@ -0,0 +1,107 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.plugins.lemur_atlas.plugin
|
||||||
|
:platform: Unix
|
||||||
|
:copyright: (c) 2016 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
|
||||||
|
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
from requests.exceptions import ConnectionError
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from flask import current_app
|
||||||
|
from lemur.plugins import lemur_atlas as atlas
|
||||||
|
from lemur.plugins.bases.metric import MetricPlugin
|
||||||
|
|
||||||
|
|
||||||
|
def millis_since_epoch():
|
||||||
|
"""
|
||||||
|
current time since epoch in milliseconds
|
||||||
|
"""
|
||||||
|
epoch = datetime.utcfromtimestamp(0)
|
||||||
|
delta = datetime.now() - epoch
|
||||||
|
return int(delta.total_seconds() * 1000.0)
|
||||||
|
|
||||||
|
|
||||||
|
class AtlasMetricPlugin(MetricPlugin):
|
||||||
|
title = 'Atlas'
|
||||||
|
slug = 'atlas-metric'
|
||||||
|
description = 'Adds support for sending key metrics to Atlas'
|
||||||
|
version = atlas.VERSION
|
||||||
|
|
||||||
|
author = 'Kevin Glisson'
|
||||||
|
author_url = 'https://github.com/netflix/lemur'
|
||||||
|
|
||||||
|
options = [
|
||||||
|
{
|
||||||
|
'name': 'sidecar_host',
|
||||||
|
'type': 'str',
|
||||||
|
'required': False,
|
||||||
|
'help_message': 'If no host is provided localhost is assumed',
|
||||||
|
'default': 'localhost'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'sidecar_port',
|
||||||
|
'type': 'int',
|
||||||
|
'required': False,
|
||||||
|
'default': 8078
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
metric_data = {}
|
||||||
|
sidecar_host = None
|
||||||
|
sidecar_port = None
|
||||||
|
|
||||||
|
def submit(self, metric_name, metric_type, metric_value, metric_tags=None, options=None):
|
||||||
|
if not options:
|
||||||
|
options = self.options
|
||||||
|
|
||||||
|
# TODO marshmallow schema?
|
||||||
|
valid_types = ['COUNTER', 'GAUGE', 'TIMER']
|
||||||
|
if metric_type.upper() not in valid_types:
|
||||||
|
raise Exception(
|
||||||
|
"Invalid Metric Type for Atlas: '{metric}' choose from: {options}".format(
|
||||||
|
metric=metric_type, options=','.join(valid_types)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if metric_tags:
|
||||||
|
if not isinstance(metric_tags, dict):
|
||||||
|
raise Exception(
|
||||||
|
"Invalid Metric Tags for Atlas: Tags must be in dict format"
|
||||||
|
)
|
||||||
|
|
||||||
|
if metric_value == "NaN" or isinstance(metric_value, int) or isinstance(metric_value, float):
|
||||||
|
self.metric_data['value'] = metric_value
|
||||||
|
else:
|
||||||
|
raise Exception(
|
||||||
|
"Invalid Metric Value for Atlas: Metric must be a number"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.metric_data['type'] = metric_type.upper()
|
||||||
|
self.metric_data['name'] = str(metric_name)
|
||||||
|
self.metric_data['tags'] = metric_tags
|
||||||
|
self.metric_data['timestamp'] = millis_since_epoch()
|
||||||
|
|
||||||
|
self.sidecar_host = self.get_option('sidecar_host', options)
|
||||||
|
self.sidecar_port = self.get_option('sidecar_port', options)
|
||||||
|
|
||||||
|
try:
|
||||||
|
res = requests.post(
|
||||||
|
'http://{host}:{port}/metrics'.format(
|
||||||
|
host=self.sidecar_host,
|
||||||
|
port=self.sidecar_port),
|
||||||
|
data=json.dumps([self.metric_data])
|
||||||
|
)
|
||||||
|
|
||||||
|
if res.status_code != 200:
|
||||||
|
current_app.logger.warning("Failed to publish altas metric. {0}".format(res.content))
|
||||||
|
|
||||||
|
except ConnectionError:
|
||||||
|
current_app.logger.warning(
|
||||||
|
"AtlasMetrics: could not connect to sidecar at {host}:{port}".format(
|
||||||
|
host=self.sidecar_host, port=self.sidecar_port
|
||||||
|
)
|
||||||
|
)
|
3
setup.py
3
setup.py
|
@ -167,7 +167,8 @@ setup(
|
||||||
'email_notification = lemur.plugins.lemur_email.plugin:EmailNotificationPlugin',
|
'email_notification = lemur.plugins.lemur_email.plugin:EmailNotificationPlugin',
|
||||||
'java_truststore_export = lemur.plugins.lemur_java.plugin:JavaTruststoreExportPlugin',
|
'java_truststore_export = lemur.plugins.lemur_java.plugin:JavaTruststoreExportPlugin',
|
||||||
'java_keystore_export = lemur.plugins.lemur_java.plugin:JavaKeystoreExportPlugin',
|
'java_keystore_export = lemur.plugins.lemur_java.plugin:JavaKeystoreExportPlugin',
|
||||||
'openssl_export = lemur.plugins.lemur_openssl.plugin:OpenSSLExportPlugin'
|
'openssl_export = lemur.plugins.lemur_openssl.plugin:OpenSSLExportPlugin',
|
||||||
|
'atlas_metric = lemur.plugins.lemur_atlas.plugin:AtlasMetricPlugin'
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
classifiers=[
|
classifiers=[
|
||||||
|
|
Loading…
Reference in New Issue