blob: 6e63932c19184c1933dcf0f4987306c1f2ffa385 [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,
Gilad Arnold821547c2015-07-07 08:57:48 -0700336 disable_verification=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
David Pursellf1d16a62015-03-25 13:31:04 -0700359
David Pursellf1d16a62015-03-25 13:31:04 -0700360 def Cleanup(self):
361 """Cleans up the temporary directory."""
362 if self.wipe:
363 logging.info('Cleaning up temporary working directory...')
364 osutils.RmDir(self.tempdir)
365 else:
366 logging.info('You can find the log files and/or payloads in %s',
367 self.tempdir)
368
xixuane851dfb2016-05-02 18:02:37 -0700369 def GetPayloadDir(self, device):
370 """Get directory of payload for update.
David Pursellf1d16a62015-03-25 13:31:04 -0700371
xixuane851dfb2016-05-02 18:02:37 -0700372 This method is used to obtain the directory of payload for cros-flash. The
373 given path 'self.image' is passed in when initializing RemoteDeviceUpdater.
David Pursellf1d16a62015-03-25 13:31:04 -0700374
xixuane851dfb2016-05-02 18:02:37 -0700375 If self.image is a directory, we directly use the provided update payload(s)
376 in this directory.
377
378 If self.image is an image, let devserver access it and generate payloads.
379
380 If not in the above cases, let devserver first obtain the image path. Then
381 devserver will access the image and generate payloads.
David Pursellf1d16a62015-03-25 13:31:04 -0700382
383 Args:
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400384 device: A ChromiumOSDevice object.
David Pursellf1d16a62015-03-25 13:31:04 -0700385
386 Returns:
xixuane851dfb2016-05-02 18:02:37 -0700387 A string payload_dir, that represents the payload directory.
David Pursellf1d16a62015-03-25 13:31:04 -0700388 """
xixuane851dfb2016-05-02 18:02:37 -0700389 payload_dir = self.tempdir
David Pursellf1d16a62015-03-25 13:31:04 -0700390
xixuane851dfb2016-05-02 18:02:37 -0700391 if os.path.isdir(self.image):
392 # The given path is a directory.
393 payload_dir = self.image
394 logging.info('Using provided payloads in %s', payload_dir)
395 elif os.path.isfile(self.image):
396 # The given path is an image.
397 logging.info('Using image %s', self.image)
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600398 try:
399 ds_wrapper.GetUpdatePayloadsFromLocalPath(
400 self.image, payload_dir,
401 src_image_to_delta=self.src_image_to_delta,
402 static_dir=DEVSERVER_STATIC_DIR)
403 except:
404 logging.error('Unable to get payloads from local path: %s', payload_dir)
405 raise
406
xixuane851dfb2016-05-02 18:02:37 -0700407 else:
408 self.board = cros_build_lib.GetBoard(device_board=device.board,
409 override_board=self.board,
410 force=self.yes)
411 if not self.board:
412 raise FlashError('No board identified')
David Pursellf1d16a62015-03-25 13:31:04 -0700413
xixuane851dfb2016-05-02 18:02:37 -0700414 if not self.force and self.board != device.board:
415 # If a board was specified, it must be compatible with the device.
Brian Norrisfab3fb22016-06-02 14:59:20 -0700416 raise FlashError('Device (%s) is incompatible with board %s' %
417 (device.board, self.board))
xixuane851dfb2016-05-02 18:02:37 -0700418
419 logging.info('Board is %s', self.board)
420
421 # Translate the xbuddy path to get the exact image to use.
422 translated_path, resolved_path = ds_wrapper.GetImagePathWithXbuddy(
423 self.image, self.board, static_dir=DEVSERVER_STATIC_DIR,
424 lookup_only=True)
Vadim Bendebury3c993462017-08-22 15:42:16 -0700425 logging.notice('Using image %s', translated_path)
xixuane851dfb2016-05-02 18:02:37 -0700426 # Convert the translated path to be used in the update request.
427 image_path = ds_wrapper.ConvertTranslatedPath(resolved_path,
428 translated_path)
429
430 # Launch a local devserver to generate/serve update payloads.
431 ds_wrapper.GetUpdatePayloads(
432 image_path, payload_dir, board=self.board,
433 src_image_to_delta=self.src_image_to_delta,
434 static_dir=DEVSERVER_STATIC_DIR)
435
436 return payload_dir
David Pursellf1d16a62015-03-25 13:31:04 -0700437
438 def Run(self):
xixuane851dfb2016-05-02 18:02:37 -0700439 """Perform remote device update.
440
441 The update process includes:
442 1. initialize a device instance for the given remote device.
443 2. achieve payload_dir which contains the required payloads for updating.
444 3. initialize an auto-updater instance to do RunUpdate().
445 4. After auto-update, all temp files and dir will be cleaned up.
446 """
David Pursellf1d16a62015-03-25 13:31:04 -0700447 try:
David Pursellf1d16a62015-03-25 13:31:04 -0700448 with remote_access.ChromiumOSDeviceHandler(
Daniel Erat30fd2072016-08-29 10:08:56 -0600449 self.ssh_hostname, port=self.ssh_port, base_dir=self.DEVICE_BASE_DIR,
450 private_key=self.ssh_private_key, ping=self.ping) as device:
David Pursellf1d16a62015-03-25 13:31:04 -0700451
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600452 try:
453 # Get payload directory
454 payload_dir = self.GetPayloadDir(device)
Bertrand SIMONNET56f773d2015-05-04 14:02:39 -0700455
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600456 # Do auto-update
457 chromeos_AU = auto_updater.ChromiumOSFlashUpdater(
458 device, payload_dir, self.tempdir,
459 do_rootfs_update=self.do_rootfs_update,
460 do_stateful_update=self.do_stateful_update,
461 reboot=self.reboot,
462 disable_verification=self.disable_verification,
463 clobber_stateful=self.clobber_stateful,
464 yes=self.yes)
465 chromeos_AU.CheckPayloads()
466 chromeos_AU.RunUpdate()
David Pursellf1d16a62015-03-25 13:31:04 -0700467
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600468 except Exception:
469 logging.error('Device update failed.')
470 lsb_entries = sorted(device.lsb_release.items())
471 logging.info(
472 'Following are the LSB version details of the device:\n%s',
473 '\n'.join('%s=%s' % (k, v) for k, v in lsb_entries))
474 raise
475
476 logging.notice('Update performed successfully.')
477
478 except remote_access.RemoteAccessException:
479 logging.error('Remote device failed to initialize.')
David Pursellf1d16a62015-03-25 13:31:04 -0700480 raise
Steven Bennetts4304c7e2017-09-05 12:30:30 -0600481
David Pursellf1d16a62015-03-25 13:31:04 -0700482 finally:
483 self.Cleanup()
484
Gilad Arnoldbfcfaff2015-07-07 10:08:02 -0700485def Flash(device, image, board=None, install=False, src_image_to_delta=None,
486 rootfs_update=True, stateful_update=True, clobber_stateful=False,
Daniel Erat30fd2072016-08-29 10:08:56 -0600487 reboot=True, wipe=True, ssh_private_key=None, ping=True,
488 disable_rootfs_verification=False, clear_cache=False, yes=False,
489 force=False, debug=False):
David Pursellf1d16a62015-03-25 13:31:04 -0700490 """Flashes a device, USB drive, or file with an image.
491
492 This provides functionality common to `cros flash` and `brillo flash`
493 so that they can parse the commandline separately but still use the
494 same underlying functionality.
495
496 Args:
David Pursell2e773382015-04-03 14:30:47 -0700497 device: commandline.Device object; None to use the default device.
David Pursellf1d16a62015-03-25 13:31:04 -0700498 image: Path (string) to the update image. Can be a local or xbuddy path;
499 non-existant local paths are converted to xbuddy.
David Pursellf1d16a62015-03-25 13:31:04 -0700500 board: Board to use; None to automatically detect.
David Pursellf1d16a62015-03-25 13:31:04 -0700501 install: Install to USB using base disk layout; USB |device| scheme only.
502 src_image_to_delta: Local path to an image to be used as the base to
503 generate delta payloads; SSH |device| scheme only.
504 rootfs_update: Update rootfs partition; SSH |device| scheme only.
505 stateful_update: Update stateful partition; SSH |device| scheme only.
506 clobber_stateful: Clobber stateful partition; SSH |device| scheme only.
507 reboot: Reboot device after update; SSH |device| scheme only.
508 wipe: Wipe temporary working directory; SSH |device| scheme only.
Daniel Erat30fd2072016-08-29 10:08:56 -0600509 ssh_private_key: Path to an SSH private key file; None to use test keys.
David Pursellf1d16a62015-03-25 13:31:04 -0700510 ping: Ping the device before attempting update; SSH |device| scheme only.
511 disable_rootfs_verification: Remove rootfs verification after update; SSH
512 |device| scheme only.
513 clear_cache: Clear the devserver static directory.
514 yes: Assume "yes" for any prompt.
515 force: Ignore sanity checks and prompts. Overrides |yes| if True.
516 debug: Print additional debugging messages.
517
518 Raises:
519 FlashError: An unrecoverable error occured.
520 ValueError: Invalid parameter combination.
521 """
522 if force:
523 yes = True
524
525 if clear_cache:
526 logging.info('Clearing the cache...')
Don Garrett97d7dc22015-01-20 14:07:56 -0800527 ds_wrapper.DevServerWrapper.WipeStaticDirectory(DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700528
529 try:
Don Garrett97d7dc22015-01-20 14:07:56 -0800530 osutils.SafeMakedirsNonRoot(DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700531 except OSError:
Don Garrett97d7dc22015-01-20 14:07:56 -0800532 logging.error('Failed to create %s', DEVSERVER_STATIC_DIR)
David Pursellf1d16a62015-03-25 13:31:04 -0700533
534 if install:
David Pursell2e773382015-04-03 14:30:47 -0700535 if not device or device.scheme != commandline.DEVICE_SCHEME_USB:
David Pursellf1d16a62015-03-25 13:31:04 -0700536 raise ValueError(
537 '--install can only be used when writing to a USB device')
538 if not cros_build_lib.IsInsideChroot():
539 raise ValueError('--install can only be used inside the chroot')
540
David Pursell2e773382015-04-03 14:30:47 -0700541 if not device or device.scheme == commandline.DEVICE_SCHEME_SSH:
542 if device:
543 hostname, port = device.hostname, device.port
544 else:
545 hostname, port = None, None
Ralph Nathan872ea4d2015-05-05 18:04:56 -0700546 logging.notice('Preparing to update the remote device %s', hostname)
David Pursellf1d16a62015-03-25 13:31:04 -0700547 updater = RemoteDeviceUpdater(
David Pursell2e773382015-04-03 14:30:47 -0700548 hostname,
549 port,
David Pursellf1d16a62015-03-25 13:31:04 -0700550 image,
551 board=board,
David Pursellf1d16a62015-03-25 13:31:04 -0700552 src_image_to_delta=src_image_to_delta,
553 rootfs_update=rootfs_update,
554 stateful_update=stateful_update,
555 clobber_stateful=clobber_stateful,
556 reboot=reboot,
557 wipe=wipe,
558 debug=debug,
559 yes=yes,
560 force=force,
Daniel Erat30fd2072016-08-29 10:08:56 -0600561 ssh_private_key=ssh_private_key,
David Pursellf1d16a62015-03-25 13:31:04 -0700562 ping=ping,
Gilad Arnold821547c2015-07-07 08:57:48 -0700563 disable_verification=disable_rootfs_verification)
David Pursellf1d16a62015-03-25 13:31:04 -0700564 updater.Run()
565 elif device.scheme == commandline.DEVICE_SCHEME_USB:
566 path = osutils.ExpandPath(device.path) if device.path else ''
567 logging.info('Preparing to image the removable device %s', path)
568 imager = USBImager(path,
569 board,
570 image,
David Pursellf1d16a62015-03-25 13:31:04 -0700571 debug=debug,
572 install=install,
573 yes=yes)
574 imager.Run()
575 elif device.scheme == commandline.DEVICE_SCHEME_FILE:
576 logging.info('Preparing to copy image to %s', device.path)
577 imager = FileImager(device.path,
578 board,
579 image,
David Pursellf1d16a62015-03-25 13:31:04 -0700580 debug=debug,
581 yes=yes)
582 imager.Run()