OLD | NEW |
---|---|
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 import argparse | |
8 import json | 9 import json |
9 import logging | 10 import logging |
10 import optparse | |
11 import os | 11 import os |
12 import psutil | 12 import psutil |
13 import re | 13 import re |
14 import signal | 14 import signal |
15 import smtplib | 15 import smtplib |
16 import subprocess | 16 import subprocess |
17 import sys | 17 import sys |
18 import time | 18 import time |
19 import urllib | 19 import urllib |
20 | 20 |
(...skipping 13 matching lines...) Expand all Loading... | |
34 from pylib.device import battery_utils | 34 from pylib.device import battery_utils |
35 from pylib.device import device_blacklist | 35 from pylib.device import device_blacklist |
36 from pylib.device import device_errors | 36 from pylib.device import device_errors |
37 from pylib.device import device_list | 37 from pylib.device import device_list |
38 from pylib.device import device_utils | 38 from pylib.device import device_utils |
39 from pylib.utils import run_tests_helper | 39 from pylib.utils import run_tests_helper |
40 from pylib.utils import timeout_retry | 40 from pylib.utils import timeout_retry |
41 | 41 |
42 _RE_DEVICE_ID = re.compile('Device ID = (\d+)') | 42 _RE_DEVICE_ID = re.compile('Device ID = (\d+)') |
43 | 43 |
44 def DeviceInfo(device, options): | 44 def DeviceInfo(device, args): |
rnephew (Reviews Here)
2015/08/07 22:26:31
Should this transition from optprase to argparse b
| |
45 """Gathers info on a device via various adb calls. | 45 """Gathers info on a device via various adb calls. |
46 | 46 |
47 Args: | 47 Args: |
48 device: A DeviceUtils instance for the device to construct info about. | 48 device: A DeviceUtils instance for the device to construct info about. |
49 | 49 |
50 Returns: | 50 Returns: |
51 Tuple of device type, build id, report as a string, error messages, and | 51 Tuple of device type, build id, report as a string, error messages, and |
52 boolean indicating whether or not device can be used for testing. | 52 boolean indicating whether or not device can be used for testing. |
53 """ | 53 """ |
54 battery = battery_utils.BatteryUtils(device) | 54 battery = battery_utils.BatteryUtils(device) |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
89 if m: | 89 if m: |
90 json_data['imei_slice'] = m.group(1)[-6:] | 90 json_data['imei_slice'] = m.group(1)[-6:] |
91 except device_errors.CommandFailedError: | 91 except device_errors.CommandFailedError: |
92 logging.exception('Failed to get IMEI slice for %s', str(device)) | 92 logging.exception('Failed to get IMEI slice for %s', str(device)) |
93 | 93 |
94 if battery_level < 15: | 94 if battery_level < 15: |
95 errors += ['Device critically low in battery.'] | 95 errors += ['Device critically low in battery.'] |
96 dev_good = False | 96 dev_good = False |
97 if not battery.GetCharging(): | 97 if not battery.GetCharging(): |
98 battery.SetCharging(True) | 98 battery.SetCharging(True) |
99 if not options.no_provisioning_check: | 99 if not args.no_provisioning_check: |
100 setup_wizard_disabled = ( | 100 setup_wizard_disabled = ( |
101 device.GetProp('ro.setupwizard.mode') == 'DISABLED') | 101 device.GetProp('ro.setupwizard.mode') == 'DISABLED') |
102 if not setup_wizard_disabled and device.build_type != 'user': | 102 if not setup_wizard_disabled and device.build_type != 'user': |
103 errors += ['Setup wizard not disabled. Was it provisioned correctly?'] | 103 errors += ['Setup wizard not disabled. Was it provisioned correctly?'] |
104 if (device.product_name == 'mantaray' and | 104 if (device.product_name == 'mantaray' and |
105 battery_info.get('AC powered', None) != 'true'): | 105 battery_info.get('AC powered', None) != 'true'): |
106 errors += ['Mantaray device not connected to AC power.'] | 106 errors += ['Mantaray device not connected to AC power.'] |
107 except device_errors.CommandFailedError: | 107 except device_errors.CommandFailedError: |
108 logging.exception('Failure while getting device status.') | 108 logging.exception('Failure while getting device status.') |
109 dev_good = False | 109 dev_good = False |
110 except device_errors.CommandTimeoutError: | 110 except device_errors.CommandTimeoutError: |
111 logging.exception('Timeout while getting device status.') | 111 logging.exception('Timeout while getting device status.') |
112 dev_good = False | 112 dev_good = False |
113 | 113 |
114 return (build_product, build_id, battery_level, errors, dev_good, json_data) | 114 return (build_product, build_id, battery_level, errors, dev_good, json_data) |
115 | 115 |
116 | 116 |
117 def CheckForMissingDevices(options, devices): | 117 def CheckForMissingDevices(args, devices): |
118 """Uses file of previous online devices to detect broken phones. | 118 """Uses file of previous online devices to detect broken phones. |
119 | 119 |
120 Args: | 120 Args: |
121 options: out_dir parameter of options argument is used as the base | 121 args: out_dir parameter of args argument is used as the base |
122 directory to load and update the cache file. | 122 directory to load and update the cache file. |
123 devices: A list of DeviceUtils instance for the currently visible and | 123 devices: A list of DeviceUtils instance for the currently visible and |
124 online attached devices. | 124 online attached devices. |
125 """ | 125 """ |
126 out_dir = os.path.abspath(options.out_dir) | 126 out_dir = os.path.abspath(args.out_dir) |
127 device_serials = set(d.adb.GetDeviceSerial() for d in devices) | 127 device_serials = set(d.adb.GetDeviceSerial() for d in devices) |
128 | 128 |
129 # last_devices denotes all known devices prior to this run | 129 # last_devices denotes all known devices prior to this run |
130 last_devices_path = os.path.join(out_dir, device_list.LAST_DEVICES_FILENAME) | 130 last_devices_path = os.path.join(out_dir, device_list.LAST_DEVICES_FILENAME) |
131 last_missing_devices_path = os.path.join(out_dir, | 131 last_missing_devices_path = os.path.join(out_dir, |
132 device_list.LAST_MISSING_DEVICES_FILENAME) | 132 device_list.LAST_MISSING_DEVICES_FILENAME) |
133 try: | 133 try: |
134 last_devices = device_list.GetPersistentDeviceList(last_devices_path) | 134 last_devices = device_list.GetPersistentDeviceList(last_devices_path) |
135 except IOError: | 135 except IOError: |
136 # Ignore error, file might not exist | 136 # Ignore error, file might not exist |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
218 pass | 218 pass |
219 for p in GetAllAdb(): | 219 for p in GetAllAdb(): |
220 try: | 220 try: |
221 logging.error('Unable to kill %d (%s [%s])', p.pid, p.name, | 221 logging.error('Unable to kill %d (%s [%s])', p.pid, p.name, |
222 ' '.join(p.cmdline)) | 222 ' '.join(p.cmdline)) |
223 except (psutil.NoSuchProcess, psutil.AccessDenied): | 223 except (psutil.NoSuchProcess, psutil.AccessDenied): |
224 pass | 224 pass |
225 | 225 |
226 | 226 |
227 def main(): | 227 def main(): |
228 parser = optparse.OptionParser() | 228 parser = argparse.ArgumentParser() |
229 parser.add_option('', '--out-dir', | 229 parser.add_argument('--out-dir', |
230 help='Directory where the device path is stored', | 230 help='Directory where the device path is stored', |
231 default=os.path.join(constants.DIR_SOURCE_ROOT, 'out')) | 231 default=os.path.join(constants.DIR_SOURCE_ROOT, 'out')) |
232 parser.add_option('--no-provisioning-check', action='store_true', | 232 parser.add_argument('--no-provisioning-check', action='store_true', |
233 help='Will not check if devices are provisioned properly.') | 233 help='Will not check if devices are provisioned ' |
234 parser.add_option('--device-status-dashboard', action='store_true', | 234 'properly.') |
235 help='Output device status data for dashboard.') | 235 parser.add_argument('--device-status-dashboard', action='store_true', |
236 parser.add_option('--restart-usb', action='store_true', | 236 help='Output device status data for dashboard.') |
237 help='DEPRECATED. ' | 237 parser.add_argument('--restart-usb', action='store_true', |
238 'This script now always tries to reset USB.') | 238 help='DEPRECATED. ' |
239 parser.add_option('--json-output', | 239 'This script now always tries to reset USB.') |
240 help='Output JSON information into a specified file.') | 240 parser.add_argument('--json-output', |
241 parser.add_option('-v', '--verbose', action='count', default=1, | 241 help='Output JSON information into a specified file.') |
242 help='Log more information.') | 242 parser.add_argument('--blacklist-file', help='Device blacklist JSON file.') |
243 parser.add_argument('-v', '--verbose', action='count', default=1, | |
244 help='Log more information.') | |
243 | 245 |
244 options, args = parser.parse_args() | 246 args = parser.parse_args() |
245 if args: | |
246 parser.error('Unknown options %s' % args) | |
247 | 247 |
248 run_tests_helper.SetLogLevel(options.verbose) | 248 run_tests_helper.SetLogLevel(args.verbose) |
249 | |
250 if args.blacklist_file: | |
251 blacklist = device_blacklist.Blacklist(args.blacklist_file) | |
252 else: | |
253 # TODO(jbudorick): Remove this once bots pass the blacklist file. | |
254 blacklist = device_blacklist.Blacklist(device_blacklist.BLACKLIST_JSON) | |
249 | 255 |
250 # Remove the last build's "bad devices" before checking device statuses. | 256 # Remove the last build's "bad devices" before checking device statuses. |
251 device_blacklist.ResetBlacklist() | 257 blacklist.Reset() |
252 | 258 |
253 KillAllAdb() | 259 KillAllAdb() |
254 reset_usb.reset_all_android_devices() | 260 reset_usb.reset_all_android_devices() |
255 | 261 |
256 try: | 262 try: |
257 expected_devices = set(device_list.GetPersistentDeviceList( | 263 expected_devices = set(device_list.GetPersistentDeviceList( |
258 os.path.join(options.out_dir, device_list.LAST_DEVICES_FILENAME))) | 264 os.path.join(args.out_dir, device_list.LAST_DEVICES_FILENAME))) |
259 except IOError: | 265 except IOError: |
260 expected_devices = set() | 266 expected_devices = set() |
261 | 267 |
262 def all_devices_found(): | 268 def all_devices_found(): |
263 devices = device_utils.DeviceUtils.HealthyDevices() | 269 devices = device_utils.DeviceUtils.HealthyDevices(blacklist) |
264 device_serials = set(d.adb.GetDeviceSerial() for d in devices) | 270 device_serials = set(d.adb.GetDeviceSerial() for d in devices) |
265 return not bool(expected_devices.difference(device_serials)) | 271 return not bool(expected_devices.difference(device_serials)) |
266 | 272 |
267 timeout_retry.WaitFor(all_devices_found, wait_period=1, max_tries=5) | 273 timeout_retry.WaitFor(all_devices_found, wait_period=1, max_tries=5) |
268 | 274 |
269 devices = device_utils.DeviceUtils.HealthyDevices() | 275 devices = device_utils.DeviceUtils.HealthyDevices(blacklist) |
270 device_serials = set(d.adb.GetDeviceSerial() for d in devices) | 276 device_serials = set(d.adb.GetDeviceSerial() for d in devices) |
271 | 277 |
272 missing_devices = expected_devices.difference(device_serials) | 278 missing_devices = expected_devices.difference(device_serials) |
273 new_devices = device_serials.difference(expected_devices) | 279 new_devices = device_serials.difference(expected_devices) |
274 | 280 |
275 if missing_devices or new_devices: | 281 if missing_devices or new_devices: |
276 logging.warning('expected_devices:') | 282 logging.warning('expected_devices:') |
277 for d in sorted(expected_devices): | 283 for d in sorted(expected_devices): |
278 logging.warning(' %s', d) | 284 logging.warning(' %s', d) |
279 logging.warning('devices:') | 285 logging.warning('devices:') |
280 for d in sorted(device_serials): | 286 for d in sorted(device_serials): |
281 logging.warning(' %s', d) | 287 logging.warning(' %s', d) |
282 | 288 |
283 types, builds, batteries, errors, devices_ok, json_data = ( | 289 types, builds, batteries, errors, devices_ok, json_data = ( |
284 [], [], [], [], [], []) | 290 [], [], [], [], [], []) |
285 if devices: | 291 if devices: |
286 types, builds, batteries, errors, devices_ok, json_data = ( | 292 types, builds, batteries, errors, devices_ok, json_data = ( |
287 zip(*[DeviceInfo(dev, options) for dev in devices])) | 293 zip(*[DeviceInfo(dev, args) for dev in devices])) |
288 | 294 |
289 # Write device info to file for buildbot info display. | 295 # Write device info to file for buildbot info display. |
290 if os.path.exists('/home/chrome-bot'): | 296 if os.path.exists('/home/chrome-bot'): |
291 with open('/home/chrome-bot/.adb_device_info', 'w') as f: | 297 with open('/home/chrome-bot/.adb_device_info', 'w') as f: |
292 for device in json_data: | 298 for device in json_data: |
293 try: | 299 try: |
294 f.write('%s %s %s %.1fC %s%%\n' % (device['serial'], device['type'], | 300 f.write('%s %s %s %.1fC %s%%\n' % (device['serial'], device['type'], |
295 device['build'], float(device['battery']['temperature']) / 10, | 301 device['build'], float(device['battery']['temperature']) / 10, |
296 device['battery']['level'])) | 302 device['battery']['level'])) |
297 except Exception: | 303 except Exception: |
298 pass | 304 pass |
299 | 305 |
300 err_msg = CheckForMissingDevices(options, devices) or [] | 306 err_msg = CheckForMissingDevices(args, devices) or [] |
301 | 307 |
302 unique_types = list(set(types)) | 308 unique_types = list(set(types)) |
303 unique_builds = list(set(builds)) | 309 unique_builds = list(set(builds)) |
304 | 310 |
305 bb_annotations.PrintMsg('Online devices: %d. Device types %s, builds %s' | 311 bb_annotations.PrintMsg('Online devices: %d. Device types %s, builds %s' |
306 % (len(devices), unique_types, unique_builds)) | 312 % (len(devices), unique_types, unique_builds)) |
307 | 313 |
308 for j in json_data: | 314 for j in json_data: |
309 logging.info('Device %s (%s)', j.get('serial'), j.get('type')) | 315 logging.info('Device %s (%s)', j.get('serial'), j.get('type')) |
310 logging.info(' Build: %s (%s)', j.get('build'), j.get('build_detail')) | 316 logging.info(' Build: %s (%s)', j.get('build'), j.get('build_detail')) |
(...skipping 13 matching lines...) Expand all Loading... | |
324 bb_annotations.PrintWarning() | 330 bb_annotations.PrintWarning() |
325 for e in err_msg: | 331 for e in err_msg: |
326 logging.error(e) | 332 logging.error(e) |
327 from_address = 'buildbot@chromium.org' | 333 from_address = 'buildbot@chromium.org' |
328 to_addresses = ['chromium-android-device-alerts@google.com'] | 334 to_addresses = ['chromium-android-device-alerts@google.com'] |
329 bot_name = os.environ.get('BUILDBOT_BUILDERNAME') | 335 bot_name = os.environ.get('BUILDBOT_BUILDERNAME') |
330 slave_name = os.environ.get('BUILDBOT_SLAVENAME') | 336 slave_name = os.environ.get('BUILDBOT_SLAVENAME') |
331 subject = 'Device status check errors on %s, %s.' % (slave_name, bot_name) | 337 subject = 'Device status check errors on %s, %s.' % (slave_name, bot_name) |
332 SendEmail(from_address, to_addresses, [], subject, '\n'.join(err_msg)) | 338 SendEmail(from_address, to_addresses, [], subject, '\n'.join(err_msg)) |
333 | 339 |
334 if options.device_status_dashboard: | 340 if args.device_status_dashboard: |
335 offline_devices = [ | 341 offline_devices = [ |
336 device_utils.DeviceUtils(a) | 342 device_utils.DeviceUtils(a) |
337 for a in adb_wrapper.AdbWrapper.Devices(is_ready=False) | 343 for a in adb_wrapper.AdbWrapper.Devices(is_ready=False) |
338 if a.GetState() == 'offline'] | 344 if a.GetState() == 'offline'] |
339 | 345 |
340 perf_tests_results_helper.PrintPerfResult('BotDevices', 'OnlineDevices', | 346 perf_tests_results_helper.PrintPerfResult('BotDevices', 'OnlineDevices', |
341 [len(devices)], 'devices') | 347 [len(devices)], 'devices') |
342 perf_tests_results_helper.PrintPerfResult('BotDevices', 'OfflineDevices', | 348 perf_tests_results_helper.PrintPerfResult('BotDevices', 'OfflineDevices', |
343 [len(offline_devices)], 'devices', | 349 [len(offline_devices)], 'devices', |
344 'unimportant') | 350 'unimportant') |
345 for dev, battery in zip(devices, batteries): | 351 for dev, battery in zip(devices, batteries): |
346 perf_tests_results_helper.PrintPerfResult('DeviceBattery', str(dev), | 352 perf_tests_results_helper.PrintPerfResult('DeviceBattery', str(dev), |
347 [battery], '%', | 353 [battery], '%', |
348 'unimportant') | 354 'unimportant') |
349 | 355 |
350 if options.json_output: | 356 if args.json_output: |
351 with open(options.json_output, 'wb') as f: | 357 with open(args.json_output, 'wb') as f: |
352 f.write(json.dumps(json_data, indent=4)) | 358 f.write(json.dumps(json_data, indent=4)) |
353 | 359 |
354 num_failed_devs = 0 | 360 num_failed_devs = 0 |
355 for device_ok, device in zip(devices_ok, devices): | 361 for device_ok, device in zip(devices_ok, devices): |
356 if not device_ok: | 362 if not device_ok: |
357 logging.warning('Blacklisting %s', str(device)) | 363 logging.warning('Blacklisting %s', str(device)) |
358 device_blacklist.ExtendBlacklist([str(device)]) | 364 blacklist.Extend([str(device)]) |
359 num_failed_devs += 1 | 365 num_failed_devs += 1 |
360 | 366 |
361 if num_failed_devs == len(devices): | 367 if num_failed_devs == len(devices): |
362 return 2 | 368 return 2 |
363 | 369 |
364 if not devices: | 370 if not devices: |
365 return 1 | 371 return 1 |
366 | 372 |
367 | 373 |
368 if __name__ == '__main__': | 374 if __name__ == '__main__': |
369 sys.exit(main()) | 375 sys.exit(main()) |
OLD | NEW |