| 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 |