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

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: 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
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>]
11 """ 11 """
12 12
13 import logging 13 import logging
14 import optparse 14 import optparse
15 import os 15 import os
16 import re 16 import re
17 import subprocess 17 import subprocess
18 import sys 18 import sys
19 import time 19 import time
20 20
21 from pylib import android_commands 21 from pylib import android_commands
22 from pylib import constants 22 from pylib import constants
23 from pylib import device_settings 23 from pylib import device_settings
24 from pylib.device import device_blacklist 24 from pylib.device import device_blacklist
25 from pylib.device import device_errors 25 from pylib.device import device_errors
26 from pylib.device import device_utils 26 from pylib.device import device_utils
27 from pylib.utils import run_tests_helper
27 28
28 sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, 29 sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT,
29 'third_party', 'android_testrunner')) 30 'third_party', 'android_testrunner'))
30 import errors 31 import errors
31 32
32 def KillHostHeartbeat(): 33 def KillHostHeartbeat():
33 ps = subprocess.Popen(['ps', 'aux'], stdout = subprocess.PIPE) 34 ps = subprocess.Popen(['ps', 'aux'], stdout = subprocess.PIPE)
34 stdout, _ = ps.communicate() 35 stdout, _ = ps.communicate()
35 matches = re.findall('\\n.*host_heartbeat.*', stdout) 36 matches = re.findall('\\n.*host_heartbeat.*', stdout)
36 for match in matches: 37 for match in matches:
37 print 'An instance of host heart beart running... will kill' 38 pid = re.findall('(\d+)', match)[0]
38 pid = re.findall('(\d+)', match)[1]
39 subprocess.call(['kill', str(pid)]) 39 subprocess.call(['kill', str(pid)])
40 40
41 41
42 def LaunchHostHeartbeat(): 42 def LaunchHostHeartbeat():
43 # Kill if existing host_heartbeat 43 # Kill if existing host_heartbeat
44 KillHostHeartbeat() 44 KillHostHeartbeat()
45 # Launch a new host_heartbeat 45 # Launch a new host_heartbeat
46 print 'Spawning host heartbeat...' 46 print 'Spawning host heartbeat...'
47 subprocess.Popen([os.path.join(constants.DIR_SOURCE_ROOT, 47 subprocess.Popen([os.path.join(constants.DIR_SOURCE_ROOT,
48 'build/android/host_heartbeat.py')]) 48 'build/android/host_heartbeat.py')])
49 49
50 50
51 def PushAndLaunchAdbReboot(devices, target): 51 def PushAndLaunchAdbReboot(device, target):
52 """Pushes and launches the adb_reboot binary on the device. 52 """Pushes and launches the adb_reboot binary on the device.
53 53
54 Arguments: 54 Arguments:
55 devices: The list of serial numbers of the device to which the 55 device: The DeviceUtils instance for the device to which the adb_reboot
56 adb_reboot binary should be pushed. 56 binary should be pushed.
57 target : The build target (example, Debug or Release) which helps in 57 target: The build target (example, Debug or Release) which helps in
58 locating the adb_reboot binary. 58 locating the adb_reboot binary.
59 """ 59 """
60 for device_serial in devices: 60 print 'Will push and launch adb_reboot on %s' % str(device)
61 print 'Will push and launch adb_reboot on %s' % device_serial 61 # Kill if adb_reboot is already running.
62 device = device_utils.DeviceUtils(device_serial) 62 try:
63 # Kill if adb_reboot is already running. 63 # Don't try to kill adb_reboot more than once. We don't expect it to be
64 try: 64 # running at all.
65 # Don't try to kill adb_reboot more than once. We don't expect it to be 65 device.KillAll('adb_reboot', blocking=True, timeout=2, retries=0)
66 # running at all. 66 except device_errors.CommandFailedError:
67 device.KillAll('adb_reboot', blocking=True, timeout=2, retries=0) 67 # We can safely ignore the exception because we don't expect adb_reboot
68 except device_errors.CommandFailedError: 68 # to be running.
69 # We can safely ignore the exception because we don't expect adb_reboot 69 pass
70 # to be running. 70 # Push adb_reboot
71 pass 71 print ' Pushing adb_reboot ...'
72 # Push adb_reboot 72 adb_reboot = os.path.join(constants.DIR_SOURCE_ROOT,
73 print ' Pushing adb_reboot ...' 73 'out/%s/adb_reboot' % target)
74 adb_reboot = os.path.join(constants.DIR_SOURCE_ROOT, 74 device.PushChangedFiles(adb_reboot, '/data/local/tmp/')
75 'out/%s/adb_reboot' % target) 75 # Launch adb_reboot
76 device.PushChangedFiles(adb_reboot, '/data/local/tmp/') 76 print ' Launching adb_reboot ...'
77 # Launch adb_reboot 77 device.old_interface.GetAndroidToolStatusAndOutput(
78 print ' Launching adb_reboot ...' 78 '/data/local/tmp/adb_reboot')
79 device.old_interface.GetAndroidToolStatusAndOutput(
80 '/data/local/tmp/adb_reboot')
81 LaunchHostHeartbeat()
82 79
83 80
84 def _ConfigureLocalProperties(device, is_perf): 81 def _ConfigureLocalProperties(device, is_perf):
85 """Set standard readonly testing device properties prior to reboot.""" 82 """Set standard readonly testing device properties prior to reboot."""
86 local_props = [ 83 local_props = [
87 'ro.monkey=1', 84 'ro.monkey=1',
88 'ro.test_harness=1', 85 'ro.test_harness=1',
89 'ro.audio.silent=1', 86 'ro.audio.silent=1',
90 'ro.setupwizard.mode=DISABLED', 87 'ro.setupwizard.mode=DISABLED',
91 ] 88 ]
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
130 device.RunShellCommand('restorecon %s' % dir_path, as_root=True) 127 device.RunShellCommand('restorecon %s' % dir_path, as_root=True)
131 device.RunShellCommand('echo %s > %s' % 128 device.RunShellCommand('echo %s > %s' %
132 (adb_keys[0], constants.ADB_KEYS_FILE), as_root=True) 129 (adb_keys[0], constants.ADB_KEYS_FILE), as_root=True)
133 for adb_key in adb_keys[1:]: 130 for adb_key in adb_keys[1:]:
134 device.RunShellCommand( 131 device.RunShellCommand(
135 'echo %s >> %s' % (adb_key, constants.ADB_KEYS_FILE), as_root=True) 132 'echo %s >> %s' % (adb_key, constants.ADB_KEYS_FILE), as_root=True)
136 device.RunShellCommand('restorecon %s' % constants.ADB_KEYS_FILE, 133 device.RunShellCommand('restorecon %s' % constants.ADB_KEYS_FILE,
137 as_root=True) 134 as_root=True)
138 135
139 136
140 def WipeDevicesIfPossible(devices): 137 def WipeDeviceIfPossible(device):
141 devices_to_reboot = [] 138 try:
142 for device_serial in devices: 139 device.EnableRoot()
143 device = device_utils.DeviceUtils(device_serial)
144 if not device.old_interface.EnableAdbRoot():
145 continue
146 WipeDeviceData(device) 140 WipeDeviceData(device)
147 devices_to_reboot.append(device) 141 device.Reboot(True, timeout=180, retries=0)
148 142 except (errors.DeviceUnresponsiveError, device_errors.CommandFailedError):
149 if devices_to_reboot: 143 pass
150 try:
151 device_utils.DeviceUtils.parallel(devices_to_reboot).Reboot(True)
152 except errors.DeviceUnresponsiveError:
153 pass
154 for device_serial in devices_to_reboot:
155 device.WaitUntilFullyBooted(timeout=90)
156 144
157 145
158 def ProvisionDevice(device_serial, is_perf, disable_location): 146 def ProvisionDevice(device, options, is_perf):
159 device = device_utils.DeviceUtils(device_serial) 147 try:
160 device.old_interface.EnableAdbRoot() 148 if not options.skip_wipe:
161 _ConfigureLocalProperties(device, is_perf) 149 WipeDeviceIfPossible(device)
162 device_settings_map = device_settings.DETERMINISTIC_DEVICE_SETTINGS 150 device.EnableRoot()
163 if disable_location: 151 _ConfigureLocalProperties(device, is_perf)
164 device_settings_map.update(device_settings.DISABLE_LOCATION_SETTING) 152 device_settings.ConfigureContentSettings(
165 else: 153 device, device_settings.DETERMINISTIC_DEVICE_SETTINGS)
166 device_settings_map.update(device_settings.ENABLE_LOCATION_SETTING) 154 if options.disable_location:
167 device_settings.ConfigureContentSettingsDict(device, device_settings_map) 155 device_settings.ConfigureContentSettings(
168 device_settings.SetLockScreenSettings(device) 156 device, device_settings.DISABLE_LOCATION_SETTINGS)
169 if is_perf: 157 else:
170 # TODO(tonyg): We eventually want network on. However, currently radios 158 device_settings.ConfigureContentSettings(
171 # can cause perfbots to drain faster than they charge. 159 device, device_settings.ENABLE_LOCATION_SETTINGS)
172 device_settings.ConfigureContentSettingsDict( 160 device_settings.SetLockScreenSettings(device)
173 device, device_settings.NETWORK_DISABLED_SETTINGS) 161 if is_perf:
174 # Some perf bots run benchmarks with USB charging disabled which leads 162 # TODO(tonyg): We eventually want network on. However, currently radios
175 # to gradual draining of the battery. We must wait for a full charge 163 # can cause perfbots to drain faster than they charge.
176 # before starting a run in order to keep the devices online. 164 device_settings.ConfigureContentSettings(
177 try: 165 device, device_settings.NETWORK_DISABLED_SETTINGS)
178 battery_info = device.old_interface.GetBatteryInfo() 166 # Some perf bots run benchmarks with USB charging disabled which leads
179 except Exception as e: 167 # to gradual draining of the battery. We must wait for a full charge
180 battery_info = {} 168 # before starting a run in order to keep the devices online.
181 logging.error('Unable to obtain battery info for %s, %s', 169 try:
182 device_serial, e) 170 battery_info = device.old_interface.GetBatteryInfo()
171 except Exception as e:
172 battery_info = {}
173 logging.error('Unable to obtain battery info for %s, %s',
174 str(device), e)
183 175
184 while int(battery_info.get('level', 100)) < 95: 176 while int(battery_info.get('level', 100)) < 95:
185 if not device.old_interface.IsDeviceCharging(): 177 if not device.old_interface.IsDeviceCharging():
186 if device.old_interface.CanControlUsbCharging(): 178 if device.old_interface.CanControlUsbCharging():
187 device.old_interface.EnableUsbCharging() 179 device.old_interface.EnableUsbCharging()
188 else: 180 else:
189 logging.error('Device is not charging') 181 logging.error('Device is not charging')
190 break 182 break
191 logging.info('Waiting for device to charge. Current level=%s', 183 logging.info('Waiting for device to charge. Current level=%s',
192 battery_info.get('level', 0)) 184 battery_info.get('level', 0))
193 time.sleep(60) 185 time.sleep(60)
194 battery_info = device.old_interface.GetBatteryInfo() 186 battery_info = device.old_interface.GetBatteryInfo()
195 device.RunShellCommand('date -u %f' % time.time(), as_root=True) 187 device.RunShellCommand('date -u %f' % time.time(), as_root=True)
188 device.Reboot(True, timeout=180, retries=0)
189 props = device.RunShellCommand('getprop')
190 for prop in props:
191 logging.info(' %s' % prop)
192 if options.auto_reconnect:
193 PushAndLaunchAdbReboot(device, options.target)
194 except (errors.WaitForResponseTimedOutError,
195 device_errors.CommandTimeoutError):
196 logging.info('Timed out waiting for device %s. Adding to blacklist.',
197 str(device))
198 # Device black list is reset by bb_device_status_check.py per build.
199 device_blacklist.ExtendBlacklist([str(device)])
200 except (device_errors.CommandFailedError):
201 logging.info('Failed to provision device %s. Adding to blacklist.',
202 str(device))
203 device_blacklist.ExtendBlacklist([str(device)])
196 204
197 205
198 def ProvisionDevices(options): 206 def ProvisionDevices(options):
199 is_perf = 'perf' in os.environ.get('BUILDBOT_BUILDERNAME', '').lower() 207 is_perf = 'perf' in os.environ.get('BUILDBOT_BUILDERNAME', '').lower()
200 # TODO(jbudorick): Parallelize provisioning of all attached devices after
201 # switching from AndroidCommands.
202 if options.device is not None: 208 if options.device is not None:
203 devices = [options.device] 209 devices = [options.device]
204 else: 210 else:
205 devices = android_commands.GetAttachedDevices() 211 devices = android_commands.GetAttachedDevices()
206 212
207 # Wipe devices (unless --skip-wipe was specified) 213 parallel_devices = device_utils.DeviceUtils.parallel(devices)
208 if not options.skip_wipe: 214 parallel_devices.pMap(ProvisionDevice, options, is_perf)
209 WipeDevicesIfPossible(devices) 215 LaunchHostHeartbeat()
210 216 blacklist = device_blacklist.ReadBlacklist()
211 bad_devices = [] 217 return 1 if all(d in blacklist for d in devices) else 0
212 # Provision devices
213 for device_serial in devices:
214 try:
215 ProvisionDevice(device_serial, is_perf, options.disable_location)
216 except errors.WaitForResponseTimedOutError:
217 logging.info('Timed out waiting for device %s. Adding to blacklist.',
218 device_serial)
219 bad_devices.append(device_serial)
220 # Device black list is reset by bb_device_status_check.py per build.
221 device_blacklist.ExtendBlacklist([device_serial])
222 devices = [device for device in devices if device not in bad_devices]
223
224 # If there are no good devices
225 if not devices:
226 raise device_errors.NoDevicesError
227
228 try:
229 device_utils.DeviceUtils.parallel(devices).Reboot(True)
230 except errors.DeviceUnresponsiveError:
231 pass
232
233 bad_devices = []
234 for device_serial in devices:
235 device = device_utils.DeviceUtils(device_serial)
236 try:
237 device.WaitUntilFullyBooted(timeout=90)
238 (_, prop) = device.old_interface.GetShellCommandStatusAndOutput('getprop')
239 for p in prop:
240 print p
241 except errors.WaitForResponseTimedOutError:
242 logging.info('Timed out waiting for device %s. Adding to blacklist.',
243 device_serial)
244 bad_devices.append(device_serial)
245 # Device black list is reset by bb_device_status_check.py per build.
246 device_blacklist.ExtendBlacklist([device_serial])
247 devices = [device for device in devices if device not in bad_devices]
248
249 # If there are no good devices
250 if not devices:
251 raise device_errors.NoDevicesError
252
253 if options.auto_reconnect:
254 PushAndLaunchAdbReboot(devices, options.target)
255 218
256 219
257 def main(argv): 220 def main(argv):
258 logging.basicConfig(level=logging.INFO) 221 custom_handler = logging.StreamHandler(sys.stdout)
222 custom_handler.setFormatter(run_tests_helper.CustomFormatter())
223 logging.getLogger().addHandler(custom_handler)
224 logging.getLogger().setLevel(logging.INFO)
259 225
260 parser = optparse.OptionParser() 226 parser = optparse.OptionParser()
261 parser.add_option('--skip-wipe', action='store_true', default=False, 227 parser.add_option('--skip-wipe', action='store_true', default=False,
262 help="Don't wipe device data during provisioning.") 228 help="Don't wipe device data during provisioning.")
263 parser.add_option('--disable-location', action='store_true', default=False, 229 parser.add_option('--disable-location', action='store_true', default=False,
264 help="Disallow Google location services on devices.") 230 help="Disallow Google location services on devices.")
265 parser.add_option('-d', '--device', 231 parser.add_option('-d', '--device',
266 help='The serial number of the device to be provisioned') 232 help='The serial number of the device to be provisioned')
267 parser.add_option('-t', '--target', default='Debug', help='The build target') 233 parser.add_option('-t', '--target', default='Debug', help='The build target')
268 parser.add_option( 234 parser.add_option(
269 '-r', '--auto-reconnect', action='store_true', 235 '-r', '--auto-reconnect', action='store_true',
270 help='Push binary which will reboot the device on adb disconnections.') 236 help='Push binary which will reboot the device on adb disconnections.')
271 options, args = parser.parse_args(argv[1:]) 237 options, args = parser.parse_args(argv[1:])
272 constants.SetBuildType(options.target) 238 constants.SetBuildType(options.target)
273 239
274 if args: 240 if args:
275 print >> sys.stderr, 'Unused args %s' % args 241 print >> sys.stderr, 'Unused args %s' % args
276 return 1 242 return 1
277 243
278 ProvisionDevices(options) 244 return ProvisionDevices(options)
279 245
280 246
281 if __name__ == '__main__': 247 if __name__ == '__main__':
282 sys.exit(main(sys.argv)) 248 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « no previous file | build/android/pylib/android_commands.py » ('j') | build/android/pylib/android_commands.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698