Chromium Code Reviews| 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 |