blob: 3fd0212240ada4fadbb2fa2158813cfe27168885 [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
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700244# Legacy test status metrics, reporting a status code between -2 and 2
Andy Bavier4021a2f2020-07-29 12:39:47 -0700245cp_status = prom.Gauge("aetheredge_status_control_plane", "Control plane status code", ["name"])
246up_status = prom.Gauge("aetheredge_status_user_plane", "User plane status code", ["name"])
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700247
248# Simplified binary test result metrics
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700249e2e_tests_ok = prom.Gauge("aetheredge_e2e_tests_ok", "Last connect and ping test both passed", ["name"])
Andy Baviera0c40aa2021-03-10 12:09:12 -0700250connect_test_ok = prom.Gauge("aetheredge_connect_test_ok", "Last connect test passed", ["name"])
Andy Baviera0c40aa2021-03-10 12:09:12 -0700251ping_test_ok = prom.Gauge("aetheredge_ping_test_ok", "Last ping test passed", ["name"])
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700252e2e_tests_down = prom.Gauge("aetheredge_e2e_tests_down", "E2E tests not reporting", ["name"])
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700253
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800254# Dongle Stats (strings can't be stored in Prometheus)
255dongle_stats_imsi = prom.Gauge("aetheredge_dongle_stats_imsi", "IMSI of the UE", ["name"])
256dongle_stats_cellglobalid = prom.Gauge("aetheredge_dongle_stats_cellglobalid", "CellGlobalID of the UE", ["name", "value"])
257dongle_stats_plmnstatus = prom.Gauge("aetheredge_dongle_stats_plmnstatus", "PLMNStatus of the UE", ["name", "value"])
258
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700259# Ping dry_run metrics
260ping_dry_run_transmitted = prom.Gauge("aetheredge_ping_dry_run_test_transmitted","Last ping test to dry_run number of transmitted packets",["name"])
261ping_dry_run_received = prom.Gauge("aetheredge_ping_dry_run_test_received","Last ping test to dry_run number of received packets",["name"])
262ping_dry_run_median = prom.Gauge("aetheredge_ping_dry_run_test_median","Last ping test to dry_run median value",["name"])
263ping_dry_run_min = prom.Gauge("aetheredge_ping_dry_run_test_min","Last ping test to dry_run minimum value",["name"])
264ping_dry_run_avg = prom.Gauge("aetheredge_ping_dry_run_test_avg","Last ping test to dry_run average",["name"])
265ping_dry_run_max = prom.Gauge("aetheredge_ping_dry_run_test_max","Last ping test to dry_run maximum value",["name"])
266ping_dry_run_stddev = prom.Gauge("aetheredge_ping_dry_run_test_stddev","Last ping test to dry_run standard deviation",["name"])
267
268# Ping dns metrics
269ping_dns_transmitted = prom.Gauge("aetheredge_ping_dns_test_transmitted","Last ping test to dns number of transmitted packets",["name"])
270ping_dns_received = prom.Gauge("aetheredge_ping_dns_test_received","Last ping test to dns number of received packets",["name"])
271ping_dns_median = prom.Gauge("aetheredge_ping_dns_test_median","Last ping test to dns median value",["name"])
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700272ping_dns_min = prom.Gauge("aetheredge_ping_dns_test_min","Last ping test to dns minimum value",["name"])
273ping_dns_avg = prom.Gauge("aetheredge_ping_dns_test_avg","Last ping test to dns average",["name"])
274ping_dns_max = prom.Gauge("aetheredge_ping_dns_test_max","Last ping test to dns maximum value",["name"])
275ping_dns_stddev = prom.Gauge("aetheredge_ping_dns_test_stddev","Last ping test to dns standard deviation",["name"])
276
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700277# Ping iperf server metrics
278ping_iperf_server_transmitted = prom.Gauge("aetheredge_ping_iperf_server_test_transmitted","Last ping test to iperf_server number of transmitted packets",["name"])
279ping_iperf_server_received = prom.Gauge("aetheredge_ping_iperf_server_test_received","Last ping test to iperf_server number of received packets",["name"])
280ping_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 -0700281ping_iperf_server_min = prom.Gauge("aetheredge_ping_iperf_server_test_min","Last ping test to iperf_server minimum value",["name"])
282ping_iperf_server_avg = prom.Gauge("aetheredge_ping_iperf_server_test_avg","Last ping test to iperf_server average",["name"])
283ping_iperf_server_max = prom.Gauge("aetheredge_ping_iperf_server_test_max","Last ping test to iperf_server maximum value",["name"])
284ping_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 -0800285
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700286# Ping management_server metrics
287ping_management_server_transmitted = prom.Gauge("aetheredge_ping_management_server_test_transmitted","Last ping test to management_server number of transmitted packets",["name"])
288ping_management_server_received = prom.Gauge("aetheredge_ping_management_server_test_received","Last ping test to management_server number of received packets",["name"])
289ping_management_server_median = prom.Gauge("aetheredge_ping_management_server_test_median","Last ping test to management_server median value",["name"])
290ping_management_server_min = prom.Gauge("aetheredge_ping_management_server_test_min","Last ping test to management_server minimum value",["name"])
291ping_management_server_avg = prom.Gauge("aetheredge_ping_management_server_test_avg","Last ping test to management_server average",["name"])
292ping_management_server_max = prom.Gauge("aetheredge_ping_management_server_test_max","Last ping test to management_server maximum value",["name"])
293ping_management_server_stddev = prom.Gauge("aetheredge_ping_management_server_test_stddev","Last ping test to management_server standard deviation",["name"])
294
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700295# Speedtest iperf metrics
296iperf_cluster_downlink = prom.Gauge("aetheredge_iperf_cluster_downlink_test","Last iperf test downlink result",["name"])
297iperf_cluster_uplink = prom.Gauge("aetheredge_iperf_cluster_uplink_test","Last iperf test downlink result",["name"])
298
Hyunsun Moon200eba52021-04-05 21:31:54 -0700299# Signal quality metrics in CESQ format not dB
300# RSRQ: >=53 excellent, 43 ~ 53 good, 33 ~ 43 mid, <=33 bad, 0 no signal
301# RSRP: >=20 excellent, 10 ~ 20 good, 0 ~ 10 mid, 0 no signal
302signal_quality_rsrq = prom.Gauge("aetheredge_signal_quality_rsrq", "Quality of the received signal", ["name"])
303signal_quality_rsrp = prom.Gauge("aetheredge_signal_quality_rsrp", "Power of the received signal", ["name"])
304
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700305# Other metrics
Andy Bavier4021a2f2020-07-29 12:39:47 -0700306last_update = prom.Gauge("aetheredge_last_update", "Last reported test result", ["name"])
Andy Bavier614af142020-08-07 14:49:56 -0700307maint_window = prom.Gauge("aetheredge_in_maintenance_window", "Currently in a maintenance window", ["name"])
308
309def is_my_event(event, name):
310 for field in ["summary", "location", "description"]:
Andy Bavier8a5c9872020-10-21 13:17:53 -0700311 fullname = name
312 if name.startswith("ace-"):
313 fullname = "%s-%s" % (name, AETHER_ENV)
314 if fullname in getattr(event, field, ""):
Andy Bavier614af142020-08-07 14:49:56 -0700315 return True
Andy Bavier0423cbd2020-10-23 10:50:29 -0700316 if fullname in room_mapping and room_mapping[fullname] in getattr(event, field, ""):
317 return True
Andy Bavier614af142020-08-07 14:49:56 -0700318 return False
319
Andy Bavierc41cf0c2020-09-02 14:49:21 -0700320def is_naive_datetime(d):
321 return d.tzinfo is None or d.tzinfo.utcoffset(d) is None
322
323def process_all_day_events(es):
324 for event in es:
325 if event.all_day:
326 # All day events have naive datetimes, which breaks comparisons
327 pacific = pytz.timezone('US/Pacific')
328 if is_naive_datetime(event.start):
329 event.start = pacific.localize(event.start)
330 if is_naive_datetime(event.end):
331 event.end = pacific.localize(event.end)
332
Andy Bavier614af142020-08-07 14:49:56 -0700333def in_maintenance_window(events, name, now):
334 for event in events:
335 if event.start < now and event.end > now:
336 if is_my_event(event, name):
337 return True
Andy Bavier614af142020-08-07 14:49:56 -0700338 return False
339
340def pull_maintenance_events():
341 while(True):
342 now = datetime.datetime.now(pytz.utc)
343 try:
344 es = events(SECRET_ICAL_URL, start = now)
Andy Bavierc41cf0c2020-09-02 14:49:21 -0700345 process_all_day_events(es)
Andy Bavier614af142020-08-07 14:49:56 -0700346 except Exception as e:
Andy Bavier55dc5872021-05-05 11:31:42 -0700347 app.logger.error(e)
Andy Bavier614af142020-08-07 14:49:56 -0700348 else:
349 for edge in edges:
350 if 'maintenance' not in edge:
351 edge['maintenance'] = {}
352 edge['maintenance']['in_window'] = in_maintenance_window(es, edge['name'], now)
353 edge['maintenance']['last_update'] = time.time()
354 time.sleep(60)
Andy Bavier4021a2f2020-07-29 12:39:47 -0700355
356def time_out_stale_results():
357 for edge in edges:
358 time_elapsed = time.time() - edge["last_update"]
359 if time_elapsed > NO_RESULT_THRESHOLD:
360 edge['status']['control_plane'] = "no result"
361 edge['status']['user_plane'] = "no result"
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800362 edge['dongle_stats'] = {'SuccessfulFetch' : False}
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700363 edge['speedtest']['ping']['dry_run'] = {'transmitted': 0,
364 'received': 0,
365 'median': 0.0,
366 'min': 0.0,
367 'avg': 0.0,
368 'max': 0.0,
369 'stddev': 0.0}
370 edge['speedtest']['ping']['dns'] = {'transmitted': 0,
371 'received': 0,
372 'median': 0.0,
373 'min': 0.0,
Jeremy Ronquilloa944fbc2021-03-30 10:57:45 -0700374 'avg': 0.0,
375 'max': 0.0,
376 'stddev': 0.0}
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700377 edge['speedtest']['ping']['iperf_server'] = {'transmitted': 0,
378 'received': 0,
379 'median': 0.0,
380 'min': 0.0,
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700381 'avg': 0.0,
382 'max': 0.0,
383 'stddev': 0.0}
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700384 edge['speedtest']['ping']['management_server'] = {'transmitted': 0,
385 'received': 0,
386 'median': 0.0,
387 'min': 0.0,
388 'avg': 0.0,
389 'max': 0.0,
390 'stddev': 0.0}
Jeremy Ronquillo56b0a1e2021-04-09 00:26:18 -0700391 edge['speedtest']['iperf'] = {'cluster': {
392 'downlink': 0.0,
393 'uplink': 0.0
394 }
395 }
Hyunsun Moon200eba52021-04-05 21:31:54 -0700396 edge.pop('signal_quality', None)
Andy Bavier4021a2f2020-07-29 12:39:47 -0700397
Andy Baviere47157d2020-12-11 14:13:12 -0700398def remove_edge_from_metrics(name):
399 try:
400 cp_status.remove(name)
401 up_status.remove(name)
402 last_update.remove(name)
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700403 e2e_tests_ok.remove(name)
Andy Baviera0c40aa2021-03-10 12:09:12 -0700404 connect_test_ok.remove(name)
Andy Baviera0c40aa2021-03-10 12:09:12 -0700405 ping_test_ok.remove(name)
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700406 e2e_tests_down.remove(name)
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700407 except:
408 pass
409
410 try:
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800411 dongle_stats_imsi.remove(name)
412 dongle_stats_cellglobalid.clear()
413 dongle_stats_plmnstatus.clear()
414 except:
415 pass
416
417 try:
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700418 ping_dry_run_min.remove(name)
419 ping_dry_run_avg.remove(name)
420 ping_dry_run_max.remove(name)
421 ping_dry_run_stddev.remove(name)
422 ping_dry_run_transmitted.remove(name)
423 ping_dry_run_received.remove(name)
424 ping_dry_run_median.remove(name)
425 except:
426 pass
427
428 try:
Hyunsun Moon200eba52021-04-05 21:31:54 -0700429 ping_dns_min.remove(name)
430 ping_dns_avg.remove(name)
431 ping_dns_max.remove(name)
432 ping_dns_stddev.remove(name)
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700433 ping_dns_transmitted.remove(name)
434 ping_dns_received.remove(name)
435 ping_dns_median.remove(name)
Hyunsun Moon200eba52021-04-05 21:31:54 -0700436 except:
437 pass
438
439 try:
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700440 ping_iperf_server_min.remove(name)
441 ping_iperf_server_avg.remove(name)
442 ping_iperf_server_max.remove(name)
443 ping_iperf_server_stddev.remove(name)
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700444 ping_iperf_server_transmitted.remove(name)
445 ping_iperf_server_received.remove(name)
446 ping_iperf_server_median.remove(name)
447 except:
448 pass
449
450 try:
451 ping_management_server_min.remove(name)
452 ping_management_server_avg.remove(name)
453 ping_management_server_max.remove(name)
454 ping_management_server_stddev.remove(name)
455 ping_management_server_transmitted.remove(name)
456 ping_management_server_received.remove(name)
457 ping_management_server_median.remove(name)
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700458 except:
459 pass
460
461 try:
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700462 iperf_cluster_downlink.remove(name)
463 iperf_cluster_uplink.remove(name)
464 except:
465 pass
466
467 try:
Hyunsun Moon200eba52021-04-05 21:31:54 -0700468 signal_quality_rsrq.remove(name)
469 signal_quality_rsrp.remove(name)
470 except:
471 pass
472
473 try:
Andy Baviere47157d2020-12-11 14:13:12 -0700474 maint_window.remove(name)
475 except:
476 pass
Andy Bavier4021a2f2020-07-29 12:39:47 -0700477
478@app.route('/edges/metrics', methods=['GET'])
479def get_prometheus_metrics():
480 res = []
481 time_out_stale_results()
482 for edge in edges:
Andy Bavier8a5c9872020-10-21 13:17:53 -0700483 if edge['name'] == "ace-example":
Andy Bavier4021a2f2020-07-29 12:39:47 -0700484 continue
485
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700486 connect_status = edge['status']['control_plane']
487 ping_status = edge['status']['user_plane']
488
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800489 try:
490 dongle_stats_imsi.labels(edge['name']).set(float(edge['dongle_stats']['IMSI']))
491 dongle_stats_cellglobalid.labels(name=edge['name'], value=edge['dongle_stats']['CellGlobalID']).set(0)
492 dongle_stats_plmnstatus.labels(name=edge['name'], value=edge['dongle_stats']['PLMNStatus']).set(0)
493 except KeyError:
494 pass
495
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700496 # Add ping dry_run latency results if available
497 try:
498 if edge['speedtest']['ping']['dry_run']['avg']:
499 ping_dry_run_min.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['min'])
500 ping_dry_run_avg.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['avg'])
501 ping_dry_run_max.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['max'])
502 ping_dry_run_stddev.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['stddev'])
503 ping_dry_run_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['transmitted'])
504 ping_dry_run_received.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['received'])
505 ping_dry_run_median.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['median'])
506 except KeyError:
507 pass
508
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700509 # Add ping dns latency results if available
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700510 try:
511 if edge['speedtest']['ping']['dns']['avg']:
512 ping_dns_min.labels(edge['name']).set(edge['speedtest']['ping']['dns']['min'])
513 ping_dns_avg.labels(edge['name']).set(edge['speedtest']['ping']['dns']['avg'])
514 ping_dns_max.labels(edge['name']).set(edge['speedtest']['ping']['dns']['max'])
515 ping_dns_stddev.labels(edge['name']).set(edge['speedtest']['ping']['dns']['stddev'])
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700516 ping_dns_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['dns']['transmitted'])
517 ping_dns_received.labels(edge['name']).set(edge['speedtest']['ping']['dns']['received'])
518 ping_dns_median.labels(edge['name']).set(edge['speedtest']['ping']['dns']['median'])
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700519 except KeyError:
520 pass
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700521
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700522 # Add ping iperf_server latency results if available
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700523 try:
524 if edge['speedtest']['ping']['iperf_server']['avg']:
525 ping_iperf_server_min.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['min'])
526 ping_iperf_server_avg.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['avg'])
527 ping_iperf_server_max.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['max'])
528 ping_iperf_server_stddev.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['stddev'])
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700529 ping_iperf_server_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['transmitted'])
530 ping_iperf_server_received.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['received'])
531 ping_iperf_server_median.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['median'])
532 except KeyError:
533 pass
534
535 # Add ping management_server latency results if available
536 try:
537 if edge['speedtest']['ping']['management_server']['avg']:
538 ping_management_server_min.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['min'])
539 ping_management_server_avg.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['avg'])
540 ping_management_server_max.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['max'])
541 ping_management_server_stddev.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['stddev'])
542 ping_management_server_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['transmitted'])
543 ping_management_server_received.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['received'])
544 ping_management_server_median.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['median'])
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700545 except KeyError:
546 pass
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700547
548 # Add iperf bandwidth results if available
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700549 try:
550 if edge['speedtest']['iperf']['cluster']['downlink']:
551 iperf_cluster_downlink.labels(edge['name']).set(edge['speedtest']['iperf']['cluster']['downlink'])
552 iperf_cluster_uplink.labels(edge['name']).set(edge['speedtest']['iperf']['cluster']['uplink'])
553 except KeyError:
554 pass
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800555
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700556 cp_status.labels(edge['name']).set(status_codes[connect_status])
557 up_status.labels(edge['name']).set(status_codes[ping_status])
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800558
Andy Bavier4021a2f2020-07-29 12:39:47 -0700559 last_update.labels(edge['name']).set(edge['last_update'])
Andy Bavier614af142020-08-07 14:49:56 -0700560 if 'maintenance' in edge:
561 maint_window.labels(edge['name']).set(int(edge['maintenance']['in_window']))
Andy Bavier4021a2f2020-07-29 12:39:47 -0700562
Andy Baviera0c40aa2021-03-10 12:09:12 -0700563 connect_test_ok.labels(edge['name']).set(0)
Andy Baviera0c40aa2021-03-10 12:09:12 -0700564 ping_test_ok.labels(edge['name']).set(0)
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700565 e2e_tests_ok.labels(edge['name']).set(0)
566 e2e_tests_down.labels(edge['name']).set(0)
567
568 if connect_status in ["error", "no result"] or ping_status in ["error", "no result"]:
569 e2e_tests_down.labels(edge['name']).set(1)
570 else:
571 if connect_status == "connected":
572 connect_test_ok.labels(edge['name']).set(1)
573 if ping_status == "connected":
574 ping_test_ok.labels(edge['name']).set(1)
575 if connect_status == "connected" and ping_status == "connected":
576 e2e_tests_ok.labels(edge['name']).set(1)
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700577
Hyunsun Moon200eba52021-04-05 21:31:54 -0700578 if 'signal_quality' in edge.keys():
579 signal_quality_rsrq.labels(edge['name']).set(edge['signal_quality']['rsrq'])
580 signal_quality_rsrp.labels(edge['name']).set(edge['signal_quality']['rsrp'])
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800581
Andy Bavier4021a2f2020-07-29 12:39:47 -0700582 res.append(prom.generate_latest(cp_status))
583 res.append(prom.generate_latest(up_status))
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700584
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800585 res.append(prom.generate_latest(dongle_stats_imsi))
586 res.append(prom.generate_latest(dongle_stats_cellglobalid))
587 res.append(prom.generate_latest(dongle_stats_plmnstatus))
588
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700589 res.append(prom.generate_latest(ping_dry_run_min))
590 res.append(prom.generate_latest(ping_dry_run_avg))
591 res.append(prom.generate_latest(ping_dry_run_max))
592 res.append(prom.generate_latest(ping_dry_run_stddev))
593 res.append(prom.generate_latest(ping_dry_run_transmitted))
594 res.append(prom.generate_latest(ping_dry_run_received))
595 res.append(prom.generate_latest(ping_dry_run_median))
596
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800597 res.append(prom.generate_latest(ping_dns_min))
598 res.append(prom.generate_latest(ping_dns_avg))
599 res.append(prom.generate_latest(ping_dns_max))
600 res.append(prom.generate_latest(ping_dns_stddev))
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700601 res.append(prom.generate_latest(ping_dns_transmitted))
602 res.append(prom.generate_latest(ping_dns_received))
603 res.append(prom.generate_latest(ping_dns_median))
604
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700605 res.append(prom.generate_latest(ping_iperf_server_min))
606 res.append(prom.generate_latest(ping_iperf_server_avg))
607 res.append(prom.generate_latest(ping_iperf_server_max))
608 res.append(prom.generate_latest(ping_iperf_server_stddev))
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700609 res.append(prom.generate_latest(ping_iperf_server_transmitted))
610 res.append(prom.generate_latest(ping_iperf_server_received))
611 res.append(prom.generate_latest(ping_iperf_server_median))
612
613 res.append(prom.generate_latest(ping_management_server_min))
614 res.append(prom.generate_latest(ping_management_server_avg))
615 res.append(prom.generate_latest(ping_management_server_max))
616 res.append(prom.generate_latest(ping_management_server_stddev))
617 res.append(prom.generate_latest(ping_management_server_transmitted))
618 res.append(prom.generate_latest(ping_management_server_received))
619 res.append(prom.generate_latest(ping_management_server_median))
620
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700621 res.append(prom.generate_latest(iperf_cluster_downlink))
622 res.append(prom.generate_latest(iperf_cluster_uplink))
Andy Bavier4021a2f2020-07-29 12:39:47 -0700623 res.append(prom.generate_latest(last_update))
Andy Bavier614af142020-08-07 14:49:56 -0700624 res.append(prom.generate_latest(maint_window))
Andy Baviera0c40aa2021-03-10 12:09:12 -0700625 res.append(prom.generate_latest(connect_test_ok))
Andy Baviera0c40aa2021-03-10 12:09:12 -0700626 res.append(prom.generate_latest(ping_test_ok))
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700627 res.append(prom.generate_latest(e2e_tests_ok))
628 res.append(prom.generate_latest(e2e_tests_down))
Hyunsun Moon200eba52021-04-05 21:31:54 -0700629 res.append(prom.generate_latest(signal_quality_rsrq))
630 res.append(prom.generate_latest(signal_quality_rsrp))
Andy Bavier614af142020-08-07 14:49:56 -0700631
Andy Bavier4021a2f2020-07-29 12:39:47 -0700632 return Response(res, mimetype="text/plain")
633
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700634
635@app.route('/edges/healthz', methods=['GET'])
636def get_health():
637 return {'message': 'healthy'}
638
639
640@app.route('/edges', methods=['GET'])
641def get_edges():
Andy Bavier4021a2f2020-07-29 12:39:47 -0700642 time_out_stale_results()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700643 return jsonify({'edges': edges})
644
645
646@app.route('/edges/<string:name>', methods=['GET'])
647def get_edge(name):
Andy Bavier4021a2f2020-07-29 12:39:47 -0700648 time_out_stale_results()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700649 edge = [edge for edge in edges if edge['name'] == name]
650 if len(edge) == 0:
651 abort(404)
652 return jsonify({'edge': edge[0]})
653
654
655@app.route('/edges', methods=['POST'])
Andy Bavierf872e9a2021-03-22 12:06:25 -0700656@app.route('/testresults', methods=['POST'])
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700657def create_or_update_edge():
Andy Bavier2d60fc52021-05-04 16:13:39 -0700658 try:
659 jsonschema.validate(instance=request.json, schema=edgeSchema)
660 except jsonschema.exceptions.ValidationError as err:
Andy Bavier55dc5872021-05-05 11:31:42 -0700661 app.logger.warn(err)
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700662 abort(400)
663
664 req_edge = {
665 'name': request.json['name'],
666 'status': {
667 'control_plane': request.json['status']['control_plane'],
668 'user_plane': request.json['status']['user_plane']
669 },
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800670 'dongle_stats': {
671 'SuccessfulFetch' : False
672 },
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800673 'speedtest': {
674 'ping': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700675 'dry_run': {
676 'transmitted' : 0,
677 'received' : 0,
678 'median' : 0.0,
679 'min': 0.0,
680 'avg': 0.0,
681 'max': 0.0,
682 'stddev': 0.0
683 },
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800684 'dns': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700685 'transmitted' : 0,
686 'received' : 0,
687 'median' : 0.0,
Jeremy Ronquilloa944fbc2021-03-30 10:57:45 -0700688 'min': 0.0,
689 'avg': 0.0,
690 'max': 0.0,
691 'stddev': 0.0
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700692 },
693 'iperf_server': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700694 'transmitted' : 0,
695 'received' : 0,
696 'median' : 0.0,
697 'min': 0.0,
698 'avg': 0.0,
699 'max': 0.0,
700 'stddev': 0.0
701 },
702 'management_server': {
703 'transmitted' : 0,
704 'received' : 0,
705 'median' : 0.0,
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700706 'min': 0.0,
707 'avg': 0.0,
708 'max': 0.0,
709 'stddev': 0.0
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800710 }
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700711 },
712 'iperf': {
713 'cluster': {
714 'downlink': 0.0,
715 'uplink': 0.0
716 }
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800717 }
718 },
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700719 'last_update': time.time()
720 }
721
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800722 if 'speedtest' in request.json:
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700723 if 'ping' in request.json['speedtest']:
724 req_edge['speedtest']['ping'] = request.json['speedtest']['ping']
725 if 'iperf' in request.json['speedtest']:
726 req_edge['speedtest']['iperf'] = request.json['speedtest']['iperf']
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800727
Hyunsun Moon200eba52021-04-05 21:31:54 -0700728 if 'signal_quality' in request.json:
729 req_edge['signal_quality'] = request.json['signal_quality']
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800730
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800731 if 'dongle_stats' in request.json:
732 req_edge['dongle_stats'] = request.json['dongle_stats']
733
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700734 edge = [edge for edge in edges if edge['name'] == req_edge['name']]
735 if len(edge) == 0:
Andy Bavier55dc5872021-05-05 11:31:42 -0700736 app.logger.info("new edge request " + req_edge['name'])
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700737 edges.append(req_edge)
738 else:
739 edge[0]['status']['control_plane'] = req_edge['status']['control_plane']
740 edge[0]['status']['user_plane'] = req_edge['status']['user_plane']
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800741 edge[0]['speedtest']['ping'] = req_edge['speedtest']['ping']
Jeremy Ronquillo56b0a1e2021-04-09 00:26:18 -0700742 edge[0]['speedtest']['iperf'] = req_edge['speedtest']['iperf']
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800743 edge[0]['dongle_stats'] = req_edge['dongle_stats']
Hyunsun Moon200eba52021-04-05 21:31:54 -0700744 if 'signal_quality' in req_edge.keys():
745 edge[0]['signal_quality'] = req_edge['signal_quality']
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700746 edge[0]['last_update'] = req_edge['last_update']
747
748 return jsonify({'edge': req_edge}), 201
749
750
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700751@app.route('/edges/<string:name>', methods=['DELETE'])
Andy Bavierf872e9a2021-03-22 12:06:25 -0700752@app.route('/testresults/<string:name>', methods=['DELETE'])
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700753def delete_edge(name):
Andy Bavier55dc5872021-05-05 11:31:42 -0700754 app.logger.info("delete edge request " + name)
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700755 result = False
756 for i in range(len(edges)):
757 if edges[i]['name'] == name:
758 del edges[i]
Andy Baviere47157d2020-12-11 14:13:12 -0700759 remove_edge_from_metrics(name)
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700760 result = True
761 break
762 if not result:
763 abort(404)
764 return jsonify({'result': True})
765
766
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700767if __name__ == '__main__':
Andy Bavier8a5c9872020-10-21 13:17:53 -0700768 if SECRET_ICAL_URL and AETHER_ENV:
Andy Bavier55dc5872021-05-05 11:31:42 -0700769 app.logger.info(" * Starting maintenance calendar polling thread (Aether env: %s)" % AETHER_ENV)
Andy Bavier614af142020-08-07 14:49:56 -0700770 t = threading.Thread(target=pull_maintenance_events)
771 t.start()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700772 app.run(debug=True, host='0.0.0.0', port=80)