blob: 2096ff6df1ed047715049fcba296357911d40ee8 [file] [log] [blame]
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -07001#!/usr/bin/env python
2
3# Copyright 2020-present Open Networking Foundation
4#
Hyunsun Moon200eba52021-04-05 21:31:54 -07005# SPDX-License-Identifier: LicenseRef-ONF-Member-Only-1.0
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -07006
Andy Bavier614af142020-08-07 14:49:56 -07007import os
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -07008import time
Andy Bavier614af142020-08-07 14:49:56 -07009import datetime
10import pytz
11import threading
12from icalevents.icalevents import events
Andy Bavier4021a2f2020-07-29 12:39:47 -070013from flask import Flask, jsonify, abort, request, Response
14import prometheus_client as prom
Andy Bavier2d60fc52021-05-04 16:13:39 -070015import jsonschema
Andy Bavier55dc5872021-05-05 11:31:42 -070016from logging.config import dictConfig
Andy Bavier4021a2f2020-07-29 12:39:47 -070017
Andy Bavier8a5c9872020-10-21 13:17:53 -070018# URL of maintenance calendar
Andy Bavier614af142020-08-07 14:49:56 -070019SECRET_ICAL_URL = os.environ.get("SECRET_ICAL_URL")
Andy Bavier8a5c9872020-10-21 13:17:53 -070020
21# Aether environment that the server is monitoring (e.g., "production")
22# To schedule downtime, postfix the cluster name with the env: "ace-tucson-production"
23AETHER_ENV = os.environ.get("AETHER_ENV", "production")
24
25# Move to "no result" status if we don't hear from agent for this many seconds
Andy Bavier4021a2f2020-07-29 12:39:47 -070026NO_RESULT_THRESHOLD = 720
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -070027
Andy Bavier55dc5872021-05-05 11:31:42 -070028dictConfig({
29 'version': 1,
30 'formatters': {'default': {
Andy Bavier41fef772021-05-12 13:48:42 -070031 'format': '%(levelname)s %(message)s',
Andy Bavier55dc5872021-05-05 11:31:42 -070032 }},
33 'handlers': {'wsgi': {
34 'class': 'logging.StreamHandler',
35 'stream': 'ext://flask.logging.wsgi_errors_stream',
36 'formatter': 'default'
37 }},
38 'root': {
39 'level': 'INFO',
40 'handlers': ['wsgi']
41 }
42})
43
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -070044app = Flask(__name__)
Andy Bavier2d60fc52021-05-04 16:13:39 -070045
46edgeSchema = {
47 "type": "object",
48 "properties": {
49 "name": {"type": "string"},
50 "status": {
51 "type": "object",
52 "properties": {
53 "control_plane": {"type": "string"},
54 "user_plane": {"type": "string"}
55 },
56 "required": ["control_plane", "user_plane"]
57 },
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -080058 "dongle_stats": {
59 "type": "object",
60 "properties": {
61 "SuccessfulFetch": {"type": "boolean"},
62 "MAC": {"type": "string"},
63 "PLMNStatus": {"type": "string"},
64 "UICCStatus": {"type": "string"},
65 "IMEI": {"type": "string"},
66 "IMSI": {"type": "string"},
67 "PLMNSelected": {"type": "string"},
68 "MCC": {"type": "string"},
69 "MNC": {"type": "string"},
70 "PhyCellID": {"type": "string"},
71 "CellGlobalID": {"type": "string"},
72 "Band": {"type": "string"},
73 "EARFCN": {"type": "string"},
74 "BandWidth": {"type": "string"},
75 "ServCellState": {"type": "string"},
76 "Connection": {"type": "string"},
77 "IPv4Addr": {"type": "string"}
78 }
79 },
Andy Bavier2d60fc52021-05-04 16:13:39 -070080 "speedtest": {
81 "type": "object",
82 "properties": {
83 "ping": {
84 "type": "object",
85 "properties": {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -070086 "dry_run": {
87 "type": "object",
88 "properties": {
89 "transmitted": {"type": "number"},
90 "received": {"type": "number"},
91 "median": {"type": "number"},
92 "min": {"type": "number"},
93 "avg": {"type": "number"},
94 "max": {"type": "number"},
95 "stddev": {"type": "number"}
96 },
97 "required": ["min", "avg", "max", "stddev"]
98 },
Andy Bavier2d60fc52021-05-04 16:13:39 -070099 "dns": {
100 "type": "object",
101 "properties": {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700102 "transmitted": {"type": "number"},
103 "received": {"type": "number"},
104 "median": {"type": "number"},
Andy Bavier2d60fc52021-05-04 16:13:39 -0700105 "min": {"type": "number"},
106 "avg": {"type": "number"},
107 "max": {"type": "number"},
108 "stddev": {"type": "number"}
109 },
110 "required": ["min", "avg", "max", "stddev"]
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700111 },
112 "iperf_server": {
113 "type": "object",
114 "properties": {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700115 "transmitted": {"type": "number"},
116 "received": {"type": "number"},
117 "median": {"type": "number"},
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700118 "min": {"type": "number"},
119 "avg": {"type": "number"},
120 "max": {"type": "number"},
121 "stddev": {"type": "number"}
122 },
123 "required": ["min", "avg", "max", "stddev"]
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700124 },
125 "management_server": {
126 "type": "object",
127 "properties": {
128 "transmitted": {"type": "number"},
129 "received": {"type": "number"},
130 "median": {"type": "number"},
131 "min": {"type": "number"},
132 "avg": {"type": "number"},
133 "max": {"type": "number"},
134 "stddev": {"type": "number"}
135 },
136 "required": ["min", "avg", "max", "stddev"]
137 },
Andy Bavier2d60fc52021-05-04 16:13:39 -0700138 }
139 },
140 "iperf": {
141 "type": "object",
142 "properties": {
143 "cluster": {
144 "type": "object",
145 "properties": {
146 "downlink": {"type": "number"},
147 "uplink": {"type": "number"}
148 },
149 "required": ["downlink", "uplink"]
150 }
151 }
152 }
153 }
154 },
155 "signal_quality": {
156 "type": "object",
157 "properties": {
158 "rsrq": {"type": "number"},
159 "rsrp": {"type": "number"}
160 },
161 "required": ["rsrq", "rsrp"]
162 }
163 },
164 "required": ["name", "status"]
165}
166
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700167edges = [
168 {
Andy Bavier8a5c9872020-10-21 13:17:53 -0700169 'name': 'ace-example',
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700170 'status': {
171 'control_plane': 'connected',
172 'user_plane': 'connected'
173 },
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800174 'dongle_stats': {
175 'SuccessfulFetch' : False
176 },
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800177 'speedtest': {
178 'ping': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700179 'dry_run': {
180 'transmitted': 0,
181 'received': 0,
182 'median': 0.0,
183 'min': 0.0,
184 'avg': 0.0,
185 'max': 0.0,
186 'stddev': 0.0
187 },
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800188 'dns': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700189 'transmitted': 0,
190 'received': 0,
191 'median': 0.0,
Jeremy Ronquilloa944fbc2021-03-30 10:57:45 -0700192 'min': 0.0,
193 'avg': 0.0,
194 'max': 0.0,
195 'stddev': 0.0
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700196 },
197 'iperf_server': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700198 'transmitted': 0,
199 'received': 0,
200 'median': 0.0,
201 'min': 0.0,
202 'avg': 0.0,
203 'max': 0.0,
204 'stddev': 0.0
205 },
206 'management_server': {
207 'transmitted': 0,
208 'received': 0,
209 'median': 0.0,
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700210 'min': 0.0,
211 'avg': 0.0,
212 'max': 0.0,
213 'stddev': 0.0
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800214 }
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700215 },
216 'iperf': {
217 'cluster': {
218 'downlink': 0.0,
219 'uplink': 0.0
220 }
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800221 }
222 },
Hyunsun Moon200eba52021-04-05 21:31:54 -0700223 'signal_quality': {
224 'rsrq': 0,
225 'rsrp': 0
226 },
227 'last_update': time.time()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700228 }
229]
230
Andy Bavier4021a2f2020-07-29 12:39:47 -0700231status_codes = {
232 "no result": -2,
233 "error": -1,
234 "disconnected": 0,
235 "connecting": 1,
236 "connected": 2
237}
238
Andy Bavier614af142020-08-07 14:49:56 -0700239room_mapping = {
Andy Baviere4591222021-07-07 12:44:19 -0700240 "ace-menlo-rasp-pi-production": "(Compute)-MP-1-Aether Production",
Andy Bavier0423cbd2020-10-23 10:50:29 -0700241 "ace-menlo-staging": "(Compute)-MP-1-Aether Staging"
Andy Bavier614af142020-08-07 14:49:56 -0700242}
243
Jeremy Ronquillo1eaff6a2021-11-16 13:46:46 -0800244plmnstatus_mapping = {
245 "Not Searching": 1,
246 "Searching": 2,
247 "Success": 3
248}
249
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700250# Legacy test status metrics, reporting a status code between -2 and 2
Andy Bavier4021a2f2020-07-29 12:39:47 -0700251cp_status = prom.Gauge("aetheredge_status_control_plane", "Control plane status code", ["name"])
252up_status = prom.Gauge("aetheredge_status_user_plane", "User plane status code", ["name"])
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700253
254# Simplified binary test result metrics
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700255e2e_tests_ok = prom.Gauge("aetheredge_e2e_tests_ok", "Last connect and ping test both passed", ["name"])
Andy Baviera0c40aa2021-03-10 12:09:12 -0700256connect_test_ok = prom.Gauge("aetheredge_connect_test_ok", "Last connect test passed", ["name"])
Andy Baviera0c40aa2021-03-10 12:09:12 -0700257ping_test_ok = prom.Gauge("aetheredge_ping_test_ok", "Last ping test passed", ["name"])
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700258e2e_tests_down = prom.Gauge("aetheredge_e2e_tests_down", "E2E tests not reporting", ["name"])
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700259
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800260# Dongle Stats (strings can't be stored in Prometheus)
261dongle_stats_imsi = prom.Gauge("aetheredge_dongle_stats_imsi", "IMSI of the UE", ["name"])
Jeremy Ronquillo1eaff6a2021-11-16 13:46:46 -0800262dongle_stats_cellglobalid = prom.Gauge("aetheredge_dongle_stats_cellglobalid", "CellGlobalID of the UE", ["name"])
263dongle_stats_plmnstatus = prom.Gauge("aetheredge_dongle_stats_plmnstatus", "PLMNStatus of the UE", ["name"])
264dongle_stats_phycellid = prom.Gauge("aetheredge_dongle_stats_phycellid", "PhyCellID of the UE", ["name"])
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800265
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700266# Ping dry_run metrics
267ping_dry_run_transmitted = prom.Gauge("aetheredge_ping_dry_run_test_transmitted","Last ping test to dry_run number of transmitted packets",["name"])
268ping_dry_run_received = prom.Gauge("aetheredge_ping_dry_run_test_received","Last ping test to dry_run number of received packets",["name"])
269ping_dry_run_median = prom.Gauge("aetheredge_ping_dry_run_test_median","Last ping test to dry_run median value",["name"])
270ping_dry_run_min = prom.Gauge("aetheredge_ping_dry_run_test_min","Last ping test to dry_run minimum value",["name"])
271ping_dry_run_avg = prom.Gauge("aetheredge_ping_dry_run_test_avg","Last ping test to dry_run average",["name"])
272ping_dry_run_max = prom.Gauge("aetheredge_ping_dry_run_test_max","Last ping test to dry_run maximum value",["name"])
273ping_dry_run_stddev = prom.Gauge("aetheredge_ping_dry_run_test_stddev","Last ping test to dry_run standard deviation",["name"])
274
275# Ping dns metrics
276ping_dns_transmitted = prom.Gauge("aetheredge_ping_dns_test_transmitted","Last ping test to dns number of transmitted packets",["name"])
277ping_dns_received = prom.Gauge("aetheredge_ping_dns_test_received","Last ping test to dns number of received packets",["name"])
278ping_dns_median = prom.Gauge("aetheredge_ping_dns_test_median","Last ping test to dns median value",["name"])
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700279ping_dns_min = prom.Gauge("aetheredge_ping_dns_test_min","Last ping test to dns minimum value",["name"])
280ping_dns_avg = prom.Gauge("aetheredge_ping_dns_test_avg","Last ping test to dns average",["name"])
281ping_dns_max = prom.Gauge("aetheredge_ping_dns_test_max","Last ping test to dns maximum value",["name"])
282ping_dns_stddev = prom.Gauge("aetheredge_ping_dns_test_stddev","Last ping test to dns standard deviation",["name"])
283
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700284# Ping iperf server metrics
285ping_iperf_server_transmitted = prom.Gauge("aetheredge_ping_iperf_server_test_transmitted","Last ping test to iperf_server number of transmitted packets",["name"])
286ping_iperf_server_received = prom.Gauge("aetheredge_ping_iperf_server_test_received","Last ping test to iperf_server number of received packets",["name"])
287ping_iperf_server_median = prom.Gauge("aetheredge_ping_iperf_server_test_median","Last ping test to iperf_server median value",["name"])
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700288ping_iperf_server_min = prom.Gauge("aetheredge_ping_iperf_server_test_min","Last ping test to iperf_server minimum value",["name"])
289ping_iperf_server_avg = prom.Gauge("aetheredge_ping_iperf_server_test_avg","Last ping test to iperf_server average",["name"])
290ping_iperf_server_max = prom.Gauge("aetheredge_ping_iperf_server_test_max","Last ping test to iperf_server maximum value",["name"])
291ping_iperf_server_stddev = prom.Gauge("aetheredge_ping_iperf_server_test_stddev","Last ping test to iperf_server standard deviation",["name"])
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800292
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700293# Ping management_server metrics
294ping_management_server_transmitted = prom.Gauge("aetheredge_ping_management_server_test_transmitted","Last ping test to management_server number of transmitted packets",["name"])
295ping_management_server_received = prom.Gauge("aetheredge_ping_management_server_test_received","Last ping test to management_server number of received packets",["name"])
296ping_management_server_median = prom.Gauge("aetheredge_ping_management_server_test_median","Last ping test to management_server median value",["name"])
297ping_management_server_min = prom.Gauge("aetheredge_ping_management_server_test_min","Last ping test to management_server minimum value",["name"])
298ping_management_server_avg = prom.Gauge("aetheredge_ping_management_server_test_avg","Last ping test to management_server average",["name"])
299ping_management_server_max = prom.Gauge("aetheredge_ping_management_server_test_max","Last ping test to management_server maximum value",["name"])
300ping_management_server_stddev = prom.Gauge("aetheredge_ping_management_server_test_stddev","Last ping test to management_server standard deviation",["name"])
301
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700302# Speedtest iperf metrics
303iperf_cluster_downlink = prom.Gauge("aetheredge_iperf_cluster_downlink_test","Last iperf test downlink result",["name"])
304iperf_cluster_uplink = prom.Gauge("aetheredge_iperf_cluster_uplink_test","Last iperf test downlink result",["name"])
305
Hyunsun Moon200eba52021-04-05 21:31:54 -0700306# Signal quality metrics in CESQ format not dB
307# RSRQ: >=53 excellent, 43 ~ 53 good, 33 ~ 43 mid, <=33 bad, 0 no signal
308# RSRP: >=20 excellent, 10 ~ 20 good, 0 ~ 10 mid, 0 no signal
309signal_quality_rsrq = prom.Gauge("aetheredge_signal_quality_rsrq", "Quality of the received signal", ["name"])
310signal_quality_rsrp = prom.Gauge("aetheredge_signal_quality_rsrp", "Power of the received signal", ["name"])
311
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700312# Other metrics
Andy Bavier4021a2f2020-07-29 12:39:47 -0700313last_update = prom.Gauge("aetheredge_last_update", "Last reported test result", ["name"])
Andy Bavier614af142020-08-07 14:49:56 -0700314maint_window = prom.Gauge("aetheredge_in_maintenance_window", "Currently in a maintenance window", ["name"])
315
316def is_my_event(event, name):
317 for field in ["summary", "location", "description"]:
Andy Bavier8a5c9872020-10-21 13:17:53 -0700318 fullname = name
319 if name.startswith("ace-"):
320 fullname = "%s-%s" % (name, AETHER_ENV)
321 if fullname in getattr(event, field, ""):
Andy Bavier614af142020-08-07 14:49:56 -0700322 return True
Andy Bavier0423cbd2020-10-23 10:50:29 -0700323 if fullname in room_mapping and room_mapping[fullname] in getattr(event, field, ""):
324 return True
Andy Bavier614af142020-08-07 14:49:56 -0700325 return False
326
Andy Bavierc41cf0c2020-09-02 14:49:21 -0700327def is_naive_datetime(d):
328 return d.tzinfo is None or d.tzinfo.utcoffset(d) is None
329
330def process_all_day_events(es):
331 for event in es:
332 if event.all_day:
333 # All day events have naive datetimes, which breaks comparisons
334 pacific = pytz.timezone('US/Pacific')
335 if is_naive_datetime(event.start):
336 event.start = pacific.localize(event.start)
337 if is_naive_datetime(event.end):
338 event.end = pacific.localize(event.end)
339
Andy Bavier614af142020-08-07 14:49:56 -0700340def in_maintenance_window(events, name, now):
341 for event in events:
342 if event.start < now and event.end > now:
343 if is_my_event(event, name):
344 return True
Andy Bavier614af142020-08-07 14:49:56 -0700345 return False
346
347def pull_maintenance_events():
348 while(True):
349 now = datetime.datetime.now(pytz.utc)
350 try:
351 es = events(SECRET_ICAL_URL, start = now)
Andy Bavierc41cf0c2020-09-02 14:49:21 -0700352 process_all_day_events(es)
Andy Bavier614af142020-08-07 14:49:56 -0700353 except Exception as e:
Andy Bavier55dc5872021-05-05 11:31:42 -0700354 app.logger.error(e)
Andy Bavier614af142020-08-07 14:49:56 -0700355 else:
356 for edge in edges:
357 if 'maintenance' not in edge:
358 edge['maintenance'] = {}
359 edge['maintenance']['in_window'] = in_maintenance_window(es, edge['name'], now)
360 edge['maintenance']['last_update'] = time.time()
361 time.sleep(60)
Andy Bavier4021a2f2020-07-29 12:39:47 -0700362
363def time_out_stale_results():
364 for edge in edges:
365 time_elapsed = time.time() - edge["last_update"]
366 if time_elapsed > NO_RESULT_THRESHOLD:
367 edge['status']['control_plane'] = "no result"
368 edge['status']['user_plane'] = "no result"
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800369 edge['dongle_stats'] = {'SuccessfulFetch' : False}
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700370 edge['speedtest']['ping']['dry_run'] = {'transmitted': 0,
371 'received': 0,
372 'median': 0.0,
373 'min': 0.0,
374 'avg': 0.0,
375 'max': 0.0,
376 'stddev': 0.0}
377 edge['speedtest']['ping']['dns'] = {'transmitted': 0,
378 'received': 0,
379 'median': 0.0,
380 'min': 0.0,
Jeremy Ronquilloa944fbc2021-03-30 10:57:45 -0700381 'avg': 0.0,
382 'max': 0.0,
383 'stddev': 0.0}
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700384 edge['speedtest']['ping']['iperf_server'] = {'transmitted': 0,
385 'received': 0,
386 'median': 0.0,
387 'min': 0.0,
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700388 'avg': 0.0,
389 'max': 0.0,
390 'stddev': 0.0}
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700391 edge['speedtest']['ping']['management_server'] = {'transmitted': 0,
392 'received': 0,
393 'median': 0.0,
394 'min': 0.0,
395 'avg': 0.0,
396 'max': 0.0,
397 'stddev': 0.0}
Jeremy Ronquillo56b0a1e2021-04-09 00:26:18 -0700398 edge['speedtest']['iperf'] = {'cluster': {
399 'downlink': 0.0,
400 'uplink': 0.0
401 }
402 }
Hyunsun Moon200eba52021-04-05 21:31:54 -0700403 edge.pop('signal_quality', None)
Andy Bavier4021a2f2020-07-29 12:39:47 -0700404
Andy Baviere47157d2020-12-11 14:13:12 -0700405def remove_edge_from_metrics(name):
406 try:
407 cp_status.remove(name)
408 up_status.remove(name)
409 last_update.remove(name)
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700410 e2e_tests_ok.remove(name)
Andy Baviera0c40aa2021-03-10 12:09:12 -0700411 connect_test_ok.remove(name)
Andy Baviera0c40aa2021-03-10 12:09:12 -0700412 ping_test_ok.remove(name)
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700413 e2e_tests_down.remove(name)
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700414 except:
415 pass
416
417 try:
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800418 dongle_stats_imsi.remove(name)
Jeremy Ronquillo1eaff6a2021-11-16 13:46:46 -0800419 dongle_stats_cellglobalid.remove(name)
420 dongle_stats_plmnstatus.remove(name)
421 dongle_stats_phycellid.remove(name)
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800422 except:
423 pass
424
425 try:
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700426 ping_dry_run_min.remove(name)
427 ping_dry_run_avg.remove(name)
428 ping_dry_run_max.remove(name)
429 ping_dry_run_stddev.remove(name)
430 ping_dry_run_transmitted.remove(name)
431 ping_dry_run_received.remove(name)
432 ping_dry_run_median.remove(name)
433 except:
434 pass
435
436 try:
Hyunsun Moon200eba52021-04-05 21:31:54 -0700437 ping_dns_min.remove(name)
438 ping_dns_avg.remove(name)
439 ping_dns_max.remove(name)
440 ping_dns_stddev.remove(name)
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700441 ping_dns_transmitted.remove(name)
442 ping_dns_received.remove(name)
443 ping_dns_median.remove(name)
Hyunsun Moon200eba52021-04-05 21:31:54 -0700444 except:
445 pass
446
447 try:
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700448 ping_iperf_server_min.remove(name)
449 ping_iperf_server_avg.remove(name)
450 ping_iperf_server_max.remove(name)
451 ping_iperf_server_stddev.remove(name)
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700452 ping_iperf_server_transmitted.remove(name)
453 ping_iperf_server_received.remove(name)
454 ping_iperf_server_median.remove(name)
455 except:
456 pass
457
458 try:
459 ping_management_server_min.remove(name)
460 ping_management_server_avg.remove(name)
461 ping_management_server_max.remove(name)
462 ping_management_server_stddev.remove(name)
463 ping_management_server_transmitted.remove(name)
464 ping_management_server_received.remove(name)
465 ping_management_server_median.remove(name)
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700466 except:
467 pass
468
469 try:
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700470 iperf_cluster_downlink.remove(name)
471 iperf_cluster_uplink.remove(name)
472 except:
473 pass
474
475 try:
Hyunsun Moon200eba52021-04-05 21:31:54 -0700476 signal_quality_rsrq.remove(name)
477 signal_quality_rsrp.remove(name)
478 except:
479 pass
480
481 try:
Andy Baviere47157d2020-12-11 14:13:12 -0700482 maint_window.remove(name)
483 except:
484 pass
Andy Bavier4021a2f2020-07-29 12:39:47 -0700485
486@app.route('/edges/metrics', methods=['GET'])
487def get_prometheus_metrics():
488 res = []
489 time_out_stale_results()
490 for edge in edges:
Andy Bavier8a5c9872020-10-21 13:17:53 -0700491 if edge['name'] == "ace-example":
Andy Bavier4021a2f2020-07-29 12:39:47 -0700492 continue
493
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700494 connect_status = edge['status']['control_plane']
495 ping_status = edge['status']['user_plane']
496
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800497 try:
498 dongle_stats_imsi.labels(edge['name']).set(float(edge['dongle_stats']['IMSI']))
Jeremy Ronquillo1eaff6a2021-11-16 13:46:46 -0800499 dongle_stats_cellglobalid.labels(edge['name']).set(int(edge['dongle_stats']['CellGlobalID'], 16))
500 dongle_stats_phycellid.labels(edge['name']).set(edge['dongle_stats']['PhyCellID'])
501 if edge['dongle_stats']['PLMNStatus'] in plmnstatus_mapping:
502 dongle_stats_plmnstatus.labels(edge['name']).set(plmnstatus_mapping[edge['dongle_stats']['PLMNStatus']])
503 else:
504 dongle_stats_plmnstatus.labels(edge['name']).set(0)
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800505 except KeyError:
506 pass
507
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700508 # Add ping dry_run latency results if available
509 try:
510 if edge['speedtest']['ping']['dry_run']['avg']:
511 ping_dry_run_min.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['min'])
512 ping_dry_run_avg.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['avg'])
513 ping_dry_run_max.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['max'])
514 ping_dry_run_stddev.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['stddev'])
515 ping_dry_run_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['transmitted'])
516 ping_dry_run_received.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['received'])
517 ping_dry_run_median.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['median'])
518 except KeyError:
519 pass
520
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700521 # Add ping dns latency results if available
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700522 try:
523 if edge['speedtest']['ping']['dns']['avg']:
524 ping_dns_min.labels(edge['name']).set(edge['speedtest']['ping']['dns']['min'])
525 ping_dns_avg.labels(edge['name']).set(edge['speedtest']['ping']['dns']['avg'])
526 ping_dns_max.labels(edge['name']).set(edge['speedtest']['ping']['dns']['max'])
527 ping_dns_stddev.labels(edge['name']).set(edge['speedtest']['ping']['dns']['stddev'])
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700528 ping_dns_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['dns']['transmitted'])
529 ping_dns_received.labels(edge['name']).set(edge['speedtest']['ping']['dns']['received'])
530 ping_dns_median.labels(edge['name']).set(edge['speedtest']['ping']['dns']['median'])
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700531 except KeyError:
532 pass
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700533
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700534 # Add ping iperf_server latency results if available
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700535 try:
536 if edge['speedtest']['ping']['iperf_server']['avg']:
537 ping_iperf_server_min.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['min'])
538 ping_iperf_server_avg.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['avg'])
539 ping_iperf_server_max.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['max'])
540 ping_iperf_server_stddev.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['stddev'])
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700541 ping_iperf_server_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['transmitted'])
542 ping_iperf_server_received.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['received'])
543 ping_iperf_server_median.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['median'])
544 except KeyError:
545 pass
546
547 # Add ping management_server latency results if available
548 try:
549 if edge['speedtest']['ping']['management_server']['avg']:
550 ping_management_server_min.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['min'])
551 ping_management_server_avg.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['avg'])
552 ping_management_server_max.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['max'])
553 ping_management_server_stddev.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['stddev'])
554 ping_management_server_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['transmitted'])
555 ping_management_server_received.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['received'])
556 ping_management_server_median.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['median'])
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700557 except KeyError:
558 pass
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700559
560 # Add iperf bandwidth results if available
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700561 try:
562 if edge['speedtest']['iperf']['cluster']['downlink']:
563 iperf_cluster_downlink.labels(edge['name']).set(edge['speedtest']['iperf']['cluster']['downlink'])
564 iperf_cluster_uplink.labels(edge['name']).set(edge['speedtest']['iperf']['cluster']['uplink'])
565 except KeyError:
566 pass
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800567
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700568 cp_status.labels(edge['name']).set(status_codes[connect_status])
569 up_status.labels(edge['name']).set(status_codes[ping_status])
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800570
Andy Bavier4021a2f2020-07-29 12:39:47 -0700571 last_update.labels(edge['name']).set(edge['last_update'])
Andy Bavier614af142020-08-07 14:49:56 -0700572 if 'maintenance' in edge:
573 maint_window.labels(edge['name']).set(int(edge['maintenance']['in_window']))
Andy Bavier4021a2f2020-07-29 12:39:47 -0700574
Andy Baviera0c40aa2021-03-10 12:09:12 -0700575 connect_test_ok.labels(edge['name']).set(0)
Andy Baviera0c40aa2021-03-10 12:09:12 -0700576 ping_test_ok.labels(edge['name']).set(0)
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700577 e2e_tests_ok.labels(edge['name']).set(0)
578 e2e_tests_down.labels(edge['name']).set(0)
579
580 if connect_status in ["error", "no result"] or ping_status in ["error", "no result"]:
581 e2e_tests_down.labels(edge['name']).set(1)
582 else:
583 if connect_status == "connected":
584 connect_test_ok.labels(edge['name']).set(1)
585 if ping_status == "connected":
586 ping_test_ok.labels(edge['name']).set(1)
587 if connect_status == "connected" and ping_status == "connected":
588 e2e_tests_ok.labels(edge['name']).set(1)
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700589
Hyunsun Moon200eba52021-04-05 21:31:54 -0700590 if 'signal_quality' in edge.keys():
591 signal_quality_rsrq.labels(edge['name']).set(edge['signal_quality']['rsrq'])
592 signal_quality_rsrp.labels(edge['name']).set(edge['signal_quality']['rsrp'])
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800593
Andy Bavier4021a2f2020-07-29 12:39:47 -0700594 res.append(prom.generate_latest(cp_status))
595 res.append(prom.generate_latest(up_status))
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700596
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800597 res.append(prom.generate_latest(dongle_stats_imsi))
598 res.append(prom.generate_latest(dongle_stats_cellglobalid))
599 res.append(prom.generate_latest(dongle_stats_plmnstatus))
Jeremy Ronquillo1eaff6a2021-11-16 13:46:46 -0800600 res.append(prom.generate_latest(dongle_stats_phycellid))
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800601
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700602 res.append(prom.generate_latest(ping_dry_run_min))
603 res.append(prom.generate_latest(ping_dry_run_avg))
604 res.append(prom.generate_latest(ping_dry_run_max))
605 res.append(prom.generate_latest(ping_dry_run_stddev))
606 res.append(prom.generate_latest(ping_dry_run_transmitted))
607 res.append(prom.generate_latest(ping_dry_run_received))
608 res.append(prom.generate_latest(ping_dry_run_median))
609
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800610 res.append(prom.generate_latest(ping_dns_min))
611 res.append(prom.generate_latest(ping_dns_avg))
612 res.append(prom.generate_latest(ping_dns_max))
613 res.append(prom.generate_latest(ping_dns_stddev))
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700614 res.append(prom.generate_latest(ping_dns_transmitted))
615 res.append(prom.generate_latest(ping_dns_received))
616 res.append(prom.generate_latest(ping_dns_median))
617
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700618 res.append(prom.generate_latest(ping_iperf_server_min))
619 res.append(prom.generate_latest(ping_iperf_server_avg))
620 res.append(prom.generate_latest(ping_iperf_server_max))
621 res.append(prom.generate_latest(ping_iperf_server_stddev))
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700622 res.append(prom.generate_latest(ping_iperf_server_transmitted))
623 res.append(prom.generate_latest(ping_iperf_server_received))
624 res.append(prom.generate_latest(ping_iperf_server_median))
625
626 res.append(prom.generate_latest(ping_management_server_min))
627 res.append(prom.generate_latest(ping_management_server_avg))
628 res.append(prom.generate_latest(ping_management_server_max))
629 res.append(prom.generate_latest(ping_management_server_stddev))
630 res.append(prom.generate_latest(ping_management_server_transmitted))
631 res.append(prom.generate_latest(ping_management_server_received))
632 res.append(prom.generate_latest(ping_management_server_median))
633
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700634 res.append(prom.generate_latest(iperf_cluster_downlink))
635 res.append(prom.generate_latest(iperf_cluster_uplink))
Andy Bavier4021a2f2020-07-29 12:39:47 -0700636 res.append(prom.generate_latest(last_update))
Andy Bavier614af142020-08-07 14:49:56 -0700637 res.append(prom.generate_latest(maint_window))
Andy Baviera0c40aa2021-03-10 12:09:12 -0700638 res.append(prom.generate_latest(connect_test_ok))
Andy Baviera0c40aa2021-03-10 12:09:12 -0700639 res.append(prom.generate_latest(ping_test_ok))
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700640 res.append(prom.generate_latest(e2e_tests_ok))
641 res.append(prom.generate_latest(e2e_tests_down))
Hyunsun Moon200eba52021-04-05 21:31:54 -0700642 res.append(prom.generate_latest(signal_quality_rsrq))
643 res.append(prom.generate_latest(signal_quality_rsrp))
Andy Bavier614af142020-08-07 14:49:56 -0700644
Andy Bavier4021a2f2020-07-29 12:39:47 -0700645 return Response(res, mimetype="text/plain")
646
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700647
648@app.route('/edges/healthz', methods=['GET'])
649def get_health():
650 return {'message': 'healthy'}
651
652
653@app.route('/edges', methods=['GET'])
654def get_edges():
Andy Bavier4021a2f2020-07-29 12:39:47 -0700655 time_out_stale_results()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700656 return jsonify({'edges': edges})
657
658
659@app.route('/edges/<string:name>', methods=['GET'])
660def get_edge(name):
Andy Bavier4021a2f2020-07-29 12:39:47 -0700661 time_out_stale_results()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700662 edge = [edge for edge in edges if edge['name'] == name]
663 if len(edge) == 0:
664 abort(404)
665 return jsonify({'edge': edge[0]})
666
667
668@app.route('/edges', methods=['POST'])
Andy Bavierf872e9a2021-03-22 12:06:25 -0700669@app.route('/testresults', methods=['POST'])
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700670def create_or_update_edge():
Andy Bavier2d60fc52021-05-04 16:13:39 -0700671 try:
672 jsonschema.validate(instance=request.json, schema=edgeSchema)
673 except jsonschema.exceptions.ValidationError as err:
Andy Bavier55dc5872021-05-05 11:31:42 -0700674 app.logger.warn(err)
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700675 abort(400)
676
677 req_edge = {
678 'name': request.json['name'],
679 'status': {
680 'control_plane': request.json['status']['control_plane'],
681 'user_plane': request.json['status']['user_plane']
682 },
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800683 'dongle_stats': {
684 'SuccessfulFetch' : False
685 },
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800686 'speedtest': {
687 'ping': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700688 'dry_run': {
689 'transmitted' : 0,
690 'received' : 0,
691 'median' : 0.0,
692 'min': 0.0,
693 'avg': 0.0,
694 'max': 0.0,
695 'stddev': 0.0
696 },
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800697 'dns': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700698 'transmitted' : 0,
699 'received' : 0,
700 'median' : 0.0,
Jeremy Ronquilloa944fbc2021-03-30 10:57:45 -0700701 'min': 0.0,
702 'avg': 0.0,
703 'max': 0.0,
704 'stddev': 0.0
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700705 },
706 'iperf_server': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700707 'transmitted' : 0,
708 'received' : 0,
709 'median' : 0.0,
710 'min': 0.0,
711 'avg': 0.0,
712 'max': 0.0,
713 'stddev': 0.0
714 },
715 'management_server': {
716 'transmitted' : 0,
717 'received' : 0,
718 'median' : 0.0,
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700719 'min': 0.0,
720 'avg': 0.0,
721 'max': 0.0,
722 'stddev': 0.0
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800723 }
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700724 },
725 'iperf': {
726 'cluster': {
727 'downlink': 0.0,
728 'uplink': 0.0
729 }
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800730 }
731 },
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700732 'last_update': time.time()
733 }
734
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800735 if 'speedtest' in request.json:
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700736 if 'ping' in request.json['speedtest']:
737 req_edge['speedtest']['ping'] = request.json['speedtest']['ping']
738 if 'iperf' in request.json['speedtest']:
739 req_edge['speedtest']['iperf'] = request.json['speedtest']['iperf']
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800740
Hyunsun Moon200eba52021-04-05 21:31:54 -0700741 if 'signal_quality' in request.json:
742 req_edge['signal_quality'] = request.json['signal_quality']
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800743
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800744 if 'dongle_stats' in request.json:
745 req_edge['dongle_stats'] = request.json['dongle_stats']
746
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700747 edge = [edge for edge in edges if edge['name'] == req_edge['name']]
748 if len(edge) == 0:
Andy Bavier55dc5872021-05-05 11:31:42 -0700749 app.logger.info("new edge request " + req_edge['name'])
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700750 edges.append(req_edge)
751 else:
752 edge[0]['status']['control_plane'] = req_edge['status']['control_plane']
753 edge[0]['status']['user_plane'] = req_edge['status']['user_plane']
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800754 edge[0]['speedtest']['ping'] = req_edge['speedtest']['ping']
Jeremy Ronquillo56b0a1e2021-04-09 00:26:18 -0700755 edge[0]['speedtest']['iperf'] = req_edge['speedtest']['iperf']
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800756 edge[0]['dongle_stats'] = req_edge['dongle_stats']
Hyunsun Moon200eba52021-04-05 21:31:54 -0700757 if 'signal_quality' in req_edge.keys():
758 edge[0]['signal_quality'] = req_edge['signal_quality']
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700759 edge[0]['last_update'] = req_edge['last_update']
760
761 return jsonify({'edge': req_edge}), 201
762
763
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700764@app.route('/edges/<string:name>', methods=['DELETE'])
Andy Bavierf872e9a2021-03-22 12:06:25 -0700765@app.route('/testresults/<string:name>', methods=['DELETE'])
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700766def delete_edge(name):
Andy Bavier55dc5872021-05-05 11:31:42 -0700767 app.logger.info("delete edge request " + name)
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700768 result = False
769 for i in range(len(edges)):
770 if edges[i]['name'] == name:
771 del edges[i]
Andy Baviere47157d2020-12-11 14:13:12 -0700772 remove_edge_from_metrics(name)
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700773 result = True
774 break
775 if not result:
776 abort(404)
777 return jsonify({'result': True})
778
779
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700780if __name__ == '__main__':
Andy Bavier8a5c9872020-10-21 13:17:53 -0700781 if SECRET_ICAL_URL and AETHER_ENV:
Andy Bavier55dc5872021-05-05 11:31:42 -0700782 app.logger.info(" * Starting maintenance calendar polling thread (Aether env: %s)" % AETHER_ENV)
Andy Bavier614af142020-08-07 14:49:56 -0700783 t = threading.Thread(target=pull_maintenance_events)
784 t.start()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700785 app.run(debug=True, host='0.0.0.0', port=80)