blob: 57209436614475dcd7998c6cfb30d5607b3b7d5b [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
David Pursellf1d16a62015-03-25 13:31:04 -07002# Copyright 2015 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Install/copy the image to the device."""
7
8from __future__ import print_function
9
David Pursellf1d16a62015-03-25 13:31:04 -070010import os
Ralph Nathan9b997232015-05-15 13:13:12 -070011import re
David Pursellf1d16a62015-03-25 13:31:04 -070012import shutil
13import tempfile
David Pursellf1d16a62015-03-25 13:31:04 -070014
Aviv Keshetb7519e12016-10-04 00:50:00 -070015from chromite.lib import constants
xixuane851dfb2016-05-02 18:02:37 -070016from chromite.lib import auto_updater
David Pursellf1d16a62015-03-25 13:31:04 -070017from chromite.lib import commandline
18from chromite.lib import cros_build_lib
19from chromite.lib import cros_logging as logging
20from chromite.lib import dev_server_wrapper as ds_wrapper
Ralph Nathan872ea4d2015-05-05 18:04:56 -070021from chromite.lib import operation
David Pursellf1d16a62015-03-25 13:31:04 -070022from chromite.lib import osutils
Gilad Arnold1c8eda52015-05-04 22:32:38 -070023from chromite.lib import path_util
David Pursellf1d16a62015-03-25 13:31:04 -070024from chromite.lib import remote_access
25
26
Don Garrett97d7dc22015-01-20 14:07:56 -080027DEVSERVER_STATIC_DIR = path_util.FromChrootPath(
David Pursellf1d16a62015-03-25 13:31:04 -070028 os.path.join(constants.CHROOT_SOURCE_ROOT, 'devserver', 'static'))
29
30
Ralph Nathan9b997232015-05-15 13:13:12 -070031class UsbImagerOperation(operation.ProgressBarOperation):
32 """Progress bar for flashing image to operation."""
33
34 def __init__(self, image):
35 super(UsbImagerOperation, self).__init__()
36 self._size = os.path.getsize(image)
37 self._transferred = 0.
38 self._bytes = re.compile(r'(\d+) bytes')
39
40 def _GetDDPid(self):
41 """Get the Pid of dd."""
42 try:
43 pids = cros_build_lib.RunCommand(['pgrep', 'dd'], capture_output=True,
44 print_cmd=False).output
45 for pid in pids.splitlines():
46 if osutils.IsChildProcess(int(pid), name='dd'):
47 return int(pid)
48 return -1
49 except cros_build_lib.RunCommandError:
50 # If dd isn't still running, then we assume that it is finished.
51 return -1
52
53 def _PingDD(self, dd_pid):
54 """Send USR1 signal to dd to get status update."""
55 try:
56 cmd = ['kill', '-USR1', str(dd_pid)]
57 cros_build_lib.SudoRunCommand(cmd, print_cmd=False)
58 except cros_build_lib.RunCommandError:
59 # Here we assume that dd finished in the background.
60 return
61
62 def ParseOutput(self, output=None):
63 """Parse the output of dd to update progress bar."""
64 dd_pid = self._GetDDPid()
65 if dd_pid == -1:
66 return
67
68 self._PingDD(dd_pid)
69
70 if output is None:
71 stdout = self._stdout.read()
72 stderr = self._stderr.read()
73 output = stdout + stderr
74
75 match = self._bytes.search(output)
76 if match:
77 self._transferred = match.groups()[0]
78
79 self.ProgressBar(float(self._transferred) / self._size)
80
81
Mike Frysinger32759e42016-12-21 18:40:16 -050082def _IsFilePathGPTDiskImage(file_path, require_pmbr=False):
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -070083 """Determines if a file is a valid GPT disk.
84
85 Args:
86 file_path: Path to the file to test.
Mike Frysinger32759e42016-12-21 18:40:16 -050087 require_pmbr: Whether to require a PMBR in LBA0.
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -070088 """
89 if os.path.isfile(file_path):
Mike Frysinger32759e42016-12-21 18:40:16 -050090 with open(file_path) as image_file:
91 if require_pmbr:
92 # Seek to the end of LBA0 and look for the PMBR boot signature.
93 image_file.seek(0x1fe)
94 if image_file.read(2) != '\x55\xaa':
95 return False
96 # Current file position is start of LBA1 now.
97 else:
98 # Seek to LBA1 where the GPT starts.
99 image_file.seek(0x200)
100
101 # See if there's a GPT here.
102 if image_file.read(8) == 'EFI PART':
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700103 return True
Mike Frysinger32759e42016-12-21 18:40:16 -0500104
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700105 return False
106
107
108def _ChooseImageFromDirectory(dir_path):
109 """Lists all image files in |dir_path| and ask user to select one.
110
111 Args:
112 dir_path: Path to the directory.
113 """
114 images = sorted([x for x in os.listdir(dir_path) if
115 _IsFilePathGPTDiskImage(os.path.join(dir_path, x))])
116 idx = 0
117 if len(images) == 0:
118 raise ValueError('No image found in %s.' % dir_path)
119 elif len(images) > 1:
120 idx = cros_build_lib.GetChoice(
121 'Multiple images found in %s. Please select one to continue:' % (
122 (dir_path,)),
123 images)
124
125 return os.path.join(dir_path, images[idx])
126
127
David Pursellf1d16a62015-03-25 13:31:04 -0700128class FlashError(Exception):
129 """Thrown when there is an unrecoverable error during flash."""
130
131
132class USBImager(object):
133 """Copy image to the target removable device."""
134
Gilad Arnoldd0461442015-07-07 11:52:46 -0700135 def __init__(self, device, board, image, debug=False, install=False,
136 yes=False):
David Pursellf1d16a62015-03-25 13:31:04 -0700137 """Initalizes USBImager."""
138 self.device = device
139 self.board = board if board else cros_build_lib.GetDefaultBoard()
140 self.image = image
David Pursellf1d16a62015-03-25 13:31:04 -0700141 self.debug = debug
142 self.debug_level = logging.DEBUG if debug else logging.INFO
143 self.install = install
144 self.yes = yes
145
146 def DeviceNameToPath(self, device_name):
147 return '/dev/%s' % device_name
148
149 def GetRemovableDeviceDescription(self, device):
150 """Returns a informational description of the removable |device|.
151
152 Args:
153 device: the device name (e.g. sdc).
154
155 Returns:
156 A string describing |device| (e.g. Patriot Memory 7918 MB).
157 """
158 desc = [
159 osutils.GetDeviceInfo(device, keyword='manufacturer'),
160 osutils.GetDeviceInfo(device, keyword='product'),
161 osutils.GetDeviceSize(self.DeviceNameToPath(device)),
162 '(%s)' % self.DeviceNameToPath(device),
163 ]
164 return ' '.join([x for x in desc if x])
165
166 def ListAllRemovableDevices(self):
167 """Returns a list of removable devices.
168
169 Returns:
170 A list of device names (e.g. ['sdb', 'sdc']).
171 """
172 devices = osutils.ListBlockDevices()
173 removable_devices = []
174 for d in devices:
175 if d.TYPE == 'disk' and d.RM == '1':
176 removable_devices.append(d.NAME)
177
178 return removable_devices
179
180 def ChooseRemovableDevice(self, devices):
181 """Lists all removable devices and asks user to select/confirm.
182
183 Args:
184 devices: a list of device names (e.g. ['sda', 'sdb']).
185
186 Returns:
187 The device name chosen by the user.
188 """
189 idx = cros_build_lib.GetChoice(
190 'Removable device(s) found. Please select/confirm to continue:',
191 [self.GetRemovableDeviceDescription(x) for x in devices])
192
193 return devices[idx]
194
195 def InstallImageToDevice(self, image, device):
196 """Installs |image| to the removable |device|.
197
198 Args:
199 image: Path to the image to copy.
200 device: Device to copy to.
201 """
202 cmd = [
203 'chromeos-install',
204 '--yes',
205 '--skip_src_removable',
206 '--skip_dst_removable',
207 '--payload_image=%s' % image,
208 '--dst=%s' % device,
209 '--skip_postinstall',
210 ]
Shuqian Zhao33242472017-08-02 18:28:44 -0700211 cros_build_lib.SudoRunCommand(cmd,
212 print_cmd=True,
213 debug_level=logging.NOTICE,
214 combine_stdout_stderr=True,
215 log_output=True)
David Pursellf1d16a62015-03-25 13:31:04 -0700216
217 def CopyImageToDevice(self, image, device):
218 """Copies |image| to the removable |device|.
219
220 Args:
221 image: Path to the image to copy.
222 device: Device to copy to.
223 """
Ralph Nathan9b997232015-05-15 13:13:12 -0700224 cmd = ['dd', 'if=%s' % image, 'of=%s' % device, 'bs=4M', 'iflag=fullblock',
225 'oflag=sync']
226 if logging.getLogger().getEffectiveLevel() <= logging.NOTICE:
227 op = UsbImagerOperation(image)
228 op.Run(cros_build_lib.SudoRunCommand, cmd, debug_level=logging.NOTICE,
229 update_period=0.5)
230 else:
231 cros_build_lib.SudoRunCommand(
232 cmd, debug_level=logging.NOTICE,
233 print_cmd=logging.getLogger().getEffectiveLevel() < logging.NOTICE)
David Pursellf1d16a62015-03-25 13:31:04 -0700234
Brian Norrisb4d99982018-10-16 16:02:49 -0700235 # dd likely didn't put the backup GPT in the last block. cgpt can fix this
236 # up for us, so we have a standards-conforming GPT.
237 cros_build_lib.SudoRunCommand(['cgpt', 'repair', device],
238 debug_level=self.debug_level)
David Pursellf1d16a62015-03-25 13:31:04 -0700239 cros_build_lib.SudoRunCommand(['sync'], debug_level=self.debug_level)
240
David Pursellf1d16a62015-03-25 13:31:04 -0700241 def _GetImagePath(self):
242 """Returns the image path to use."""
243 image_path = translated_path = None
244 if os.path.isfile(self.image):
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700245 if not self.yes and not _IsFilePathGPTDiskImage(self.image):
David Pursellf1d16a62015-03-25 13:31:04 -0700246 # TODO(wnwen): Open the tarball and if there is just one file in it,
247 # use that instead. Existing code in upload_symbols.py.
248 if cros_build_lib.BooleanPrompt(
249 prolog='The given image file is not a valid disk image. Perhaps '
250 'you forgot to untar it.',
251 prompt='Terminate the current flash process?'):
252 raise FlashError('Update terminated by user.')
253 image_path = self.image
254 elif os.path.isdir(self.image):
255 # Ask user which image (*.bin) in the folder to use.
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700256 image_path = _ChooseImageFromDirectory(self.image)
David Pursellf1d16a62015-03-25 13:31:04 -0700257 else:
258 # Translate the xbuddy path to get the exact image to use.
Gilad Arnolde62ec902015-04-24 14:41:02 -0700259 translated_path, _ = ds_wrapper.GetImagePathWithXbuddy(
Don Garrett97d7dc22015-01-20 14:07:56 -0800260 self.image, self.board, static_dir=DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700261 image_path = ds_wrapper.TranslatedPathToLocalPath(
Don Garrett97d7dc22015-01-20 14:07:56 -0800262 translated_path, DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700263
264 logging.info('Using image %s', translated_path or image_path)
265 return image_path
266
267 def Run(self):
268 """Image the removable device."""
269 devices = self.ListAllRemovableDevices()
270
271 if self.device:
272 # If user specified a device path, check if it exists.
273 if not os.path.exists(self.device):
274 raise FlashError('Device path %s does not exist.' % self.device)
275
276 # Then check if it is removable.
277 if self.device not in [self.DeviceNameToPath(x) for x in devices]:
278 msg = '%s is not a removable device.' % self.device
279 if not (self.yes or cros_build_lib.BooleanPrompt(
280 default=False, prolog=msg)):
281 raise FlashError('You can specify usb:// to choose from a list of '
282 'removable devices.')
283 target = None
284 if self.device:
285 # Get device name from path (e.g. sdc in /dev/sdc).
286 target = self.device.rsplit(os.path.sep, 1)[-1]
287 elif devices:
288 # Ask user to choose from the list.
289 target = self.ChooseRemovableDevice(devices)
290 else:
291 raise FlashError('No removable devices detected.')
292
293 image_path = self._GetImagePath()
294 try:
295 device = self.DeviceNameToPath(target)
296 if self.install:
297 self.InstallImageToDevice(image_path, device)
298 else:
299 self.CopyImageToDevice(image_path, device)
300 except cros_build_lib.RunCommandError:
301 logging.error('Failed copying image to device %s',
302 self.DeviceNameToPath(target))
303
304
305class FileImager(USBImager):
306 """Copy image to the target path."""
307
308 def Run(self):
309 """Copy the image to the path specified by self.device."""
Mao Huangc4777e82016-03-14 20:20:08 +0800310 if not os.path.isdir(os.path.dirname(self.device)):
311 raise FlashError('Parent of path %s is not a directory.' % self.device)
David Pursellf1d16a62015-03-25 13:31:04 -0700312
313 image_path = self._GetImagePath()
314 if os.path.isdir(self.device):
315 logging.info('Copying to %s',
316 os.path.join(self.device, os.path.basename(image_path)))
317 else:
318 logging.info('Copying to %s', self.device)
319 try:
320 shutil.copy(image_path, self.device)
321 except IOError:
322 logging.error('Failed to copy image %s to %s', image_path, self.device)
323
324
325class RemoteDeviceUpdater(object):
326 """Performs update on a remote device."""
327 DEVSERVER_FILENAME = 'devserver.py'
328 STATEFUL_UPDATE_BIN = '/usr/bin/stateful_update'
329 UPDATE_ENGINE_BIN = 'update_engine_client'
David Pursellf1d16a62015-03-25 13:31:04 -0700330 # Root working directory on the device. This directory is in the
331 # stateful partition and thus has enough space to store the payloads.
332 DEVICE_BASE_DIR = '/mnt/stateful_partition/cros-flash'
Ralph Nathan872ea4d2015-05-05 18:04:56 -0700333 UPDATE_CHECK_INTERVAL_PROGRESSBAR = 0.5
334 UPDATE_CHECK_INTERVAL_NORMAL = 10
David Pursellf1d16a62015-03-25 13:31:04 -0700335
336 def __init__(self, ssh_hostname, ssh_port, image, stateful_update=True,
337 rootfs_update=True, clobber_stateful=False, reboot=True,
Gilad Arnoldd0461442015-07-07 11:52:46 -0700338 board=None, src_image_to_delta=None, wipe=True, debug=False,
Daniel Erat30fd2072016-08-29 10:08:56 -0600339 yes=False, force=False, ssh_private_key=None, ping=True,
Chung-yih Wang3145bf52017-10-24 16:08:02 +0800340 disable_verification=False, send_payload_in_parallel=False):
David Pursellf1d16a62015-03-25 13:31:04 -0700341 """Initializes RemoteDeviceUpdater"""
342 if not stateful_update and not rootfs_update:
343 raise ValueError('No update operation to perform; either stateful or'
344 ' rootfs partitions must be updated.')
345 self.tempdir = tempfile.mkdtemp(prefix='cros-flash')
346 self.ssh_hostname = ssh_hostname
347 self.ssh_port = ssh_port
348 self.image = image
349 self.board = board
David Pursellf1d16a62015-03-25 13:31:04 -0700350 self.src_image_to_delta = src_image_to_delta
351 self.do_stateful_update = stateful_update
352 self.do_rootfs_update = rootfs_update
353 self.disable_verification = disable_verification
354 self.clobber_stateful = clobber_stateful
355 self.reboot = reboot
356 self.debug = debug
Daniel Erat30fd2072016-08-29 10:08:56 -0600357 self.ssh_private_key = ssh_private_key
David Pursellf1d16a62015-03-25 13:31:04 -0700358 self.ping = ping
359 # Do not wipe if debug is set.
360 self.wipe = wipe and not debug
361 self.yes = yes
362 self.force = force
Chung-yih Wang3145bf52017-10-24 16:08:02 +0800363 self.send_payload_in_parallel = send_payload_in_parallel
David Pursellf1d16a62015-03-25 13:31:04 -0700364
David Pursellf1d16a62015-03-25 13:31:04 -0700365 def Cleanup(self):
366 """Cleans up the temporary directory."""
367 if self.wipe:
368 logging.info('Cleaning up temporary working directory...')
369 osutils.RmDir(self.tempdir)
370 else:
371 logging.info('You can find the log files and/or payloads in %s',
372 self.tempdir)
373
xixuane851dfb2016-05-02 18:02:37 -0700374 def GetPayloadDir(self, device):
375 """Get directory of payload for update.
David Pursellf1d16a62015-03-25 13:31:04 -0700376
xixuane851dfb2016-05-02 18:02:37 -0700377 This method is used to obtain the directory of payload for cros-flash. The
378 given path 'self.image' is passed in when initializing RemoteDeviceUpdater.
David Pursellf1d16a62015-03-25 13:31:04 -0700379
xixuane851dfb2016-05-02 18:02:37 -0700380 If self.image is a directory, we directly use the provided update payload(s)
381 in this directory.
382
383 If self.image is an image, let devserver access it and generate payloads.
384
385 If not in the above cases, let devserver first obtain the image path. Then
386 devserver will access the image and generate payloads.
David Pursellf1d16a62015-03-25 13:31:04 -0700387
388 Args:
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400389 device: A ChromiumOSDevice object.
David Pursellf1d16a62015-03-25 13:31:04 -0700390
391 Returns:
xixuane851dfb2016-05-02 18:02:37 -0700392 A string payload_dir, that represents the payload directory.
David Pursellf1d16a62015-03-25 13:31:04 -0700393 """
xixuane851dfb2016-05-02 18:02:37 -0700394 payload_dir = self.tempdir
David Pursellf1d16a62015-03-25 13:31:04 -0700395
xixuane851dfb2016-05-02 18:02:37 -0700396 if os.path.isdir(self.image):
397 # The given path is a directory.
398 payload_dir = self.image
399 logging.info('Using provided payloads in %s', payload_dir)
400 elif os.path.isfile(self.image):
401 # The given path is an image.
402 logging.info('Using image %s', self.image)
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600403 try:
404 ds_wrapper.GetUpdatePayloadsFromLocalPath(
405 self.image, payload_dir,
406 src_image_to_delta=self.src_image_to_delta,
407 static_dir=DEVSERVER_STATIC_DIR)
408 except:
409 logging.error('Unable to get payloads from local path: %s', payload_dir)
410 raise
411
xixuane851dfb2016-05-02 18:02:37 -0700412 else:
413 self.board = cros_build_lib.GetBoard(device_board=device.board,
414 override_board=self.board,
415 force=self.yes)
416 if not self.board:
417 raise FlashError('No board identified')
David Pursellf1d16a62015-03-25 13:31:04 -0700418
xixuane851dfb2016-05-02 18:02:37 -0700419 if not self.force and self.board != device.board:
420 # If a board was specified, it must be compatible with the device.
Brian Norrisfab3fb22016-06-02 14:59:20 -0700421 raise FlashError('Device (%s) is incompatible with board %s' %
422 (device.board, self.board))
xixuane851dfb2016-05-02 18:02:37 -0700423
424 logging.info('Board is %s', self.board)
425
426 # Translate the xbuddy path to get the exact image to use.
427 translated_path, resolved_path = ds_wrapper.GetImagePathWithXbuddy(
428 self.image, self.board, static_dir=DEVSERVER_STATIC_DIR,
429 lookup_only=True)
Vadim Bendebury3c993462017-08-22 15:42:16 -0700430 logging.notice('Using image %s', translated_path)
xixuane851dfb2016-05-02 18:02:37 -0700431 # Convert the translated path to be used in the update request.
432 image_path = ds_wrapper.ConvertTranslatedPath(resolved_path,
433 translated_path)
434
435 # Launch a local devserver to generate/serve update payloads.
436 ds_wrapper.GetUpdatePayloads(
437 image_path, payload_dir, board=self.board,
438 src_image_to_delta=self.src_image_to_delta,
439 static_dir=DEVSERVER_STATIC_DIR)
440
441 return payload_dir
David Pursellf1d16a62015-03-25 13:31:04 -0700442
443 def Run(self):
xixuane851dfb2016-05-02 18:02:37 -0700444 """Perform remote device update.
445
446 The update process includes:
447 1. initialize a device instance for the given remote device.
448 2. achieve payload_dir which contains the required payloads for updating.
449 3. initialize an auto-updater instance to do RunUpdate().
450 4. After auto-update, all temp files and dir will be cleaned up.
451 """
David Pursellf1d16a62015-03-25 13:31:04 -0700452 try:
David Pursellf1d16a62015-03-25 13:31:04 -0700453 with remote_access.ChromiumOSDeviceHandler(
Daniel Erat30fd2072016-08-29 10:08:56 -0600454 self.ssh_hostname, port=self.ssh_port, base_dir=self.DEVICE_BASE_DIR,
455 private_key=self.ssh_private_key, ping=self.ping) as device:
David Pursellf1d16a62015-03-25 13:31:04 -0700456
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600457 try:
458 # Get payload directory
459 payload_dir = self.GetPayloadDir(device)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700460
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600461 # Do auto-update
462 chromeos_AU = auto_updater.ChromiumOSFlashUpdater(
463 device, payload_dir, self.tempdir,
464 do_rootfs_update=self.do_rootfs_update,
465 do_stateful_update=self.do_stateful_update,
466 reboot=self.reboot,
467 disable_verification=self.disable_verification,
468 clobber_stateful=self.clobber_stateful,
Chung-yih Wang3145bf52017-10-24 16:08:02 +0800469 yes=self.yes,
470 send_payload_in_parallel=self.send_payload_in_parallel)
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600471 chromeos_AU.CheckPayloads()
472 chromeos_AU.RunUpdate()
David Pursellf1d16a62015-03-25 13:31:04 -0700473
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600474 except Exception:
475 logging.error('Device update failed.')
476 lsb_entries = sorted(device.lsb_release.items())
477 logging.info(
478 'Following are the LSB version details of the device:\n%s',
479 '\n'.join('%s=%s' % (k, v) for k, v in lsb_entries))
480 raise
481
482 logging.notice('Update performed successfully.')
483
484 except remote_access.RemoteAccessException:
485 logging.error('Remote device failed to initialize.')
David Pursellf1d16a62015-03-25 13:31:04 -0700486 raise
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600487
David Pursellf1d16a62015-03-25 13:31:04 -0700488 finally:
489 self.Cleanup()
490
Gilad Arnoldbfcfaff2015-07-07 10:08:02 -0700491def Flash(device, image, board=None, install=False, src_image_to_delta=None,
492 rootfs_update=True, stateful_update=True, clobber_stateful=False,
Daniel Erat30fd2072016-08-29 10:08:56 -0600493 reboot=True, wipe=True, ssh_private_key=None, ping=True,
494 disable_rootfs_verification=False, clear_cache=False, yes=False,
Chung-yih Wang3145bf52017-10-24 16:08:02 +0800495 force=False, debug=False, send_payload_in_parallel=False):
David Pursellf1d16a62015-03-25 13:31:04 -0700496 """Flashes a device, USB drive, or file with an image.
497
498 This provides functionality common to `cros flash` and `brillo flash`
499 so that they can parse the commandline separately but still use the
500 same underlying functionality.
501
502 Args:
David Pursell2e773382015-04-03 14:30:47 -0700503 device: commandline.Device object; None to use the default device.
David Pursellf1d16a62015-03-25 13:31:04 -0700504 image: Path (string) to the update image. Can be a local or xbuddy path;
505 non-existant local paths are converted to xbuddy.
David Pursellf1d16a62015-03-25 13:31:04 -0700506 board: Board to use; None to automatically detect.
David Pursellf1d16a62015-03-25 13:31:04 -0700507 install: Install to USB using base disk layout; USB |device| scheme only.
508 src_image_to_delta: Local path to an image to be used as the base to
509 generate delta payloads; SSH |device| scheme only.
510 rootfs_update: Update rootfs partition; SSH |device| scheme only.
511 stateful_update: Update stateful partition; SSH |device| scheme only.
512 clobber_stateful: Clobber stateful partition; SSH |device| scheme only.
513 reboot: Reboot device after update; SSH |device| scheme only.
514 wipe: Wipe temporary working directory; SSH |device| scheme only.
Daniel Erat30fd2072016-08-29 10:08:56 -0600515 ssh_private_key: Path to an SSH private key file; None to use test keys.
David Pursellf1d16a62015-03-25 13:31:04 -0700516 ping: Ping the device before attempting update; SSH |device| scheme only.
517 disable_rootfs_verification: Remove rootfs verification after update; SSH
518 |device| scheme only.
519 clear_cache: Clear the devserver static directory.
520 yes: Assume "yes" for any prompt.
521 force: Ignore sanity checks and prompts. Overrides |yes| if True.
522 debug: Print additional debugging messages.
Chung-yih Wang3145bf52017-10-24 16:08:02 +0800523 send_payload_in_parallel: Transfer payloads in chunks in parallel to speed
524 up transmissions for long haul between endpoints.
David Pursellf1d16a62015-03-25 13:31:04 -0700525
526 Raises:
527 FlashError: An unrecoverable error occured.
528 ValueError: Invalid parameter combination.
529 """
530 if force:
531 yes = True
532
533 if clear_cache:
534 logging.info('Clearing the cache...')
Don Garrett97d7dc22015-01-20 14:07:56 -0800535 ds_wrapper.DevServerWrapper.WipeStaticDirectory(DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700536
537 try:
Don Garrett97d7dc22015-01-20 14:07:56 -0800538 osutils.SafeMakedirsNonRoot(DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700539 except OSError:
Don Garrett97d7dc22015-01-20 14:07:56 -0800540 logging.error('Failed to create %s', DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700541
542 if install:
David Pursell2e773382015-04-03 14:30:47 -0700543 if not device or device.scheme != commandline.DEVICE_SCHEME_USB:
David Pursellf1d16a62015-03-25 13:31:04 -0700544 raise ValueError(
545 '--install can only be used when writing to a USB device')
546 if not cros_build_lib.IsInsideChroot():
547 raise ValueError('--install can only be used inside the chroot')
548
David Pursell2e773382015-04-03 14:30:47 -0700549 if not device or device.scheme == commandline.DEVICE_SCHEME_SSH:
550 if device:
551 hostname, port = device.hostname, device.port
552 else:
553 hostname, port = None, None
Ralph Nathan872ea4d2015-05-05 18:04:56 -0700554 logging.notice('Preparing to update the remote device %s', hostname)
David Pursellf1d16a62015-03-25 13:31:04 -0700555 updater = RemoteDeviceUpdater(
David Pursell2e773382015-04-03 14:30:47 -0700556 hostname,
557 port,
David Pursellf1d16a62015-03-25 13:31:04 -0700558 image,
559 board=board,
David Pursellf1d16a62015-03-25 13:31:04 -0700560 src_image_to_delta=src_image_to_delta,
561 rootfs_update=rootfs_update,
562 stateful_update=stateful_update,
563 clobber_stateful=clobber_stateful,
564 reboot=reboot,
565 wipe=wipe,
566 debug=debug,
567 yes=yes,
568 force=force,
Daniel Erat30fd2072016-08-29 10:08:56 -0600569 ssh_private_key=ssh_private_key,
David Pursellf1d16a62015-03-25 13:31:04 -0700570 ping=ping,
Chung-yih Wang3145bf52017-10-24 16:08:02 +0800571 disable_verification=disable_rootfs_verification,
572 send_payload_in_parallel=send_payload_in_parallel)
David Pursellf1d16a62015-03-25 13:31:04 -0700573 updater.Run()
574 elif device.scheme == commandline.DEVICE_SCHEME_USB:
575 path = osutils.ExpandPath(device.path) if device.path else ''
576 logging.info('Preparing to image the removable device %s', path)
577 imager = USBImager(path,
578 board,
579 image,
David Pursellf1d16a62015-03-25 13:31:04 -0700580 debug=debug,
581 install=install,
582 yes=yes)
583 imager.Run()
584 elif device.scheme == commandline.DEVICE_SCHEME_FILE:
585 logging.info('Preparing to copy image to %s', device.path)
586 imager = FileImager(device.path,
587 board,
588 image,
David Pursellf1d16a62015-03-25 13:31:04 -0700589 debug=debug,
590 yes=yes)
591 imager.Run()