def add_fake_data(): """ Adds data to the database for testing purposes. Module dashboard must be imported locally. """ from flask_monitoringdashboard.database import session_scope, FunctionCall, MonitorRule, Tests, TestsGrouped from flask_monitoringdashboard import config # Add functionCalls with session_scope() as db_session: for i in range(len(EXECUTION_TIMES)): call = FunctionCall(endpoint=NAME, execution_time=EXECUTION_TIMES[i], version=config.version, time=TIMES[i], group_by=GROUP_BY, ip=IP) db_session.add(call) # Add MonitorRule with session_scope() as db_session: db_session.add(MonitorRule(endpoint=NAME, monitor=True, time_added=datetime.datetime.utcnow(), version_added=config.version, last_accessed=TIMES[0])) # Add Tests with session_scope() as db_session: db_session.add(Tests(name=NAME, succeeded=True)) # Add TestsGrouped with session_scope() as db_session: for test_name in TEST_NAMES: db_session.add(TestsGrouped(endpoint=NAME, test_name=test_name))
def get_all_measurement(endpoint): """Return all entries with measurements from a given endpoint. Used for creating a histogram. """ with session_scope() as db_session: result = db_session.query(FunctionCall).filter( FunctionCall.endpoint == endpoint).all() db_session.expunge_all() return result
def run(self): # sleep for average * ODC ms with session_scope() as db_session: average = get_avg_duration( db_session, self._endpoint.id) * config.outlier_detection_constant time.sleep(average / 1000) if not self._stopped: stack_list = [] try: frame = sys._current_frames()[self._current_thread] except KeyError: log('Can\'t get the stacktrace of the main thread.') return in_endpoint_code = False # filename, line number, function name, source code line for fn, ln, fun, line in traceback.extract_stack(frame): if self._endpoint.name == fun: in_endpoint_code = True if in_endpoint_code: stack_list.append( 'File: "{}", line {}, in "{}": "{}"'.format( fn, ln, fun, line)) # Set the values in the object self._stacktrace = '<br />'.join(stack_list) self._cpu_percent = str( psutil.cpu_percent(interval=None, percpu=True)) self._memory = str(psutil.virtual_memory())
def run(self): with session_scope() as db_session: update_last_accessed(db_session, endpoint_name=self._endpoint.name) add_request(db_session, duration=self._duration, endpoint_id=self._endpoint.id, ip=self._ip)
def get_outliers_sorted(endpoint, sort_column): """ Returns a list of all outliers of a specific endpoint. The list is sorted based on the column that is given.""" with session_scope() as db_session: result = db_session.query(Outlier).filter( Outlier.endpoint == endpoint).order_by(desc(sort_column)).all() db_session.expunge_all() return result
def get_data_per_version(version): """ Returns all data in the FunctionCall table, grouped by their version. """ with session_scope() as db_session: result = db_session.query(FunctionCall.execution_time, FunctionCall.version). \ filter(FunctionCall.version == version).all() db_session.expunge_all() return result
def get_endpoints(): with session_scope() as db_session: result = db_session.query(FunctionCall.endpoint, func.count(FunctionCall.endpoint).label('cnt')). \ group_by(FunctionCall.endpoint).order_by(asc('cnt')).all() db_session.expunge_all() return result
def get_answer(self, endpoint, requests_criterion, baseline_requests_criterion): with session_scope() as session: latencies_sample = get_latencies_sample(session, endpoint.id, requests_criterion) baseline_latencies_sample = get_latencies_sample( session, endpoint.id, baseline_requests_criterion ) if len(latencies_sample) == 0 or len(baseline_latencies_sample) == 0: return MedianLatencyReportAnswer( is_significant=False, latencies_sample=latencies_sample, baseline_latencies_sample=baseline_latencies_sample, ) median = float(np.median(latencies_sample)) baseline_median = float(np.median(baseline_latencies_sample)) percentual_diff = (median - baseline_median) / baseline_median * 100 _, p, _, _ = median_test(latencies_sample, baseline_latencies_sample) is_significant = abs(float(percentual_diff)) > 0 and float(p) < 0.05 return MedianLatencyReportAnswer( is_significant=is_significant, percentual_diff=percentual_diff, # Sample latencies latencies_sample=latencies_sample, baseline_latencies_sample=baseline_latencies_sample, # Latency medians median=median, baseline_median=baseline_median, )
def add_fake_data(): """ Adds data to the database for testing purposes. Module flask_monitoringdashboard must be imported locally. """ from flask_monitoringdashboard.database import session_scope, Request, Endpoint, Outlier from flask_monitoringdashboard import config # Add requests with session_scope() as db_session: for i in range(len(REQUESTS)): call = Request(id=REQUEST_IDS[i], endpoint_id=ENDPOINT_ID, duration=REQUESTS[i], version_requested=config.version, time_requested=TIMES[i], group_by=GROUP_BY, ip=IP) db_session.add(call) # Add endpoint db_session.add( Endpoint(id=ENDPOINT_ID, name=NAME, monitor_level=1, time_added=datetime.datetime.utcnow(), version_added=config.version, last_requested=TIMES[0])) # Add Outliers for i in range(OUTLIER_COUNT): db_session.add( Outlier(request_id=i + 1, cpu_percent='[%d, %d, %d, %d]' % (i, i + 1, i + 2, i + 3)))
def get_boxplot_endpoints(endpoint=None, form=None): """ Generates a box plot visualization for the unit test endpoint hits performance results. :param endpoint: if specified, generate box plot for a specific endpoint, otherwise, generate for all tests :param form: the form that can be used for showing a subset of the data :return: """ trace = [] with session_scope() as db_session: if form: ids = get_travis_builds(db_session, limit=form.get_slider_value()) else: ids = get_travis_builds(db_session) if not ids: return None for id in ids: if endpoint: values = get_endpoint_measurements_job(db_session, name=endpoint, job_id=id) else: values = get_endpoint_measurements(db_session, suite=id) trace.append(boxplot(values=values, label='{} -'.format(id))) layout = get_layout(xaxis={'title': 'Execution time (ms)'}, yaxis={ 'title': 'Travis Build', 'autorange': 'reversed' }) return get_figure(layout=layout, data=trace)
def get_last_accessed_times(): """ Returns a list of all endpoints and their last accessed time. """ with session_scope() as db_session: result = db_session.query(MonitorRule.endpoint, MonitorRule.last_accessed).all() db_session.expunge_all() return result
def user_edit(): """Update the user in the database.""" user_id = int(request.form['user_id']) is_admin = request.form['is_admin'] == 'true' if flask.session.get(config.link + '_user_id') == user_id and not is_admin: return jsonify({ 'message': 'Cannot remove the admin permissions from itself.' }), BAD_REQUEST_STATUS with session_scope() as session: try: user = session.query(User).filter(User.id == user_id).one() user.is_admin = is_admin old_password = request.form.get('old_password') if old_password is not None: if user.check_password(old_password): new_password = request.form['new_password'] new_password2 = request.form['new_password2'] if new_password != new_password2: return jsonify({'message': "Passwords don't match." }), BAD_REQUEST_STATUS user.set_password(new_password) else: return jsonify({'message': "Old password doesn't match." }), BAD_REQUEST_STATUS except NoResultFound: return jsonify({'message': "User ID doesn't exist."}), BAD_REQUEST_STATUS except Exception as e: return jsonify({'message': str(e)}), BAD_REQUEST_STATUS return 'OK'
def make_report(): arguments = request.json try: comparison_interval = DateInterval( datetime.fromtimestamp( int(arguments['comparison_interval']['from'])), datetime.fromtimestamp(int( arguments['comparison_interval']['to']))) compared_to_interval = DateInterval( datetime.fromtimestamp( int(arguments['compared_to_interval']['from'])), datetime.fromtimestamp(int( arguments['compared_to_interval']['to']))) except Exception: return 'Invalid payload', 422 endpoint_summaries = [] with session_scope() as db_session: for endpoint in get_endpoints(db_session): endpoint_summary = make_endpoint_summary(endpoint, comparison_interval, compared_to_interval) endpoint_summaries.append(endpoint_summary) return dict(summaries=endpoint_summaries)
def version_ip(endpoint_id): """ input must be a JSON-object, with a list of versions and IP-addresses, such as: { ip: ['127.0.0.1', '127.0.0.2'], versions: ['0.1', '0.2', '0.3'] } :return: A JSON-list for all IP-addresses, with a JSON-list for every version. output: { data: [ [10, 11, 12], [13, 14, 15] ], versions: [ { date: '...', version: '0.1'}, { date: '...', version: '0.2'}, { date: '...', version: '0.3'} ] } """ data = json.loads(request.data)['data'] versions = data['versions'] ips = data['ip'] with session_scope() as db_session: return jsonify(get_version_ip_data(db_session, endpoint_id, versions, ips))
def testmonitor(): """ Gives an overview of the unit test performance results and the endpoints that they hit. :return: """ from numpy import median with session_scope() as db_session: latest_version = get_latest_test_version(db_session) tests_latest = count_times_tested(db_session, TestEndpoint.app_version == latest_version) tests = count_times_tested(db_session) median_latest = get_test_data_grouped(db_session, median, TestEndpoint.app_version == latest_version) median = get_test_data_grouped(db_session, median) tested_times = get_last_tested_times(db_session) result = [] for endpoint in get_tested_endpoint_names(db_session): result.append({ 'name': endpoint, 'color': get_color(endpoint), 'tests-latest-version': get_value(tests_latest, endpoint), 'tests-overall': get_value(tests, endpoint), 'median-latest-version': get_value(median_latest, endpoint), 'median-overall': get_value(median, endpoint), 'last-tested': get_value(tested_times, endpoint, default=None) }) return render_template('fmd_testmonitor/testmonitor.html', result=result)
def get_overview(): """ Get information per endpoint about the number of hits and median execution time :return: A JSON-list with a JSON-object per endpoint """ with session_scope() as session: return jsonify(get_endpoint_overview(session))
def version_user(endpoint_id): """ input must be a JSON-object, with a list of versions and users, such as: { users: ['user0', user1], versions: ['0.1', '0.2', '0.3'] } :return: A JSON-list for all users, with a JSON-list for every version. output: { data: [ [10, 11, 12], [13, 14, 15] ], versions: [ { date: '...', version: '0.1'}, { date: '...', version: '0.2'}, { date: '...', version: '0.3'} ] } """ data = json.loads(request.data)['data'] versions = data['versions'] users = data['users'] with session_scope() as db_session: return jsonify(get_version_user_data(db_session, endpoint_id, versions, users))
def endpoints(): """ :return: A JSON-list with information about every endpoint (encoded in a JSON-object) For more information per endpoint, see :func: get_overview """ with session_scope() as session: return jsonify([row2dict(row) for row in get_endpoints(session)])
def get_json_data_from(time_from, time_to=None): """ The returned data is the data that is encrypted using a security token. This security token is set in the configuration. :param time_from: (optional) if specified, only the data-values after this date are returned. input must be an timestamp value (utc) (= integer) :param time_to: (optional) if specified, only the data-values before this date are returned. input must be an timestamp value (utc) (= integer) :return: all entries from the database. (endpoint-table) """ data = [] try: with session_scope() as db_session: time1 = datetime.datetime.utcfromtimestamp(int(time_from)) time2 = None if time_to: time2 = datetime.datetime.utcfromtimestamp(int(time_to)) for entry in get_data_between(db_session, time1, time2): # nice conversion to json-object data.append({ 'endpoint_id': entry.endpoint_id, 'duration': entry.duration, 'time_requested': str(entry.time_requested), 'version_requested': entry.version_requested, 'group_by': entry.group_by, 'ip': entry.ip }) return jwt.encode({'data': json.dumps(data)}, config.security_token, algorithm='HS256') except ValueError as e: return 'ValueError: {}'.format(e)
def add_tests_grouped(json): """ Adds endpoint - unit tests combinations to the database. """ with session_scope() as db_session: for combination in json: db_session.add( TestsGrouped(endpoint=combination['endpoint'], test_name=combination['test_name']))
def get_json_details(): """ Some details about the deployment, such as the current version, etc... :return: a json-object with the details. """ with session_scope() as db_session: return jsonify(get_details(db_session))
def grouped_profiler(endpoint_id): with session_scope() as db_session: details = get_endpoint_details(db_session, endpoint_id) requests = get_grouped_profiled_requests(db_session, endpoint_id) db_session.expunge_all() total_duration = sum([r.duration for r in requests]) histogram = defaultdict(list) # path -> [list of values] path_hash = PathHash() for r in requests: for index, stack_line in enumerate(r.stack_lines): key = path_hash.get_stacklines_path(r.stack_lines, index) histogram[key].append(stack_line.duration) table = [] for key, duration_list in sorted(histogram.items(), key=lambda row: row[0]): table.append( GroupedStackLine(indent=path_hash.get_indent(key) - 1, code=path_hash.get_code(key), values=duration_list, total_sum=total_duration, total_hits=len(requests))) for index, item in enumerate(table): table[index].compute_body(index, table) sunburst = json.dumps(table_to_json(table)) return render_template('fmd_dashboard/profiler_grouped.html', details=details, table=table, sunburst=sunburst, title='Grouped Profiler results for {}'.format( details['endpoint']))
def get_boxplot_tests(form=None): """ Generates a box plot visualization for the unit test performance results. :param form: the form that can be used for showing a subset of the data :return: """ trace = [] with session_scope() as db_session: if form: suites = get_test_suites(db_session, limit=form.get_slider_value()) else: suites = get_test_suites(db_session) if not suites: return None for s in suites: values = get_suite_measurements(db_session, suite=s) trace.append(boxplot(values=values, label='{} -'.format(s))) layout = get_layout(xaxis={'title': 'Execution time (ms)'}, yaxis={ 'title': 'Travis Build', 'autorange': 'reversed' }) return get_figure(layout=layout, data=trace)
def submit_test_results(): """ Endpoint for letting Travis submit its unit test performance results to the Dashboard. :return: nothing, 204 (No Content) """ results = request.get_json() travis_job_id = -1 if results['travis_job']: travis_job_id = results['travis_job'] app_version = '-1' if 'app_version' in results: app_version = results['app_version'] test_runs = results['test_runs'] endpoint_hits = results['endpoint_exec_times'] with session_scope() as db_session: for test_run in test_runs: time = datetime.datetime.strptime(test_run['time'], '%Y-%m-%d %H:%M:%S.%f') add_or_update_test(db_session, test_run['name'], test_run['successful'], time, app_version) add_test_result(db_session, test_run['name'], test_run['exec_time'], time, app_version, travis_job_id, test_run['iter']) for endpoint_hit in endpoint_hits: add_endpoint_hit(db_session, endpoint_hit['endpoint'], endpoint_hit['exec_time'], endpoint_hit['test_name'], app_version, travis_job_id) return '', 204
def grouped_profiler(endpoint_id): with session_scope() as session: changed_functions = get_lines_changed_since_version('9b0948', '9c72e0') previous_version = get_grouped_profiler(session, endpoint_id, '9b0948') new_version = jsonify( get_grouped_profiler(session, endpoint_id, '9c72e0', previous_version, changed_functions)) return new_version
def test_get_date_of_first_request(self): """ Test whether the function returns the right values. """ from flask_monitoringdashboard.database.request import get_date_of_first_request with session_scope() as db_session: self.assertEqual(get_date_of_first_request(db_session), int(time.mktime(TIMES[0].timetuple())))
def endpoint(): # if session_scope is imported at the top of the file, the database config won't take effect from flask_monitoringdashboard.database import session_scope with session_scope() as db_session: print(db_session.bind.dialect.name) print("Hello, world") return 'Ok'
def test_get_endpoint_measurements(self): with session_scope() as db_session: self.assertEqual(get_endpoint_measurements(db_session, "1"), [0]) db_session.add(TestEndpoint(endpoint_id=ENDPOINT_ID, test_id=1, duration=1234, app_version="1.0", travis_job_id="1", time_added=datetime.datetime.utcnow())) db_session.add(TestEndpoint(endpoint_id=ENDPOINT_ID, test_id=1, duration=2345, app_version="1.0", travis_job_id="1", time_added=datetime.datetime.utcnow())) self.assertEqual(get_endpoint_measurements(db_session, "1"), [1234, 2345])
def view_csv(): csv = [','.join(CSV_COLUMNS)] with session_scope() as db_session: for entry in get_data(db_session): csv.append( ','.join([str(entry.__getattribute__(c)) for c in CSV_COLUMNS]) + '\n') return render_template('fmd_export-data.html', data=csv)
def users_list(): """ :return: A JSON-object with configuration details """ if not is_admin(): return jsonify([]) with session_scope() as session: return jsonify(get_all_users(session))