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 logging | 8 import logging |
9 import optparse | 9 import optparse |
10 import os | 10 import os |
11 import smtplib | 11 import smtplib |
12 import sys | 12 import sys |
13 import re | 13 import re |
14 | 14 |
15 import bb_annotations | 15 import bb_annotations |
16 | 16 |
17 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) | 17 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) |
18 from pylib import android_commands | 18 from pylib import android_commands |
19 from pylib import constants | 19 from pylib import constants |
| 20 from pylib import perf_tests_helper |
20 from pylib.cmd_helper import GetCmdOutput | 21 from pylib.cmd_helper import GetCmdOutput |
21 | 22 |
22 | 23 |
23 def DeviceInfo(serial, options): | 24 def DeviceInfo(serial, options): |
24 """Gathers info on a device via various adb calls. | 25 """Gathers info on a device via various adb calls. |
25 | 26 |
26 Args: | 27 Args: |
27 serial: The serial of the attached device to construct info about. | 28 serial: The serial of the attached device to construct info about. |
28 | 29 |
29 Returns: | 30 Returns: |
30 Tuple of device type, build id, report as a string, error messages, and | 31 Tuple of device type, build id, report as a string, error messages, and |
31 boolean indicating whether or not device can be used for testing. | 32 boolean indicating whether or not device can be used for testing. |
32 """ | 33 """ |
33 | 34 |
34 def AdbShellCmd(cmd): | |
35 return GetCmdOutput('adb -s %s shell %s' % (serial, cmd), | |
36 shell=True).strip() | |
37 | |
38 device_adb = android_commands.AndroidCommands(serial) | 35 device_adb = android_commands.AndroidCommands(serial) |
39 | 36 |
40 # TODO(navabi): Replace AdbShellCmd with device_adb. | 37 # TODO(navabi): Replace AdbShellCmd with device_adb. |
41 device_type = AdbShellCmd('getprop ro.build.product') | 38 device_type = device_adb.GetBuildProduct() |
42 device_build = AdbShellCmd('getprop ro.build.id') | 39 device_build = device_adb.GetBuildId() |
43 device_build_type = AdbShellCmd('getprop ro.build.type') | 40 device_build_type = device_adb.GetBuildType() |
44 device_product_name = AdbShellCmd('getprop ro.product.name') | 41 device_product_name = device_adb.GetProductName() |
45 | 42 |
46 setup_wizard_disabled = AdbShellCmd( | 43 setup_wizard_disabled = device_adb.GetSetupWizardStatus() == 'DISABLED' |
47 'getprop ro.setupwizard.mode') == 'DISABLED' | 44 battery = device_adb.GetBatteryInfo() |
48 battery = AdbShellCmd('dumpsys battery') | |
49 install_output = GetCmdOutput( | 45 install_output = GetCmdOutput( |
50 ['%s/build/android/adb_install_apk.py' % constants.DIR_SOURCE_ROOT, '--apk', | 46 ['%s/build/android/adb_install_apk.py' % constants.DIR_SOURCE_ROOT, '--apk', |
51 '%s/build/android/CheckInstallApk-debug.apk' % constants.DIR_SOURCE_ROOT]) | 47 '%s/build/android/CheckInstallApk-debug.apk' % constants.DIR_SOURCE_ROOT]) |
52 install_speed_found = re.findall('(\d+) KB/s', install_output) | 48 install_speed_found = re.findall('(\d+) KB/s', install_output) |
53 if install_speed_found: | 49 if install_speed_found: |
54 install_speed = int(install_speed_found[0]) | 50 install_speed = int(install_speed_found[0]) |
55 else: | 51 else: |
56 install_speed = 'Unknown' | 52 install_speed = 'Unknown' |
57 if 'Error' in battery: | 53 if 'Error' in battery: |
58 ac_power = 'Unknown' | 54 ac_power = 'Unknown' |
59 battery_level = 'Unknown' | 55 battery_level = 'Unknown' |
60 battery_temp = 'Unknown' | 56 battery_temp = 'Unknown' |
61 else: | 57 else: |
62 ac_power = re.findall('AC powered: (\w+)', battery)[0] | 58 ac_power = re.findall('AC powered: (\w+)', battery)[0] |
63 battery_level = int(re.findall('level: (\d+)', battery)[0]) | 59 battery_level = int(re.findall('level: (\d+)', battery)[0]) |
64 battery_temp = float(re.findall('temperature: (\d+)', battery)[0]) / 10 | 60 battery_temp = float(re.findall('temperature: (\d+)', battery)[0]) / 10 |
| 61 sub_info = device_adb.GetSubscriberInfo() |
| 62 imei_slice = re.findall('Device ID = (\d+)', sub_info)[0][-6:] |
65 report = ['Device %s (%s)' % (serial, device_type), | 63 report = ['Device %s (%s)' % (serial, device_type), |
66 ' Build: %s (%s)' % (device_build, | 64 ' Build: %s (%s)' % |
67 AdbShellCmd('getprop ro.build.fingerprint')), | 65 (device_build, device_adb.GetBuildFingerprint()), |
68 ' Battery: %s%%' % battery_level, | 66 ' Battery: %s%%' % battery_level, |
69 ' Battery temp: %s' % battery_temp, | 67 ' Battery temp: %s' % battery_temp, |
70 ' IMEI slice: %s' % AdbShellCmd('dumpsys iphonesubinfo ' | 68 ' IMEI slice: %s' % imei_slice, |
71 '| grep Device' | 69 ' Wifi IP: %s' % device_adb.GetWifiIP(), |
72 "| awk '{print $4}'")[-6:], | |
73 ' Wifi IP: %s' % AdbShellCmd('getprop dhcp.wlan0.ipaddress'), | |
74 ' Install Speed: %s KB/s' % install_speed, | 70 ' Install Speed: %s KB/s' % install_speed, |
75 ''] | 71 ''] |
76 | 72 |
77 errors = [] | 73 errors = [] |
78 if battery_level < 15: | 74 if battery_level < 15: |
79 errors += ['Device critically low in battery. Turning off device.'] | 75 errors += ['Device critically low in battery. Turning off device.'] |
80 if (not setup_wizard_disabled and device_build_type != 'user' and | 76 if (not setup_wizard_disabled and device_build_type != 'user' and |
81 not options.no_provisioning_check): | 77 not options.no_provisioning_check): |
82 errors += ['Setup wizard not disabled. Was it provisioned correctly?'] | 78 errors += ['Setup wizard not disabled. Was it provisioned correctly?'] |
83 if device_product_name == 'mantaray' and ac_power != 'true': | 79 if device_product_name == 'mantaray' and ac_power != 'true': |
84 errors += ['Mantaray device not connected to AC power.'] | 80 errors += ['Mantaray device not connected to AC power.'] |
85 # TODO(navabi): Insert warning once we have a better handle of what install | 81 # TODO(navabi): Insert warning once we have a better handle of what install |
86 # speeds to expect. The following lines were causing too many alerts. | 82 # speeds to expect. The following lines were causing too many alerts. |
87 # if install_speed < 500: | 83 # if install_speed < 500: |
88 # errors += ['Device install speed too low. Do not use for testing.'] | 84 # errors += ['Device install speed too low. Do not use for testing.'] |
89 | 85 |
90 # Causing the device status check step fail for slow install speed or low | 86 # Causing the device status check step fail for slow install speed or low |
91 # battery currently is too disruptive to the bots (especially try bots). | 87 # battery currently is too disruptive to the bots (especially try bots). |
92 # Turn off devices with low battery and the step does not fail. | 88 # Turn off devices with low battery and the step does not fail. |
93 if battery_level < 15: | 89 if battery_level < 15: |
94 device_adb.EnableAdbRoot() | 90 device_adb.EnableAdbRoot() |
95 AdbShellCmd('reboot -p') | 91 device_adb.Shutdown() |
96 return device_type, device_build, '\n'.join(report), errors, True | 92 full_report = '\n'.join(report) |
| 93 return device_type, device_build, battery_level, full_report, errors, True |
97 | 94 |
98 | 95 |
99 def CheckForMissingDevices(options, adb_online_devs): | 96 def CheckForMissingDevices(options, adb_online_devs): |
100 """Uses file of previous online devices to detect broken phones. | 97 """Uses file of previous online devices to detect broken phones. |
101 | 98 |
102 Args: | 99 Args: |
103 options: out_dir parameter of options argument is used as the base | 100 options: out_dir parameter of options argument is used as the base |
104 directory to load and update the cache file. | 101 directory to load and update the cache file. |
105 adb_online_devs: A list of serial numbers of the currently visible | 102 adb_online_devs: A list of serial numbers of the currently visible |
106 and online attached devices. | 103 and online attached devices. |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
182 print 'Failed to send alert email. Error: %s' % e | 179 print 'Failed to send alert email. Error: %s' % e |
183 | 180 |
184 | 181 |
185 def main(): | 182 def main(): |
186 parser = optparse.OptionParser() | 183 parser = optparse.OptionParser() |
187 parser.add_option('', '--out-dir', | 184 parser.add_option('', '--out-dir', |
188 help='Directory where the device path is stored', | 185 help='Directory where the device path is stored', |
189 default=os.path.join(constants.DIR_SOURCE_ROOT, 'out')) | 186 default=os.path.join(constants.DIR_SOURCE_ROOT, 'out')) |
190 parser.add_option('--no-provisioning-check', | 187 parser.add_option('--no-provisioning-check', |
191 help='Will not check if devices are provisioned properly.') | 188 help='Will not check if devices are provisioned properly.') |
192 | 189 parser.add_option('--device-status-dashboard', |
| 190 help='Output device status data for dashboard.') |
193 options, args = parser.parse_args() | 191 options, args = parser.parse_args() |
194 if args: | 192 if args: |
195 parser.error('Unknown options %s' % args) | 193 parser.error('Unknown options %s' % args) |
196 devices = android_commands.GetAttachedDevices() | 194 devices = android_commands.GetAttachedDevices() |
197 types, builds, reports, errors = [], [], [], [] | 195 # TODO(navabi): Test to make sure this fails and then fix call |
| 196 offline_devices = android_commands.GetAttachedDevices(hardware=False, |
| 197 emulator=False, |
| 198 offline=True) |
| 199 |
| 200 types, builds, batteries, reports, errors = [], [], [], [], [] |
198 fail_step_lst = [] | 201 fail_step_lst = [] |
199 if devices: | 202 if devices: |
200 types, builds, reports, errors, fail_step_lst = ( | 203 types, builds, batteries, reports, errors, fail_step_lst = ( |
201 zip(*[DeviceInfo(dev, options) for dev in devices])) | 204 zip(*[DeviceInfo(dev, options) for dev in devices])) |
202 | 205 |
203 err_msg = CheckForMissingDevices(options, devices) or [] | 206 err_msg = CheckForMissingDevices(options, devices) or [] |
204 | 207 |
205 unique_types = list(set(types)) | 208 unique_types = list(set(types)) |
206 unique_builds = list(set(builds)) | 209 unique_builds = list(set(builds)) |
207 | 210 |
208 bb_annotations.PrintMsg('Online devices: %d. Device types %s, builds %s' | 211 bb_annotations.PrintMsg('Online devices: %d. Device types %s, builds %s' |
209 % (len(devices), unique_types, unique_builds)) | 212 % (len(devices), unique_types, unique_builds)) |
210 print '\n'.join(reports) | 213 print '\n'.join(reports) |
211 | 214 |
212 for serial, dev_errors in zip(devices, errors): | 215 for serial, dev_errors in zip(devices, errors): |
213 if dev_errors: | 216 if dev_errors: |
214 err_msg += ['%s errors:' % serial] | 217 err_msg += ['%s errors:' % serial] |
215 err_msg += [' %s' % error for error in dev_errors] | 218 err_msg += [' %s' % error for error in dev_errors] |
216 | 219 |
217 if err_msg: | 220 if err_msg: |
218 bb_annotations.PrintWarning() | 221 bb_annotations.PrintWarning() |
219 msg = '\n'.join(err_msg) | 222 msg = '\n'.join(err_msg) |
220 print msg | 223 print msg |
221 SendDeviceStatusAlert(msg) | 224 SendDeviceStatusAlert(msg) |
222 | 225 |
| 226 if options.device_status_dashboard: |
| 227 perf_tests_helper.PrintPerfResult('BotDevices', 'OnlineDevices', |
| 228 [len(devices)], 'devices') |
| 229 perf_tests_helper.PrintPerfResult('BotDevices', 'OfflineDevices', |
| 230 [len(offline_devices)], 'devices', |
| 231 'unimportant') |
| 232 for serial, battery in zip(devices, batteries): |
| 233 perf_tests_helper.PrintPerfResult('DeviceBattery', serial, [battery], '%', |
| 234 'unimportant') |
| 235 |
223 if False in fail_step_lst: | 236 if False in fail_step_lst: |
224 # TODO(navabi): Build fails on device status check step if there exists any | 237 # TODO(navabi): Build fails on device status check step if there exists any |
225 # devices with critically low battery or install speed. Remove those devices | 238 # devices with critically low battery or install speed. Remove those devices |
226 # from testing, allowing build to continue with good devices. | 239 # from testing, allowing build to continue with good devices. |
227 return 1 | 240 return 1 |
228 | 241 |
229 if not devices: | 242 if not devices: |
230 return 1 | 243 return 1 |
231 | 244 |
232 | 245 |
233 if __name__ == '__main__': | 246 if __name__ == '__main__': |
234 sys.exit(main()) | 247 sys.exit(main()) |
OLD | NEW |