blob: 472cca2c0353c2c83443636bc08b88845eb7b591 [file] [log] [blame]
Hyunsun Moon4506e3e2020-10-07 01:27:12 +00001#!/usr/bin/env python3
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -07002
3# Copyright 2020-present Open Networking Foundation
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License
16
Hyunsun Moon4506e3e2020-10-07 01:27:12 +000017import sys
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -070018import os
19import time
20import requests
21import json
22import enum
Hyunsun Moon4506e3e2020-10-07 01:27:12 +000023import logging
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -070024from collections import namedtuple
25from pyadb import ADB
26
27'''
28Check Aether network operational status and report it to
29central monitoring server
30
311) check mobile connctivity after toggling the airplane mode
322) check if ping to 8.8.8.8 works
33'''
34
35CONF = json.loads(
36 open(os.getenv('CONFIG_FILE', "./config.json")).read(),
37 object_hook=lambda d: namedtuple('X', d.keys())(*d.values())
38)
39
Hyunsun Moon4506e3e2020-10-07 01:27:12 +000040logging.basicConfig(
41 filename=CONF.log_file,
42 format='%(asctime)s [%(levelname)s] %(message)s',
43 level=logging.WARN
44)
45
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -070046ADB_GET_COMMANDS = {
47 "apn_mode": "settings get global airplane_mode_on",
48 "lte_state": "dumpsys telephony.registry | grep -m1 mDataConnectionState",
Jeremy Ronquillod996b512021-02-13 13:45:47 -080049 "ping_result": "ping -c 3 8.8.8.8&>/dev/null; echo $?",
50 "ping_dns_result": "ping -c 10 " + "8.8.8.8" +
51 " | tail -1 | awk '{print $4}'"
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -070052}
53ADB_APN_COMMANDS = {
54 "home": "input keyevent 3",
55 "setting": "am start -a android.settings.AIRPLANE_MODE_SETTINGS",
56 "toggle": "input tap " + \
57 CONF.adb.apn_mode_toggle_location.x + " " + \
58 CONF.adb.apn_mode_toggle_location.y
59}
60
61
62class State(enum.Enum):
63 error = "-1"
64 disconnected = "0"
65 connecting = "1"
66 connected = "2"
67
68 @classmethod
69 def has_value(cls, value):
70 return value in cls._value2member_map_
71
72
73edge_status = {
74 'name': CONF.edge_name,
75 'status': {
76 'control_plane': None,
77 'user_plane': 'connected'
Jeremy Ronquillod996b512021-02-13 13:45:47 -080078 },
79 'speedtest': {
80 'ping': {
81 'dns': {
Jeremy Ronquilloa944fbc2021-03-30 10:57:45 -070082 'min': 0.0,
83 'avg': 0.0,
84 'max': 0.0,
85 'stddev': 0.0
Jeremy Ronquillod996b512021-02-13 13:45:47 -080086 }
87 }
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -070088 }
89}
90
91
92def _run_adb_shell(adb, command):
Hyunsun Moon53097ea2020-09-04 17:20:29 -070093 result, error = adb.shell_command(command)
Hyunsun Moon4506e3e2020-10-07 01:27:12 +000094 # error is returned even when succeeded
95 # so ignore error value here
96 if result:
97 logging.debug(result)
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -070098 time.sleep(2)
99 result = result[0] if result is not None else None
100 return True, result
101
102
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700103def get_control_plane_state(adb):
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700104 '''
105 check aether control plane works by toggling airplane mode
106 '''
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700107 # get the current airplane mode
108 success, result = _run_adb_shell(adb, ADB_GET_COMMANDS['apn_mode'])
109 if not success or result is None:
110 return State.error, result
111 apn_mode_on = True if result == "1" else False
112
113 # toggle the airplane mode
114 for command in ADB_APN_COMMANDS.values():
115 success, result = _run_adb_shell(adb, command)
116 if not success:
117 return State.error, result
118 if not apn_mode_on:
119 success, result = _run_adb_shell(adb, ADB_APN_COMMANDS['toggle'])
120 if not success:
121 return State.error, result
122
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700123 # get connection state
124 state = State.connecting.value
Hyunsun Moona40bda52021-01-21 12:04:43 -0800125 retry_count = 10
126 while retry_count > 0:
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700127 success, result = _run_adb_shell(adb, ADB_GET_COMMANDS['lte_state'])
128 if not success or result is None:
Hyunsun Moona40bda52021-01-21 12:04:43 -0800129 continue
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700130 state = result.split("=")[1]
Hyunsun Moona40bda52021-01-21 12:04:43 -0800131 if state == State.connected.value:
132 break
Hyunsun Moon659f2812021-01-21 13:43:49 -0800133 time.sleep(1)
Hyunsun Moona40bda52021-01-21 12:04:43 -0800134 retry_count -= 1
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700135
136 if not State.has_value(state):
137 return State.error, None
138 return State(state), None
139
140
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700141def get_user_plane_state(adb):
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700142 '''
143 checks aether user plane connectivity with ping to 8.8.8.8
144 '''
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700145 success, result = _run_adb_shell(adb, ADB_GET_COMMANDS['ping_result'])
146 if not success or result is None:
147 return State.error, result
148
149 state = State.connected if result == "0" else State.disconnected
150 return state, None
151
152
Jeremy Ronquillod996b512021-02-13 13:45:47 -0800153def run_ping_test(adb, ip, count):
154 '''
155 Runs the ping test
156 Input: IP to ping, # times to ping
157 Returns: dict of the min/avg/max/stddev numbers from the ping command result
158 '''
Jeremy Ronquilloa944fbc2021-03-30 10:57:45 -0700159 result = {'min': 0.0,
160 'avg': 0.0,
161 'max': 0.0,
162 'stddev': 0.0}
Jeremy Ronquillod996b512021-02-13 13:45:47 -0800163 try:
164 commandResult, commandOutput = _run_adb_shell(adb, ADB_GET_COMMANDS['ping_dns_result'])
165 pingResult = commandOutput.split("/")
166 result = {'min': float(pingResult[0]),
167 'avg': float(pingResult[1]),
168 'max': float(pingResult[2]),
169 'stddev': float(pingResult[3])}
170 except Exception as e:
171 logging.error("Ping test failed for " + ip + ": %s", e)
172 return result
173
174
175def get_ping_test(adb):
176 '''
177 Each ping test result saves the min/avg/max/stddev to dict.
178 1) Performs ping test to Google Public DNS for 10 iterations.
179 2) # TODO: Performs ping to device on network.
180 '''
181 speedtest_ping = {}
182 speedtest_ping['dns'] = run_ping_test(adb, "8.8.8.8", 10)
183 return speedtest_ping
184
185
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700186def report_aether_network_state():
187 '''
188 report the aether network state to the monitoring server
189 '''
Hyunsun Moon4506e3e2020-10-07 01:27:12 +0000190 logging.info("Sending report %s", edge_status)
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700191 try:
192 result = requests.post(CONF.report_url, json=edge_status)
193 except requests.exceptions.ConnectionError:
Hyunsun Moon4506e3e2020-10-07 01:27:12 +0000194 logging.error("Failed to report for %s", e)
195 pass
196 try:
197 result.raise_for_status()
198 except requests.exceptions.HTTPError as e:
199 logging.error("Failed to report for %s", e)
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700200 pass
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700201
202
Hyunsun Moon4506e3e2020-10-07 01:27:12 +0000203def main():
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700204 adb = ADB()
205 if adb.set_adb_path(CONF.adb.path) is False:
Hyunsun Moon4506e3e2020-10-07 01:27:12 +0000206 logging.error(CONF.adb.path + " not found")
207 sys.exit(1)
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700208
209 dev = adb.get_devices()
210 if len(dev) == 0:
Hyunsun Moon4506e3e2020-10-07 01:27:12 +0000211 logging.error("No device found")
212 sys.exit(1)
213
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700214 adb.set_target_device(dev[0])
Hyunsun Moon4506e3e2020-10-07 01:27:12 +0000215 success, result = _run_adb_shell(adb, "svc data enable")
216 if not success:
217 logging.error("Failed to turn data on")
218 sys.exit(1)
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700219
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700220 while True:
Hyunsun Moonda22c762021-01-22 13:00:24 -0800221 _run_adb_shell(adb, "svc power stayon true")
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700222 cp_state, err = get_control_plane_state(adb)
223 up_state, err = get_user_plane_state(adb)
Jeremy Ronquillod996b512021-02-13 13:45:47 -0800224 speedtest_ping = get_ping_test(adb)
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700225
226 edge_status['status']['control_plane'] = cp_state.name
227 edge_status['status']['user_plane'] = up_state.name
Jeremy Ronquillod996b512021-02-13 13:45:47 -0800228 edge_status['speedtest']['ping'] = speedtest_ping
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700229
230 report_aether_network_state()
Hyunsun Moonda22c762021-01-22 13:00:24 -0800231 _run_adb_shell(adb, "svc power stayon false")
232
Hyunsun Moon53097ea2020-09-04 17:20:29 -0700233 time.sleep(CONF.report_interval)
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700234
235
Hyunsun Moonf32ae9a2020-05-28 13:17:45 -0700236if __name__ == "__main__":
237 main()