blob: 03156556e9d899ff7173bd60663ed4e90710f852 [file] [log] [blame]
Hyunsun Moondb72b8f2020-11-02 18:03:39 -08001#!/usr/bin/env python3
Hyunsun Moon53097ea2020-09-04 17:20:29 -07002
3# Copyright 2020-present Open Networking Foundation
4#
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -07005# SPDX-License-Identifier: LicenseRef-ONF-Member-Only-1.0
Hyunsun Moon53097ea2020-09-04 17:20:29 -07006
7import sys
8import os
9import json
10import logging
11import enum
Jeremy Ronquillo8d108652021-11-22 17:34:58 -080012import pycurl
Hyunsun Moon53097ea2020-09-04 17:20:29 -070013import time
14import serial
Jeremy Ronquillod996b512021-02-13 13:45:47 -080015import subprocess
Jeremy Ronquilloef17e362021-11-08 10:56:42 -080016import time
Hyunsun Moon53097ea2020-09-04 17:20:29 -070017from collections import namedtuple
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -070018from statistics import median
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -080019import xml.etree.ElementTree as ET
Hyunsun Moon53097ea2020-09-04 17:20:29 -070020
21'''
Jeremy Ronquilloa944fbc2021-03-30 10:57:45 -070022"Simple" script that checks Aether network operational status periodically
Hyunsun Moon53097ea2020-09-04 17:20:29 -070023by controlling the attached 4G/LTE modem with AT commands and
24report the result to the central monitoring server.
25'''
26
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -070027# Parse config with backwards compatibility with config.json pre 0.6.6
28config_file_contents = open(os.getenv('CONFIG_FILE', "./config.json")).read()
29config_file_contents = config_file_contents.replace("user_plane_ping_test", "dns")
30config_file_contents = config_file_contents.replace("speedtest_iperf", "iperf_server")
Jeremy Ronquilloc03ba682021-10-06 10:27:09 -070031config_file_contents = config_file_contents.replace("\"speedtest_ping_dns\": \"1.1.1.1\",", "")
Hyunsun Moon53097ea2020-09-04 17:20:29 -070032CONF = json.loads(
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -070033 config_file_contents, object_hook=lambda d: namedtuple('X', d.keys())(*d.values())
Hyunsun Moon53097ea2020-09-04 17:20:29 -070034)
35
36logging.basicConfig(
37 filename=CONF.log_file,
38 format='%(asctime)s [%(levelname)s] %(message)s',
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -070039 level=logging.getLevelName(CONF.log_level)
Hyunsun Moon53097ea2020-09-04 17:20:29 -070040)
41
Hyunsun Moon53097ea2020-09-04 17:20:29 -070042
43class State(enum.Enum):
44 error = "-1"
45 disconnected = "0"
46 connected = "1"
47
48 @classmethod
49 def has_value(cls, value):
50 return value in cls._value2member_map_
51
52
53class Modem():
54 log = logging.getLogger('aether_edge_monitoring.Modem')
55
56 read_timeout = 0.1
57
58 def __init__(self, port, baudrate):
59 self.port = port
60 self.baudrate = baudrate
61 self._response = None
62
Don Newtonbd91ae22021-05-11 14:58:18 -070063 def get_modem_port(self):
64 cmd = "ls " + CONF.modem.port
65 sp = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,
66 stderr=subprocess.PIPE, universal_newlines=True)
67 rc = sp.wait()
68 ret,err = sp.communicate()
69 if err != "" :
70 logging.error("unable to find serial port " + err)
71
72 ret = ret.replace(CONF.modem.port,"").strip()
73 logging.info("Modem.get_modem_port found " + ret)
74 return ret
75
Hyunsun Moon53097ea2020-09-04 17:20:29 -070076 def connect(self):
Don Newtonbd91ae22021-05-11 14:58:18 -070077 self.port=self.get_modem_port()
78 logging.info("modem.connect Port: %s, BaudRate: %i",self.port,self.baudrate)
Hyunsun Moon53097ea2020-09-04 17:20:29 -070079 self.serial = serial.Serial(
80 port=self.port,
81 baudrate=self.baudrate,
82 timeout=1)
83
84 def _write(self, command):
85 if self.serial.inWaiting() > 0:
86 self.serial.flushInput()
87
88 self._response = b""
89
90 self.serial.write(bytearray(command + "\r", "ascii"))
91 read = self.serial.inWaiting()
92 while True:
93 if read > 0:
94 self._response += self.serial.read(read)
95 else:
96 time.sleep(self.read_timeout)
97 read = self.serial.inWaiting()
98 if read == 0:
99 break
100 return self._response.decode("ascii").replace('\r\n', ' ')
101
102 def write(self, command, wait_resp=True):
103 response = self._write(command)
104 self.log.debug("%s: %s", command, response)
105
106 if wait_resp and "ERROR" in response:
107 return False, None
108 return True, response
109
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700110 def get_state(self):
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700111 success, result = self.write('AT+CGATT?')
112 if not success or 'CGATT:' not in result:
113 return State.error
114 state = result.split('CGATT:')[1].split(' ')[0]
115 return State(state)
116
117 def close(self):
118 self.serial.close()
119
120
121def get_control_plane_state(modem):
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700122 # Disable radio fuction
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700123 # "echo" works more stable than serial for this action
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700124 try:
Don Newtonbd91ae22021-05-11 14:58:18 -0700125 logging.debug("echo 'AT+CFUN=0' > " + modem.port)
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700126 subprocess.check_output(
Don Newtonbd91ae22021-05-11 14:58:18 -0700127 "echo 'AT+CFUN=0' > " + modem.port, shell=True)
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700128 except subprocess.CalledProcessError as e:
129 logging.error("Write 'AT+CFUN=0' failed")
130 return State.error
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700131
132 # Wait until the modem is fully disconnected
133 retry = 0
134 state = None
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700135 while retry < CONF.detach_timeout:
136 state = modem.get_state()
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700137 if state is State.disconnected:
138 break
139 time.sleep(1)
140 retry += 1
141
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700142 if state is not State.disconnected:
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700143 logging.error("Failed to disconnect")
144 return State.error
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700145
146 time.sleep(2)
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700147 # Enable radio function
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700148 # "echo" works more stable than serial for this action
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700149 try:
Don Newtonbd91ae22021-05-11 14:58:18 -0700150 logging.debug("echo 'AT+CFUN=1' > " + modem.port)
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700151 subprocess.check_output(
Don Newtonbd91ae22021-05-11 14:58:18 -0700152 "echo 'AT+CFUN=1' > " + modem.port, shell=True)
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700153 except subprocess.CalledProcessError as e:
154 logging.error("Write 'AT+CFUN=1' failed")
155 return State.error
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700156
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700157 # Wait attach_timeout sec for the modem to be fully connected
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700158 retry = 0
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700159 while retry < CONF.attach_timeout:
160 state = modem.get_state()
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700161 if state is State.connected:
162 break
163 time.sleep(1)
164 retry += 1
165 # CGATT sometimes returns None
166 if state is State.error:
167 state = State.disconnected
168
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700169 return state
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700170
171
172def get_user_plane_state(modem):
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700173 try:
174 subprocess.check_output(
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700175 "ping -c 3 " + CONF.ips.dns + ">/dev/null 2>&1",
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700176 shell=True)
177 return State.connected
178 except subprocess.CalledProcessError as e:
179 logging.warning("User plane test failed")
180 return State.disconnected
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700181
182
Jeremy Ronquillod996b512021-02-13 13:45:47 -0800183def run_ping_test(ip, count):
184 '''
185 Runs the ping test
186 Input: IP to ping, # times to ping
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700187 Returns: Transmitted packets
188 Received packets
189 Median ping ms
190 Min ping ms
191 Avg ping ms
192 Max ping ms
193 Std Dev ping ms
Jeremy Ronquillod996b512021-02-13 13:45:47 -0800194 '''
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700195 result = {'transmitted': 0,
196 'received': 0,
197 'median': 0.0,
198 'min': 0.0,
Jeremy Ronquilloa944fbc2021-03-30 10:57:45 -0700199 'avg': 0.0,
200 'max': 0.0,
201 'stddev': 0.0}
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700202 if not ip:
Jeremy Ronquillo115c5e32021-09-30 11:15:56 -0700203 return result, True
Jeremy Ronquillod996b512021-02-13 13:45:47 -0800204 try:
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700205 pingOutput = subprocess.check_output(
206 "ping -c " + str(count) + " " + \
207 ip, shell=True).decode("UTF-8").split()
208 result['transmitted'] = int(pingOutput[-15])
209 result['received'] = int(pingOutput[-12])
210 if result['received'] > 0:
211 pingValues = []
212
213 # Hack for getting all ping values for median
214 for word in pingOutput:
215 if "time=" in word:
216 pingValues.append(float(word.split("=")[1]))
217 result['median'] = round(median(pingValues), 3)
218
219 pingResult = pingOutput[-2].split('/')
220 result['min'] = float(pingResult[0])
221 result['avg'] = float(pingResult[1])
222 result['max'] = float(pingResult[2])
223 result['stddev'] = float(pingResult[3])
224 else:
225 logging.error("No packets received during ping " + ip)
226 return result, False
Jeremy Ronquillod996b512021-02-13 13:45:47 -0800227 except Exception as e:
228 logging.error("Ping test failed for " + ip + ": %s", e)
Jeremy Ronquillo115c5e32021-09-30 11:15:56 -0700229 return result, False
230 return result, True
Jeremy Ronquillod996b512021-02-13 13:45:47 -0800231
232
233def get_ping_test(modem):
234 '''
Jeremy Ronquillo677c8832021-04-06 13:53:36 -0700235 Prepares the ping test.
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700236 Runs ping tests from 'ips' entry in config.json in order.
237 Note: 'dry_run' IP entry runs 3 ping iterations. Other IPs run 10 iterations.
Jeremy Ronquillod996b512021-02-13 13:45:47 -0800238 '''
239 speedtest_ping = {}
Jeremy Ronquillo115c5e32021-09-30 11:15:56 -0700240 status = True
241 ping_test_passed = True
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700242
243 for i in range(0, len(CONF.ips)):
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700244 count = 10 if CONF.ips._fields[i] != "dry_run" else 3
245 speedtest_ping[CONF.ips._fields[i]], status = run_ping_test(CONF.ips[i], count)
Jeremy Ronquillo115c5e32021-09-30 11:15:56 -0700246 if not status:
247 ping_test_passed = False
248 logging.error("Ping test failed. Not running further tests.")
249 return speedtest_ping, ping_test_passed
Jeremy Ronquillod996b512021-02-13 13:45:47 -0800250
Jeremy Ronquillo677c8832021-04-06 13:53:36 -0700251def run_iperf_test(ip, port, time_duration, is_downlink):
252 '''
253 Runs iperf test to specified IP in the config file.
254 - Runs for 10 seconds (10 iterations)
255 - Retrieves downlink and uplink test results from json output
256 '''
257 result = 0.0
Jeremy Ronquilloc03ba682021-10-06 10:27:09 -0700258 if not ip or port == 0:
Jeremy Ronquillo677c8832021-04-06 13:53:36 -0700259 return result
Jeremy Ronquillo115c5e32021-09-30 11:15:56 -0700260 maxRetries = 2
Jeremy Ronquillo79c3e672021-09-03 12:54:55 -0700261 err = None
Jeremy Ronquillo012ac662021-08-06 12:13:43 -0700262 for _ in range(0, maxRetries):
263 try:
264 iperfResult = json.loads(subprocess.check_output(
265 "iperf3 -c " + ip +
266 " -p " + str(port) +
267 " -t " + str(time_duration) +
268 (" -R " if is_downlink else "") +
269 " --json", shell=True).decode("UTF-8"))
270 received_mbps = iperfResult['end']['sum_received']['bits_per_second'] / 1000000
271 sent_mbps = iperfResult['end']['sum_sent']['bits_per_second'] / 1000000.0
272 result = received_mbps if is_downlink else sent_mbps
273 return result
274 except Exception as e:
Jeremy Ronquillo79c3e672021-09-03 12:54:55 -0700275 err = e
Jeremy Ronquillo012ac662021-08-06 12:13:43 -0700276 time.sleep(5)
277 pass
Jeremy Ronquillo79c3e672021-09-03 12:54:55 -0700278 logging.error("After " + str(maxRetries) + " retries, iperf test failed for " + ip + ": %s", err)
Jeremy Ronquillo677c8832021-04-06 13:53:36 -0700279 return result
280
281
282def get_iperf_test(modem):
283 '''
284 Prepares the iperf test.
285 '''
Jeremy Ronquilloef17e362021-11-08 10:56:42 -0800286 global hour_iperf_scheduled_time_last_ran
Jeremy Ronquillo677c8832021-04-06 13:53:36 -0700287 speedtest_iperf = {}
288 speedtest_iperf['cluster'] = {}
Jeremy Ronquilloef17e362021-11-08 10:56:42 -0800289
290 if "iperf_schedule" in CONF._fields and len(CONF.iperf_schedule) > 0:
291 if int(time.strftime("%H")) not in CONF.iperf_schedule: # not in the schedule
292 hour_iperf_scheduled_time_last_ran = -1
293 return None
294 elif int(time.strftime("%H")) == hour_iperf_scheduled_time_last_ran: # already ran this hour
295 return None
296 hour_iperf_scheduled_time_last_ran = int(time.strftime("%H"))
297
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700298 speedtest_iperf['cluster']['downlink'] = run_iperf_test(CONF.ips.iperf_server, CONF.iperf_port, 10, True)
299 speedtest_iperf['cluster']['uplink'] = run_iperf_test(CONF.ips.iperf_server, CONF.iperf_port, 10, False)
Jeremy Ronquillo677c8832021-04-06 13:53:36 -0700300
301 return speedtest_iperf
302
Jeremy Ronquillod996b512021-02-13 13:45:47 -0800303
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700304def get_signal_quality(modem):
305 success, result = modem.write('AT+CESQ')
Don Newtonbd91ae22021-05-11 14:58:18 -0700306 logging.debug("get_signal_quality success %i result %s",success,result)
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700307 if not success or 'CESQ: ' not in result:
308 logging.error("Failed to get signal quality")
Don Newtonbd91ae22021-05-11 14:58:18 -0700309
310 return {'rsrq':0, 'rsrp':0}
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700311
312 logging.debug("%s", result)
313 tmp_rsrq = result.split('CESQ:')[1].split(',')[4]
314 tmp_rsrp = result.split('CESQ:')[1].split(',')[5]
315
316 rsrq = int(tmp_rsrq.strip())
317 rsrp = int(tmp_rsrp.strip().split(' ')[0])
318 result = {
319 'rsrq': 0 if rsrq is 255 else rsrq,
320 'rsrp': 0 if rsrp is 255 else rsrp
321 }
322
323 return result
324
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800325def get_dongle_stats(modem):
326 result = {'SuccessfulFetch' : False}
327 XMLkeys = ["MAC",
328 "PLMNStatus",
329 "UICCStatus",
330 "IMEI",
331 "IMSI",
332 "PLMNSelected",
333 "MCC",
334 "MNC",
335 "PhyCellID",
336 "CellGlobalID",
337 "Band",
338 "EARFCN",
339 "BandWidth",
340 "ServCellState",
341 "Connection",
342 "IPv4Addr"]
343 dongleStatsXML = None
344 try:
345 dongleStatsXML = ET.fromstring(subprocess.check_output("curl -u admin:admin 'http://192.168.0.1:8080/cgi-bin/ltestatus.cgi?Command=Status'", shell=True).decode("UTF-8"))
346 except Exception as e:
347 logging.error("Failed to fetch dongle stats from URL: " + str(e))
348 return result
349 try:
350 for key in XMLkeys:
351 try:
352 result[key] = dongleStatsXML.find(key).text
353 except AttributeError as e:
354 logging.warn("Failed to find " + key + " in XML.")
355 result[key] = ""
356 result["SuccessfulFetch"] = True
357 except Exception as e:
358 logging.error("Failed to fetch dongle stats from XML: " + str(e))
359 return result
360 return result
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700361
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800362
363def report_status(signal_quality, dongle_stats, cp_state=None, up_state=None, speedtest_ping=None, speedtest_iperf=None):
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700364 report = {
365 'name': CONF.edge_name,
366 'status': {
367 'control_plane': "disconnected",
368 'user_plane': "disconnected"
369 },
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800370 'dongle_stats': {
371 'SuccessfulFetch' : False
372 },
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700373 'speedtest': {
374 'ping': {
375 'dns': {
Jeremy Ronquilloe0a8b422021-11-02 12:49:15 -0700376 'transmitted' : 0,
377 'received' : 0,
378 'median' : 0.0,
Jeremy Ronquillo6e352b72021-06-08 10:33:25 -0700379 'min': 0.0,
380 'avg': 0.0,
381 'max': 0.0,
382 'stddev': 0.0
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700383 }
Jeremy Ronquillo677c8832021-04-06 13:53:36 -0700384 },
385 'iperf': {
386 'cluster': {
387 'downlink': 0.0,
388 'uplink': 0.0
389 }
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700390 }
391 },
392 'signal_quality': {
393 'rsrq': 0,
394 'rsrp': 0
395 }
396 }
397
398 if cp_state is not None:
399 report['status']['control_plane'] = cp_state.name
400 if up_state is not None:
401 report['status']['user_plane'] = up_state.name
402 if speedtest_ping is not None:
403 report['speedtest']['ping'] = speedtest_ping
Jeremy Ronquillo677c8832021-04-06 13:53:36 -0700404 if speedtest_iperf is not None:
405 report['speedtest']['iperf'] = speedtest_iperf
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700406 report['signal_quality'] = signal_quality
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800407 report['dongle_stats'] = dongle_stats
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700408
409 logging.info("Sending report %s", report)
Don Newtonbd91ae22021-05-11 14:58:18 -0700410 global cycles
411 cycles += 1
412 logging.info("Number of cycles since modem restart %i",cycles)
Jeremy Ronquillo8d108652021-11-22 17:34:58 -0800413
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700414 try:
Jeremy Ronquillo8d108652021-11-22 17:34:58 -0800415 c = pycurl.Curl()
416 c.setopt(pycurl.URL, CONF.report_url)
417 c.setopt(pycurl.POST, True)
418 c.setopt(pycurl.HTTPHEADER, ['Content-Type: application/json'])
419 c.setopt(pycurl.TIMEOUT, 10)
420 c.setopt(pycurl.POSTFIELDS, json.dumps(report))
421 if "report_in_band" in CONF._fields and CONF.report_in_band and \
422 "iface" in CONF.modem._fields and CONF.modem.iface:
423 c.setopt(pycurl.INTERFACE, CONF.modem.iface)
424 elif "report_iface" in CONF._fields and CONF.report_iface:
425 c.setopt(pycurl.INTERFACE, CONF.report_iface)
426 c.perform()
427 c.close()
428 except Exception as e:
429 logging.error("Failed to send report: " + str(e))
430
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700431 time.sleep(CONF.report_interval)
Jeremy Ronquillo82a14612021-10-08 12:08:20 -0700432
Don Newtonbd91ae22021-05-11 14:58:18 -0700433def reset_usb():
Jeremy Ronquillo82a14612021-10-08 12:08:20 -0700434 try:
435 # Attempt to run uhubctl
436 if (int(subprocess.call("which uhubctl",shell=True)) == 0):
437 cmd = "/usr/sbin/uhubctl -a 0 -l 2" # -a 0 = action is shutdown -l 2 location = bus 2 on pi controls power to all hubs
438 ret = subprocess.call(cmd,shell=True)
439 logging.info("Shutting down usb hub 2 results %s" , ret)
440 time.sleep(10)# let power down process settle out
441 cmd = "/usr/sbin/uhubctl -a 1 -l 2" # -a 1 = action is start -l 2 location = bus 2 on pi controls power to all hubs
442 ret = subprocess.call(cmd,shell=True)
443 logging.info("Starting up usb hub 2 results %s" , ret)
444 time.sleep(10) #allow dbus to finish
445 global cycles
446 cycles = 0
447 else:
448 reboot(120)
449 except Exception as e:
450 logging.error("Failed to run uhubctl: %s", e)
451 reboot(120)
452
453def reboot(delay):
454 logging.error("Failed to run uhubctl. Reboot system in " + str(delay) + " second(s).")
455 time.sleep(delay)
456 subprocess.check_output("sudo shutdown -r now", shell=True)
Don Newtonbd91ae22021-05-11 14:58:18 -0700457
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700458def main():
Don Newtonbd91ae22021-05-11 14:58:18 -0700459 global cycles
Jeremy Ronquilloef17e362021-11-08 10:56:42 -0800460 global hour_iperf_scheduled_time_last_ran
Don Newtonbd91ae22021-05-11 14:58:18 -0700461 cycles = 0
Jeremy Ronquilloef17e362021-11-08 10:56:42 -0800462 hour_iperf_scheduled_time_last_ran = -1
Jeremy Ronquillo8d108652021-11-22 17:34:58 -0800463
464 try:
465 if "report_in_band" in CONF._fields and \
466 "iface" in CONF.modem._fields and CONF.modem.iface:
467 if CONF.report_in_band: # need to add default gateway if reporting in-band
468 subprocess.check_output("sudo route add default gw " + CONF.modem.ip_addr + " " + CONF.modem.iface + " || true", shell=True)
469 else:
470 subprocess.check_output("sudo route del default gw " + CONF.modem.ip_addr + " " + CONF.modem.iface + " || true", shell=True)
471 except Exception as e:
472 logging.error("Failed to change default route for modem: " + str(e))
473
Don Newtonbd91ae22021-05-11 14:58:18 -0700474 for ip in CONF.ips:
475 if not ip:
476 continue
477 try:
478 subprocess.check_output("sudo ip route replace {}/32 via {}".format(
479 ip, CONF.modem.ip_addr), shell=True)
480 except subprocess.CalledProcessError as e:
Jeremy Ronquilloef17e362021-11-08 10:56:42 -0800481 logging.error("Failed to add routes: " + str(e.returncode) + str(e.output))
Jeremy Ronquillo82a14612021-10-08 12:08:20 -0700482 time.sleep(10) # Sleep for 10 seconds before retry
Don Newtonbd91ae22021-05-11 14:58:18 -0700483 sys.exit(1)
484
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700485 modem = Modem(CONF.modem.port, CONF.modem.baud)
486 try:
487 modem.connect()
488 except serial.serialutil.SerialException as e:
489 logging.error("Failed to connect the modem for %s", e)
Hyunsun Moon2b7d3e12021-01-04 13:30:19 -0800490 sys.exit(1)
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700491
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700492 while True:
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800493 dongle_stats = get_dongle_stats(modem)
494
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700495 signal_quality = get_signal_quality(modem)
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700496
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700497 cp_state = get_control_plane_state(modem)
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700498 if cp_state is State.error:
499 logging.error("Modem is in error state.")
Don Newtonbd91ae22021-05-11 14:58:18 -0700500 reset_usb()
Hyunsun Moonf4242372020-10-04 23:32:38 -0700501 sys.exit(1)
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700502 if cp_state is State.disconnected:
503 # Failed to attach, don't need to run other tests
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800504 report_status(signal_quality, dongle_stats)
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700505 continue
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700506
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700507 up_state = get_user_plane_state(modem)
508 if up_state is State.disconnected:
Jeremy Ronquillo677c8832021-04-06 13:53:36 -0700509 # Basic user plane test failed, don't need to run the rest of tests
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800510 report_status(signal_quality, dongle_stats, cp_state)
Hyunsun Moon5cd1eec2021-04-02 22:33:42 -0700511 continue
512
Jeremy Ronquillo115c5e32021-09-30 11:15:56 -0700513 speedtest_ping, speedtest_status = get_ping_test(modem)
514 if speedtest_status:
515 speedtest_iperf = get_iperf_test(modem)
516 else:
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800517 report_status(signal_quality, dongle_stats, cp_state, up_state, speedtest_ping)
Jeremy Ronquillo115c5e32021-09-30 11:15:56 -0700518 continue
Jeremy Ronquillo677c8832021-04-06 13:53:36 -0700519
Jeremy Ronquilloc45955a2021-11-09 12:04:57 -0800520 report_status(signal_quality, dongle_stats, cp_state, up_state, speedtest_ping, speedtest_iperf)
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700521
522 modem.close()
523
524
525if __name__ == "__main__":
526 main()