blob: bc4cdbcb230f60b41fb62710f86216111cc48b55 [file] [log] [blame]
# Copyright 2013 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Install/copy the image to the device."""
import datetime
import logging
from chromite.cli import command
from chromite.cli import flash
from chromite.cli.cros import cros_chrome_sdk
from chromite.lib import commandline
from chromite.lib import cros_build_lib
from chromite.lib import dev_server_wrapper
from chromite.lib import path_util
from chromite.lib import sudo
from chromite.lib.telemetry import trace
from chromite.utils import timer
tracer = trace.get_tracer(__name__)
@command.command_decorator("flash")
class FlashCommand(command.CliCommand):
"""Update the device with an image.
This command updates the device with the image
(ssh://<hostname>:{port}, copies an image to a removable device
(usb://<device_path>), or copies a xbuddy path to a local
file path with (file://file_path).
For device update, it assumes that device is able to accept ssh
connections.
For rootfs partition update, this command may launch a devserver to
generate payloads. As a side effect, it may create symlinks in
static_dir/others used by the devserver.
"""
EPILOG = """
To update/image the device with the latest locally built image:
cros flash device latest
cros flash device
To update/image the device with an xbuddy path:
cros flash device xbuddy://{local, remote}/<board>/<version>[/<image_type>]
Common xbuddy version aliases are 'latest' (alias for 'latest-stable')
latest-{dev, beta, stable, canary}, and latest-official.
The optional image_type can be one of 'test' (the default), 'base',
'recovery', or 'signed'. The 'dev' image_type is supported for local images.
To update/image the device with a local image path:
cros flash device /path/to/image.bin
Examples:
cros flash 192.168.1.7 xbuddy://remote/amd64-generic/latest-canary
cros flash 192.168.1.7 xbuddy://remote/amd64-generic-full/R84-13039.0.0
cros flash usb:// xbuddy://remote/kevin
cros flash usb:///dev/sde kevin/latest
cros flash file:///~/images kevin
# For a recovery image
cros flash usb:// xbuddy://remote/link/latest-stable/recovery
# For a signed image
cros flash usb:// xbuddy://remote/eve/latest-stable/signed
For more information and known problems/fixes, please see:
https://www.chromium.org/chromium-os/developer-library/reference/tools/cros-flash/
Note: When flashing a signed image, ssh connection to the device will be lost
and flash must be invoked with --no-stateful-update.
"""
# Override base class property to use cache related commandline options.
use_caching_options = True
use_telemetry = True
# The default reboot timeout.
DEFAULT_REBOOT_TIMEOUT = datetime.timedelta(seconds=300)
@classmethod
def AddParser(cls, parser) -> None:
"""Add parser arguments."""
super(FlashCommand, cls).AddParser(parser)
cls.AddDeviceArgument(
parser,
positional=True,
schemes=[
commandline.DeviceScheme.FILE,
commandline.DeviceScheme.SSH,
commandline.DeviceScheme.USB,
],
)
parser.add_argument(
"image",
nargs="?",
help="A local path or an xbuddy path: "
"xbuddy://{local|remote}/board/version/{image_type} image_type "
"can be: 'test', 'dev', 'base', 'recovery', or 'signed'. Note any "
"strings that do not map to a real file path will be converted to "
"an xbuddy path i.e., latest, will map to xbuddy://latest.",
)
parser.add_argument(
"--clear-cache",
default=False,
action="store_true",
help="Clear the devserver static directory. This deletes all the "
"downloaded images and payloads, and also payloads generated by "
"the devserver. Default is not to clear.",
)
update = parser.add_argument_group("Advanced device update options")
update.add_argument(
"--board",
help="The board to use. By default it is "
"automatically detected. You can override the detected board with "
"this option.",
)
update.add_argument(
"--yes",
default=False,
action="store_true",
help="Answer yes to any prompt. Use with caution.",
)
update.add_argument(
"--force",
action="store_true",
help="Ignore confidence checks, just do it. Implies --yes.",
)
update.add_argument(
"--no-reboot",
action="store_false",
dest="reboot",
default=True,
help="Do not reboot after update. Default is always reboot.",
)
update.add_argument(
"--reboot-timeout",
default=cls.DEFAULT_REBOOT_TIMEOUT,
type="timedelta",
help="Timeout (sec) to wait for reboot. (Default: %(default)s)",
)
update.add_argument(
"--no-wipe",
action="store_false",
dest="wipe",
default=True,
help="Do not wipe the temporary working directory. Default "
"is always wipe.",
deprecated="No temp directory anymore.",
)
update.add_argument(
"--no-stateful-update",
action="store_true",
help="Do not update the stateful partition on the device. "
"Default is always update.",
)
update.add_argument(
"--no-rootfs-update",
action="store_true",
help="Do not update the rootfs partition on the device. "
"Default is always update.",
)
# TODO(b/190631159, b/196056723): Remove default + update help
# description.
update.add_argument(
"--minios-update",
action="store_true",
default=False,
help="Do update the minios partition on the device. "
"Default is to always not update.",
)
update.add_argument(
"--src-image-to-delta",
type="str_path",
help="Local path to an image to be used as the base to generate "
"delta payloads.",
deprecated="Not used anymore.",
)
update.add_argument(
"--clobber-stateful",
action="store_true",
default=False,
help="Clobber stateful partition when performing update. Recommend "
"to set clear-tpm-owner to true when this option is true.",
)
update.add_argument(
"--clear-tpm-owner",
action="store_true",
default=False,
help="Clear the TPM owner on reboot.",
)
update.add_argument(
"--restore-stateful",
action="store_false",
help="Restore the stateful partition. Same as --no-rootfs-update.",
deprecated="Not used anymore.",
)
update.add_argument(
"--private-key",
type="str_path",
default=None,
help="SSH identify file (private key).",
)
update.add_argument(
"--no-ping",
dest="ping",
action="store_false",
default=True,
help="Do not ping the device before attempting to connect to it.",
)
update.add_argument(
"--disable-rootfs-verification",
"-r",
default=False,
action="store_true",
help="Disable rootfs verification after update is completed.",
)
update.add_argument(
"--send-payload-in-parallel",
default=False,
action="store_true",
help=(
"To speed up transfer payload files for long haul, chop "
"payload in chunks and transfer them in parallel."
),
deprecated="Not used anymore.",
)
update.add_argument(
"--no-copy-payloads-to-device",
dest="copy_payloads_to_device",
action="store_false",
default=True,
help=(
"Do not copy the update payloads to the device. For now this "
"only works for the stateful payload."
),
deprecated="This is default behavior now.",
)
update.add_argument(
"--exp-new-flash",
action="store_true",
default=True,
help=("Use the faster version of cros flash (experimental)."),
deprecated="The new flash mechanism is ON by default.",
)
delta_group = update.add_mutually_exclusive_group()
delta_group.add_argument(
"--delta",
action="store_true",
default=False,
help="Enable delta compression for image bytes. Default: disabled",
)
def _GetDefaultVersion(self):
"""Get default full SDK version.
For non-chrome, use 'latest'. For chrome, look up the
full version in the misc cache.
"""
if path_util.DetermineCheckout().type != path_util.CheckoutType.GCLIENT:
return "latest"
board = self.options.board or flash.GetDefaultBoard()
if not board:
raise flash.FlashError("Must specify board.")
full_version = (
cros_chrome_sdk.SDKFetcher.GetCachedFullVersion(
self.options.cache_dir or path_util.GetCacheDir(), board
)
or "latest"
)
logging.notice("CrOS SDK version: %s", full_version)
return full_version
def _Flash(self) -> None:
"""Perform the cros flash command."""
try:
with timer.Timer() as t:
flash.Flash(
self.options.device,
self.options.image,
board=self.options.board,
version=self._GetDefaultVersion(),
no_rootfs_update=self.options.no_rootfs_update,
no_stateful_update=self.options.no_stateful_update,
no_minios_update=not self.options.minios_update,
clobber_stateful=self.options.clobber_stateful,
clear_tpm_owner=self.options.clear_tpm_owner,
reboot=self.options.reboot,
ssh_private_key=self.options.private_key,
ping=self.options.ping,
disable_rootfs_verification=(
self.options.disable_rootfs_verification
),
clear_cache=self.options.clear_cache,
delta=self.options.delta,
yes=self.options.yes,
force=self.options.force,
debug=self.options.debug,
reboot_timeout=self.options.reboot_timeout,
)
logging.notice("cros flash completed successfully in %s", t)
except dev_server_wrapper.ImagePathError:
logging.error(
"To get the latest remote image, please run:\n"
"cros flash --board=%s %s remote/latest",
self.options.board,
self.options.device.raw,
)
raise
def Run(self) -> None:
"""Run the cros flash command inside sudo wrappers."""
# In most (all?) cases, "cros flash" requires sudo. Ensure that sudo
# is cached here ahead of everything, because
# operation.ProgressBarOperation, which is run in RunParallelSteps,
# can interfere with prompting for the sudo password.
# TODO(b/302557861): stop using `losetup`.
previous_strict_sudo = cros_build_lib.STRICT_SUDO
try:
cros_build_lib.STRICT_SUDO = True
with sudo.SudoKeepAlive():
with tracer.start_as_current_span(
"cli.cros.cros_flash.run"
) as span:
span.set_attributes(
{
"board": self.options.board or "",
"device": self.options.device.raw or "",
"image": self.options.image or "",
}
)
self._Flash()
finally:
cros_build_lib.STRICT_SUDO = previous_strict_sudo