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] | |
Isaac (away)
2013/03/05 07:12:22
This will throw an exception if the regex does not
Siva Chandra
2013/03/13 02:18:33
As per xsdg@, yes.
| |
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 warnings = [] |
61 errors = [] | |
58 if battery_level < 5: | 62 if battery_level < 5: |
59 warnings += ['critically low battery'] | 63 warnings += ['\tDevice critically low in battery.\n'] |
60 if not setup_wizard_disabled: | 64 if not setup_wizard_disabled: |
61 warnings += ['Setup wizard not disabled. Was it provisioned correctly?'] | 65 warnings += ['\tSetup wizard not disabled. Was it provisioned correctly?\n'] |
66 if device_product_name == 'mantaray' and ac_power == 'false': | |
Isaac (away)
2013/03/05 07:12:22
I would prefer a positive check, i.e. ac_power !=
Siva Chandra
2013/03/13 02:18:33
Done.
| |
67 errors += ['\tMantaray device not connected to AC power.\n'] | |
68 if warnings: | |
69 warnings = [''.join(warnings)] | |
Isaac (away)
2013/03/05 07:12:22
What is this code for?
Siva Chandra
2013/03/13 02:18:33
Now removed.
| |
70 if errors: | |
71 errors = [''.join(errors)] | |
62 | 72 |
63 return device_type, device_build, '\n'.join(report), warnings | 73 return device_type, device_build, '\n'.join(report), warnings, errors |
64 | 74 |
65 | 75 |
66 def CheckForMissingDevices(options, adb_online_devs): | 76 def CheckForMissingDevices(options, adb_online_devs): |
67 """Uses file of previous online devices to detect broken phones. | 77 """Uses file of previous online devices to detect broken phones. |
68 | 78 |
69 Args: | 79 Args: |
70 options: out_dir parameter of options argument is used as the base | 80 options: out_dir parameter of options argument is used as the base |
71 directory to load and update the cache file. | 81 directory to load and update the cache file. |
72 adb_online_devs: A list of serial numbers of the currently visible | 82 adb_online_devs: A list of serial numbers of the currently visible |
73 and online attached devices. | 83 and online attached devices. |
(...skipping 21 matching lines...) Expand all Loading... | |
95 if not os.path.exists(out_dir): | 105 if not os.path.exists(out_dir): |
96 os.makedirs(out_dir) | 106 os.makedirs(out_dir) |
97 with open(path, 'w') as f: | 107 with open(path, 'w') as f: |
98 # Write devices currently visible plus devices previously seen. | 108 # Write devices currently visible plus devices previously seen. |
99 f.write('\n'.join(set(device_list))) | 109 f.write('\n'.join(set(device_list))) |
100 | 110 |
101 last_devices_path = os.path.join(out_dir, '.last_devices') | 111 last_devices_path = os.path.join(out_dir, '.last_devices') |
102 last_devices = ReadDeviceList('.last_devices') | 112 last_devices = ReadDeviceList('.last_devices') |
103 | 113 |
104 missing_devs = list(set(last_devices) - set(adb_online_devs)) | 114 missing_devs = list(set(last_devices) - set(adb_online_devs)) |
115 err_msg = '' | |
105 if missing_devs: | 116 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() | 117 buildbot_report.PrintWarning() |
115 devices_missing_msg = '%d devices not detected.' % len(missing_devs) | 118 devices_missing_msg = '%d devices not detected.' % len(missing_devs) |
116 buildbot_report.PrintSummaryText(devices_missing_msg) | 119 buildbot_report.PrintSummaryText(devices_missing_msg) |
117 | 120 |
118 # TODO(navabi): Debug by printing both output from GetCmdOutput and | 121 # TODO(navabi): Debug by printing both output from GetCmdOutput and |
119 # GetAttachedDevices to compare results. | 122 # GetAttachedDevices to compare results. |
120 body = '\n'.join( | 123 err = '\n'.join( |
121 ['Current online devices: %s' % adb_online_devs, | 124 ['Current online devices: %s' % adb_online_devs, |
122 '%s are no longer visible. Were they removed?\n' % missing_devs, | 125 '%s are no longer visible. Were they removed?\n' % missing_devs, |
123 'SHERIFF: See go/chrome_device_monitor', | 126 'SHERIFF: See go/chrome_device_monitor', |
124 'Cache file: %s\n\n' % last_devices_path, | 127 'Cache file: %s\n\n' % last_devices_path, |
125 'adb devices: %s' % GetCmdOutput(['adb', 'devices']), | 128 'adb devices: %s' % GetCmdOutput(['adb', 'devices']), |
126 'adb devices(GetAttachedDevices): %s' % | 129 'adb devices(GetAttachedDevices): %s' % |
127 android_commands.GetAttachedDevices()]) | 130 android_commands.GetAttachedDevices()]) |
128 | 131 |
129 print body | 132 print err |
130 | 133 |
131 # Only send email if the first time a particular device goes offline | 134 # Only send email if the first time a particular device goes offline |
132 last_missing = ReadDeviceList('.last_missing') | 135 last_missing = ReadDeviceList('.last_missing') |
133 new_missing_devs = set(missing_devs) - set(last_missing) | 136 new_missing_devs = set(missing_devs) - set(last_missing) |
134 | 137 |
135 if new_missing_devs and bot_name: | 138 if new_missing_devs: |
136 msg_body = '\r\n'.join( | 139 err_msg = err |
Isaac (away)
2013/03/05 07:12:22
Just create the message here and return directly
Siva Chandra
2013/03/13 02:18:33
Done.
| |
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: | 140 else: |
148 new_devs = set(adb_online_devs) - set(last_devices) | 141 new_devs = set(adb_online_devs) - set(last_devices) |
149 if new_devs and os.path.exists(last_devices_path): | 142 if new_devs and os.path.exists(last_devices_path): |
150 buildbot_report.PrintWarning() | 143 buildbot_report.PrintWarning() |
151 buildbot_report.PrintSummaryText( | 144 buildbot_report.PrintSummaryText( |
152 '%d new devices detected' % len(new_devs)) | 145 '%d new devices detected' % len(new_devs)) |
153 print ('New devices detected %s. And now back to your ' | 146 print ('New devices detected %s. And now back to your ' |
154 'regularly scheduled program.' % list(new_devs)) | 147 'regularly scheduled program.' % list(new_devs)) |
155 WriteDeviceList('.last_devices', (adb_online_devs + last_devices)) | 148 WriteDeviceList('.last_devices', (adb_online_devs + last_devices)) |
156 WriteDeviceList('.last_missing', missing_devs) | 149 WriteDeviceList('.last_missing', missing_devs) |
157 | 150 |
151 return err_msg | |
152 | |
153 | |
154 def SendDeviceStatusAlert(msg): | |
155 if not msg: | |
156 return | |
Isaac (away)
2013/03/05 07:12:22
remove 155-156. Don't call this function if you d
Siva Chandra
2013/03/13 02:18:33
Done.
| |
157 from_address = 'buildbot@chromium.org' | |
158 to_address = 'chromium-android-device-alerts@google.com' | |
159 bot_name = os.environ.get('BUILDBOT_BUILDERNAME') | |
160 slave_name = os.environ.get('BUILDBOT_SLAVENAME') | |
161 subject = 'Device status check errors on %s, %s.' % (slave_name, bot_name) | |
162 msg_body = '\r\n'.join(['From: %s' % from_address, 'To: %s' % to_address, | |
163 'Subject: %s' % subject, '', msg]) | |
164 try: | |
165 server = smtplib.SMTP('localhost') | |
166 server.sendmail(from_address, [to_address], msg_body) | |
167 server.quit() | |
168 except Exception as e: | |
169 print 'Failed to send alert email. Error: %s' % e | |
170 | |
158 | 171 |
159 def main(): | 172 def main(): |
160 parser = optparse.OptionParser() | 173 parser = optparse.OptionParser() |
161 parser.add_option('', '--out-dir', | 174 parser.add_option('', '--out-dir', |
162 help='Directory where the device path is stored', | 175 help='Directory where the device path is stored', |
163 default=os.path.join(os.path.dirname(__file__), '..', | 176 default=os.path.join(os.path.dirname(__file__), '..', |
164 '..', 'out')) | 177 '..', 'out')) |
165 | 178 |
166 options, args = parser.parse_args() | 179 options, args = parser.parse_args() |
167 if args: | 180 if args: |
168 parser.error('Unknown options %s' % args) | 181 parser.error('Unknown options %s' % args) |
169 devices = android_commands.GetAttachedDevices() | 182 devices = android_commands.GetAttachedDevices() |
170 types, builds, reports, errors = [], [], [], [] | 183 types, builds, reports, warnings, errors = [], [], [], [], [] |
Isaac (away)
2013/03/05 07:12:22
It's not clear to me that we need a new class of a
Siva Chandra
2013/03/13 02:18:33
Converted everything into an error.
| |
171 if devices: | 184 if devices: |
172 types, builds, reports, errors = zip(*[DeviceInfo(dev) for dev in devices]) | 185 types, builds, reports, warnings, errors = zip( |
186 *[DeviceInfo(dev) for dev in devices]) | |
187 | |
188 missing_devices_msg = CheckForMissingDevices(options, devices) | |
173 | 189 |
174 unique_types = list(set(types)) | 190 unique_types = list(set(types)) |
175 unique_builds = list(set(builds)) | 191 unique_builds = list(set(builds)) |
176 | 192 |
177 buildbot_report.PrintMsg('Online devices: %d. Device types %s, builds %s' | 193 buildbot_report.PrintMsg('Online devices: %d. Device types %s, builds %s' |
178 % (len(devices), unique_types, unique_builds)) | 194 % (len(devices), unique_types, unique_builds)) |
179 print '\n'.join(reports) | 195 print '\n'.join(reports) |
180 | 196 |
197 full_warnings = [] | |
181 full_errors = [] | 198 full_errors = [] |
182 for serial, device_errors in zip(devices, errors): | 199 device_info_err_msg = '' |
183 full_errors.extend('%s: %s' % (serial, error) for error in device_errors) | 200 for serial, dev_warnings in zip(devices, warnings): |
201 full_warnings.extend('%s:\n%s' % (serial, error) for error in dev_warnings) | |
202 for serial, dev_errors in zip(devices, errors): | |
203 full_errors.extend('%s:\n%s' % (serial, error) for error in dev_errors) | |
204 | |
205 if full_warnings or full_errors or missing_devices_msg: | |
206 buildbot_report.PrintWarning() | |
207 | |
208 if full_warnings: | |
209 print '\n'.join(full_warnings) | |
184 if full_errors: | 210 if full_errors: |
185 buildbot_report.PrintWarning() | 211 device_info_err_msg = '\n'.join(full_errors) |
186 print '\n'.join(full_errors) | 212 print device_info_err_msg |
187 | 213 |
188 CheckForMissingDevices(options, devices) | 214 SendDeviceStatusAlert(missing_devices_msg + device_info_err_msg) |
215 | |
189 | 216 |
190 if __name__ == '__main__': | 217 if __name__ == '__main__': |
191 sys.exit(main()) | 218 sys.exit(main()) |
OLD | NEW |