Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: build/android/provision_devices.py

Issue 434193002: [Android] Parallelize provision_devices.py. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | build/android/pylib/device/device_blacklist.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright (c) 2013 The Chromium Authors. All rights reserved. 3 # Copyright (c) 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 """Provisions Android devices with settings required for bots. 7 """Provisions Android devices with settings required for bots.
8 8
9 Usage: 9 Usage:
10 ./provision_devices.py [-d <device serial number>] 10 ./provision_devices.py [-d <device serial number>]
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
42 42
43 def LaunchHostHeartbeat(): 43 def LaunchHostHeartbeat():
44 # Kill if existing host_heartbeat 44 # Kill if existing host_heartbeat
45 KillHostHeartbeat() 45 KillHostHeartbeat()
46 # Launch a new host_heartbeat 46 # Launch a new host_heartbeat
47 logging.info('Spawning host heartbeat...') 47 logging.info('Spawning host heartbeat...')
48 subprocess.Popen([os.path.join(constants.DIR_SOURCE_ROOT, 48 subprocess.Popen([os.path.join(constants.DIR_SOURCE_ROOT,
49 'build/android/host_heartbeat.py')]) 49 'build/android/host_heartbeat.py')])
50 50
51 51
52 def PushAndLaunchAdbReboot(devices, target): 52 def PushAndLaunchAdbReboot(device, target):
53 """Pushes and launches the adb_reboot binary on the device. 53 """Pushes and launches the adb_reboot binary on the device.
54 54
55 Arguments: 55 Arguments:
56 devices: The list of serial numbers of the device to which the 56 device: The DeviceUtils instance for the device to which the adb_reboot
57 adb_reboot binary should be pushed. 57 binary should be pushed.
58 target : The build target (example, Debug or Release) which helps in 58 target: The build target (example, Debug or Release) which helps in
59 locating the adb_reboot binary. 59 locating the adb_reboot binary.
60 """ 60 """
61 for device_serial in devices: 61 logging.info('Will push and launch adb_reboot on %s' % str(device))
62 logging.info('Will push and launch adb_reboot on %s' % device_serial) 62 # Kill if adb_reboot is already running.
63 device = device_utils.DeviceUtils(device_serial) 63 try:
64 # Kill if adb_reboot is already running. 64 # Don't try to kill adb_reboot more than once. We don't expect it to be
65 try: 65 # running at all.
66 # Don't try to kill adb_reboot more than once. We don't expect it to be 66 device.KillAll('adb_reboot', blocking=True, timeout=2, retries=0)
67 # running at all. 67 except device_errors.CommandFailedError:
68 device.KillAll('adb_reboot', blocking=True, timeout=2, retries=0) 68 # We can safely ignore the exception because we don't expect adb_reboot
69 except device_errors.CommandFailedError: 69 # to be running.
70 # We can safely ignore the exception because we don't expect adb_reboot 70 pass
71 # to be running. 71 # Push adb_reboot
72 pass 72 logging.info(' Pushing adb_reboot ...')
73 # Push adb_reboot 73 adb_reboot = os.path.join(constants.DIR_SOURCE_ROOT,
74 logging.info(' Pushing adb_reboot ...') 74 'out/%s/adb_reboot' % target)
75 adb_reboot = os.path.join(constants.DIR_SOURCE_ROOT, 75 device.PushChangedFiles(adb_reboot, '/data/local/tmp/')
76 'out/%s/adb_reboot' % target) 76 # Launch adb_reboot
77 device.PushChangedFiles(adb_reboot, '/data/local/tmp/') 77 logging.info(' Launching adb_reboot ...')
78 # Launch adb_reboot 78 device.old_interface.GetAndroidToolStatusAndOutput(
79 logging.info(' Launching adb_reboot ...') 79 '/data/local/tmp/adb_reboot')
80 device.old_interface.GetAndroidToolStatusAndOutput(
81 '/data/local/tmp/adb_reboot')
82 LaunchHostHeartbeat()
83 80
84 81
85 def _ConfigureLocalProperties(device, is_perf): 82 def _ConfigureLocalProperties(device, is_perf):
86 """Set standard readonly testing device properties prior to reboot.""" 83 """Set standard readonly testing device properties prior to reboot."""
87 local_props = [ 84 local_props = [
85 'persist.sys.usb.config=adb',
88 'ro.monkey=1', 86 'ro.monkey=1',
89 'ro.test_harness=1', 87 'ro.test_harness=1',
90 'ro.audio.silent=1', 88 'ro.audio.silent=1',
91 'ro.setupwizard.mode=DISABLED', 89 'ro.setupwizard.mode=DISABLED',
92 ] 90 ]
93 if not is_perf: 91 if not is_perf:
94 local_props.append('%s=all' % android_commands.JAVA_ASSERT_PROPERTY) 92 local_props.append('%s=all' % android_commands.JAVA_ASSERT_PROPERTY)
95 local_props.append('debug.checkjni=1') 93 local_props.append('debug.checkjni=1')
96 try: 94 try:
97 device.WriteFile( 95 device.WriteFile(
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
131 device.RunShellCommand('restorecon %s' % dir_path, as_root=True) 129 device.RunShellCommand('restorecon %s' % dir_path, as_root=True)
132 device.RunShellCommand('echo %s > %s' % 130 device.RunShellCommand('echo %s > %s' %
133 (adb_keys[0], constants.ADB_KEYS_FILE), as_root=True) 131 (adb_keys[0], constants.ADB_KEYS_FILE), as_root=True)
134 for adb_key in adb_keys[1:]: 132 for adb_key in adb_keys[1:]:
135 device.RunShellCommand( 133 device.RunShellCommand(
136 'echo %s >> %s' % (adb_key, constants.ADB_KEYS_FILE), as_root=True) 134 'echo %s >> %s' % (adb_key, constants.ADB_KEYS_FILE), as_root=True)
137 device.RunShellCommand('restorecon %s' % constants.ADB_KEYS_FILE, 135 device.RunShellCommand('restorecon %s' % constants.ADB_KEYS_FILE,
138 as_root=True) 136 as_root=True)
139 137
140 138
141 def WipeDevicesIfPossible(devices): 139 def WipeDeviceIfPossible(device):
142 devices_to_reboot = [] 140 try:
143 for device_serial in devices: 141 device.EnableRoot()
144 device = device_utils.DeviceUtils(device_serial)
145 if not device.old_interface.EnableAdbRoot():
146 continue
147 WipeDeviceData(device) 142 WipeDeviceData(device)
148 devices_to_reboot.append(device) 143 device.Reboot(True, timeout=180, retries=0)
149 144 except (errors.DeviceUnresponsiveError, device_errors.CommandFailedError):
150 if devices_to_reboot: 145 pass
151 try:
152 device_utils.DeviceUtils.parallel(devices_to_reboot).Reboot(True)
153 except errors.DeviceUnresponsiveError:
154 pass
155 for device_serial in devices_to_reboot:
156 device.WaitUntilFullyBooted(timeout=90)
157 146
158 147
159 def ProvisionDevice(device_serial, is_perf, disable_location): 148 def ProvisionDevice(device, options, is_perf):
160 device = device_utils.DeviceUtils(device_serial) 149 try:
161 device.old_interface.EnableAdbRoot() 150 if not options.skip_wipe:
162 _ConfigureLocalProperties(device, is_perf) 151 WipeDeviceIfPossible(device)
163 device_settings.ConfigureContentSettings( 152 device.EnableRoot()
164 device, device_settings.DETERMINISTIC_DEVICE_SETTINGS) 153 _ConfigureLocalProperties(device, is_perf)
165 if disable_location:
166 device_settings.ConfigureContentSettings( 154 device_settings.ConfigureContentSettings(
167 device, device_settings.DISABLE_LOCATION_SETTINGS) 155 device, device_settings.DETERMINISTIC_DEVICE_SETTINGS)
168 else: 156 if options.disable_location:
169 device_settings.ConfigureContentSettings( 157 device_settings.ConfigureContentSettings(
170 device, device_settings.ENABLE_LOCATION_SETTINGS) 158 device, device_settings.DISABLE_LOCATION_SETTINGS)
171 device_settings.SetLockScreenSettings(device) 159 else:
172 if is_perf: 160 device_settings.ConfigureContentSettings(
173 # TODO(tonyg): We eventually want network on. However, currently radios 161 device, device_settings.ENABLE_LOCATION_SETTINGS)
174 # can cause perfbots to drain faster than they charge. 162 device_settings.SetLockScreenSettings(device)
175 device_settings.ConfigureContentSettings( 163 if is_perf:
176 device, device_settings.NETWORK_DISABLED_SETTINGS) 164 # TODO(tonyg): We eventually want network on. However, currently radios
177 # Some perf bots run benchmarks with USB charging disabled which leads 165 # can cause perfbots to drain faster than they charge.
178 # to gradual draining of the battery. We must wait for a full charge 166 device_settings.ConfigureContentSettings(
179 # before starting a run in order to keep the devices online. 167 device, device_settings.NETWORK_DISABLED_SETTINGS)
180 try: 168 # Some perf bots run benchmarks with USB charging disabled which leads
181 battery_info = device.old_interface.GetBatteryInfo() 169 # to gradual draining of the battery. We must wait for a full charge
182 except Exception as e: 170 # before starting a run in order to keep the devices online.
183 battery_info = {} 171 try:
184 logging.error('Unable to obtain battery info for %s, %s', 172 battery_info = device.old_interface.GetBatteryInfo()
185 device_serial, e) 173 except Exception as e:
174 battery_info = {}
175 logging.error('Unable to obtain battery info for %s, %s',
176 str(device), e)
186 177
187 while int(battery_info.get('level', 100)) < 95: 178 while int(battery_info.get('level', 100)) < 95:
188 if not device.old_interface.IsDeviceCharging(): 179 if not device.old_interface.IsDeviceCharging():
189 if device.old_interface.CanControlUsbCharging(): 180 if device.old_interface.CanControlUsbCharging():
190 device.old_interface.EnableUsbCharging() 181 device.old_interface.EnableUsbCharging()
191 else: 182 else:
192 logging.error('Device is not charging') 183 logging.error('Device is not charging')
193 break 184 break
194 logging.info('Waiting for device to charge. Current level=%s', 185 logging.info('Waiting for device to charge. Current level=%s',
195 battery_info.get('level', 0)) 186 battery_info.get('level', 0))
196 time.sleep(60) 187 time.sleep(60)
197 battery_info = device.old_interface.GetBatteryInfo() 188 battery_info = device.old_interface.GetBatteryInfo()
198 device.RunShellCommand('date -u %f' % time.time(), as_root=True) 189 device.RunShellCommand('date -u %f' % time.time(), as_root=True)
190 device.Reboot(True, timeout=180, retries=0)
191 props = device.RunShellCommand('getprop')
192 for prop in props:
193 logging.info(' %s' % prop)
194 if options.auto_reconnect:
195 PushAndLaunchAdbReboot(device, options.target)
196 except (errors.WaitForResponseTimedOutError,
197 device_errors.CommandTimeoutError):
198 logging.info('Timed out waiting for device %s. Adding to blacklist.',
199 str(device))
200 # Device black list is reset by bb_device_status_check.py per build.
201 device_blacklist.ExtendBlacklist([str(device)])
202 except (device_errors.CommandFailedError):
203 logging.info('Failed to provision device %s. Adding to blacklist.',
204 str(device))
205 device_blacklist.ExtendBlacklist([str(device)])
199 206
200 207
201 def ProvisionDevices(options): 208 def ProvisionDevices(options):
202 is_perf = 'perf' in os.environ.get('BUILDBOT_BUILDERNAME', '').lower() 209 is_perf = 'perf' in os.environ.get('BUILDBOT_BUILDERNAME', '').lower()
203 # TODO(jbudorick): Parallelize provisioning of all attached devices after
204 # switching from AndroidCommands.
205 if options.device is not None: 210 if options.device is not None:
206 devices = [options.device] 211 devices = [options.device]
207 else: 212 else:
208 devices = android_commands.GetAttachedDevices() 213 devices = android_commands.GetAttachedDevices()
209 214
210 # Wipe devices (unless --skip-wipe was specified) 215 parallel_devices = device_utils.DeviceUtils.parallel(devices)
211 if not options.skip_wipe: 216 parallel_devices.pMap(ProvisionDevice, options, is_perf)
212 WipeDevicesIfPossible(devices) 217 if options.auto_reconnect:
213 218 LaunchHostHeartbeat()
214 bad_devices = [] 219 blacklist = device_blacklist.ReadBlacklist()
215 # Provision devices 220 if all(d in blacklist for d in devices):
216 for device_serial in devices:
217 try:
218 ProvisionDevice(device_serial, is_perf, options.disable_location)
219 except (errors.WaitForResponseTimedOutError,
220 device_errors.CommandTimeoutError):
221 logging.info('Timed out waiting for device %s. Adding to blacklist.',
222 device_serial)
223 bad_devices.append(device_serial)
224 # Device black list is reset by bb_device_status_check.py per build.
225 device_blacklist.ExtendBlacklist([device_serial])
226 except device_errors.CommandFailedError:
227 logging.info('Failed to provision device %s. Adding to blacklist.',
228 device_serial)
229 bad_devices.append(device_serial)
230 device_blacklist.ExtendBlacklist([device_serial])
231 devices = [device for device in devices if device not in bad_devices]
232
233 # If there are no good devices
234 if not devices:
235 raise device_errors.NoDevicesError 221 raise device_errors.NoDevicesError
236 222 return 0
237 try:
238 device_utils.DeviceUtils.parallel(devices).Reboot(True)
239 except errors.DeviceUnresponsiveError:
240 pass
241
242 bad_devices = []
243 for device_serial in devices:
244 device = device_utils.DeviceUtils(device_serial)
245 try:
246 device.WaitUntilFullyBooted(timeout=90)
247 (_, prop) = device.old_interface.GetShellCommandStatusAndOutput('getprop')
248 for p in prop:
249 logging.info(p)
250 except (errors.WaitForResponseTimedOutError,
251 device_errors.CommandTimeoutError):
252 logging.info('Timed out waiting for device %s. Adding to blacklist.',
253 device_serial)
254 bad_devices.append(device_serial)
255 # Device black list is reset by bb_device_status_check.py per build.
256 device_blacklist.ExtendBlacklist([device_serial])
257 except device_errors.CommandFailedError:
258 logging.info('Failed to provision device %s. Adding to blacklist.',
259 device_serial)
260 bad_devices.append(device_serial)
261 device_blacklist.ExtendBlacklist([device_serial])
262 devices = [device for device in devices if device not in bad_devices]
263
264 # If there are no good devices
265 if not devices:
266 raise device_errors.NoDevicesError
267
268 if options.auto_reconnect:
269 PushAndLaunchAdbReboot(devices, options.target)
270 223
271 224
272 def main(argv): 225 def main(argv):
273 custom_handler = logging.StreamHandler(sys.stdout) 226 custom_handler = logging.StreamHandler(sys.stdout)
274 custom_handler.setFormatter(run_tests_helper.CustomFormatter()) 227 custom_handler.setFormatter(run_tests_helper.CustomFormatter())
275 logging.getLogger().addHandler(custom_handler) 228 logging.getLogger().addHandler(custom_handler)
276 logging.getLogger().setLevel(logging.INFO) 229 logging.getLogger().setLevel(logging.INFO)
277 230
278 parser = optparse.OptionParser() 231 parser = optparse.OptionParser()
279 parser.add_option('--skip-wipe', action='store_true', default=False, 232 parser.add_option('--skip-wipe', action='store_true', default=False,
280 help="Don't wipe device data during provisioning.") 233 help="Don't wipe device data during provisioning.")
281 parser.add_option('--disable-location', action='store_true', default=False, 234 parser.add_option('--disable-location', action='store_true', default=False,
282 help="Disallow Google location services on devices.") 235 help="Disallow Google location services on devices.")
283 parser.add_option('-d', '--device', 236 parser.add_option('-d', '--device',
284 help='The serial number of the device to be provisioned') 237 help='The serial number of the device to be provisioned')
285 parser.add_option('-t', '--target', default='Debug', help='The build target') 238 parser.add_option('-t', '--target', default='Debug', help='The build target')
286 parser.add_option( 239 parser.add_option(
287 '-r', '--auto-reconnect', action='store_true', 240 '-r', '--auto-reconnect', action='store_true',
288 help='Push binary which will reboot the device on adb disconnections.') 241 help='Push binary which will reboot the device on adb disconnections.')
289 options, args = parser.parse_args(argv[1:]) 242 options, args = parser.parse_args(argv[1:])
290 constants.SetBuildType(options.target) 243 constants.SetBuildType(options.target)
291 244
292 if args: 245 if args:
293 print >> sys.stderr, 'Unused args %s' % args 246 print >> sys.stderr, 'Unused args %s' % args
294 return 1 247 return 1
295 248
296 ProvisionDevices(options) 249 return ProvisionDevices(options)
297 250
298 251
299 if __name__ == '__main__': 252 if __name__ == '__main__':
300 sys.exit(main(sys.argv)) 253 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « no previous file | build/android/pylib/device/device_blacklist.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698