blob: b16d9ad9fb086567d511bb1d2c9d5fb4f1e80417 [file] [log] [blame]
David Pursellf1d16a62015-03-25 13:31:04 -07001# Copyright 2015 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Install/copy the image to the device."""
6
7from __future__ import print_function
8
David Pursellf1d16a62015-03-25 13:31:04 -07009import os
Ralph Nathan9b997232015-05-15 13:13:12 -070010import re
David Pursellf1d16a62015-03-25 13:31:04 -070011import shutil
12import tempfile
David Pursellf1d16a62015-03-25 13:31:04 -070013
14from chromite.cbuildbot import constants
xixuane851dfb2016-05-02 18:02:37 -070015from chromite.lib import auto_updater
David Pursellf1d16a62015-03-25 13:31:04 -070016from chromite.lib import commandline
17from chromite.lib import cros_build_lib
18from chromite.lib import cros_logging as logging
19from chromite.lib import dev_server_wrapper as ds_wrapper
Ralph Nathan872ea4d2015-05-05 18:04:56 -070020from chromite.lib import operation
David Pursellf1d16a62015-03-25 13:31:04 -070021from chromite.lib import osutils
Gilad Arnold1c8eda52015-05-04 22:32:38 -070022from chromite.lib import path_util
David Pursellf1d16a62015-03-25 13:31:04 -070023from chromite.lib import remote_access
24
25
Don Garrett97d7dc22015-01-20 14:07:56 -080026DEVSERVER_STATIC_DIR = path_util.FromChrootPath(
David Pursellf1d16a62015-03-25 13:31:04 -070027 os.path.join(constants.CHROOT_SOURCE_ROOT, 'devserver', 'static'))
28
29
Ralph Nathan9b997232015-05-15 13:13:12 -070030class UsbImagerOperation(operation.ProgressBarOperation):
31 """Progress bar for flashing image to operation."""
32
33 def __init__(self, image):
34 super(UsbImagerOperation, self).__init__()
35 self._size = os.path.getsize(image)
36 self._transferred = 0.
37 self._bytes = re.compile(r'(\d+) bytes')
38
39 def _GetDDPid(self):
40 """Get the Pid of dd."""
41 try:
42 pids = cros_build_lib.RunCommand(['pgrep', 'dd'], capture_output=True,
43 print_cmd=False).output
44 for pid in pids.splitlines():
45 if osutils.IsChildProcess(int(pid), name='dd'):
46 return int(pid)
47 return -1
48 except cros_build_lib.RunCommandError:
49 # If dd isn't still running, then we assume that it is finished.
50 return -1
51
52 def _PingDD(self, dd_pid):
53 """Send USR1 signal to dd to get status update."""
54 try:
55 cmd = ['kill', '-USR1', str(dd_pid)]
56 cros_build_lib.SudoRunCommand(cmd, print_cmd=False)
57 except cros_build_lib.RunCommandError:
58 # Here we assume that dd finished in the background.
59 return
60
61 def ParseOutput(self, output=None):
62 """Parse the output of dd to update progress bar."""
63 dd_pid = self._GetDDPid()
64 if dd_pid == -1:
65 return
66
67 self._PingDD(dd_pid)
68
69 if output is None:
70 stdout = self._stdout.read()
71 stderr = self._stderr.read()
72 output = stdout + stderr
73
74 match = self._bytes.search(output)
75 if match:
76 self._transferred = match.groups()[0]
77
78 self.ProgressBar(float(self._transferred) / self._size)
79
80
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -070081def _IsFilePathGPTDiskImage(file_path):
82 """Determines if a file is a valid GPT disk.
83
84 Args:
85 file_path: Path to the file to test.
86 """
87 if os.path.isfile(file_path):
88 with cros_build_lib.Open(file_path) as image_file:
89 image_file.seek(0x1fe)
90 if image_file.read(10) == '\x55\xaaEFI PART':
91 return True
92 return False
93
94
95def _ChooseImageFromDirectory(dir_path):
96 """Lists all image files in |dir_path| and ask user to select one.
97
98 Args:
99 dir_path: Path to the directory.
100 """
101 images = sorted([x for x in os.listdir(dir_path) if
102 _IsFilePathGPTDiskImage(os.path.join(dir_path, x))])
103 idx = 0
104 if len(images) == 0:
105 raise ValueError('No image found in %s.' % dir_path)
106 elif len(images) > 1:
107 idx = cros_build_lib.GetChoice(
108 'Multiple images found in %s. Please select one to continue:' % (
109 (dir_path,)),
110 images)
111
112 return os.path.join(dir_path, images[idx])
113
114
David Pursellf1d16a62015-03-25 13:31:04 -0700115class FlashError(Exception):
116 """Thrown when there is an unrecoverable error during flash."""
117
118
119class USBImager(object):
120 """Copy image to the target removable device."""
121
Gilad Arnoldd0461442015-07-07 11:52:46 -0700122 def __init__(self, device, board, image, debug=False, install=False,
123 yes=False):
David Pursellf1d16a62015-03-25 13:31:04 -0700124 """Initalizes USBImager."""
125 self.device = device
126 self.board = board if board else cros_build_lib.GetDefaultBoard()
127 self.image = image
David Pursellf1d16a62015-03-25 13:31:04 -0700128 self.debug = debug
129 self.debug_level = logging.DEBUG if debug else logging.INFO
130 self.install = install
131 self.yes = yes
132
133 def DeviceNameToPath(self, device_name):
134 return '/dev/%s' % device_name
135
136 def GetRemovableDeviceDescription(self, device):
137 """Returns a informational description of the removable |device|.
138
139 Args:
140 device: the device name (e.g. sdc).
141
142 Returns:
143 A string describing |device| (e.g. Patriot Memory 7918 MB).
144 """
145 desc = [
146 osutils.GetDeviceInfo(device, keyword='manufacturer'),
147 osutils.GetDeviceInfo(device, keyword='product'),
148 osutils.GetDeviceSize(self.DeviceNameToPath(device)),
149 '(%s)' % self.DeviceNameToPath(device),
150 ]
151 return ' '.join([x for x in desc if x])
152
153 def ListAllRemovableDevices(self):
154 """Returns a list of removable devices.
155
156 Returns:
157 A list of device names (e.g. ['sdb', 'sdc']).
158 """
159 devices = osutils.ListBlockDevices()
160 removable_devices = []
161 for d in devices:
162 if d.TYPE == 'disk' and d.RM == '1':
163 removable_devices.append(d.NAME)
164
165 return removable_devices
166
167 def ChooseRemovableDevice(self, devices):
168 """Lists all removable devices and asks user to select/confirm.
169
170 Args:
171 devices: a list of device names (e.g. ['sda', 'sdb']).
172
173 Returns:
174 The device name chosen by the user.
175 """
176 idx = cros_build_lib.GetChoice(
177 'Removable device(s) found. Please select/confirm to continue:',
178 [self.GetRemovableDeviceDescription(x) for x in devices])
179
180 return devices[idx]
181
182 def InstallImageToDevice(self, image, device):
183 """Installs |image| to the removable |device|.
184
185 Args:
186 image: Path to the image to copy.
187 device: Device to copy to.
188 """
189 cmd = [
190 'chromeos-install',
191 '--yes',
192 '--skip_src_removable',
193 '--skip_dst_removable',
194 '--payload_image=%s' % image,
195 '--dst=%s' % device,
196 '--skip_postinstall',
197 ]
198 cros_build_lib.SudoRunCommand(cmd)
199
200 def CopyImageToDevice(self, image, device):
201 """Copies |image| to the removable |device|.
202
203 Args:
204 image: Path to the image to copy.
205 device: Device to copy to.
206 """
Ralph Nathan9b997232015-05-15 13:13:12 -0700207 cmd = ['dd', 'if=%s' % image, 'of=%s' % device, 'bs=4M', 'iflag=fullblock',
208 'oflag=sync']
209 if logging.getLogger().getEffectiveLevel() <= logging.NOTICE:
210 op = UsbImagerOperation(image)
211 op.Run(cros_build_lib.SudoRunCommand, cmd, debug_level=logging.NOTICE,
212 update_period=0.5)
213 else:
214 cros_build_lib.SudoRunCommand(
215 cmd, debug_level=logging.NOTICE,
216 print_cmd=logging.getLogger().getEffectiveLevel() < logging.NOTICE)
David Pursellf1d16a62015-03-25 13:31:04 -0700217
David Pursellf1d16a62015-03-25 13:31:04 -0700218 cros_build_lib.SudoRunCommand(['sync'], debug_level=self.debug_level)
219
David Pursellf1d16a62015-03-25 13:31:04 -0700220 def _GetImagePath(self):
221 """Returns the image path to use."""
222 image_path = translated_path = None
223 if os.path.isfile(self.image):
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700224 if not self.yes and not _IsFilePathGPTDiskImage(self.image):
David Pursellf1d16a62015-03-25 13:31:04 -0700225 # TODO(wnwen): Open the tarball and if there is just one file in it,
226 # use that instead. Existing code in upload_symbols.py.
227 if cros_build_lib.BooleanPrompt(
228 prolog='The given image file is not a valid disk image. Perhaps '
229 'you forgot to untar it.',
230 prompt='Terminate the current flash process?'):
231 raise FlashError('Update terminated by user.')
232 image_path = self.image
233 elif os.path.isdir(self.image):
234 # Ask user which image (*.bin) in the folder to use.
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700235 image_path = _ChooseImageFromDirectory(self.image)
David Pursellf1d16a62015-03-25 13:31:04 -0700236 else:
237 # Translate the xbuddy path to get the exact image to use.
Gilad Arnolde62ec902015-04-24 14:41:02 -0700238 translated_path, _ = ds_wrapper.GetImagePathWithXbuddy(
Don Garrett97d7dc22015-01-20 14:07:56 -0800239 self.image, self.board, static_dir=DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700240 image_path = ds_wrapper.TranslatedPathToLocalPath(
Don Garrett97d7dc22015-01-20 14:07:56 -0800241 translated_path, DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700242
243 logging.info('Using image %s', translated_path or image_path)
244 return image_path
245
246 def Run(self):
247 """Image the removable device."""
248 devices = self.ListAllRemovableDevices()
249
250 if self.device:
251 # If user specified a device path, check if it exists.
252 if not os.path.exists(self.device):
253 raise FlashError('Device path %s does not exist.' % self.device)
254
255 # Then check if it is removable.
256 if self.device not in [self.DeviceNameToPath(x) for x in devices]:
257 msg = '%s is not a removable device.' % self.device
258 if not (self.yes or cros_build_lib.BooleanPrompt(
259 default=False, prolog=msg)):
260 raise FlashError('You can specify usb:// to choose from a list of '
261 'removable devices.')
262 target = None
263 if self.device:
264 # Get device name from path (e.g. sdc in /dev/sdc).
265 target = self.device.rsplit(os.path.sep, 1)[-1]
266 elif devices:
267 # Ask user to choose from the list.
268 target = self.ChooseRemovableDevice(devices)
269 else:
270 raise FlashError('No removable devices detected.')
271
272 image_path = self._GetImagePath()
273 try:
274 device = self.DeviceNameToPath(target)
275 if self.install:
276 self.InstallImageToDevice(image_path, device)
277 else:
278 self.CopyImageToDevice(image_path, device)
279 except cros_build_lib.RunCommandError:
280 logging.error('Failed copying image to device %s',
281 self.DeviceNameToPath(target))
282
283
284class FileImager(USBImager):
285 """Copy image to the target path."""
286
287 def Run(self):
288 """Copy the image to the path specified by self.device."""
Mao Huangc4777e82016-03-14 20:20:08 +0800289 if not os.path.isdir(os.path.dirname(self.device)):
290 raise FlashError('Parent of path %s is not a directory.' % self.device)
David Pursellf1d16a62015-03-25 13:31:04 -0700291
292 image_path = self._GetImagePath()
293 if os.path.isdir(self.device):
294 logging.info('Copying to %s',
295 os.path.join(self.device, os.path.basename(image_path)))
296 else:
297 logging.info('Copying to %s', self.device)
298 try:
299 shutil.copy(image_path, self.device)
300 except IOError:
301 logging.error('Failed to copy image %s to %s', image_path, self.device)
302
303
304class RemoteDeviceUpdater(object):
305 """Performs update on a remote device."""
306 DEVSERVER_FILENAME = 'devserver.py'
307 STATEFUL_UPDATE_BIN = '/usr/bin/stateful_update'
308 UPDATE_ENGINE_BIN = 'update_engine_client'
David Pursellf1d16a62015-03-25 13:31:04 -0700309 # Root working directory on the device. This directory is in the
310 # stateful partition and thus has enough space to store the payloads.
311 DEVICE_BASE_DIR = '/mnt/stateful_partition/cros-flash'
Ralph Nathan872ea4d2015-05-05 18:04:56 -0700312 UPDATE_CHECK_INTERVAL_PROGRESSBAR = 0.5
313 UPDATE_CHECK_INTERVAL_NORMAL = 10
David Pursellf1d16a62015-03-25 13:31:04 -0700314
315 def __init__(self, ssh_hostname, ssh_port, image, stateful_update=True,
316 rootfs_update=True, clobber_stateful=False, reboot=True,
Gilad Arnoldd0461442015-07-07 11:52:46 -0700317 board=None, src_image_to_delta=None, wipe=True, debug=False,
Daniel Erat30fd2072016-08-29 10:08:56 -0600318 yes=False, force=False, ssh_private_key=None, ping=True,
Gilad Arnold821547c2015-07-07 08:57:48 -0700319 disable_verification=False):
David Pursellf1d16a62015-03-25 13:31:04 -0700320 """Initializes RemoteDeviceUpdater"""
321 if not stateful_update and not rootfs_update:
322 raise ValueError('No update operation to perform; either stateful or'
323 ' rootfs partitions must be updated.')
324 self.tempdir = tempfile.mkdtemp(prefix='cros-flash')
325 self.ssh_hostname = ssh_hostname
326 self.ssh_port = ssh_port
327 self.image = image
328 self.board = board
David Pursellf1d16a62015-03-25 13:31:04 -0700329 self.src_image_to_delta = src_image_to_delta
330 self.do_stateful_update = stateful_update
331 self.do_rootfs_update = rootfs_update
332 self.disable_verification = disable_verification
333 self.clobber_stateful = clobber_stateful
334 self.reboot = reboot
335 self.debug = debug
Daniel Erat30fd2072016-08-29 10:08:56 -0600336 self.ssh_private_key = ssh_private_key
David Pursellf1d16a62015-03-25 13:31:04 -0700337 self.ping = ping
338 # Do not wipe if debug is set.
339 self.wipe = wipe and not debug
340 self.yes = yes
341 self.force = force
David Pursellf1d16a62015-03-25 13:31:04 -0700342
David Pursellf1d16a62015-03-25 13:31:04 -0700343 def Cleanup(self):
344 """Cleans up the temporary directory."""
345 if self.wipe:
346 logging.info('Cleaning up temporary working directory...')
347 osutils.RmDir(self.tempdir)
348 else:
349 logging.info('You can find the log files and/or payloads in %s',
350 self.tempdir)
351
xixuane851dfb2016-05-02 18:02:37 -0700352 def GetPayloadDir(self, device):
353 """Get directory of payload for update.
David Pursellf1d16a62015-03-25 13:31:04 -0700354
xixuane851dfb2016-05-02 18:02:37 -0700355 This method is used to obtain the directory of payload for cros-flash. The
356 given path 'self.image' is passed in when initializing RemoteDeviceUpdater.
David Pursellf1d16a62015-03-25 13:31:04 -0700357
xixuane851dfb2016-05-02 18:02:37 -0700358 If self.image is a directory, we directly use the provided update payload(s)
359 in this directory.
360
361 If self.image is an image, let devserver access it and generate payloads.
362
363 If not in the above cases, let devserver first obtain the image path. Then
364 devserver will access the image and generate payloads.
David Pursellf1d16a62015-03-25 13:31:04 -0700365
366 Args:
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400367 device: A ChromiumOSDevice object.
David Pursellf1d16a62015-03-25 13:31:04 -0700368
369 Returns:
xixuane851dfb2016-05-02 18:02:37 -0700370 A string payload_dir, that represents the payload directory.
David Pursellf1d16a62015-03-25 13:31:04 -0700371 """
xixuane851dfb2016-05-02 18:02:37 -0700372 payload_dir = self.tempdir
David Pursellf1d16a62015-03-25 13:31:04 -0700373
xixuane851dfb2016-05-02 18:02:37 -0700374 if os.path.isdir(self.image):
375 # The given path is a directory.
376 payload_dir = self.image
377 logging.info('Using provided payloads in %s', payload_dir)
378 elif os.path.isfile(self.image):
379 # The given path is an image.
380 logging.info('Using image %s', self.image)
381 ds_wrapper.GetUpdatePayloadsFromLocalPath(
382 self.image, payload_dir,
383 src_image_to_delta=self.src_image_to_delta,
384 static_dir=DEVSERVER_STATIC_DIR)
385 else:
386 self.board = cros_build_lib.GetBoard(device_board=device.board,
387 override_board=self.board,
388 force=self.yes)
389 if not self.board:
390 raise FlashError('No board identified')
David Pursellf1d16a62015-03-25 13:31:04 -0700391
xixuane851dfb2016-05-02 18:02:37 -0700392 if not self.force and self.board != device.board:
393 # If a board was specified, it must be compatible with the device.
Brian Norrisfab3fb22016-06-02 14:59:20 -0700394 raise FlashError('Device (%s) is incompatible with board %s' %
395 (device.board, self.board))
xixuane851dfb2016-05-02 18:02:37 -0700396
397 logging.info('Board is %s', self.board)
398
399 # Translate the xbuddy path to get the exact image to use.
400 translated_path, resolved_path = ds_wrapper.GetImagePathWithXbuddy(
401 self.image, self.board, static_dir=DEVSERVER_STATIC_DIR,
402 lookup_only=True)
403 logging.info('Using image %s', translated_path)
404 # Convert the translated path to be used in the update request.
405 image_path = ds_wrapper.ConvertTranslatedPath(resolved_path,
406 translated_path)
407
408 # Launch a local devserver to generate/serve update payloads.
409 ds_wrapper.GetUpdatePayloads(
410 image_path, payload_dir, board=self.board,
411 src_image_to_delta=self.src_image_to_delta,
412 static_dir=DEVSERVER_STATIC_DIR)
413
414 return payload_dir
David Pursellf1d16a62015-03-25 13:31:04 -0700415
416 def Run(self):
xixuane851dfb2016-05-02 18:02:37 -0700417 """Perform remote device update.
418
419 The update process includes:
420 1. initialize a device instance for the given remote device.
421 2. achieve payload_dir which contains the required payloads for updating.
422 3. initialize an auto-updater instance to do RunUpdate().
423 4. After auto-update, all temp files and dir will be cleaned up.
424 """
David Pursellf1d16a62015-03-25 13:31:04 -0700425 try:
426 device_connected = False
xixuane851dfb2016-05-02 18:02:37 -0700427
David Pursellf1d16a62015-03-25 13:31:04 -0700428 with remote_access.ChromiumOSDeviceHandler(
Daniel Erat30fd2072016-08-29 10:08:56 -0600429 self.ssh_hostname, port=self.ssh_port, base_dir=self.DEVICE_BASE_DIR,
430 private_key=self.ssh_private_key, ping=self.ping) as device:
David Pursellf1d16a62015-03-25 13:31:04 -0700431 device_connected = True
432
xixuane851dfb2016-05-02 18:02:37 -0700433 # Get payload directory
434 payload_dir = self.GetPayloadDir(device)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700435
xixuane851dfb2016-05-02 18:02:37 -0700436 # Do auto-update
xixuan9a157272016-05-31 11:35:13 -0700437 chromeos_AU = auto_updater.ChromiumOSFlashUpdater(
xixuane851dfb2016-05-02 18:02:37 -0700438 device, payload_dir, self.tempdir,
439 do_rootfs_update=self.do_rootfs_update,
440 do_stateful_update=self.do_stateful_update,
441 reboot=self.reboot,
442 disable_verification=self.disable_verification,
443 clobber_stateful=self.clobber_stateful,
444 yes=self.yes)
445 chromeos_AU.CheckPayloads()
446 chromeos_AU.RunUpdate()
David Pursellf1d16a62015-03-25 13:31:04 -0700447
448 except Exception:
449 logging.error('Device update failed.')
450 if device_connected and device.lsb_release:
451 lsb_entries = sorted(device.lsb_release.items())
452 logging.info('Following are the LSB version details of the device:\n%s',
453 '\n'.join('%s=%s' % (k, v) for k, v in lsb_entries))
454 raise
455 else:
Ralph Nathan872ea4d2015-05-05 18:04:56 -0700456 logging.notice('Update performed successfully.')
David Pursellf1d16a62015-03-25 13:31:04 -0700457 finally:
458 self.Cleanup()
459
Gilad Arnoldbfcfaff2015-07-07 10:08:02 -0700460def Flash(device, image, board=None, install=False, src_image_to_delta=None,
461 rootfs_update=True, stateful_update=True, clobber_stateful=False,
Daniel Erat30fd2072016-08-29 10:08:56 -0600462 reboot=True, wipe=True, ssh_private_key=None, ping=True,
463 disable_rootfs_verification=False, clear_cache=False, yes=False,
464 force=False, debug=False):
David Pursellf1d16a62015-03-25 13:31:04 -0700465 """Flashes a device, USB drive, or file with an image.
466
467 This provides functionality common to `cros flash` and `brillo flash`
468 so that they can parse the commandline separately but still use the
469 same underlying functionality.
470
471 Args:
David Pursell2e773382015-04-03 14:30:47 -0700472 device: commandline.Device object; None to use the default device.
David Pursellf1d16a62015-03-25 13:31:04 -0700473 image: Path (string) to the update image. Can be a local or xbuddy path;
474 non-existant local paths are converted to xbuddy.
David Pursellf1d16a62015-03-25 13:31:04 -0700475 board: Board to use; None to automatically detect.
David Pursellf1d16a62015-03-25 13:31:04 -0700476 install: Install to USB using base disk layout; USB |device| scheme only.
477 src_image_to_delta: Local path to an image to be used as the base to
478 generate delta payloads; SSH |device| scheme only.
479 rootfs_update: Update rootfs partition; SSH |device| scheme only.
480 stateful_update: Update stateful partition; SSH |device| scheme only.
481 clobber_stateful: Clobber stateful partition; SSH |device| scheme only.
482 reboot: Reboot device after update; SSH |device| scheme only.
483 wipe: Wipe temporary working directory; SSH |device| scheme only.
Daniel Erat30fd2072016-08-29 10:08:56 -0600484 ssh_private_key: Path to an SSH private key file; None to use test keys.
David Pursellf1d16a62015-03-25 13:31:04 -0700485 ping: Ping the device before attempting update; SSH |device| scheme only.
486 disable_rootfs_verification: Remove rootfs verification after update; SSH
487 |device| scheme only.
488 clear_cache: Clear the devserver static directory.
489 yes: Assume "yes" for any prompt.
490 force: Ignore sanity checks and prompts. Overrides |yes| if True.
491 debug: Print additional debugging messages.
492
493 Raises:
494 FlashError: An unrecoverable error occured.
495 ValueError: Invalid parameter combination.
496 """
497 if force:
498 yes = True
499
500 if clear_cache:
501 logging.info('Clearing the cache...')
Don Garrett97d7dc22015-01-20 14:07:56 -0800502 ds_wrapper.DevServerWrapper.WipeStaticDirectory(DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700503
504 try:
Don Garrett97d7dc22015-01-20 14:07:56 -0800505 osutils.SafeMakedirsNonRoot(DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700506 except OSError:
Don Garrett97d7dc22015-01-20 14:07:56 -0800507 logging.error('Failed to create %s', DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700508
509 if install:
David Pursell2e773382015-04-03 14:30:47 -0700510 if not device or device.scheme != commandline.DEVICE_SCHEME_USB:
David Pursellf1d16a62015-03-25 13:31:04 -0700511 raise ValueError(
512 '--install can only be used when writing to a USB device')
513 if not cros_build_lib.IsInsideChroot():
514 raise ValueError('--install can only be used inside the chroot')
515
David Pursell2e773382015-04-03 14:30:47 -0700516 if not device or device.scheme == commandline.DEVICE_SCHEME_SSH:
517 if device:
518 hostname, port = device.hostname, device.port
519 else:
520 hostname, port = None, None
Ralph Nathan872ea4d2015-05-05 18:04:56 -0700521 logging.notice('Preparing to update the remote device %s', hostname)
David Pursellf1d16a62015-03-25 13:31:04 -0700522 updater = RemoteDeviceUpdater(
David Pursell2e773382015-04-03 14:30:47 -0700523 hostname,
524 port,
David Pursellf1d16a62015-03-25 13:31:04 -0700525 image,
526 board=board,
David Pursellf1d16a62015-03-25 13:31:04 -0700527 src_image_to_delta=src_image_to_delta,
528 rootfs_update=rootfs_update,
529 stateful_update=stateful_update,
530 clobber_stateful=clobber_stateful,
531 reboot=reboot,
532 wipe=wipe,
533 debug=debug,
534 yes=yes,
535 force=force,
Daniel Erat30fd2072016-08-29 10:08:56 -0600536 ssh_private_key=ssh_private_key,
David Pursellf1d16a62015-03-25 13:31:04 -0700537 ping=ping,
Gilad Arnold821547c2015-07-07 08:57:48 -0700538 disable_verification=disable_rootfs_verification)
David Pursellf1d16a62015-03-25 13:31:04 -0700539 updater.Run()
540 elif device.scheme == commandline.DEVICE_SCHEME_USB:
541 path = osutils.ExpandPath(device.path) if device.path else ''
542 logging.info('Preparing to image the removable device %s', path)
543 imager = USBImager(path,
544 board,
545 image,
David Pursellf1d16a62015-03-25 13:31:04 -0700546 debug=debug,
547 install=install,
548 yes=yes)
549 imager.Run()
550 elif device.scheme == commandline.DEVICE_SCHEME_FILE:
551 logging.info('Preparing to copy image to %s', device.path)
552 imager = FileImager(device.path,
553 board,
554 image,
David Pursellf1d16a62015-03-25 13:31:04 -0700555 debug=debug,
556 yes=yes)
557 imager.Run()