blob: f68f4b22a6816ac823a06704e50ec1c1f1994663 [file] [log] [blame]
Basil Eljuse4b14afb2020-09-30 13:07:23 +01001#!/usr/bin/env python3
2
3from __future__ import print_function
4from data_validator import DataValidator
5import credentials
6
7__copyright__ = """
8/*
9 * Copyright (c) 2020, Arm Limited. All rights reserved.
10 *
11 * SPDX-License-Identifier: BSD-3-Clause
12 *
13 */
14 """
15
16""" quality_metrics_server.py:
17
18 This is the broker component which accepts the data from data
19 generator scripts, and performs basic sanity check and pushes
20 the data to Influx-DB for visualisation with Grafana component.
21 It is not mandatory to push data via data generator scripts.
22 The request to push data to database, in this case - InfluxDB,
23 is expected to be be a POST request with right credentials and
24 should be in agreed upon format.
25
26"""
27
28from pprint import pprint
29from pprint import pformat
30from db_manager import dbManager
31from flask_jwt import JWT, jwt_required
32from flask import Flask, jsonify, request
33from werkzeug.security import safe_str_cmp
34from logging.handlers import RotatingFileHandler
35
36import sys
37import json
38import random
39import logging
40import argparse
41import datetime
42
43import constants
44""" It is suggested to keep credentials.py is kept locally in the
45 system where server is running. This file has been provided
46 for reference.
47"""
48
49username_table = {u.username: u for u in credentials.users}
50userid_table = {u.id: u for u in credentials.users}
51
52
53def authenticate(username, password):
54 user = username_table.get(username, None)
55 if user and safe_str_cmp(
56 user.password.encode('utf-8'),
57 password.encode('utf-8')):
58 return user
59
60
61def identity(payload):
62 user_id = payload['identity']
63 return userid_table.get(user_id, None)
64
65
66def setup_logging(app):
67 # maxBytes and backupCount values to allow the file to rollover at a predetermined size.
68 # When the size is about to be exceeded, the file is closed and a new file is silently
69 # opened for output. Rollover occurs whenever the current log file is nearly maxBytes in length.
70 # When backupCount is non-zero, the system will save old log files by appending the extensions
71 # ‘.1’, ‘.2’ etc., to the filename.
72 file_handler = RotatingFileHandler(
73 "./flask.log",
74 maxBytes=1024 * 1024 * 1024 * 5,
75 backupCount=5)
76 file_handler.setFormatter(
77 logging.Formatter(
78 '[%(asctime)s][PID:%(process)d][%(levelname)s]'
79 '[%(lineno)s][%(name)s.%(funcName)s()] %(message)s'))
80 file_handler.setLevel(logging.INFO)
81 loggers = [app.logger]
82 for logger in loggers:
83 logger.addHandler(file_handler)
84 app.logger.setLevel(logging.INFO)
85
86
87app = Flask(__name__)
88
89setup_logging(app)
90
91logger = logging.getLogger(__name__)
92
93app.debug = True
94app.config['SECRET_KEY'] = credentials.SECRET_KEY
95app.config['JWT_EXPIRATION_DELTA'] = datetime.timedelta(
96 days=constants.JWT_EXPIRATION_DAYS)
97
98dbm = dbManager(app=app).start_daemon()
99
100jwt = JWT(app, authenticate, identity)
101
102# ----------------------- Database Methods ----------------------------------#
103
104
105def store_to_db(data_dict):
106 """
107 Use the database manager to asynchronously update the database
108
109 :param: data_dict: Dictionary containing data to be stored
110 """
111 validation, err_code = dbm.store(data_dict)
112 return validation, err_code
113
114# ----------------------- FLASK API Methods ---------------------------------- #
115
116
117@app.route('/', methods=['POST'])
118@jwt_required()
119def add_db_entry():
120 """
121 Store received data to database if validation is okay
122
123 :return: validation information and error code
124 """
125
126 data = request.get_json()
127 app.logger.debug("Received Data (POST)")
128 app.logger.debug(pformat(data))
129 # Make sure the data is valid
130 validation, err_code = DataValidator.validate_request_sanity(data)
131 if validation == "OK":
132 app.logger.info("<<<<VALIDATION OK>>>>")
133 validation, err_code = store_to_db(data)
134 else:
135 app.logger.error("<<<<VALIDATION NOT OK>>>>")
136 app.logger.error(pformat({"data": validation, "error_code": err_code}))
137 info_json = jsonify({"data": validation, "error_code": err_code})
138 return info_json, err_code
139
140
141@app.route("/")
142def home():
143 info_json = jsonify({"type": "INFO", "data": "Quality Metrics"})
144 return info_json, 200
145
146
147if __name__ == '__main__':
148 try:
149 app.run(host=constants.LISTEN_ALL_IPS, port=5000)
150 except Exception as ex:
151 template = "An exception of type {0} occurred. Arguments:\n{1!r}"
152 message = template.format(type(ex).__name__, ex.args)
153 app.logger.error("message")
154 dbm.stop()