| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # | 2 # |
| 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2012 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 logging | 8 import logging |
| 9 import optparse | 9 import optparse |
| 10 import os | 10 import os |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 Returns: | 26 Returns: |
| 27 Tuple of device type, build id and report as a string. | 27 Tuple of device type, build id and report as a string. |
| 28 """ | 28 """ |
| 29 | 29 |
| 30 def AdbShellCmd(cmd): | 30 def AdbShellCmd(cmd): |
| 31 return GetCmdOutput('adb -s %s shell %s' % (serial, cmd), | 31 return GetCmdOutput('adb -s %s shell %s' % (serial, cmd), |
| 32 shell=True).strip() | 32 shell=True).strip() |
| 33 | 33 |
| 34 device_type = AdbShellCmd('getprop ro.build.product') | 34 device_type = AdbShellCmd('getprop ro.build.product') |
| 35 device_build = AdbShellCmd('getprop ro.build.id') | 35 device_build = AdbShellCmd('getprop ro.build.id') |
| 36 device_product_name = AdbShellCmd('getprop ro.product.name') |
| 36 | 37 |
| 37 setup_wizard_disabled = AdbShellCmd( | 38 setup_wizard_disabled = AdbShellCmd( |
| 38 'getprop ro.setupwizard.mode') == 'DISABLED' | 39 'getprop ro.setupwizard.mode') == 'DISABLED' |
| 39 battery = AdbShellCmd('dumpsys battery') | 40 battery = AdbShellCmd('dumpsys battery') |
| 40 if 'Error' in battery: | 41 if 'Error' in battery: |
| 42 ac_power = 'Unknown' |
| 41 battery_level = 'Unknown' | 43 battery_level = 'Unknown' |
| 42 battery_temp = 'Unknown' | 44 battery_temp = 'Unknown' |
| 43 else: | 45 else: |
| 46 ac_power = re.findall('AC powered: (\w+)', battery)[0] |
| 44 battery_level = int(re.findall('level: (\d+)', battery)[0]) | 47 battery_level = int(re.findall('level: (\d+)', battery)[0]) |
| 45 battery_temp = float(re.findall('temperature: (\d+)', battery)[0]) / 10 | 48 battery_temp = float(re.findall('temperature: (\d+)', battery)[0]) / 10 |
| 46 report = ['Device %s (%s)' % (serial, device_type), | 49 report = ['Device %s (%s)' % (serial, device_type), |
| 47 ' Build: %s (%s)' % (device_build, | 50 ' Build: %s (%s)' % (device_build, |
| 48 AdbShellCmd('getprop ro.build.fingerprint')), | 51 AdbShellCmd('getprop ro.build.fingerprint')), |
| 49 ' Battery: %s%%' % battery_level, | 52 ' Battery: %s%%' % battery_level, |
| 50 ' Battery temp: %s' % battery_temp, | 53 ' Battery temp: %s' % battery_temp, |
| 51 ' IMEI slice: %s' % AdbShellCmd('dumpsys iphonesubinfo ' | 54 ' IMEI slice: %s' % AdbShellCmd('dumpsys iphonesubinfo ' |
| 52 '| grep Device' | 55 '| grep Device' |
| 53 "| awk '{print $4}'")[-6:], | 56 "| awk '{print $4}'")[-6:], |
| 54 ' Wifi IP: %s' % AdbShellCmd('getprop dhcp.wlan0.ipaddress'), | 57 ' Wifi IP: %s' % AdbShellCmd('getprop dhcp.wlan0.ipaddress'), |
| 55 ''] | 58 ''] |
| 56 | 59 |
| 57 warnings = [] | 60 errors = [] |
| 58 if battery_level < 5: | 61 if battery_level < 5: |
| 59 warnings += ['critically low battery'] | 62 errors += ['Device critically low in battery.'] |
| 60 if not setup_wizard_disabled: | 63 if not setup_wizard_disabled: |
| 61 warnings += ['Setup wizard not disabled. Was it provisioned correctly?'] | 64 errors += ['Setup wizard not disabled. Was it provisioned correctly?'] |
| 65 if device_product_name == 'mantaray' and ac_power != 'true': |
| 66 errors += ['Mantaray device not connected to AC power.'] |
| 62 | 67 |
| 63 return device_type, device_build, '\n'.join(report), warnings | 68 return device_type, device_build, '\n'.join(report), errors |
| 64 | 69 |
| 65 | 70 |
| 66 def CheckForMissingDevices(options, adb_online_devs): | 71 def CheckForMissingDevices(options, adb_online_devs): |
| 67 """Uses file of previous online devices to detect broken phones. | 72 """Uses file of previous online devices to detect broken phones. |
| 68 | 73 |
| 69 Args: | 74 Args: |
| 70 options: out_dir parameter of options argument is used as the base | 75 options: out_dir parameter of options argument is used as the base |
| 71 directory to load and update the cache file. | 76 directory to load and update the cache file. |
| 72 adb_online_devs: A list of serial numbers of the currently visible | 77 adb_online_devs: A list of serial numbers of the currently visible |
| 73 and online attached devices. | 78 and online attached devices. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 93 def WriteDeviceList(file_name, device_list): | 98 def WriteDeviceList(file_name, device_list): |
| 94 path = os.path.join(out_dir, file_name) | 99 path = os.path.join(out_dir, file_name) |
| 95 if not os.path.exists(out_dir): | 100 if not os.path.exists(out_dir): |
| 96 os.makedirs(out_dir) | 101 os.makedirs(out_dir) |
| 97 with open(path, 'w') as f: | 102 with open(path, 'w') as f: |
| 98 # Write devices currently visible plus devices previously seen. | 103 # Write devices currently visible plus devices previously seen. |
| 99 f.write('\n'.join(set(device_list))) | 104 f.write('\n'.join(set(device_list))) |
| 100 | 105 |
| 101 last_devices_path = os.path.join(out_dir, '.last_devices') | 106 last_devices_path = os.path.join(out_dir, '.last_devices') |
| 102 last_devices = ReadDeviceList('.last_devices') | 107 last_devices = ReadDeviceList('.last_devices') |
| 108 missing_devs = list(set(last_devices) - set(adb_online_devs)) |
| 103 | 109 |
| 104 missing_devs = list(set(last_devices) - set(adb_online_devs)) | 110 WriteDeviceList('.last_devices', (adb_online_devs + last_devices)) |
| 111 WriteDeviceList('.last_missing', missing_devs) |
| 112 |
| 105 if missing_devs: | 113 if missing_devs: |
| 106 from_address = 'buildbot@chromium.org' | |
| 107 to_address = 'chromium-android-device-alerts@google.com' | |
| 108 bot_name = os.environ.get('BUILDBOT_BUILDERNAME') | |
| 109 slave_name = os.environ.get('BUILDBOT_SLAVENAME') | |
| 110 num_online_devs = len(adb_online_devs) | |
| 111 subject = 'Devices offline on %s, %s (%d remaining).' % (slave_name, | |
| 112 bot_name, | |
| 113 num_online_devs) | |
| 114 buildbot_report.PrintWarning() | |
| 115 devices_missing_msg = '%d devices not detected.' % len(missing_devs) | 114 devices_missing_msg = '%d devices not detected.' % len(missing_devs) |
| 116 buildbot_report.PrintSummaryText(devices_missing_msg) | 115 buildbot_report.PrintSummaryText(devices_missing_msg) |
| 117 | 116 |
| 118 # TODO(navabi): Debug by printing both output from GetCmdOutput and | 117 # TODO(navabi): Debug by printing both output from GetCmdOutput and |
| 119 # GetAttachedDevices to compare results. | 118 # GetAttachedDevices to compare results. |
| 120 body = '\n'.join( | 119 return ['Current online devices: %s' % adb_online_devs, |
| 121 ['Current online devices: %s' % adb_online_devs, | 120 '%s are no longer visible. Were they removed?\n' % missing_devs, |
| 122 '%s are no longer visible. Were they removed?\n' % missing_devs, | 121 'SHERIFF: See go/chrome_device_monitor', |
| 123 'SHERIFF: See go/chrome_device_monitor', | 122 'Cache file: %s\n\n' % last_devices_path, |
| 124 'Cache file: %s\n\n' % last_devices_path, | 123 'adb devices: %s' % GetCmdOutput(['adb', 'devices']), |
| 125 'adb devices: %s' % GetCmdOutput(['adb', 'devices']), | 124 'adb devices(GetAttachedDevices): %s' % |
| 126 'adb devices(GetAttachedDevices): %s' % | 125 android_commands.GetAttachedDevices()] |
| 127 android_commands.GetAttachedDevices()]) | |
| 128 | |
| 129 print body | |
| 130 | |
| 131 # Only send email if the first time a particular device goes offline | |
| 132 last_missing = ReadDeviceList('.last_missing') | |
| 133 new_missing_devs = set(missing_devs) - set(last_missing) | |
| 134 | |
| 135 if new_missing_devs and bot_name: | |
| 136 msg_body = '\r\n'.join( | |
| 137 ['From: %s' % from_address, | |
| 138 'To: %s' % to_address, | |
| 139 'Subject: %s' % subject, | |
| 140 '', body]) | |
| 141 try: | |
| 142 server = smtplib.SMTP('localhost') | |
| 143 server.sendmail(from_address, [to_address], msg_body) | |
| 144 server.quit() | |
| 145 except Exception as e: | |
| 146 print 'Failed to send alert email. Error: %s' % e | |
| 147 else: | 126 else: |
| 148 new_devs = set(adb_online_devs) - set(last_devices) | 127 new_devs = set(adb_online_devs) - set(last_devices) |
| 149 if new_devs and os.path.exists(last_devices_path): | 128 if new_devs and os.path.exists(last_devices_path): |
| 150 buildbot_report.PrintWarning() | 129 buildbot_report.PrintWarning() |
| 151 buildbot_report.PrintSummaryText( | 130 buildbot_report.PrintSummaryText( |
| 152 '%d new devices detected' % len(new_devs)) | 131 '%d new devices detected' % len(new_devs)) |
| 153 print ('New devices detected %s. And now back to your ' | 132 print ('New devices detected %s. And now back to your ' |
| 154 'regularly scheduled program.' % list(new_devs)) | 133 'regularly scheduled program.' % list(new_devs)) |
| 155 WriteDeviceList('.last_devices', (adb_online_devs + last_devices)) | 134 |
| 156 WriteDeviceList('.last_missing', missing_devs) | 135 |
| 136 def SendDeviceStatusAlert(msg): |
| 137 from_address = 'buildbot@chromium.org' |
| 138 to_address = 'chromium-android-device-alerts@google.com' |
| 139 bot_name = os.environ.get('BUILDBOT_BUILDERNAME') |
| 140 slave_name = os.environ.get('BUILDBOT_SLAVENAME') |
| 141 subject = 'Device status check errors on %s, %s.' % (slave_name, bot_name) |
| 142 msg_body = '\r\n'.join(['From: %s' % from_address, 'To: %s' % to_address, |
| 143 'Subject: %s' % subject, '', msg]) |
| 144 try: |
| 145 server = smtplib.SMTP('localhost') |
| 146 server.sendmail(from_address, [to_address], msg_body) |
| 147 server.quit() |
| 148 except Exception as e: |
| 149 print 'Failed to send alert email. Error: %s' % e |
| 157 | 150 |
| 158 | 151 |
| 159 def main(): | 152 def main(): |
| 160 parser = optparse.OptionParser() | 153 parser = optparse.OptionParser() |
| 161 parser.add_option('', '--out-dir', | 154 parser.add_option('', '--out-dir', |
| 162 help='Directory where the device path is stored', | 155 help='Directory where the device path is stored', |
| 163 default=os.path.join(os.path.dirname(__file__), '..', | 156 default=os.path.join(os.path.dirname(__file__), '..', |
| 164 '..', 'out')) | 157 '..', 'out')) |
| 165 | 158 |
| 166 options, args = parser.parse_args() | 159 options, args = parser.parse_args() |
| 167 if args: | 160 if args: |
| 168 parser.error('Unknown options %s' % args) | 161 parser.error('Unknown options %s' % args) |
| 169 devices = android_commands.GetAttachedDevices() | 162 devices = android_commands.GetAttachedDevices() |
| 170 types, builds, reports, errors = [], [], [], [] | 163 types, builds, reports, errors = [], [], [], [] |
| 171 if devices: | 164 if devices: |
| 172 types, builds, reports, errors = zip(*[DeviceInfo(dev) for dev in devices]) | 165 types, builds, reports, errors = zip(*[DeviceInfo(dev) for dev in devices]) |
| 173 | 166 |
| 167 err_msg = CheckForMissingDevices(options, devices) or [] |
| 168 |
| 174 unique_types = list(set(types)) | 169 unique_types = list(set(types)) |
| 175 unique_builds = list(set(builds)) | 170 unique_builds = list(set(builds)) |
| 176 | 171 |
| 177 buildbot_report.PrintMsg('Online devices: %d. Device types %s, builds %s' | 172 buildbot_report.PrintMsg('Online devices: %d. Device types %s, builds %s' |
| 178 % (len(devices), unique_types, unique_builds)) | 173 % (len(devices), unique_types, unique_builds)) |
| 179 print '\n'.join(reports) | 174 print '\n'.join(reports) |
| 180 | 175 |
| 181 full_errors = [] | 176 for serial, dev_errors in zip(devices, errors): |
| 182 for serial, device_errors in zip(devices, errors): | 177 if dev_errors: |
| 183 full_errors.extend('%s: %s' % (serial, error) for error in device_errors) | 178 err_msg += ['%s errors:' % serial] |
| 184 if full_errors: | 179 err_msg += [' %s' % error for error in dev_errors] |
| 180 |
| 181 if err_msg: |
| 185 buildbot_report.PrintWarning() | 182 buildbot_report.PrintWarning() |
| 186 print '\n'.join(full_errors) | 183 msg = '\n'.join(err_msg) |
| 184 print msg |
| 185 SendDeviceStatusAlert(msg) |
| 186 return 1 |
| 187 | 187 |
| 188 CheckForMissingDevices(options, devices) | |
| 189 | 188 |
| 190 if __name__ == '__main__': | 189 if __name__ == '__main__': |
| 191 sys.exit(main()) | 190 sys.exit(main()) |
| OLD | NEW |