blob: 6e71973e65b7dad7f949aefcd3db8d9c7f1253f0 [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
Jeremy Ronquillo56d23b12021-12-02 14:57:42 -080026NO_RESULT_THRESHOLD = 360
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 Ronquillocd711a22021-11-18 10:37:56 -0800497 # Add PLMN Status
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800498 try:
Jeremy Ronquillocd711a22021-11-18 10:37:56 -0800499 if edge['dongle_stats']['PLMNStatus'] and edge['dongle_stats']['PLMNStatus'] in plmnstatus_mapping:
Jeremy Ronquillo1eaff6a2021-11-16 13:46:46 -0800500 dongle_stats_plmnstatus.labels(edge['name']).set(plmnstatus_mapping[edge['dongle_stats']['PLMNStatus']])
501 else:
Jeremy Ronquillocd711a22021-11-18 10:37:56 -0800502 pass
503 except:
504 pass
505
506 # Add IMSI
507 try:
508 if edge['dongle_stats']['IMSI']:
509 dongle_stats_imsi.labels(edge['name']).set(float(edge['dongle_stats']['IMSI']))
510 except:
511 pass
512
513 # Add CellGlobalID
514 try:
515 if edge['dongle_stats']['CellGlobalID']:
516 dongle_stats_cellglobalid.labels(edge['name']).set(int(edge['dongle_stats']['CellGlobalID'], 16))
517 except:
518 pass
519
520 # Add PhyCellID
521 try:
522 if edge['dongle_stats']['PhyCellID']:
523 dongle_stats_phycellid.labels(edge['name']).set(edge['dongle_stats']['PhyCellID'])
524 except:
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800525 pass
526
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700527 # Add ping dry_run latency results if available
528 try:
529 if edge['speedtest']['ping']['dry_run']['avg']:
530 ping_dry_run_min.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['min'])
531 ping_dry_run_avg.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['avg'])
532 ping_dry_run_max.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['max'])
533 ping_dry_run_stddev.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['stddev'])
534 ping_dry_run_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['transmitted'])
535 ping_dry_run_received.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['received'])
536 ping_dry_run_median.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['median'])
537 except KeyError:
538 pass
539
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700540 # Add ping dns latency results if available
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700541 try:
542 if edge['speedtest']['ping']['dns']['avg']:
543 ping_dns_min.labels(edge['name']).set(edge['speedtest']['ping']['dns']['min'])
544 ping_dns_avg.labels(edge['name']).set(edge['speedtest']['ping']['dns']['avg'])
545 ping_dns_max.labels(edge['name']).set(edge['speedtest']['ping']['dns']['max'])
546 ping_dns_stddev.labels(edge['name']).set(edge['speedtest']['ping']['dns']['stddev'])
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700547 ping_dns_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['dns']['transmitted'])
548 ping_dns_received.labels(edge['name']).set(edge['speedtest']['ping']['dns']['received'])
549 ping_dns_median.labels(edge['name']).set(edge['speedtest']['ping']['dns']['median'])
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700550 except KeyError:
551 pass
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700552
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700553 # Add ping iperf_server latency results if available
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700554 try:
555 if edge['speedtest']['ping']['iperf_server']['avg']:
556 ping_iperf_server_min.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['min'])
557 ping_iperf_server_avg.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['avg'])
558 ping_iperf_server_max.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['max'])
559 ping_iperf_server_stddev.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['stddev'])
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700560 ping_iperf_server_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['transmitted'])
561 ping_iperf_server_received.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['received'])
562 ping_iperf_server_median.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['median'])
563 except KeyError:
564 pass
565
566 # Add ping management_server latency results if available
567 try:
568 if edge['speedtest']['ping']['management_server']['avg']:
569 ping_management_server_min.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['min'])
570 ping_management_server_avg.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['avg'])
571 ping_management_server_max.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['max'])
572 ping_management_server_stddev.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['stddev'])
573 ping_management_server_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['transmitted'])
574 ping_management_server_received.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['received'])
575 ping_management_server_median.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['median'])
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700576 except KeyError:
577 pass
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700578
579 # Add iperf bandwidth results if available
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700580 try:
581 if edge['speedtest']['iperf']['cluster']['downlink']:
582 iperf_cluster_downlink.labels(edge['name']).set(edge['speedtest']['iperf']['cluster']['downlink'])
583 iperf_cluster_uplink.labels(edge['name']).set(edge['speedtest']['iperf']['cluster']['uplink'])
584 except KeyError:
585 pass
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800586
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700587 cp_status.labels(edge['name']).set(status_codes[connect_status])
588 up_status.labels(edge['name']).set(status_codes[ping_status])
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800589
Andy Bavier4021a2f2020-07-29 12:39:47 -0700590 last_update.labels(edge['name']).set(edge['last_update'])
Andy Bavier614af142020-08-07 14:49:56 -0700591 if 'maintenance' in edge:
592 maint_window.labels(edge['name']).set(int(edge['maintenance']['in_window']))
Andy Bavier4021a2f2020-07-29 12:39:47 -0700593
Andy Baviera0c40aa2021-03-10 12:09:12 -0700594 connect_test_ok.labels(edge['name']).set(0)
Andy Baviera0c40aa2021-03-10 12:09:12 -0700595 ping_test_ok.labels(edge['name']).set(0)
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700596 e2e_tests_ok.labels(edge['name']).set(0)
597 e2e_tests_down.labels(edge['name']).set(0)
598
599 if connect_status in ["error", "no result"] or ping_status in ["error", "no result"]:
600 e2e_tests_down.labels(edge['name']).set(1)
601 else:
602 if connect_status == "connected":
603 connect_test_ok.labels(edge['name']).set(1)
604 if ping_status == "connected":
605 ping_test_ok.labels(edge['name']).set(1)
606 if connect_status == "connected" and ping_status == "connected":
607 e2e_tests_ok.labels(edge['name']).set(1)
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700608
Hyunsun Moon200eba52021-04-05 21:31:54 -0700609 if 'signal_quality' in edge.keys():
610 signal_quality_rsrq.labels(edge['name']).set(edge['signal_quality']['rsrq'])
611 signal_quality_rsrp.labels(edge['name']).set(edge['signal_quality']['rsrp'])
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800612
Andy Bavier4021a2f2020-07-29 12:39:47 -0700613 res.append(prom.generate_latest(cp_status))
614 res.append(prom.generate_latest(up_status))
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700615
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800616 res.append(prom.generate_latest(dongle_stats_imsi))
617 res.append(prom.generate_latest(dongle_stats_cellglobalid))
618 res.append(prom.generate_latest(dongle_stats_plmnstatus))
Jeremy Ronquillo1eaff6a2021-11-16 13:46:46 -0800619 res.append(prom.generate_latest(dongle_stats_phycellid))
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800620
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700621 res.append(prom.generate_latest(ping_dry_run_min))
622 res.append(prom.generate_latest(ping_dry_run_avg))
623 res.append(prom.generate_latest(ping_dry_run_max))
624 res.append(prom.generate_latest(ping_dry_run_stddev))
625 res.append(prom.generate_latest(ping_dry_run_transmitted))
626 res.append(prom.generate_latest(ping_dry_run_received))
627 res.append(prom.generate_latest(ping_dry_run_median))
628
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800629 res.append(prom.generate_latest(ping_dns_min))
630 res.append(prom.generate_latest(ping_dns_avg))
631 res.append(prom.generate_latest(ping_dns_max))
632 res.append(prom.generate_latest(ping_dns_stddev))
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700633 res.append(prom.generate_latest(ping_dns_transmitted))
634 res.append(prom.generate_latest(ping_dns_received))
635 res.append(prom.generate_latest(ping_dns_median))
636
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700637 res.append(prom.generate_latest(ping_iperf_server_min))
638 res.append(prom.generate_latest(ping_iperf_server_avg))
639 res.append(prom.generate_latest(ping_iperf_server_max))
640 res.append(prom.generate_latest(ping_iperf_server_stddev))
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700641 res.append(prom.generate_latest(ping_iperf_server_transmitted))
642 res.append(prom.generate_latest(ping_iperf_server_received))
643 res.append(prom.generate_latest(ping_iperf_server_median))
644
645 res.append(prom.generate_latest(ping_management_server_min))
646 res.append(prom.generate_latest(ping_management_server_avg))
647 res.append(prom.generate_latest(ping_management_server_max))
648 res.append(prom.generate_latest(ping_management_server_stddev))
649 res.append(prom.generate_latest(ping_management_server_transmitted))
650 res.append(prom.generate_latest(ping_management_server_received))
651 res.append(prom.generate_latest(ping_management_server_median))
652
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700653 res.append(prom.generate_latest(iperf_cluster_downlink))
654 res.append(prom.generate_latest(iperf_cluster_uplink))
Andy Bavier4021a2f2020-07-29 12:39:47 -0700655 res.append(prom.generate_latest(last_update))
Andy Bavier614af142020-08-07 14:49:56 -0700656 res.append(prom.generate_latest(maint_window))
Andy Baviera0c40aa2021-03-10 12:09:12 -0700657 res.append(prom.generate_latest(connect_test_ok))
Andy Baviera0c40aa2021-03-10 12:09:12 -0700658 res.append(prom.generate_latest(ping_test_ok))
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700659 res.append(prom.generate_latest(e2e_tests_ok))
660 res.append(prom.generate_latest(e2e_tests_down))
Hyunsun Moon200eba52021-04-05 21:31:54 -0700661 res.append(prom.generate_latest(signal_quality_rsrq))
662 res.append(prom.generate_latest(signal_quality_rsrp))
Andy Bavier614af142020-08-07 14:49:56 -0700663
Andy Bavier4021a2f2020-07-29 12:39:47 -0700664 return Response(res, mimetype="text/plain")
665
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700666
667@app.route('/edges/healthz', methods=['GET'])
668def get_health():
669 return {'message': 'healthy'}
670
671
672@app.route('/edges', methods=['GET'])
673def get_edges():
Andy Bavier4021a2f2020-07-29 12:39:47 -0700674 time_out_stale_results()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700675 return jsonify({'edges': edges})
676
677
678@app.route('/edges/<string:name>', methods=['GET'])
679def get_edge(name):
Andy Bavier4021a2f2020-07-29 12:39:47 -0700680 time_out_stale_results()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700681 edge = [edge for edge in edges if edge['name'] == name]
682 if len(edge) == 0:
683 abort(404)
684 return jsonify({'edge': edge[0]})
685
686
687@app.route('/edges', methods=['POST'])
Andy Bavierf872e9a2021-03-22 12:06:25 -0700688@app.route('/testresults', methods=['POST'])
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700689def create_or_update_edge():
Andy Bavier2d60fc52021-05-04 16:13:39 -0700690 try:
691 jsonschema.validate(instance=request.json, schema=edgeSchema)
692 except jsonschema.exceptions.ValidationError as err:
Andy Bavier55dc5872021-05-05 11:31:42 -0700693 app.logger.warn(err)
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700694 abort(400)
695
696 req_edge = {
697 'name': request.json['name'],
698 'status': {
699 'control_plane': request.json['status']['control_plane'],
700 'user_plane': request.json['status']['user_plane']
701 },
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800702 'dongle_stats': {
703 'SuccessfulFetch' : False
704 },
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800705 'speedtest': {
706 'ping': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700707 'dry_run': {
708 'transmitted' : 0,
709 'received' : 0,
710 'median' : 0.0,
711 'min': 0.0,
712 'avg': 0.0,
713 'max': 0.0,
714 'stddev': 0.0
715 },
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800716 'dns': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700717 'transmitted' : 0,
718 'received' : 0,
719 'median' : 0.0,
Jeremy Ronquilloa944fbc2021-03-30 10:57:45 -0700720 'min': 0.0,
721 'avg': 0.0,
722 'max': 0.0,
723 'stddev': 0.0
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700724 },
725 'iperf_server': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700726 'transmitted' : 0,
727 'received' : 0,
728 'median' : 0.0,
729 'min': 0.0,
730 'avg': 0.0,
731 'max': 0.0,
732 'stddev': 0.0
733 },
734 'management_server': {
735 'transmitted' : 0,
736 'received' : 0,
737 'median' : 0.0,
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700738 'min': 0.0,
739 'avg': 0.0,
740 'max': 0.0,
741 'stddev': 0.0
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800742 }
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700743 },
744 'iperf': {
745 'cluster': {
746 'downlink': 0.0,
747 'uplink': 0.0
748 }
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800749 }
750 },
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700751 'last_update': time.time()
752 }
753
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800754 if 'speedtest' in request.json:
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700755 if 'ping' in request.json['speedtest']:
756 req_edge['speedtest']['ping'] = request.json['speedtest']['ping']
757 if 'iperf' in request.json['speedtest']:
758 req_edge['speedtest']['iperf'] = request.json['speedtest']['iperf']
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800759
Hyunsun Moon200eba52021-04-05 21:31:54 -0700760 if 'signal_quality' in request.json:
761 req_edge['signal_quality'] = request.json['signal_quality']
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800762
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800763 if 'dongle_stats' in request.json:
764 req_edge['dongle_stats'] = request.json['dongle_stats']
765
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700766 edge = [edge for edge in edges if edge['name'] == req_edge['name']]
767 if len(edge) == 0:
Andy Bavier55dc5872021-05-05 11:31:42 -0700768 app.logger.info("new edge request " + req_edge['name'])
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700769 edges.append(req_edge)
770 else:
771 edge[0]['status']['control_plane'] = req_edge['status']['control_plane']
772 edge[0]['status']['user_plane'] = req_edge['status']['user_plane']
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800773 edge[0]['speedtest']['ping'] = req_edge['speedtest']['ping']
Jeremy Ronquillo56b0a1e2021-04-09 00:26:18 -0700774 edge[0]['speedtest']['iperf'] = req_edge['speedtest']['iperf']
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800775 edge[0]['dongle_stats'] = req_edge['dongle_stats']
Hyunsun Moon200eba52021-04-05 21:31:54 -0700776 if 'signal_quality' in req_edge.keys():
777 edge[0]['signal_quality'] = req_edge['signal_quality']
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700778 edge[0]['last_update'] = req_edge['last_update']
779
780 return jsonify({'edge': req_edge}), 201
781
782
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700783@app.route('/edges/<string:name>', methods=['DELETE'])
Andy Bavierf872e9a2021-03-22 12:06:25 -0700784@app.route('/testresults/<string:name>', methods=['DELETE'])
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700785def delete_edge(name):
Andy Bavier55dc5872021-05-05 11:31:42 -0700786 app.logger.info("delete edge request " + name)
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700787 result = False
788 for i in range(len(edges)):
789 if edges[i]['name'] == name:
790 del edges[i]
Andy Baviere47157d2020-12-11 14:13:12 -0700791 remove_edge_from_metrics(name)
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700792 result = True
793 break
794 if not result:
795 abort(404)
796 return jsonify({'result': True})
797
798
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700799if __name__ == '__main__':
Andy Bavier8a5c9872020-10-21 13:17:53 -0700800 if SECRET_ICAL_URL and AETHER_ENV:
Andy Bavier55dc5872021-05-05 11:31:42 -0700801 app.logger.info(" * Starting maintenance calendar polling thread (Aether env: %s)" % AETHER_ENV)
Andy Bavier614af142020-08-07 14:49:56 -0700802 t = threading.Thread(target=pull_maintenance_events)
803 t.start()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700804 app.run(debug=True, host='0.0.0.0', port=80)