blob: 59fe1421c67ebd2fb9287b518496266e719ef0a5 [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
David Pursellf1d16a62015-03-25 13:31:04 -0700235 cros_build_lib.SudoRunCommand(['sync'], debug_level=self.debug_level)
236
David Pursellf1d16a62015-03-25 13:31:04 -0700237 def _GetImagePath(self):
238 """Returns the image path to use."""
239 image_path = translated_path = None
240 if os.path.isfile(self.image):
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700241 if not self.yes and not _IsFilePathGPTDiskImage(self.image):
David Pursellf1d16a62015-03-25 13:31:04 -0700242 # TODO(wnwen): Open the tarball and if there is just one file in it,
243 # use that instead. Existing code in upload_symbols.py.
244 if cros_build_lib.BooleanPrompt(
245 prolog='The given image file is not a valid disk image. Perhaps '
246 'you forgot to untar it.',
247 prompt='Terminate the current flash process?'):
248 raise FlashError('Update terminated by user.')
249 image_path = self.image
250 elif os.path.isdir(self.image):
251 # Ask user which image (*.bin) in the folder to use.
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700252 image_path = _ChooseImageFromDirectory(self.image)
David Pursellf1d16a62015-03-25 13:31:04 -0700253 else:
254 # Translate the xbuddy path to get the exact image to use.
Gilad Arnolde62ec902015-04-24 14:41:02 -0700255 translated_path, _ = ds_wrapper.GetImagePathWithXbuddy(
Don Garrett97d7dc22015-01-20 14:07:56 -0800256 self.image, self.board, static_dir=DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700257 image_path = ds_wrapper.TranslatedPathToLocalPath(
Don Garrett97d7dc22015-01-20 14:07:56 -0800258 translated_path, DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700259
260 logging.info('Using image %s', translated_path or image_path)
261 return image_path
262
263 def Run(self):
264 """Image the removable device."""
265 devices = self.ListAllRemovableDevices()
266
267 if self.device:
268 # If user specified a device path, check if it exists.
269 if not os.path.exists(self.device):
270 raise FlashError('Device path %s does not exist.' % self.device)
271
272 # Then check if it is removable.
273 if self.device not in [self.DeviceNameToPath(x) for x in devices]:
274 msg = '%s is not a removable device.' % self.device
275 if not (self.yes or cros_build_lib.BooleanPrompt(
276 default=False, prolog=msg)):
277 raise FlashError('You can specify usb:// to choose from a list of '
278 'removable devices.')
279 target = None
280 if self.device:
281 # Get device name from path (e.g. sdc in /dev/sdc).
282 target = self.device.rsplit(os.path.sep, 1)[-1]
283 elif devices:
284 # Ask user to choose from the list.
285 target = self.ChooseRemovableDevice(devices)
286 else:
287 raise FlashError('No removable devices detected.')
288
289 image_path = self._GetImagePath()
290 try:
291 device = self.DeviceNameToPath(target)
292 if self.install:
293 self.InstallImageToDevice(image_path, device)
294 else:
295 self.CopyImageToDevice(image_path, device)
296 except cros_build_lib.RunCommandError:
297 logging.error('Failed copying image to device %s',
298 self.DeviceNameToPath(target))
299
300
301class FileImager(USBImager):
302 """Copy image to the target path."""
303
304 def Run(self):
305 """Copy the image to the path specified by self.device."""
Mao Huangc4777e82016-03-14 20:20:08 +0800306 if not os.path.isdir(os.path.dirname(self.device)):
307 raise FlashError('Parent of path %s is not a directory.' % self.device)
David Pursellf1d16a62015-03-25 13:31:04 -0700308
309 image_path = self._GetImagePath()
310 if os.path.isdir(self.device):
311 logging.info('Copying to %s',
312 os.path.join(self.device, os.path.basename(image_path)))
313 else:
314 logging.info('Copying to %s', self.device)
315 try:
316 shutil.copy(image_path, self.device)
317 except IOError:
318 logging.error('Failed to copy image %s to %s', image_path, self.device)
319
320
321class RemoteDeviceUpdater(object):
322 """Performs update on a remote device."""
323 DEVSERVER_FILENAME = 'devserver.py'
324 STATEFUL_UPDATE_BIN = '/usr/bin/stateful_update'
325 UPDATE_ENGINE_BIN = 'update_engine_client'
David Pursellf1d16a62015-03-25 13:31:04 -0700326 # Root working directory on the device. This directory is in the
327 # stateful partition and thus has enough space to store the payloads.
328 DEVICE_BASE_DIR = '/mnt/stateful_partition/cros-flash'
Ralph Nathan872ea4d2015-05-05 18:04:56 -0700329 UPDATE_CHECK_INTERVAL_PROGRESSBAR = 0.5
330 UPDATE_CHECK_INTERVAL_NORMAL = 10
David Pursellf1d16a62015-03-25 13:31:04 -0700331
332 def __init__(self, ssh_hostname, ssh_port, image, stateful_update=True,
333 rootfs_update=True, clobber_stateful=False, reboot=True,
Gilad Arnoldd0461442015-07-07 11:52:46 -0700334 board=None, src_image_to_delta=None, wipe=True, debug=False,
Daniel Erat30fd2072016-08-29 10:08:56 -0600335 yes=False, force=False, ssh_private_key=None, ping=True,
Chung-yih Wang3145bf52017-10-24 16:08:02 +0800336 disable_verification=False, send_payload_in_parallel=False):
David Pursellf1d16a62015-03-25 13:31:04 -0700337 """Initializes RemoteDeviceUpdater"""
338 if not stateful_update and not rootfs_update:
339 raise ValueError('No update operation to perform; either stateful or'
340 ' rootfs partitions must be updated.')
341 self.tempdir = tempfile.mkdtemp(prefix='cros-flash')
342 self.ssh_hostname = ssh_hostname
343 self.ssh_port = ssh_port
344 self.image = image
345 self.board = board
David Pursellf1d16a62015-03-25 13:31:04 -0700346 self.src_image_to_delta = src_image_to_delta
347 self.do_stateful_update = stateful_update
348 self.do_rootfs_update = rootfs_update
349 self.disable_verification = disable_verification
350 self.clobber_stateful = clobber_stateful
351 self.reboot = reboot
352 self.debug = debug
Daniel Erat30fd2072016-08-29 10:08:56 -0600353 self.ssh_private_key = ssh_private_key
David Pursellf1d16a62015-03-25 13:31:04 -0700354 self.ping = ping
355 # Do not wipe if debug is set.
356 self.wipe = wipe and not debug
357 self.yes = yes
358 self.force = force
Chung-yih Wang3145bf52017-10-24 16:08:02 +0800359 self.send_payload_in_parallel = send_payload_in_parallel
David Pursellf1d16a62015-03-25 13:31:04 -0700360
David Pursellf1d16a62015-03-25 13:31:04 -0700361 def Cleanup(self):
362 """Cleans up the temporary directory."""
363 if self.wipe:
364 logging.info('Cleaning up temporary working directory...')
365 osutils.RmDir(self.tempdir)
366 else:
367 logging.info('You can find the log files and/or payloads in %s',
368 self.tempdir)
369
xixuane851dfb2016-05-02 18:02:37 -0700370 def GetPayloadDir(self, device):
371 """Get directory of payload for update.
David Pursellf1d16a62015-03-25 13:31:04 -0700372
xixuane851dfb2016-05-02 18:02:37 -0700373 This method is used to obtain the directory of payload for cros-flash. The
374 given path 'self.image' is passed in when initializing RemoteDeviceUpdater.
David Pursellf1d16a62015-03-25 13:31:04 -0700375
xixuane851dfb2016-05-02 18:02:37 -0700376 If self.image is a directory, we directly use the provided update payload(s)
377 in this directory.
378
379 If self.image is an image, let devserver access it and generate payloads.
380
381 If not in the above cases, let devserver first obtain the image path. Then
382 devserver will access the image and generate payloads.
David Pursellf1d16a62015-03-25 13:31:04 -0700383
384 Args:
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400385 device: A ChromiumOSDevice object.
David Pursellf1d16a62015-03-25 13:31:04 -0700386
387 Returns:
xixuane851dfb2016-05-02 18:02:37 -0700388 A string payload_dir, that represents the payload directory.
David Pursellf1d16a62015-03-25 13:31:04 -0700389 """
xixuane851dfb2016-05-02 18:02:37 -0700390 payload_dir = self.tempdir
David Pursellf1d16a62015-03-25 13:31:04 -0700391
xixuane851dfb2016-05-02 18:02:37 -0700392 if os.path.isdir(self.image):
393 # The given path is a directory.
394 payload_dir = self.image
395 logging.info('Using provided payloads in %s', payload_dir)
396 elif os.path.isfile(self.image):
397 # The given path is an image.
398 logging.info('Using image %s', self.image)
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600399 try:
400 ds_wrapper.GetUpdatePayloadsFromLocalPath(
401 self.image, payload_dir,
402 src_image_to_delta=self.src_image_to_delta,
403 static_dir=DEVSERVER_STATIC_DIR)
404 except:
405 logging.error('Unable to get payloads from local path: %s', payload_dir)
406 raise
407
xixuane851dfb2016-05-02 18:02:37 -0700408 else:
409 self.board = cros_build_lib.GetBoard(device_board=device.board,
410 override_board=self.board,
411 force=self.yes)
412 if not self.board:
413 raise FlashError('No board identified')
David Pursellf1d16a62015-03-25 13:31:04 -0700414
xixuane851dfb2016-05-02 18:02:37 -0700415 if not self.force and self.board != device.board:
416 # If a board was specified, it must be compatible with the device.
Brian Norrisfab3fb22016-06-02 14:59:20 -0700417 raise FlashError('Device (%s) is incompatible with board %s' %
418 (device.board, self.board))
xixuane851dfb2016-05-02 18:02:37 -0700419
420 logging.info('Board is %s', self.board)
421
422 # Translate the xbuddy path to get the exact image to use.
423 translated_path, resolved_path = ds_wrapper.GetImagePathWithXbuddy(
424 self.image, self.board, static_dir=DEVSERVER_STATIC_DIR,
425 lookup_only=True)
Vadim Bendebury3c993462017-08-22 15:42:16 -0700426 logging.notice('Using image %s', translated_path)
xixuane851dfb2016-05-02 18:02:37 -0700427 # Convert the translated path to be used in the update request.
428 image_path = ds_wrapper.ConvertTranslatedPath(resolved_path,
429 translated_path)
430
431 # Launch a local devserver to generate/serve update payloads.
432 ds_wrapper.GetUpdatePayloads(
433 image_path, payload_dir, board=self.board,
434 src_image_to_delta=self.src_image_to_delta,
435 static_dir=DEVSERVER_STATIC_DIR)
436
437 return payload_dir
David Pursellf1d16a62015-03-25 13:31:04 -0700438
439 def Run(self):
xixuane851dfb2016-05-02 18:02:37 -0700440 """Perform remote device update.
441
442 The update process includes:
443 1. initialize a device instance for the given remote device.
444 2. achieve payload_dir which contains the required payloads for updating.
445 3. initialize an auto-updater instance to do RunUpdate().
446 4. After auto-update, all temp files and dir will be cleaned up.
447 """
David Pursellf1d16a62015-03-25 13:31:04 -0700448 try:
David Pursellf1d16a62015-03-25 13:31:04 -0700449 with remote_access.ChromiumOSDeviceHandler(
Daniel Erat30fd2072016-08-29 10:08:56 -0600450 self.ssh_hostname, port=self.ssh_port, base_dir=self.DEVICE_BASE_DIR,
451 private_key=self.ssh_private_key, ping=self.ping) as device:
David Pursellf1d16a62015-03-25 13:31:04 -0700452
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600453 try:
454 # Get payload directory
455 payload_dir = self.GetPayloadDir(device)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700456
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600457 # Do auto-update
458 chromeos_AU = auto_updater.ChromiumOSFlashUpdater(
459 device, payload_dir, self.tempdir,
460 do_rootfs_update=self.do_rootfs_update,
461 do_stateful_update=self.do_stateful_update,
462 reboot=self.reboot,
463 disable_verification=self.disable_verification,
464 clobber_stateful=self.clobber_stateful,
Chung-yih Wang3145bf52017-10-24 16:08:02 +0800465 yes=self.yes,
466 send_payload_in_parallel=self.send_payload_in_parallel)
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600467 chromeos_AU.CheckPayloads()
468 chromeos_AU.RunUpdate()
David Pursellf1d16a62015-03-25 13:31:04 -0700469
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600470 except Exception:
471 logging.error('Device update failed.')
472 lsb_entries = sorted(device.lsb_release.items())
473 logging.info(
474 'Following are the LSB version details of the device:\n%s',
475 '\n'.join('%s=%s' % (k, v) for k, v in lsb_entries))
476 raise
477
478 logging.notice('Update performed successfully.')
479
480 except remote_access.RemoteAccessException:
481 logging.error('Remote device failed to initialize.')
David Pursellf1d16a62015-03-25 13:31:04 -0700482 raise
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600483
David Pursellf1d16a62015-03-25 13:31:04 -0700484 finally:
485 self.Cleanup()
486
Gilad Arnoldbfcfaff2015-07-07 10:08:02 -0700487def Flash(device, image, board=None, install=False, src_image_to_delta=None,
488 rootfs_update=True, stateful_update=True, clobber_stateful=False,
Daniel Erat30fd2072016-08-29 10:08:56 -0600489 reboot=True, wipe=True, ssh_private_key=None, ping=True,
490 disable_rootfs_verification=False, clear_cache=False, yes=False,
Chung-yih Wang3145bf52017-10-24 16:08:02 +0800491 force=False, debug=False, send_payload_in_parallel=False):
David Pursellf1d16a62015-03-25 13:31:04 -0700492 """Flashes a device, USB drive, or file with an image.
493
494 This provides functionality common to `cros flash` and `brillo flash`
495 so that they can parse the commandline separately but still use the
496 same underlying functionality.
497
498 Args:
David Pursell2e773382015-04-03 14:30:47 -0700499 device: commandline.Device object; None to use the default device.
David Pursellf1d16a62015-03-25 13:31:04 -0700500 image: Path (string) to the update image. Can be a local or xbuddy path;
501 non-existant local paths are converted to xbuddy.
David Pursellf1d16a62015-03-25 13:31:04 -0700502 board: Board to use; None to automatically detect.
David Pursellf1d16a62015-03-25 13:31:04 -0700503 install: Install to USB using base disk layout; USB |device| scheme only.
504 src_image_to_delta: Local path to an image to be used as the base to
505 generate delta payloads; SSH |device| scheme only.
506 rootfs_update: Update rootfs partition; SSH |device| scheme only.
507 stateful_update: Update stateful partition; SSH |device| scheme only.
508 clobber_stateful: Clobber stateful partition; SSH |device| scheme only.
509 reboot: Reboot device after update; SSH |device| scheme only.
510 wipe: Wipe temporary working directory; SSH |device| scheme only.
Daniel Erat30fd2072016-08-29 10:08:56 -0600511 ssh_private_key: Path to an SSH private key file; None to use test keys.
David Pursellf1d16a62015-03-25 13:31:04 -0700512 ping: Ping the device before attempting update; SSH |device| scheme only.
513 disable_rootfs_verification: Remove rootfs verification after update; SSH
514 |device| scheme only.
515 clear_cache: Clear the devserver static directory.
516 yes: Assume "yes" for any prompt.
517 force: Ignore sanity checks and prompts. Overrides |yes| if True.
518 debug: Print additional debugging messages.
Chung-yih Wang3145bf52017-10-24 16:08:02 +0800519 send_payload_in_parallel: Transfer payloads in chunks in parallel to speed
520 up transmissions for long haul between endpoints.
David Pursellf1d16a62015-03-25 13:31:04 -0700521
522 Raises:
523 FlashError: An unrecoverable error occured.
524 ValueError: Invalid parameter combination.
525 """
526 if force:
527 yes = True
528
529 if clear_cache:
530 logging.info('Clearing the cache...')
Don Garrett97d7dc22015-01-20 14:07:56 -0800531 ds_wrapper.DevServerWrapper.WipeStaticDirectory(DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700532
533 try:
Don Garrett97d7dc22015-01-20 14:07:56 -0800534 osutils.SafeMakedirsNonRoot(DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700535 except OSError:
Don Garrett97d7dc22015-01-20 14:07:56 -0800536 logging.error('Failed to create %s', DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700537
538 if install:
David Pursell2e773382015-04-03 14:30:47 -0700539 if not device or device.scheme != commandline.DEVICE_SCHEME_USB:
David Pursellf1d16a62015-03-25 13:31:04 -0700540 raise ValueError(
541 '--install can only be used when writing to a USB device')
542 if not cros_build_lib.IsInsideChroot():
543 raise ValueError('--install can only be used inside the chroot')
544
David Pursell2e773382015-04-03 14:30:47 -0700545 if not device or device.scheme == commandline.DEVICE_SCHEME_SSH:
546 if device:
547 hostname, port = device.hostname, device.port
548 else:
549 hostname, port = None, None
Ralph Nathan872ea4d2015-05-05 18:04:56 -0700550 logging.notice('Preparing to update the remote device %s', hostname)
David Pursellf1d16a62015-03-25 13:31:04 -0700551 updater = RemoteDeviceUpdater(
David Pursell2e773382015-04-03 14:30:47 -0700552 hostname,
553 port,
David Pursellf1d16a62015-03-25 13:31:04 -0700554 image,
555 board=board,
David Pursellf1d16a62015-03-25 13:31:04 -0700556 src_image_to_delta=src_image_to_delta,
557 rootfs_update=rootfs_update,
558 stateful_update=stateful_update,
559 clobber_stateful=clobber_stateful,
560 reboot=reboot,
561 wipe=wipe,
562 debug=debug,
563 yes=yes,
564 force=force,
Daniel Erat30fd2072016-08-29 10:08:56 -0600565 ssh_private_key=ssh_private_key,
David Pursellf1d16a62015-03-25 13:31:04 -0700566 ping=ping,
Chung-yih Wang3145bf52017-10-24 16:08:02 +0800567 disable_verification=disable_rootfs_verification,
568 send_payload_in_parallel=send_payload_in_parallel)
David Pursellf1d16a62015-03-25 13:31:04 -0700569 updater.Run()
570 elif device.scheme == commandline.DEVICE_SCHEME_USB:
571 path = osutils.ExpandPath(device.path) if device.path else ''
572 logging.info('Preparing to image the removable device %s', path)
573 imager = USBImager(path,
574 board,
575 image,
David Pursellf1d16a62015-03-25 13:31:04 -0700576 debug=debug,
577 install=install,
578 yes=yes)
579 imager.Run()
580 elif device.scheme == commandline.DEVICE_SCHEME_FILE:
581 logging.info('Preparing to copy image to %s', device.path)
582 imager = FileImager(device.path,
583 board,
584 image,
David Pursellf1d16a62015-03-25 13:31:04 -0700585 debug=debug,
586 yes=yes)
587 imager.Run()