blob: 0110e3411838e07bfab72c23cf59d3945d02669c [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 },
58 "speedtest": {
59 "type": "object",
60 "properties": {
61 "ping": {
62 "type": "object",
63 "properties": {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -070064 "dry_run": {
65 "type": "object",
66 "properties": {
67 "transmitted": {"type": "number"},
68 "received": {"type": "number"},
69 "median": {"type": "number"},
70 "min": {"type": "number"},
71 "avg": {"type": "number"},
72 "max": {"type": "number"},
73 "stddev": {"type": "number"}
74 },
75 "required": ["min", "avg", "max", "stddev"]
76 },
Andy Bavier2d60fc52021-05-04 16:13:39 -070077 "dns": {
78 "type": "object",
79 "properties": {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -070080 "transmitted": {"type": "number"},
81 "received": {"type": "number"},
82 "median": {"type": "number"},
Andy Bavier2d60fc52021-05-04 16:13:39 -070083 "min": {"type": "number"},
84 "avg": {"type": "number"},
85 "max": {"type": "number"},
86 "stddev": {"type": "number"}
87 },
88 "required": ["min", "avg", "max", "stddev"]
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -070089 },
90 "iperf_server": {
91 "type": "object",
92 "properties": {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -070093 "transmitted": {"type": "number"},
94 "received": {"type": "number"},
95 "median": {"type": "number"},
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -070096 "min": {"type": "number"},
97 "avg": {"type": "number"},
98 "max": {"type": "number"},
99 "stddev": {"type": "number"}
100 },
101 "required": ["min", "avg", "max", "stddev"]
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700102 },
103 "management_server": {
104 "type": "object",
105 "properties": {
106 "transmitted": {"type": "number"},
107 "received": {"type": "number"},
108 "median": {"type": "number"},
109 "min": {"type": "number"},
110 "avg": {"type": "number"},
111 "max": {"type": "number"},
112 "stddev": {"type": "number"}
113 },
114 "required": ["min", "avg", "max", "stddev"]
115 },
Andy Bavier2d60fc52021-05-04 16:13:39 -0700116 }
117 },
118 "iperf": {
119 "type": "object",
120 "properties": {
121 "cluster": {
122 "type": "object",
123 "properties": {
124 "downlink": {"type": "number"},
125 "uplink": {"type": "number"}
126 },
127 "required": ["downlink", "uplink"]
128 }
129 }
130 }
131 }
132 },
133 "signal_quality": {
134 "type": "object",
135 "properties": {
136 "rsrq": {"type": "number"},
137 "rsrp": {"type": "number"}
138 },
139 "required": ["rsrq", "rsrp"]
140 }
141 },
142 "required": ["name", "status"]
143}
144
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700145edges = [
146 {
Andy Bavier8a5c9872020-10-21 13:17:53 -0700147 'name': 'ace-example',
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700148 'status': {
149 'control_plane': 'connected',
150 'user_plane': 'connected'
151 },
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800152 'speedtest': {
153 'ping': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700154 'dry_run': {
155 'transmitted': 0,
156 'received': 0,
157 'median': 0.0,
158 'min': 0.0,
159 'avg': 0.0,
160 'max': 0.0,
161 'stddev': 0.0
162 },
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800163 'dns': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700164 'transmitted': 0,
165 'received': 0,
166 'median': 0.0,
Jeremy Ronquilloa944fbc2021-03-30 10:57:45 -0700167 'min': 0.0,
168 'avg': 0.0,
169 'max': 0.0,
170 'stddev': 0.0
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700171 },
172 'iperf_server': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700173 'transmitted': 0,
174 'received': 0,
175 'median': 0.0,
176 'min': 0.0,
177 'avg': 0.0,
178 'max': 0.0,
179 'stddev': 0.0
180 },
181 'management_server': {
182 'transmitted': 0,
183 'received': 0,
184 'median': 0.0,
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700185 'min': 0.0,
186 'avg': 0.0,
187 'max': 0.0,
188 'stddev': 0.0
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800189 }
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700190 },
191 'iperf': {
192 'cluster': {
193 'downlink': 0.0,
194 'uplink': 0.0
195 }
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800196 }
197 },
Hyunsun Moon200eba52021-04-05 21:31:54 -0700198 'signal_quality': {
199 'rsrq': 0,
200 'rsrp': 0
201 },
202 'last_update': time.time()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700203 }
204]
205
Andy Bavier4021a2f2020-07-29 12:39:47 -0700206status_codes = {
207 "no result": -2,
208 "error": -1,
209 "disconnected": 0,
210 "connecting": 1,
211 "connected": 2
212}
213
Andy Bavier614af142020-08-07 14:49:56 -0700214room_mapping = {
Andy Baviere4591222021-07-07 12:44:19 -0700215 "ace-menlo-rasp-pi-production": "(Compute)-MP-1-Aether Production",
Andy Bavier0423cbd2020-10-23 10:50:29 -0700216 "ace-menlo-staging": "(Compute)-MP-1-Aether Staging"
Andy Bavier614af142020-08-07 14:49:56 -0700217}
218
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700219# Legacy test status metrics, reporting a status code between -2 and 2
Andy Bavier4021a2f2020-07-29 12:39:47 -0700220cp_status = prom.Gauge("aetheredge_status_control_plane", "Control plane status code", ["name"])
221up_status = prom.Gauge("aetheredge_status_user_plane", "User plane status code", ["name"])
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700222
223# Simplified binary test result metrics
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700224e2e_tests_ok = prom.Gauge("aetheredge_e2e_tests_ok", "Last connect and ping test both passed", ["name"])
Andy Baviera0c40aa2021-03-10 12:09:12 -0700225connect_test_ok = prom.Gauge("aetheredge_connect_test_ok", "Last connect test passed", ["name"])
Andy Baviera0c40aa2021-03-10 12:09:12 -0700226ping_test_ok = prom.Gauge("aetheredge_ping_test_ok", "Last ping test passed", ["name"])
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700227e2e_tests_down = prom.Gauge("aetheredge_e2e_tests_down", "E2E tests not reporting", ["name"])
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700228
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700229# Ping dry_run metrics
230ping_dry_run_transmitted = prom.Gauge("aetheredge_ping_dry_run_test_transmitted","Last ping test to dry_run number of transmitted packets",["name"])
231ping_dry_run_received = prom.Gauge("aetheredge_ping_dry_run_test_received","Last ping test to dry_run number of received packets",["name"])
232ping_dry_run_median = prom.Gauge("aetheredge_ping_dry_run_test_median","Last ping test to dry_run median value",["name"])
233ping_dry_run_min = prom.Gauge("aetheredge_ping_dry_run_test_min","Last ping test to dry_run minimum value",["name"])
234ping_dry_run_avg = prom.Gauge("aetheredge_ping_dry_run_test_avg","Last ping test to dry_run average",["name"])
235ping_dry_run_max = prom.Gauge("aetheredge_ping_dry_run_test_max","Last ping test to dry_run maximum value",["name"])
236ping_dry_run_stddev = prom.Gauge("aetheredge_ping_dry_run_test_stddev","Last ping test to dry_run standard deviation",["name"])
237
238# Ping dns metrics
239ping_dns_transmitted = prom.Gauge("aetheredge_ping_dns_test_transmitted","Last ping test to dns number of transmitted packets",["name"])
240ping_dns_received = prom.Gauge("aetheredge_ping_dns_test_received","Last ping test to dns number of received packets",["name"])
241ping_dns_median = prom.Gauge("aetheredge_ping_dns_test_median","Last ping test to dns median value",["name"])
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700242ping_dns_min = prom.Gauge("aetheredge_ping_dns_test_min","Last ping test to dns minimum value",["name"])
243ping_dns_avg = prom.Gauge("aetheredge_ping_dns_test_avg","Last ping test to dns average",["name"])
244ping_dns_max = prom.Gauge("aetheredge_ping_dns_test_max","Last ping test to dns maximum value",["name"])
245ping_dns_stddev = prom.Gauge("aetheredge_ping_dns_test_stddev","Last ping test to dns standard deviation",["name"])
246
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700247# Ping iperf server metrics
248ping_iperf_server_transmitted = prom.Gauge("aetheredge_ping_iperf_server_test_transmitted","Last ping test to iperf_server number of transmitted packets",["name"])
249ping_iperf_server_received = prom.Gauge("aetheredge_ping_iperf_server_test_received","Last ping test to iperf_server number of received packets",["name"])
250ping_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 -0700251ping_iperf_server_min = prom.Gauge("aetheredge_ping_iperf_server_test_min","Last ping test to iperf_server minimum value",["name"])
252ping_iperf_server_avg = prom.Gauge("aetheredge_ping_iperf_server_test_avg","Last ping test to iperf_server average",["name"])
253ping_iperf_server_max = prom.Gauge("aetheredge_ping_iperf_server_test_max","Last ping test to iperf_server maximum value",["name"])
254ping_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 -0800255
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700256# Ping management_server metrics
257ping_management_server_transmitted = prom.Gauge("aetheredge_ping_management_server_test_transmitted","Last ping test to management_server number of transmitted packets",["name"])
258ping_management_server_received = prom.Gauge("aetheredge_ping_management_server_test_received","Last ping test to management_server number of received packets",["name"])
259ping_management_server_median = prom.Gauge("aetheredge_ping_management_server_test_median","Last ping test to management_server median value",["name"])
260ping_management_server_min = prom.Gauge("aetheredge_ping_management_server_test_min","Last ping test to management_server minimum value",["name"])
261ping_management_server_avg = prom.Gauge("aetheredge_ping_management_server_test_avg","Last ping test to management_server average",["name"])
262ping_management_server_max = prom.Gauge("aetheredge_ping_management_server_test_max","Last ping test to management_server maximum value",["name"])
263ping_management_server_stddev = prom.Gauge("aetheredge_ping_management_server_test_stddev","Last ping test to management_server standard deviation",["name"])
264
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700265# Speedtest iperf metrics
266iperf_cluster_downlink = prom.Gauge("aetheredge_iperf_cluster_downlink_test","Last iperf test downlink result",["name"])
267iperf_cluster_uplink = prom.Gauge("aetheredge_iperf_cluster_uplink_test","Last iperf test downlink result",["name"])
268
Hyunsun Moon200eba52021-04-05 21:31:54 -0700269# Signal quality metrics in CESQ format not dB
270# RSRQ: >=53 excellent, 43 ~ 53 good, 33 ~ 43 mid, <=33 bad, 0 no signal
271# RSRP: >=20 excellent, 10 ~ 20 good, 0 ~ 10 mid, 0 no signal
272signal_quality_rsrq = prom.Gauge("aetheredge_signal_quality_rsrq", "Quality of the received signal", ["name"])
273signal_quality_rsrp = prom.Gauge("aetheredge_signal_quality_rsrp", "Power of the received signal", ["name"])
274
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700275# Other metrics
Andy Bavier4021a2f2020-07-29 12:39:47 -0700276last_update = prom.Gauge("aetheredge_last_update", "Last reported test result", ["name"])
Andy Bavier614af142020-08-07 14:49:56 -0700277maint_window = prom.Gauge("aetheredge_in_maintenance_window", "Currently in a maintenance window", ["name"])
278
279def is_my_event(event, name):
280 for field in ["summary", "location", "description"]:
Andy Bavier8a5c9872020-10-21 13:17:53 -0700281 fullname = name
282 if name.startswith("ace-"):
283 fullname = "%s-%s" % (name, AETHER_ENV)
284 if fullname in getattr(event, field, ""):
Andy Bavier614af142020-08-07 14:49:56 -0700285 return True
Andy Bavier0423cbd2020-10-23 10:50:29 -0700286 if fullname in room_mapping and room_mapping[fullname] in getattr(event, field, ""):
287 return True
Andy Bavier614af142020-08-07 14:49:56 -0700288 return False
289
Andy Bavierc41cf0c2020-09-02 14:49:21 -0700290def is_naive_datetime(d):
291 return d.tzinfo is None or d.tzinfo.utcoffset(d) is None
292
293def process_all_day_events(es):
294 for event in es:
295 if event.all_day:
296 # All day events have naive datetimes, which breaks comparisons
297 pacific = pytz.timezone('US/Pacific')
298 if is_naive_datetime(event.start):
299 event.start = pacific.localize(event.start)
300 if is_naive_datetime(event.end):
301 event.end = pacific.localize(event.end)
302
Andy Bavier614af142020-08-07 14:49:56 -0700303def in_maintenance_window(events, name, now):
304 for event in events:
305 if event.start < now and event.end > now:
306 if is_my_event(event, name):
307 return True
Andy Bavier614af142020-08-07 14:49:56 -0700308 return False
309
310def pull_maintenance_events():
311 while(True):
312 now = datetime.datetime.now(pytz.utc)
313 try:
314 es = events(SECRET_ICAL_URL, start = now)
Andy Bavierc41cf0c2020-09-02 14:49:21 -0700315 process_all_day_events(es)
Andy Bavier614af142020-08-07 14:49:56 -0700316 except Exception as e:
Andy Bavier55dc5872021-05-05 11:31:42 -0700317 app.logger.error(e)
Andy Bavier614af142020-08-07 14:49:56 -0700318 else:
319 for edge in edges:
320 if 'maintenance' not in edge:
321 edge['maintenance'] = {}
322 edge['maintenance']['in_window'] = in_maintenance_window(es, edge['name'], now)
323 edge['maintenance']['last_update'] = time.time()
324 time.sleep(60)
Andy Bavier4021a2f2020-07-29 12:39:47 -0700325
326def time_out_stale_results():
327 for edge in edges:
328 time_elapsed = time.time() - edge["last_update"]
329 if time_elapsed > NO_RESULT_THRESHOLD:
330 edge['status']['control_plane'] = "no result"
331 edge['status']['user_plane'] = "no result"
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700332 edge['speedtest']['ping']['dry_run'] = {'transmitted': 0,
333 'received': 0,
334 'median': 0.0,
335 'min': 0.0,
336 'avg': 0.0,
337 'max': 0.0,
338 'stddev': 0.0}
339 edge['speedtest']['ping']['dns'] = {'transmitted': 0,
340 'received': 0,
341 'median': 0.0,
342 'min': 0.0,
Jeremy Ronquilloa944fbc2021-03-30 10:57:45 -0700343 'avg': 0.0,
344 'max': 0.0,
345 'stddev': 0.0}
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700346 edge['speedtest']['ping']['iperf_server'] = {'transmitted': 0,
347 'received': 0,
348 'median': 0.0,
349 'min': 0.0,
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700350 'avg': 0.0,
351 'max': 0.0,
352 'stddev': 0.0}
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700353 edge['speedtest']['ping']['management_server'] = {'transmitted': 0,
354 'received': 0,
355 'median': 0.0,
356 'min': 0.0,
357 'avg': 0.0,
358 'max': 0.0,
359 'stddev': 0.0}
Jeremy Ronquillo56b0a1e2021-04-09 00:26:18 -0700360 edge['speedtest']['iperf'] = {'cluster': {
361 'downlink': 0.0,
362 'uplink': 0.0
363 }
364 }
Hyunsun Moon200eba52021-04-05 21:31:54 -0700365 edge.pop('signal_quality', None)
Andy Bavier4021a2f2020-07-29 12:39:47 -0700366
Andy Baviere47157d2020-12-11 14:13:12 -0700367def remove_edge_from_metrics(name):
368 try:
369 cp_status.remove(name)
370 up_status.remove(name)
371 last_update.remove(name)
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700372 e2e_tests_ok.remove(name)
Andy Baviera0c40aa2021-03-10 12:09:12 -0700373 connect_test_ok.remove(name)
Andy Baviera0c40aa2021-03-10 12:09:12 -0700374 ping_test_ok.remove(name)
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700375 e2e_tests_down.remove(name)
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700376 except:
377 pass
378
379 try:
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700380 ping_dry_run_min.remove(name)
381 ping_dry_run_avg.remove(name)
382 ping_dry_run_max.remove(name)
383 ping_dry_run_stddev.remove(name)
384 ping_dry_run_transmitted.remove(name)
385 ping_dry_run_received.remove(name)
386 ping_dry_run_median.remove(name)
387 except:
388 pass
389
390 try:
Hyunsun Moon200eba52021-04-05 21:31:54 -0700391 ping_dns_min.remove(name)
392 ping_dns_avg.remove(name)
393 ping_dns_max.remove(name)
394 ping_dns_stddev.remove(name)
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700395 ping_dns_transmitted.remove(name)
396 ping_dns_received.remove(name)
397 ping_dns_median.remove(name)
Hyunsun Moon200eba52021-04-05 21:31:54 -0700398 except:
399 pass
400
401 try:
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700402 ping_iperf_server_min.remove(name)
403 ping_iperf_server_avg.remove(name)
404 ping_iperf_server_max.remove(name)
405 ping_iperf_server_stddev.remove(name)
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700406 ping_iperf_server_transmitted.remove(name)
407 ping_iperf_server_received.remove(name)
408 ping_iperf_server_median.remove(name)
409 except:
410 pass
411
412 try:
413 ping_management_server_min.remove(name)
414 ping_management_server_avg.remove(name)
415 ping_management_server_max.remove(name)
416 ping_management_server_stddev.remove(name)
417 ping_management_server_transmitted.remove(name)
418 ping_management_server_received.remove(name)
419 ping_management_server_median.remove(name)
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700420 except:
421 pass
422
423 try:
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700424 iperf_cluster_downlink.remove(name)
425 iperf_cluster_uplink.remove(name)
426 except:
427 pass
428
429 try:
Hyunsun Moon200eba52021-04-05 21:31:54 -0700430 signal_quality_rsrq.remove(name)
431 signal_quality_rsrp.remove(name)
432 except:
433 pass
434
435 try:
Andy Baviere47157d2020-12-11 14:13:12 -0700436 maint_window.remove(name)
437 except:
438 pass
Andy Bavier4021a2f2020-07-29 12:39:47 -0700439
440@app.route('/edges/metrics', methods=['GET'])
441def get_prometheus_metrics():
442 res = []
443 time_out_stale_results()
444 for edge in edges:
Andy Bavier8a5c9872020-10-21 13:17:53 -0700445 if edge['name'] == "ace-example":
Andy Bavier4021a2f2020-07-29 12:39:47 -0700446 continue
447
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700448 connect_status = edge['status']['control_plane']
449 ping_status = edge['status']['user_plane']
450
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700451 # Add ping dry_run latency results if available
452 try:
453 if edge['speedtest']['ping']['dry_run']['avg']:
454 ping_dry_run_min.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['min'])
455 ping_dry_run_avg.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['avg'])
456 ping_dry_run_max.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['max'])
457 ping_dry_run_stddev.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['stddev'])
458 ping_dry_run_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['transmitted'])
459 ping_dry_run_received.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['received'])
460 ping_dry_run_median.labels(edge['name']).set(edge['speedtest']['ping']['dry_run']['median'])
461 except KeyError:
462 pass
463
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700464 # Add ping dns latency results if available
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700465 try:
466 if edge['speedtest']['ping']['dns']['avg']:
467 ping_dns_min.labels(edge['name']).set(edge['speedtest']['ping']['dns']['min'])
468 ping_dns_avg.labels(edge['name']).set(edge['speedtest']['ping']['dns']['avg'])
469 ping_dns_max.labels(edge['name']).set(edge['speedtest']['ping']['dns']['max'])
470 ping_dns_stddev.labels(edge['name']).set(edge['speedtest']['ping']['dns']['stddev'])
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700471 ping_dns_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['dns']['transmitted'])
472 ping_dns_received.labels(edge['name']).set(edge['speedtest']['ping']['dns']['received'])
473 ping_dns_median.labels(edge['name']).set(edge['speedtest']['ping']['dns']['median'])
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700474 except KeyError:
475 pass
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700476
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700477 # Add ping iperf_server latency results if available
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700478 try:
479 if edge['speedtest']['ping']['iperf_server']['avg']:
480 ping_iperf_server_min.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['min'])
481 ping_iperf_server_avg.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['avg'])
482 ping_iperf_server_max.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['max'])
483 ping_iperf_server_stddev.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['stddev'])
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700484 ping_iperf_server_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['transmitted'])
485 ping_iperf_server_received.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['received'])
486 ping_iperf_server_median.labels(edge['name']).set(edge['speedtest']['ping']['iperf_server']['median'])
487 except KeyError:
488 pass
489
490 # Add ping management_server latency results if available
491 try:
492 if edge['speedtest']['ping']['management_server']['avg']:
493 ping_management_server_min.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['min'])
494 ping_management_server_avg.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['avg'])
495 ping_management_server_max.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['max'])
496 ping_management_server_stddev.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['stddev'])
497 ping_management_server_transmitted.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['transmitted'])
498 ping_management_server_received.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['received'])
499 ping_management_server_median.labels(edge['name']).set(edge['speedtest']['ping']['management_server']['median'])
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700500 except KeyError:
501 pass
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700502
503 # Add iperf bandwidth results if available
Jeremy Ronquillo5e559a82021-06-09 11:48:35 -0700504 try:
505 if edge['speedtest']['iperf']['cluster']['downlink']:
506 iperf_cluster_downlink.labels(edge['name']).set(edge['speedtest']['iperf']['cluster']['downlink'])
507 iperf_cluster_uplink.labels(edge['name']).set(edge['speedtest']['iperf']['cluster']['uplink'])
508 except KeyError:
509 pass
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800510
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700511 cp_status.labels(edge['name']).set(status_codes[connect_status])
512 up_status.labels(edge['name']).set(status_codes[ping_status])
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800513
Andy Bavier4021a2f2020-07-29 12:39:47 -0700514 last_update.labels(edge['name']).set(edge['last_update'])
Andy Bavier614af142020-08-07 14:49:56 -0700515 if 'maintenance' in edge:
516 maint_window.labels(edge['name']).set(int(edge['maintenance']['in_window']))
Andy Bavier4021a2f2020-07-29 12:39:47 -0700517
Andy Baviera0c40aa2021-03-10 12:09:12 -0700518 connect_test_ok.labels(edge['name']).set(0)
Andy Baviera0c40aa2021-03-10 12:09:12 -0700519 ping_test_ok.labels(edge['name']).set(0)
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700520 e2e_tests_ok.labels(edge['name']).set(0)
521 e2e_tests_down.labels(edge['name']).set(0)
522
523 if connect_status in ["error", "no result"] or ping_status in ["error", "no result"]:
524 e2e_tests_down.labels(edge['name']).set(1)
525 else:
526 if connect_status == "connected":
527 connect_test_ok.labels(edge['name']).set(1)
528 if ping_status == "connected":
529 ping_test_ok.labels(edge['name']).set(1)
530 if connect_status == "connected" and ping_status == "connected":
531 e2e_tests_ok.labels(edge['name']).set(1)
Andy Bavier5b4e28f2021-03-09 15:48:20 -0700532
Hyunsun Moon200eba52021-04-05 21:31:54 -0700533 if 'signal_quality' in edge.keys():
534 signal_quality_rsrq.labels(edge['name']).set(edge['signal_quality']['rsrq'])
535 signal_quality_rsrp.labels(edge['name']).set(edge['signal_quality']['rsrp'])
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800536
Andy Bavier4021a2f2020-07-29 12:39:47 -0700537 res.append(prom.generate_latest(cp_status))
538 res.append(prom.generate_latest(up_status))
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700539
540 res.append(prom.generate_latest(ping_dry_run_min))
541 res.append(prom.generate_latest(ping_dry_run_avg))
542 res.append(prom.generate_latest(ping_dry_run_max))
543 res.append(prom.generate_latest(ping_dry_run_stddev))
544 res.append(prom.generate_latest(ping_dry_run_transmitted))
545 res.append(prom.generate_latest(ping_dry_run_received))
546 res.append(prom.generate_latest(ping_dry_run_median))
547
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800548 res.append(prom.generate_latest(ping_dns_min))
549 res.append(prom.generate_latest(ping_dns_avg))
550 res.append(prom.generate_latest(ping_dns_max))
551 res.append(prom.generate_latest(ping_dns_stddev))
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700552 res.append(prom.generate_latest(ping_dns_transmitted))
553 res.append(prom.generate_latest(ping_dns_received))
554 res.append(prom.generate_latest(ping_dns_median))
555
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700556 res.append(prom.generate_latest(ping_iperf_server_min))
557 res.append(prom.generate_latest(ping_iperf_server_avg))
558 res.append(prom.generate_latest(ping_iperf_server_max))
559 res.append(prom.generate_latest(ping_iperf_server_stddev))
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700560 res.append(prom.generate_latest(ping_iperf_server_transmitted))
561 res.append(prom.generate_latest(ping_iperf_server_received))
562 res.append(prom.generate_latest(ping_iperf_server_median))
563
564 res.append(prom.generate_latest(ping_management_server_min))
565 res.append(prom.generate_latest(ping_management_server_avg))
566 res.append(prom.generate_latest(ping_management_server_max))
567 res.append(prom.generate_latest(ping_management_server_stddev))
568 res.append(prom.generate_latest(ping_management_server_transmitted))
569 res.append(prom.generate_latest(ping_management_server_received))
570 res.append(prom.generate_latest(ping_management_server_median))
571
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700572 res.append(prom.generate_latest(iperf_cluster_downlink))
573 res.append(prom.generate_latest(iperf_cluster_uplink))
Andy Bavier4021a2f2020-07-29 12:39:47 -0700574 res.append(prom.generate_latest(last_update))
Andy Bavier614af142020-08-07 14:49:56 -0700575 res.append(prom.generate_latest(maint_window))
Andy Baviera0c40aa2021-03-10 12:09:12 -0700576 res.append(prom.generate_latest(connect_test_ok))
Andy Baviera0c40aa2021-03-10 12:09:12 -0700577 res.append(prom.generate_latest(ping_test_ok))
Andy Bavier3c7b78d2021-03-11 14:16:43 -0700578 res.append(prom.generate_latest(e2e_tests_ok))
579 res.append(prom.generate_latest(e2e_tests_down))
Hyunsun Moon200eba52021-04-05 21:31:54 -0700580 res.append(prom.generate_latest(signal_quality_rsrq))
581 res.append(prom.generate_latest(signal_quality_rsrp))
Andy Bavier614af142020-08-07 14:49:56 -0700582
Andy Bavier4021a2f2020-07-29 12:39:47 -0700583 return Response(res, mimetype="text/plain")
584
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700585
586@app.route('/edges/healthz', methods=['GET'])
587def get_health():
588 return {'message': 'healthy'}
589
590
591@app.route('/edges', methods=['GET'])
592def get_edges():
Andy Bavier4021a2f2020-07-29 12:39:47 -0700593 time_out_stale_results()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700594 return jsonify({'edges': edges})
595
596
597@app.route('/edges/<string:name>', methods=['GET'])
598def get_edge(name):
Andy Bavier4021a2f2020-07-29 12:39:47 -0700599 time_out_stale_results()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700600 edge = [edge for edge in edges if edge['name'] == name]
601 if len(edge) == 0:
602 abort(404)
603 return jsonify({'edge': edge[0]})
604
605
606@app.route('/edges', methods=['POST'])
Andy Bavierf872e9a2021-03-22 12:06:25 -0700607@app.route('/testresults', methods=['POST'])
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700608def create_or_update_edge():
Andy Bavier2d60fc52021-05-04 16:13:39 -0700609 try:
610 jsonschema.validate(instance=request.json, schema=edgeSchema)
611 except jsonschema.exceptions.ValidationError as err:
Andy Bavier55dc5872021-05-05 11:31:42 -0700612 app.logger.warn(err)
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700613 abort(400)
614
615 req_edge = {
616 'name': request.json['name'],
617 'status': {
618 'control_plane': request.json['status']['control_plane'],
619 'user_plane': request.json['status']['user_plane']
620 },
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800621 'speedtest': {
622 'ping': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700623 'dry_run': {
624 'transmitted' : 0,
625 'received' : 0,
626 'median' : 0.0,
627 'min': 0.0,
628 'avg': 0.0,
629 'max': 0.0,
630 'stddev': 0.0
631 },
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800632 'dns': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700633 'transmitted' : 0,
634 'received' : 0,
635 'median' : 0.0,
Jeremy Ronquilloa944fbc2021-03-30 10:57:45 -0700636 'min': 0.0,
637 'avg': 0.0,
638 'max': 0.0,
639 'stddev': 0.0
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700640 },
641 'iperf_server': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700642 'transmitted' : 0,
643 'received' : 0,
644 'median' : 0.0,
645 'min': 0.0,
646 'avg': 0.0,
647 'max': 0.0,
648 'stddev': 0.0
649 },
650 'management_server': {
651 'transmitted' : 0,
652 'received' : 0,
653 'median' : 0.0,
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700654 'min': 0.0,
655 'avg': 0.0,
656 'max': 0.0,
657 'stddev': 0.0
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800658 }
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700659 },
660 'iperf': {
661 'cluster': {
662 'downlink': 0.0,
663 'uplink': 0.0
664 }
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800665 }
666 },
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700667 'last_update': time.time()
668 }
669
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800670 if 'speedtest' in request.json:
Jeremy Ronquilloc7434622021-04-08 21:06:00 -0700671 if 'ping' in request.json['speedtest']:
672 req_edge['speedtest']['ping'] = request.json['speedtest']['ping']
673 if 'iperf' in request.json['speedtest']:
674 req_edge['speedtest']['iperf'] = request.json['speedtest']['iperf']
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800675
Hyunsun Moon200eba52021-04-05 21:31:54 -0700676 if 'signal_quality' in request.json:
677 req_edge['signal_quality'] = request.json['signal_quality']
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800678
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700679 edge = [edge for edge in edges if edge['name'] == req_edge['name']]
680 if len(edge) == 0:
Andy Bavier55dc5872021-05-05 11:31:42 -0700681 app.logger.info("new edge request " + req_edge['name'])
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700682 edges.append(req_edge)
683 else:
684 edge[0]['status']['control_plane'] = req_edge['status']['control_plane']
685 edge[0]['status']['user_plane'] = req_edge['status']['user_plane']
Jeremy Ronquillof4200252021-02-13 16:11:04 -0800686 edge[0]['speedtest']['ping'] = req_edge['speedtest']['ping']
Jeremy Ronquillo56b0a1e2021-04-09 00:26:18 -0700687 edge[0]['speedtest']['iperf'] = req_edge['speedtest']['iperf']
Hyunsun Moon200eba52021-04-05 21:31:54 -0700688 if 'signal_quality' in req_edge.keys():
689 edge[0]['signal_quality'] = req_edge['signal_quality']
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700690 edge[0]['last_update'] = req_edge['last_update']
691
692 return jsonify({'edge': req_edge}), 201
693
694
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700695@app.route('/edges/<string:name>', methods=['DELETE'])
Andy Bavierf872e9a2021-03-22 12:06:25 -0700696@app.route('/testresults/<string:name>', methods=['DELETE'])
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700697def delete_edge(name):
Andy Bavier55dc5872021-05-05 11:31:42 -0700698 app.logger.info("delete edge request " + name)
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700699 result = False
700 for i in range(len(edges)):
701 if edges[i]['name'] == name:
702 del edges[i]
Andy Baviere47157d2020-12-11 14:13:12 -0700703 remove_edge_from_metrics(name)
Hyunsun Moon5f237ec2020-09-29 14:45:52 -0700704 result = True
705 break
706 if not result:
707 abort(404)
708 return jsonify({'result': True})
709
710
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700711if __name__ == '__main__':
Andy Bavier8a5c9872020-10-21 13:17:53 -0700712 if SECRET_ICAL_URL and AETHER_ENV:
Andy Bavier55dc5872021-05-05 11:31:42 -0700713 app.logger.info(" * Starting maintenance calendar polling thread (Aether env: %s)" % AETHER_ENV)
Andy Bavier614af142020-08-07 14:49:56 -0700714 t = threading.Thread(target=pull_maintenance_events)
715 t.start()
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700716 app.run(debug=True, host='0.0.0.0', port=80)