Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Side by Side Diff: build/android/buildbot/bb_device_status_check.py

Issue 2056343003: [Android] Use devil for status and recovery in bb_device_status_check. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright 2013 The Chromium Authors. All rights reserved. 3 # Copyright 2013 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 """A class to keep track of devices across builds and report state.""" 7 """A class to keep track of devices across builds and report state."""
8 8
9 import argparse 9 import argparse
10 import json 10 import json
11 import logging 11 import logging
12 import os 12 import os
13 import psutil
14 import re 13 import re
15 import signal
16 import sys 14 import sys
17 15
18 sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 16 sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
19 import devil_chromium 17 import devil_chromium
20 from devil.android import battery_utils
21 from devil.android import device_blacklist 18 from devil.android import device_blacklist
22 from devil.android import device_errors
23 from devil.android import device_list 19 from devil.android import device_list
24 from devil.android import device_utils 20 from devil.android import device_utils
25 from devil.android.sdk import adb_wrapper 21 from devil.android.tools import device_recovery
22 from devil.android.tools import device_status
26 from devil.constants import exit_codes 23 from devil.constants import exit_codes
27 from devil.utils import lsusb 24 from devil.utils import lsusb
28 from devil.utils import reset_usb
29 from devil.utils import run_tests_helper 25 from devil.utils import run_tests_helper
30 from pylib.constants import host_paths 26 from pylib.constants import host_paths
31 27
32 _RE_DEVICE_ID = re.compile(r'Device ID = (\d+)') 28 _RE_DEVICE_ID = re.compile(r'Device ID = (\d+)')
33 29
34 30
35 def KillAllAdb():
36 def GetAllAdb():
37 for p in psutil.process_iter():
38 try:
39 if 'adb' in p.name:
40 yield p
41 except (psutil.NoSuchProcess, psutil.AccessDenied):
42 pass
43
44 for sig in [signal.SIGTERM, signal.SIGQUIT, signal.SIGKILL]:
45 for p in GetAllAdb():
46 try:
47 logging.info('kill %d %d (%s [%s])', sig, p.pid, p.name,
48 ' '.join(p.cmdline))
49 p.send_signal(sig)
50 except (psutil.NoSuchProcess, psutil.AccessDenied):
51 pass
52 for p in GetAllAdb():
53 try:
54 logging.error('Unable to kill %d (%s [%s])', p.pid, p.name,
55 ' '.join(p.cmdline))
56 except (psutil.NoSuchProcess, psutil.AccessDenied):
57 pass
58
59
60 def _IsBlacklisted(serial, blacklist):
61 return blacklist and serial in blacklist.Read()
62
63
64 def _BatteryStatus(device, blacklist):
65 battery_info = {}
66 try:
67 battery = battery_utils.BatteryUtils(device)
68 battery_info = battery.GetBatteryInfo(timeout=5)
69 battery_level = int(battery_info.get('level', 100))
70
71 if battery_level < 15:
72 logging.error('Critically low battery level (%d)', battery_level)
73 battery = battery_utils.BatteryUtils(device)
74 if not battery.GetCharging():
75 battery.SetCharging(True)
76 if blacklist:
77 blacklist.Extend([device.adb.GetDeviceSerial()], reason='low_battery')
78
79 except device_errors.CommandFailedError:
80 logging.exception('Failed to get battery information for %s',
81 str(device))
82
83 return battery_info
84
85
86 def _IMEISlice(device):
87 imei_slice = ''
88 try:
89 for l in device.RunShellCommand(['dumpsys', 'iphonesubinfo'],
90 check_return=True, timeout=5):
91 m = _RE_DEVICE_ID.match(l)
92 if m:
93 imei_slice = m.group(1)[-6:]
94 except device_errors.CommandFailedError:
95 logging.exception('Failed to get IMEI slice for %s', str(device))
96
97 return imei_slice
98
99
100 def DeviceStatus(devices, blacklist):
101 """Generates status information for the given devices.
102
103 Args:
104 devices: The devices to generate status for.
105 blacklist: The current device blacklist.
106 Returns:
107 A dict of the following form:
108 {
109 '<serial>': {
110 'serial': '<serial>',
111 'adb_status': str,
112 'usb_status': bool,
113 'blacklisted': bool,
114 # only if the device is connected and not blacklisted
115 'type': ro.build.product,
116 'build': ro.build.id,
117 'build_detail': ro.build.fingerprint,
118 'battery': {
119 ...
120 },
121 'imei_slice': str,
122 'wifi_ip': str,
123 },
124 ...
125 }
126 """
127 adb_devices = {
128 a[0].GetDeviceSerial(): a
129 for a in adb_wrapper.AdbWrapper.Devices(desired_state=None, long_list=True)
130 }
131 usb_devices = set(lsusb.get_android_devices())
132
133 def blacklisting_device_status(device):
134 serial = device.adb.GetDeviceSerial()
135 adb_status = (
136 adb_devices[serial][1] if serial in adb_devices
137 else 'missing')
138 usb_status = bool(serial in usb_devices)
139
140 device_status = {
141 'serial': serial,
142 'adb_status': adb_status,
143 'usb_status': usb_status,
144 }
145
146 if not _IsBlacklisted(serial, blacklist):
147 if adb_status == 'device':
148 try:
149 build_product = device.build_product
150 build_id = device.build_id
151 build_fingerprint = device.GetProp('ro.build.fingerprint', cache=True)
152 wifi_ip = device.GetProp('dhcp.wlan0.ipaddress')
153 battery_info = _BatteryStatus(device, blacklist)
154 imei_slice = _IMEISlice(device)
155
156 if (device.product_name == 'mantaray' and
157 battery_info.get('AC powered', None) != 'true'):
158 logging.error('Mantaray device not connected to AC power.')
159
160 device_status.update({
161 'ro.build.product': build_product,
162 'ro.build.id': build_id,
163 'ro.build.fingerprint': build_fingerprint,
164 'battery': battery_info,
165 'imei_slice': imei_slice,
166 'wifi_ip': wifi_ip,
167
168 # TODO(jbudorick): Remove these once no clients depend on them.
169 'type': build_product,
170 'build': build_id,
171 'build_detail': build_fingerprint,
172 })
173
174 except device_errors.CommandFailedError:
175 logging.exception('Failure while getting device status for %s.',
176 str(device))
177 if blacklist:
178 blacklist.Extend([serial], reason='status_check_failure')
179
180 except device_errors.CommandTimeoutError:
181 logging.exception('Timeout while getting device status for %s.',
182 str(device))
183 if blacklist:
184 blacklist.Extend([serial], reason='status_check_timeout')
185
186 elif blacklist:
187 blacklist.Extend([serial],
188 reason=adb_status if usb_status else 'offline')
189
190 device_status['blacklisted'] = _IsBlacklisted(serial, blacklist)
191
192 return device_status
193
194 parallel_devices = device_utils.DeviceUtils.parallel(devices)
195 statuses = parallel_devices.pMap(blacklisting_device_status).pGet(None)
196 return statuses
197
198
199 def RecoverDevices(devices, blacklist):
200 """Attempts to recover any inoperable devices in the provided list.
201
202 Args:
203 devices: The list of devices to attempt to recover.
204 blacklist: The current device blacklist, which will be used then
205 reset.
206 Returns:
207 Nothing.
208 """
209
210 statuses = DeviceStatus(devices, blacklist)
211
212 should_restart_usb = set(
213 status['serial'] for status in statuses
214 if (not status['usb_status']
215 or status['adb_status'] in ('offline', 'missing')))
216 should_restart_adb = should_restart_usb.union(set(
217 status['serial'] for status in statuses
218 if status['adb_status'] == 'unauthorized'))
219 should_reboot_device = should_restart_adb.union(set(
220 status['serial'] for status in statuses
221 if status['blacklisted']))
222
223 logging.debug('Should restart USB for:')
224 for d in should_restart_usb:
225 logging.debug(' %s', d)
226 logging.debug('Should restart ADB for:')
227 for d in should_restart_adb:
228 logging.debug(' %s', d)
229 logging.debug('Should reboot:')
230 for d in should_reboot_device:
231 logging.debug(' %s', d)
232
233 if blacklist:
234 blacklist.Reset()
235
236 if should_restart_adb:
237 KillAllAdb()
238 for serial in should_restart_usb:
239 try:
240 reset_usb.reset_android_usb(serial)
241 except IOError:
242 logging.exception('Unable to reset USB for %s.', serial)
243 if blacklist:
244 blacklist.Extend([serial], reason='usb_failure')
245 except device_errors.DeviceUnreachableError:
246 logging.exception('Unable to reset USB for %s.', serial)
247 if blacklist:
248 blacklist.Extend([serial], reason='offline')
249
250 def blacklisting_recovery(device):
251 if _IsBlacklisted(device.adb.GetDeviceSerial(), blacklist):
252 logging.debug('%s is blacklisted, skipping recovery.', str(device))
253 return
254
255 if str(device) in should_reboot_device:
256 try:
257 device.WaitUntilFullyBooted(retries=0)
258 return
259 except (device_errors.CommandTimeoutError,
260 device_errors.CommandFailedError):
261 logging.exception('Failure while waiting for %s. '
262 'Attempting to recover.', str(device))
263
264 try:
265 try:
266 device.Reboot(block=False, timeout=5, retries=0)
267 except device_errors.CommandTimeoutError:
268 logging.warning('Timed out while attempting to reboot %s normally.'
269 'Attempting alternative reboot.', str(device))
270 # The device drops offline before we can grab the exit code, so
271 # we don't check for status.
272 device.adb.Root()
273 device.adb.Shell('echo b > /proc/sysrq-trigger', expect_status=None,
274 timeout=5, retries=0)
275 except device_errors.CommandFailedError:
276 logging.exception('Failed to reboot %s.', str(device))
277 if blacklist:
278 blacklist.Extend([device.adb.GetDeviceSerial()],
279 reason='reboot_failure')
280 except device_errors.CommandTimeoutError:
281 logging.exception('Timed out while rebooting %s.', str(device))
282 if blacklist:
283 blacklist.Extend([device.adb.GetDeviceSerial()],
284 reason='reboot_timeout')
285
286 try:
287 device.WaitUntilFullyBooted(retries=0)
288 except device_errors.CommandFailedError:
289 logging.exception('Failure while waiting for %s.', str(device))
290 if blacklist:
291 blacklist.Extend([device.adb.GetDeviceSerial()],
292 reason='reboot_failure')
293 except device_errors.CommandTimeoutError:
294 logging.exception('Timed out while waiting for %s.', str(device))
295 if blacklist:
296 blacklist.Extend([device.adb.GetDeviceSerial()],
297 reason='reboot_timeout')
298
299 device_utils.DeviceUtils.parallel(devices).pMap(blacklisting_recovery)
300
301
302 def main(): 31 def main():
303 parser = argparse.ArgumentParser() 32 parser = argparse.ArgumentParser()
33 device_status.AddArguments(parser)
304 parser.add_argument('--out-dir', 34 parser.add_argument('--out-dir',
305 help='Directory where the device path is stored', 35 help='DEPRECATED. '
36 'Directory where the device path is stored',
306 default=os.path.join(host_paths.DIR_SOURCE_ROOT, 'out')) 37 default=os.path.join(host_paths.DIR_SOURCE_ROOT, 'out'))
307 parser.add_argument('--restart-usb', action='store_true', 38 parser.add_argument('--restart-usb', action='store_true',
308 help='DEPRECATED. ' 39 help='DEPRECATED. '
309 'This script now always tries to reset USB.') 40 'This script now always tries to reset USB.')
310 parser.add_argument('--json-output',
311 help='Output JSON information into a specified file.')
312 parser.add_argument('--adb-path', type=os.path.abspath,
313 help='Absolute path to the adb binary to use.')
314 parser.add_argument('--blacklist-file', help='Device blacklist JSON file.')
315 parser.add_argument('--known-devices-file', action='append', default=[],
316 dest='known_devices_files',
317 help='Path to known device lists.')
318 parser.add_argument('-v', '--verbose', action='count', default=1,
319 help='Log more information.')
320
321 args = parser.parse_args() 41 args = parser.parse_args()
322 42
323 run_tests_helper.SetLogLevel(args.verbose) 43 run_tests_helper.SetLogLevel(args.verbose)
324 44
325 devil_chromium.Initialize(adb_path=args.adb_path) 45 devil_chromium.Initialize(adb_path=args.adb_path)
326 46
327 blacklist = (device_blacklist.Blacklist(args.blacklist_file) 47 blacklist = (device_blacklist.Blacklist(args.blacklist_file)
328 if args.blacklist_file 48 if args.blacklist_file
329 else None) 49 else None)
330 50
(...skipping 10 matching lines...) Expand all
341 logging.warning('Problem reading %s, skipping.', path) 61 logging.warning('Problem reading %s, skipping.', path)
342 62
343 logging.info('Expected devices:') 63 logging.info('Expected devices:')
344 for device in expected_devices: 64 for device in expected_devices:
345 logging.info(' %s', device) 65 logging.info(' %s', device)
346 66
347 usb_devices = set(lsusb.get_android_devices()) 67 usb_devices = set(lsusb.get_android_devices())
348 devices = [device_utils.DeviceUtils(s) 68 devices = [device_utils.DeviceUtils(s)
349 for s in expected_devices.union(usb_devices)] 69 for s in expected_devices.union(usb_devices)]
350 70
351 RecoverDevices(devices, blacklist) 71 device_recovery.RecoverDevices(devices, blacklist)
352 statuses = DeviceStatus(devices, blacklist) 72 statuses = device_status.DeviceStatus(devices, blacklist)
353 73
354 # Log the state of all devices. 74 # Log the state of all devices.
355 for status in statuses: 75 for status in statuses:
356 logging.info(status['serial']) 76 logging.info(status['serial'])
357 adb_status = status.get('adb_status') 77 adb_status = status.get('adb_status')
358 blacklisted = status.get('blacklisted') 78 blacklisted = status.get('blacklisted')
359 logging.info(' USB status: %s', 79 logging.info(' USB status: %s',
360 'online' if status.get('usb_status') else 'offline') 80 'online' if status.get('usb_status') else 'offline')
361 logging.info(' ADB status: %s', adb_status) 81 logging.info(' ADB status: %s', adb_status)
362 logging.info(' Blacklisted: %s', str(blacklisted)) 82 logging.info(' Blacklisted: %s', str(blacklisted))
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
401 serial=status['serial'] 121 serial=status['serial']
402 )) 122 ))
403 except Exception: # pylint: disable=broad-except 123 except Exception: # pylint: disable=broad-except
404 pass 124 pass
405 125
406 # Dump the device statuses to JSON. 126 # Dump the device statuses to JSON.
407 if args.json_output: 127 if args.json_output:
408 with open(args.json_output, 'wb') as f: 128 with open(args.json_output, 'wb') as f:
409 f.write(json.dumps(statuses, indent=4)) 129 f.write(json.dumps(statuses, indent=4))
410 130
411 live_devices = [status['serial'] for status in statuses 131 live_devices = [
412 if (status['adb_status'] == 'device' 132 status['serial'] for status in statuses
413 and not _IsBlacklisted(status['serial'], blacklist))] 133 if (status['adb_status'] == 'device'
134 and not device_status.IsBlacklisted(status['serial'], blacklist))]
414 135
415 # If all devices failed, or if there are no devices, it's an infra error. 136 # If all devices failed, or if there are no devices, it's an infra error.
416 return 0 if live_devices else exit_codes.INFRA 137 return 0 if live_devices else exit_codes.INFRA
417 138
418 139
419 if __name__ == '__main__': 140 if __name__ == '__main__':
420 sys.exit(main()) 141 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698