| 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 json | 8 import json |
| 9 import logging | 9 import logging |
| 10 import optparse | 10 import optparse |
| (...skipping 12 matching lines...) Expand all Loading... |
| 23 | 23 |
| 24 sys.path.append(os.path.join(os.path.dirname(__file__), | 24 sys.path.append(os.path.join(os.path.dirname(__file__), |
| 25 os.pardir, os.pardir, 'util', 'lib', | 25 os.pardir, os.pardir, 'util', 'lib', |
| 26 'common')) | 26 'common')) |
| 27 import perf_tests_results_helper # pylint: disable=F0401 | 27 import perf_tests_results_helper # pylint: disable=F0401 |
| 28 | 28 |
| 29 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) | 29 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) |
| 30 from pylib import android_commands | 30 from pylib import android_commands |
| 31 from pylib import constants | 31 from pylib import constants |
| 32 from pylib.cmd_helper import GetCmdOutput | 32 from pylib.cmd_helper import GetCmdOutput |
| 33 from pylib.device import battery_utils |
| 33 from pylib.device import device_blacklist | 34 from pylib.device import device_blacklist |
| 35 from pylib.device import device_errors |
| 34 from pylib.device import device_list | 36 from pylib.device import device_list |
| 35 from pylib.device import device_utils | 37 from pylib.device import device_utils |
| 38 from pylib.utils import run_tests_helper |
| 39 |
| 40 _RE_DEVICE_ID = re.compile('Device ID = (\d+)') |
| 36 | 41 |
| 37 def DeviceInfo(serial, options): | 42 def DeviceInfo(serial, options): |
| 38 """Gathers info on a device via various adb calls. | 43 """Gathers info on a device via various adb calls. |
| 39 | 44 |
| 40 Args: | 45 Args: |
| 41 serial: The serial of the attached device to construct info about. | 46 serial: The serial of the attached device to construct info about. |
| 42 | 47 |
| 43 Returns: | 48 Returns: |
| 44 Tuple of device type, build id, report as a string, error messages, and | 49 Tuple of device type, build id, report as a string, error messages, and |
| 45 boolean indicating whether or not device can be used for testing. | 50 boolean indicating whether or not device can be used for testing. |
| 46 """ | 51 """ |
| 52 device = device_utils.DeviceUtils(serial) |
| 53 battery = battery_utils.BatteryUtils(device) |
| 47 | 54 |
| 48 device_adb = device_utils.DeviceUtils(serial) | 55 battery_info = {} |
| 49 device_type = device_adb.build_product | 56 battery_level = 100 |
| 50 device_build = device_adb.build_id | 57 errors = [] |
| 51 device_build_type = device_adb.build_type | 58 dev_good = True |
| 52 device_product_name = device_adb.product_name | 59 json_data = { |
| 60 'serial': serial, |
| 61 'type': device.build_product, |
| 62 'build': device.build_id, |
| 63 'build_detail': device.GetProp('ro.build.fingerprint'), |
| 64 'battery': {}, |
| 65 'imei_slice': 'Unknown', |
| 66 'wifi_ip': device.GetProp('dhcp.wlan0.ipaddress'), |
| 67 } |
| 53 | 68 |
| 54 try: | 69 try: |
| 55 battery_info = device_adb.old_interface.GetBatteryInfo() | 70 try: |
| 56 except Exception as e: | 71 battery_info = battery.GetBatteryInfo(timeout=5) |
| 57 battery_info = {} | 72 battery_level = int(battery_info.get('level', battery_level)) |
| 58 logging.error('Unable to obtain battery info for %s, %s', serial, e) | 73 json_data['battery'] = battery_info |
| 74 except device_errors.CommandFailedError: |
| 75 logging.exception('Failed to get battery information for %s', serial) |
| 59 | 76 |
| 60 def _GetData(re_expression, line, lambda_function=lambda x: x): | 77 try: |
| 61 if not line: | 78 for l in device.RunShellCommand(['dumpsys', 'iphonesubinfo'], |
| 62 return 'Unknown' | 79 check_return=True, timeout=5): |
| 63 found = re.findall(re_expression, line) | 80 m = _RE_DEVICE_ID.match(l) |
| 64 if found and len(found): | 81 if m: |
| 65 return lambda_function(found[0]) | 82 json_data['imei_slice'] = m.group(1)[-6:] |
| 66 return 'Unknown' | 83 except device_errors.CommandFailedError: |
| 84 logging.exception('Failed to get IMEI slice for %s', serial) |
| 67 | 85 |
| 68 battery_level = int(battery_info.get('level', 100)) | 86 if battery_level < 15: |
| 69 imei_slice = _GetData(r'Device ID = (\d+)', | 87 errors += ['Device critically low in battery.'] |
| 70 device_adb.old_interface.GetSubscriberInfo(), | 88 dev_good = False |
| 71 lambda x: x[-6:]) | 89 if not battery.GetCharging(): |
| 72 json_data = { | 90 battery.SetCharging(True) |
| 73 'serial': serial, | 91 if not options.no_provisioning_check: |
| 74 'type': device_type, | 92 setup_wizard_disabled = ( |
| 75 'build': device_build, | 93 device.GetProp('ro.setupwizard.mode') == 'DISABLED') |
| 76 'build_detail': device_adb.GetProp('ro.build.fingerprint'), | 94 if not setup_wizard_disabled and device.build_type != 'user': |
| 77 'battery': battery_info, | 95 errors += ['Setup wizard not disabled. Was it provisioned correctly?'] |
| 78 'imei_slice': imei_slice, | 96 if (device.product_name == 'mantaray' and |
| 79 'wifi_ip': device_adb.GetProp('dhcp.wlan0.ipaddress'), | 97 battery_info.get('AC powered', None) != 'true'): |
| 80 } | 98 errors += ['Mantaray device not connected to AC power.'] |
| 81 report = ['Device %s (%s)' % (serial, device_type), | 99 except device_errors.CommandFailedError: |
| 82 ' Build: %s (%s)' % | 100 logging.exception('Failure while getting device status.') |
| 83 (device_build, json_data['build_detail']), | 101 dev_good = False |
| 84 ' Current Battery Service state: ', | 102 except device_errors.CommandTimeoutError: |
| 85 '\n'.join([' %s: %s' % (k, v) | 103 logging.exception('Timeout while getting device status.') |
| 86 for k, v in battery_info.iteritems()]), | 104 dev_good = False |
| 87 ' IMEI slice: %s' % imei_slice, | |
| 88 ' Wifi IP: %s' % json_data['wifi_ip'], | |
| 89 ''] | |
| 90 | 105 |
| 91 errors = [] | 106 return (device.build_product, device.build_id, battery_level, errors, |
| 92 dev_good = True | 107 dev_good, json_data) |
| 93 if battery_level < 15: | |
| 94 errors += ['Device critically low in battery. Will add to blacklist.'] | |
| 95 dev_good = False | |
| 96 if not device_adb.old_interface.IsDeviceCharging(): | |
| 97 if device_adb.old_interface.CanControlUsbCharging(): | |
| 98 device_adb.old_interface.EnableUsbCharging() | |
| 99 else: | |
| 100 logging.error('Device %s is not charging' % serial) | |
| 101 if not options.no_provisioning_check: | |
| 102 setup_wizard_disabled = ( | |
| 103 device_adb.GetProp('ro.setupwizard.mode') == 'DISABLED') | |
| 104 if not setup_wizard_disabled and device_build_type != 'user': | |
| 105 errors += ['Setup wizard not disabled. Was it provisioned correctly?'] | |
| 106 if (device_product_name == 'mantaray' and | |
| 107 battery_info.get('AC powered', None) != 'true'): | |
| 108 errors += ['Mantaray device not connected to AC power.'] | |
| 109 | |
| 110 full_report = '\n'.join(report) | |
| 111 | |
| 112 return (device_type, device_build, battery_level, full_report, errors, | |
| 113 dev_good, json_data) | |
| 114 | 108 |
| 115 | 109 |
| 116 def CheckForMissingDevices(options, adb_online_devs): | 110 def CheckForMissingDevices(options, adb_online_devs): |
| 117 """Uses file of previous online devices to detect broken phones. | 111 """Uses file of previous online devices to detect broken phones. |
| 118 | 112 |
| 119 Args: | 113 Args: |
| 120 options: out_dir parameter of options argument is used as the base | 114 options: out_dir parameter of options argument is used as the base |
| 121 directory to load and update the cache file. | 115 directory to load and update the cache file. |
| 122 adb_online_devs: A list of serial numbers of the currently visible | 116 adb_online_devs: A list of serial numbers of the currently visible |
| 123 and online attached devices. | 117 and online attached devices. |
| 124 """ | 118 """ |
| 125 # TODO(navabi): remove this once the bug that causes different number | |
| 126 # of devices to be detected between calls is fixed. | |
| 127 logger = logging.getLogger() | |
| 128 logger.setLevel(logging.INFO) | |
| 129 | |
| 130 out_dir = os.path.abspath(options.out_dir) | 119 out_dir = os.path.abspath(options.out_dir) |
| 131 | 120 |
| 132 # last_devices denotes all known devices prior to this run | 121 # last_devices denotes all known devices prior to this run |
| 133 last_devices_path = os.path.join(out_dir, device_list.LAST_DEVICES_FILENAME) | 122 last_devices_path = os.path.join(out_dir, device_list.LAST_DEVICES_FILENAME) |
| 134 last_missing_devices_path = os.path.join(out_dir, | 123 last_missing_devices_path = os.path.join(out_dir, |
| 135 device_list.LAST_MISSING_DEVICES_FILENAME) | 124 device_list.LAST_MISSING_DEVICES_FILENAME) |
| 136 try: | 125 try: |
| 137 last_devices = device_list.GetPersistentDeviceList(last_devices_path) | 126 last_devices = device_list.GetPersistentDeviceList(last_devices_path) |
| 138 except IOError: | 127 except IOError: |
| 139 # Ignore error, file might not exist | 128 # Ignore error, file might not exist |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 182 crbug_link = ('https://code.google.com/p/chromium/issues/entry?summary=' | 171 crbug_link = ('https://code.google.com/p/chromium/issues/entry?summary=' |
| 183 '%s&comment=%s&labels=Restrict-View-Google,OS-Android,Infra' % | 172 '%s&comment=%s&labels=Restrict-View-Google,OS-Android,Infra' % |
| 184 (urllib.quote('Device Offline'), | 173 (urllib.quote('Device Offline'), |
| 185 urllib.quote('Buildbot: %s %s\n' | 174 urllib.quote('Buildbot: %s %s\n' |
| 186 'Build: %s\n' | 175 'Build: %s\n' |
| 187 '(please don\'t change any labels)' % | 176 '(please don\'t change any labels)' % |
| 188 (os.environ.get('BUILDBOT_BUILDERNAME'), | 177 (os.environ.get('BUILDBOT_BUILDERNAME'), |
| 189 os.environ.get('BUILDBOT_SLAVENAME'), | 178 os.environ.get('BUILDBOT_SLAVENAME'), |
| 190 os.environ.get('BUILDBOT_BUILDNUMBER'))))) | 179 os.environ.get('BUILDBOT_BUILDNUMBER'))))) |
| 191 return ['Current online devices: %s' % adb_online_devs, | 180 return ['Current online devices: %s' % adb_online_devs, |
| 192 '%s are no longer visible. Were they removed?\n' % missing_devs, | 181 '%s are no longer visible. Were they removed?' % missing_devs, |
| 193 'SHERIFF:\n', | 182 'SHERIFF:', |
| 194 '@@@STEP_LINK@Click here to file a bug@%s@@@\n' % crbug_link, | 183 '@@@STEP_LINK@Click here to file a bug@%s@@@' % crbug_link, |
| 195 'Cache file: %s\n\n' % last_devices_path, | 184 'Cache file: %s' % last_devices_path, |
| 196 'adb devices: %s' % GetCmdOutput(['adb', 'devices']), | 185 'adb devices: %s' % GetCmdOutput(['adb', 'devices']), |
| 197 'adb devices(GetAttachedDevices): %s' % adb_online_devs] | 186 'adb devices(GetAttachedDevices): %s' % adb_online_devs] |
| 198 else: | 187 else: |
| 199 new_devs = set(adb_online_devs) - set(last_devices) | 188 new_devs = set(adb_online_devs) - set(last_devices) |
| 200 if new_devs and os.path.exists(last_devices_path): | 189 if new_devs and os.path.exists(last_devices_path): |
| 201 bb_annotations.PrintWarning() | 190 bb_annotations.PrintWarning() |
| 202 bb_annotations.PrintSummaryText( | 191 bb_annotations.PrintSummaryText( |
| 203 '%d new devices detected' % len(new_devs)) | 192 '%d new devices detected' % len(new_devs)) |
| 204 print ('New devices detected %s. And now back to your ' | 193 logging.info('New devices detected:') |
| 205 'regularly scheduled program.' % list(new_devs)) | 194 for d in new_devs: |
| 195 logging.info(' %s', d) |
| 206 | 196 |
| 207 | 197 |
| 208 def SendEmail(from_address, to_addresses, cc_addresses, subject, msg): | 198 def SendEmail(from_address, to_addresses, cc_addresses, subject, msg): |
| 209 msg_body = '\r\n'.join(['From: %s' % from_address, | 199 msg_body = '\r\n'.join(['From: %s' % from_address, |
| 210 'To: %s' % ', '.join(to_addresses), | 200 'To: %s' % ', '.join(to_addresses), |
| 211 'CC: %s' % ', '.join(cc_addresses), | 201 'CC: %s' % ', '.join(cc_addresses), |
| 212 'Subject: %s' % subject, '', msg]) | 202 'Subject: %s' % subject, '', msg]) |
| 213 try: | 203 try: |
| 214 server = smtplib.SMTP('localhost') | 204 server = smtplib.SMTP('localhost') |
| 215 server.sendmail(from_address, to_addresses, msg_body) | 205 server.sendmail(from_address, to_addresses, msg_body) |
| 216 server.quit() | 206 server.quit() |
| 217 except Exception as e: | 207 except Exception: |
| 218 print 'Failed to send alert email. Error: %s' % e | 208 logging.exception('Failed to send alert email.') |
| 219 | 209 |
| 220 | 210 |
| 221 def RestartUsb(): | 211 def RestartUsb(): |
| 222 if not os.path.isfile('/usr/bin/restart_usb'): | 212 if not os.path.isfile('/usr/bin/restart_usb'): |
| 223 print ('ERROR: Could not restart usb. /usr/bin/restart_usb not installed ' | 213 logging.error('Could not restart usb. ''/usr/bin/restart_usb not ' |
| 224 'on host (see BUG=305769).') | 214 'installed on host (see BUG=305769).') |
| 225 return False | 215 return False |
| 226 | 216 |
| 227 lsusb_proc = bb_utils.SpawnCmd(['lsusb'], stdout=subprocess.PIPE) | 217 lsusb_proc = bb_utils.SpawnCmd(['lsusb'], stdout=subprocess.PIPE) |
| 228 lsusb_output, _ = lsusb_proc.communicate() | 218 lsusb_output, _ = lsusb_proc.communicate() |
| 229 if lsusb_proc.returncode: | 219 if lsusb_proc.returncode: |
| 230 print 'Error: Could not get list of USB ports (i.e. lsusb).' | 220 logging.error('Could not get list of USB ports (i.e. lsusb).') |
| 231 return lsusb_proc.returncode | 221 return lsusb_proc.returncode |
| 232 | 222 |
| 233 usb_devices = [re.findall(r'Bus (\d\d\d) Device (\d\d\d)', lsusb_line)[0] | 223 usb_devices = [re.findall(r'Bus (\d\d\d) Device (\d\d\d)', lsusb_line)[0] |
| 234 for lsusb_line in lsusb_output.strip().split('\n')] | 224 for lsusb_line in lsusb_output.strip().split('\n')] |
| 235 | 225 |
| 236 all_restarted = True | 226 all_restarted = True |
| 237 # Walk USB devices from leaves up (i.e reverse sorted) restarting the | 227 # Walk USB devices from leaves up (i.e reverse sorted) restarting the |
| 238 # connection. If a parent node (e.g. usb hub) is restarted before the | 228 # connection. If a parent node (e.g. usb hub) is restarted before the |
| 239 # devices connected to it, the (bus, dev) for the hub can change, making the | 229 # devices connected to it, the (bus, dev) for the hub can change, making the |
| 240 # output we have wrong. This way we restart the devices before the hub. | 230 # output we have wrong. This way we restart the devices before the hub. |
| 241 for (bus, dev) in reversed(sorted(usb_devices)): | 231 for (bus, dev) in reversed(sorted(usb_devices)): |
| 242 # Can not restart root usb connections | 232 # Can not restart root usb connections |
| 243 if dev != '001': | 233 if dev != '001': |
| 244 return_code = bb_utils.RunCmd(['/usr/bin/restart_usb', bus, dev]) | 234 return_code = bb_utils.RunCmd(['/usr/bin/restart_usb', bus, dev]) |
| 245 if return_code: | 235 if return_code: |
| 246 print 'Error restarting USB device /dev/bus/usb/%s/%s' % (bus, dev) | 236 logging.error('Error restarting USB device /dev/bus/usb/%s/%s', |
| 237 bus, dev) |
| 247 all_restarted = False | 238 all_restarted = False |
| 248 else: | 239 else: |
| 249 print 'Restarted USB device /dev/bus/usb/%s/%s' % (bus, dev) | 240 logging.info('Restarted USB device /dev/bus/usb/%s/%s', bus, dev) |
| 250 | 241 |
| 251 return all_restarted | 242 return all_restarted |
| 252 | 243 |
| 253 | 244 |
| 254 def KillAllAdb(): | 245 def KillAllAdb(): |
| 255 def GetAllAdb(): | 246 def GetAllAdb(): |
| 256 for p in psutil.process_iter(): | 247 for p in psutil.process_iter(): |
| 257 try: | 248 try: |
| 258 if 'adb' in p.name: | 249 if 'adb' in p.name: |
| 259 yield p | 250 yield p |
| 260 except (psutil.NoSuchProcess, psutil.AccessDenied): | 251 except (psutil.NoSuchProcess, psutil.AccessDenied): |
| 261 pass | 252 pass |
| 262 | 253 |
| 263 for sig in [signal.SIGTERM, signal.SIGQUIT, signal.SIGKILL]: | 254 for sig in [signal.SIGTERM, signal.SIGQUIT, signal.SIGKILL]: |
| 264 for p in GetAllAdb(): | 255 for p in GetAllAdb(): |
| 265 try: | 256 try: |
| 266 print 'kill %d %d (%s [%s])' % (sig, p.pid, p.name, | 257 logging.info('kill %d %d (%s [%s])', sig, p.pid, p.name, |
| 267 ' '.join(p.cmdline)) | 258 ' '.join(p.cmdline)) |
| 268 p.send_signal(sig) | 259 p.send_signal(sig) |
| 269 except (psutil.NoSuchProcess, psutil.AccessDenied): | 260 except (psutil.NoSuchProcess, psutil.AccessDenied): |
| 270 pass | 261 pass |
| 271 for p in GetAllAdb(): | 262 for p in GetAllAdb(): |
| 272 try: | 263 try: |
| 273 print 'Unable to kill %d (%s [%s])' % (p.pid, p.name, ' '.join(p.cmdline)) | 264 logging.error('Unable to kill %d (%s [%s])', p.pid, p.name, |
| 265 ' '.join(p.cmdline)) |
| 274 except (psutil.NoSuchProcess, psutil.AccessDenied): | 266 except (psutil.NoSuchProcess, psutil.AccessDenied): |
| 275 pass | 267 pass |
| 276 | 268 |
| 277 | 269 |
| 278 def main(): | 270 def main(): |
| 279 parser = optparse.OptionParser() | 271 parser = optparse.OptionParser() |
| 280 parser.add_option('', '--out-dir', | 272 parser.add_option('', '--out-dir', |
| 281 help='Directory where the device path is stored', | 273 help='Directory where the device path is stored', |
| 282 default=os.path.join(constants.DIR_SOURCE_ROOT, 'out')) | 274 default=os.path.join(constants.DIR_SOURCE_ROOT, 'out')) |
| 283 parser.add_option('--no-provisioning-check', action='store_true', | 275 parser.add_option('--no-provisioning-check', action='store_true', |
| 284 help='Will not check if devices are provisioned properly.') | 276 help='Will not check if devices are provisioned properly.') |
| 285 parser.add_option('--device-status-dashboard', action='store_true', | 277 parser.add_option('--device-status-dashboard', action='store_true', |
| 286 help='Output device status data for dashboard.') | 278 help='Output device status data for dashboard.') |
| 287 parser.add_option('--restart-usb', action='store_true', | 279 parser.add_option('--restart-usb', action='store_true', |
| 288 help='Restart USB ports before running device check.') | 280 help='Restart USB ports before running device check.') |
| 289 parser.add_option('--json-output', | 281 parser.add_option('--json-output', |
| 290 help='Output JSON information into a specified file.') | 282 help='Output JSON information into a specified file.') |
| 283 parser.add_option('-v', '--verbose', action='count', default=1, |
| 284 help='Log more information.') |
| 291 | 285 |
| 292 options, args = parser.parse_args() | 286 options, args = parser.parse_args() |
| 293 if args: | 287 if args: |
| 294 parser.error('Unknown options %s' % args) | 288 parser.error('Unknown options %s' % args) |
| 295 | 289 |
| 290 run_tests_helper.SetLogLevel(options.verbose) |
| 291 |
| 296 # Remove the last build's "bad devices" before checking device statuses. | 292 # Remove the last build's "bad devices" before checking device statuses. |
| 297 device_blacklist.ResetBlacklist() | 293 device_blacklist.ResetBlacklist() |
| 298 | 294 |
| 299 try: | 295 try: |
| 300 expected_devices = device_list.GetPersistentDeviceList( | 296 expected_devices = device_list.GetPersistentDeviceList( |
| 301 os.path.join(options.out_dir, device_list.LAST_DEVICES_FILENAME)) | 297 os.path.join(options.out_dir, device_list.LAST_DEVICES_FILENAME)) |
| 302 except IOError: | 298 except IOError: |
| 303 expected_devices = [] | 299 expected_devices = [] |
| 304 devices = android_commands.GetAttachedDevices() | 300 devices = android_commands.GetAttachedDevices() |
| 305 # Only restart usb if devices are missing. | 301 # Only restart usb if devices are missing. |
| 306 if set(expected_devices) != set(devices): | 302 if set(expected_devices) != set(devices): |
| 307 print 'expected_devices: %s, devices: %s' % (expected_devices, devices) | 303 logging.warning('expected_devices: %s', expected_devices) |
| 304 logging.warning('devices: %s', devices) |
| 308 KillAllAdb() | 305 KillAllAdb() |
| 309 retries = 5 | 306 retries = 5 |
| 310 usb_restarted = True | 307 usb_restarted = True |
| 311 if options.restart_usb: | 308 if options.restart_usb: |
| 312 if not RestartUsb(): | 309 if not RestartUsb(): |
| 313 usb_restarted = False | 310 usb_restarted = False |
| 314 bb_annotations.PrintWarning() | 311 bb_annotations.PrintWarning() |
| 315 print 'USB reset stage failed, wait for any device to come back.' | 312 logging.error('USB reset stage failed, ' |
| 313 'wait for any device to come back.') |
| 316 while retries: | 314 while retries: |
| 317 print 'retry adb devices...' | 315 logging.info('retry adb devices...') |
| 318 time.sleep(1) | 316 time.sleep(1) |
| 319 devices = android_commands.GetAttachedDevices() | 317 devices = android_commands.GetAttachedDevices() |
| 320 if set(expected_devices) == set(devices): | 318 if set(expected_devices) == set(devices): |
| 321 # All devices are online, keep going. | 319 # All devices are online, keep going. |
| 322 break | 320 break |
| 323 if not usb_restarted and devices: | 321 if not usb_restarted and devices: |
| 324 # The USB wasn't restarted, but there's at least one device online. | 322 # The USB wasn't restarted, but there's at least one device online. |
| 325 # No point in trying to wait for all devices. | 323 # No point in trying to wait for all devices. |
| 326 break | 324 break |
| 327 retries -= 1 | 325 retries -= 1 |
| 328 | 326 |
| 329 # TODO(navabi): Test to make sure this fails and then fix call | 327 # TODO(navabi): Test to make sure this fails and then fix call |
| 330 offline_devices = android_commands.GetAttachedDevices( | 328 offline_devices = android_commands.GetAttachedDevices( |
| 331 hardware=False, emulator=False, offline=True) | 329 hardware=False, emulator=False, offline=True) |
| 332 | 330 |
| 333 types, builds, batteries, reports, errors, json_data = [], [], [], [], [], [] | 331 types, builds, batteries, errors, devices_ok, json_data = ( |
| 334 fail_step_lst = [] | 332 [], [], [], [], [], []) |
| 335 if devices: | 333 if devices: |
| 336 types, builds, batteries, reports, errors, fail_step_lst, json_data = ( | 334 types, builds, batteries, errors, devices_ok, json_data = ( |
| 337 zip(*[DeviceInfo(dev, options) for dev in devices])) | 335 zip(*[DeviceInfo(dev, options) for dev in devices])) |
| 338 | 336 |
| 339 # Write device info to file for buildbot info display. | 337 # Write device info to file for buildbot info display. |
| 340 if os.path.exists('/home/chrome-bot'): | 338 if os.path.exists('/home/chrome-bot'): |
| 341 with open('/home/chrome-bot/.adb_device_info', 'w') as f: | 339 with open('/home/chrome-bot/.adb_device_info', 'w') as f: |
| 342 for device in json_data: | 340 for device in json_data: |
| 343 try: | 341 try: |
| 344 f.write('%s %s %s %.1fC %s%%\n' % (device['serial'], device['type'], | 342 f.write('%s %s %s %.1fC %s%%\n' % (device['serial'], device['type'], |
| 345 device['build'], float(device['battery']['temperature']) / 10, | 343 device['build'], float(device['battery']['temperature']) / 10, |
| 346 device['battery']['level'])) | 344 device['battery']['level'])) |
| 347 except Exception: | 345 except Exception: |
| 348 pass | 346 pass |
| 349 | 347 |
| 350 err_msg = CheckForMissingDevices(options, devices) or [] | 348 err_msg = CheckForMissingDevices(options, devices) or [] |
| 351 | 349 |
| 352 unique_types = list(set(types)) | 350 unique_types = list(set(types)) |
| 353 unique_builds = list(set(builds)) | 351 unique_builds = list(set(builds)) |
| 354 | 352 |
| 355 bb_annotations.PrintMsg('Online devices: %d. Device types %s, builds %s' | 353 bb_annotations.PrintMsg('Online devices: %d. Device types %s, builds %s' |
| 356 % (len(devices), unique_types, unique_builds)) | 354 % (len(devices), unique_types, unique_builds)) |
| 357 print '\n'.join(reports) | 355 |
| 356 for j in json_data: |
| 357 logging.info('Device %s (%s)', j.get('serial'), j.get('type')) |
| 358 logging.info(' Build: %s (%s)', j.get('build'), j.get('build_detail')) |
| 359 logging.info(' Current Battery Service state:') |
| 360 for k, v in j.get('battery', {}).iteritems(): |
| 361 logging.info(' %s: %s', k, v) |
| 362 logging.info(' IMEI slice: %s', j.get('imei_slice')) |
| 363 logging.info(' WiFi IP: %s', j.get('wifi_ip')) |
| 364 |
| 358 | 365 |
| 359 for serial, dev_errors in zip(devices, errors): | 366 for serial, dev_errors in zip(devices, errors): |
| 360 if dev_errors: | 367 if dev_errors: |
| 361 err_msg += ['%s errors:' % serial] | 368 err_msg += ['%s errors:' % serial] |
| 362 err_msg += [' %s' % error for error in dev_errors] | 369 err_msg += [' %s' % error for error in dev_errors] |
| 363 | 370 |
| 364 if err_msg: | 371 if err_msg: |
| 365 bb_annotations.PrintWarning() | 372 bb_annotations.PrintWarning() |
| 366 msg = '\n'.join(err_msg) | 373 for e in err_msg: |
| 367 print msg | 374 logging.error(e) |
| 368 from_address = 'buildbot@chromium.org' | 375 from_address = 'buildbot@chromium.org' |
| 369 to_addresses = ['chromium-android-device-alerts@google.com'] | 376 to_addresses = ['chromium-android-device-alerts@google.com'] |
| 370 bot_name = os.environ.get('BUILDBOT_BUILDERNAME') | 377 bot_name = os.environ.get('BUILDBOT_BUILDERNAME') |
| 371 slave_name = os.environ.get('BUILDBOT_SLAVENAME') | 378 slave_name = os.environ.get('BUILDBOT_SLAVENAME') |
| 372 subject = 'Device status check errors on %s, %s.' % (slave_name, bot_name) | 379 subject = 'Device status check errors on %s, %s.' % (slave_name, bot_name) |
| 373 SendEmail(from_address, to_addresses, [], subject, msg) | 380 SendEmail(from_address, to_addresses, [], subject, '\n'.join(err_msg)) |
| 374 | 381 |
| 375 if options.device_status_dashboard: | 382 if options.device_status_dashboard: |
| 376 perf_tests_results_helper.PrintPerfResult('BotDevices', 'OnlineDevices', | 383 perf_tests_results_helper.PrintPerfResult('BotDevices', 'OnlineDevices', |
| 377 [len(devices)], 'devices') | 384 [len(devices)], 'devices') |
| 378 perf_tests_results_helper.PrintPerfResult('BotDevices', 'OfflineDevices', | 385 perf_tests_results_helper.PrintPerfResult('BotDevices', 'OfflineDevices', |
| 379 [len(offline_devices)], 'devices', | 386 [len(offline_devices)], 'devices', |
| 380 'unimportant') | 387 'unimportant') |
| 381 for serial, battery in zip(devices, batteries): | 388 for serial, battery in zip(devices, batteries): |
| 382 perf_tests_results_helper.PrintPerfResult('DeviceBattery', serial, | 389 perf_tests_results_helper.PrintPerfResult('DeviceBattery', serial, |
| 383 [battery], '%', | 390 [battery], '%', |
| 384 'unimportant') | 391 'unimportant') |
| 385 | 392 |
| 386 if options.json_output: | 393 if options.json_output: |
| 387 with open(options.json_output, 'wb') as f: | 394 with open(options.json_output, 'wb') as f: |
| 388 f.write(json.dumps(json_data, indent=4)) | 395 f.write(json.dumps(json_data, indent=4)) |
| 389 | 396 |
| 390 num_failed_devs = 0 | 397 num_failed_devs = 0 |
| 391 for fail_status, device in zip(fail_step_lst, devices): | 398 for device_ok, device in zip(devices_ok, devices): |
| 392 if not fail_status: | 399 if not device_ok: |
| 400 logging.warning('Blacklisting %s', str(device)) |
| 393 device_blacklist.ExtendBlacklist([str(device)]) | 401 device_blacklist.ExtendBlacklist([str(device)]) |
| 394 num_failed_devs += 1 | 402 num_failed_devs += 1 |
| 395 | 403 |
| 396 if num_failed_devs == len(devices): | 404 if num_failed_devs == len(devices): |
| 397 return 2 | 405 return 2 |
| 398 | 406 |
| 399 if not devices: | 407 if not devices: |
| 400 return 1 | 408 return 1 |
| 401 | 409 |
| 402 | 410 |
| 403 if __name__ == '__main__': | 411 if __name__ == '__main__': |
| 404 sys.exit(main()) | 412 sys.exit(main()) |
| OLD | NEW |