Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refine(v3.7.x): refine bootctrl.common CMDHelperFuncs #289

Merged
merged 29 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
00f40fc
common.subprocess_*: now become a single function
Bodong-Yang Apr 21, 2024
f94ade5
grub: integrate updated CMDHelperFuncs
Bodong-Yang Apr 21, 2024
2cf5f0c
rpi_boot: integrate new CMDHelperFuncs
Bodong-Yang Apr 21, 2024
13f7f9a
remove cboot
Bodong-Yang Apr 21, 2024
751dfb0
remove cboot
Bodong-Yang Apr 21, 2024
3597bf9
boot_control.common: refactor
Bodong-Yang Apr 21, 2024
5134c8c
remove boot_control.errors as not used
Bodong-Yang Apr 21, 2024
796c5b8
minor typing
Bodong-Yang Apr 21, 2024
2f8e098
boot_control.selecter: minor fix
Bodong-Yang Apr 21, 2024
b3aceae
rpi_boot: check new CMDHelperFunc integration again
Bodong-Yang Apr 21, 2024
d27f9bb
grub: use mp_control's prepare_standby_dev
Bodong-Yang Apr 21, 2024
e7266ed
test-grub: minor fix
Bodong-Yang Apr 21, 2024
374abe7
ota_client_stub: minor fix
Bodong-Yang Apr 21, 2024
36a6bb4
bootctrl.common: add -F option to mkfs.ext4
Bodong-Yang Apr 21, 2024
616e90f
bootctrl.common: subprocess_check_output now has default=""
Bodong-Yang Apr 21, 2024
d815fb5
bootctrl.common.cmdhelperfuncs: now all funcs except reboot by defaul…
Bodong-Yang Apr 21, 2024
d4bffdf
bootctrl.common.cmdhelperfuncs: refine mount related
Bodong-Yang Apr 21, 2024
b0bce15
minor update
Bodong-Yang Apr 21, 2024
58a54a3
common.subprocess_*: refine
Bodong-Yang Apr 21, 2024
fc3641c
common.subprocess_*: minor fix
Bodong-Yang Apr 21, 2024
fa25c11
fix ota_client_stub
Bodong-Yang Apr 21, 2024
736e4fc
otaclient_stub: fix related to mount external cache storage
Bodong-Yang Apr 21, 2024
afe4f82
otaclient_stub: fix related to mount external cache storage
Bodong-Yang Apr 22, 2024
9220161
common: minor docstring update
Bodong-Yang Apr 24, 2024
f73f08e
common: update copytree_identical, now it will create dst if dst does…
Bodong-Yang Apr 24, 2024
afcbf33
(WIP) update docstring of cmdhelperfuncs
Bodong-Yang Apr 24, 2024
192e36b
boot_control.common: update a lot of docstrings
Bodong-Yang Apr 25, 2024
d157e60
fix typo
Bodong-Yang Apr 25, 2024
7a483ad
test_common: refine subprocess_* methods test
Bodong-Yang Apr 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
579 changes: 0 additions & 579 deletions otaclient/app/boot_control/_cboot.py

This file was deleted.

740 changes: 302 additions & 438 deletions otaclient/app/boot_control/_common.py

Large diffs are not rendered by default.

98 changes: 0 additions & 98 deletions otaclient/app/boot_control/_errors.py

This file was deleted.

72 changes: 43 additions & 29 deletions otaclient/app/boot_control/_grub.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"""


from __future__ import annotations
import logging
import re
import shutil
Expand Down Expand Up @@ -327,9 +328,16 @@ def _get_sibling_dev(self, active_dev: str) -> str:
raise ValueError(_err_msg)

# list children device file from parent device
cmd = f"-Pp -o NAME,FSTYPE {parent}"
cmd = ["lsblk", "-Ppo", "NAME,FSTYPE", parent]
try:
cmd_result = subprocess_check_output(cmd, raise_exception=True)
except Exception as e:
_err_msg = f"failed to detect boot device family tree: {e!r}"
logger.error(_err_msg)
raise _GrubBootControllerError(_err_msg) from e

# exclude parent dev
output = CMDHelperFuncs._lsblk(cmd).splitlines()[1:]
output = cmd_result.splitlines()[1:]
# FSTYPE="ext4" and
# not (parent_device_file, root_device_file and boot_device_file)
for blk in output:
Expand All @@ -352,7 +360,14 @@ def _detect_active_slot(self) -> Tuple[str, str]:
A tuple contains the slot_name and the full dev path
of the active slot.
"""
dev_path = CMDHelperFuncs.get_current_rootfs_dev()
try:
dev_path = CMDHelperFuncs.get_current_rootfs_dev()
assert dev_path
except Exception as e:
_err_msg = f"failed to detect current rootfs dev: {e!r}"
logger.error(_err_msg)
raise _GrubBootControllerError(_err_msg) from e

_dev_path_ma = self.DEV_PATH_PA.match(dev_path)
assert _dev_path_ma, f"dev path is invalid for OTA: {dev_path}"

Expand Down Expand Up @@ -605,7 +620,17 @@ def _grub_update_on_booted_slot(self, *, abort_on_standby_missed=True):
active_slot_grub_file = self.active_ota_partition_folder / cfg.GRUB_CFG_FNAME

grub_cfg_content = GrubHelper.grub_mkconfig()
standby_uuid_str = CMDHelperFuncs.get_uuid_str_by_dev(self.standby_root_dev)
try:
standby_uuid = CMDHelperFuncs.get_attrs_by_dev(
"UUID", self.standby_root_dev
)
assert standby_uuid
except Exception as e:
_err_msg = f"failed to get UUID of {self.standby_root_dev}: {e!r}"
logger.error(_err_msg)
raise _GrubBootControllerError(_err_msg) from e

standby_uuid_str = f"UUID={standby_uuid}"
if grub_cfg_updated := GrubHelper.update_entry_rootfs(
grub_cfg_content,
kernel_ver=GrubHelper.SUFFIX_OTA_STANDBY,
Expand Down Expand Up @@ -667,27 +692,6 @@ def _ensure_standby_slot_boot_files_symlinks(self, standby_slot: str):

# API

def prepare_standby_dev(self, *, erase_standby: bool):
"""
Args:
erase_standby: indicate boot_controller whether to format the
standby slot's file system or not. This value is indicated and
passed to boot controller by the standby slot creator.
"""
try:
# try to unmount the standby root dev unconditionally
if CMDHelperFuncs.is_target_mounted(self.standby_root_dev):
CMDHelperFuncs.umount(self.standby_root_dev)

if erase_standby:
CMDHelperFuncs.mkfs_ext4(self.standby_root_dev)
# TODO: check the standby file system status
# if not erase the standby slot
except Exception as e:
_err_msg = f"failed to prepare standby dev: {e!r}"
logger.error(_err_msg)
raise _GrubBootControllerError(_err_msg) from e

def finalize_update_switch_boot(self):
"""Finalize switch boot and use boot files from current booted slot."""
# NOTE: since we have not yet switched boot, the active/standby relationship is
Expand Down Expand Up @@ -769,9 +773,19 @@ def _update_fstab(self, *, active_slot_fstab: Path, standby_slot_fstab: Path):

Override existed entries in standby fstab, merge new entries from active fstab.
"""
standby_uuid_str = CMDHelperFuncs.get_uuid_str_by_dev(
self._boot_control.standby_root_dev
)
try:
standby_uuid = CMDHelperFuncs.get_attrs_by_dev(
"UUID", self._boot_control.standby_root_dev
)
assert standby_uuid
except Exception as e:
_err_msg = (
f"failed to get UUID of {self._boot_control.standby_root_dev}: {e!r}"
)
logger.error(_err_msg)
raise _GrubBootControllerError(_err_msg) from e

standby_uuid_str = f"UUID={standby_uuid}"
fstab_entry_pa = re.compile(
r"^\s*(?P<file_system>[^# ]*)\s+"
r"(?P<mount_point>[^ ]*)\s+"
Expand Down Expand Up @@ -869,7 +883,7 @@ def pre_update(self, version: str, *, standby_as_ref: bool, erase_standby=False)
self._ota_status_control.pre_update_current()

### mount slots ###
self._boot_control.prepare_standby_dev(erase_standby=erase_standby)
self._mp_control.prepare_standby_dev(erase_standby=erase_standby)
self._mp_control.mount_standby()
self._mp_control.mount_active()

Expand Down
69 changes: 25 additions & 44 deletions otaclient/app/boot_control/_rpi_boot.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"""Boot control support for Raspberry pi 4 Model B."""


from __future__ import annotations
import logging
import os
import re
Expand All @@ -23,7 +24,7 @@

from .. import errors as ota_errors
from ..proto import wrapper
from ..common import replace_atomic, subprocess_call
from ..common import replace_atomic, subprocess_call, subprocess_check_output

from ._common import (
OTAStatusFilesControl,
Expand Down Expand Up @@ -77,9 +78,8 @@ class _RPIBootControl:

def __init__(self) -> None:
self.system_boot_path = Path(cfg.SYSTEM_BOOT_MOUNT_POINT)
if not (
self.system_boot_path.is_dir()
and CMDHelperFuncs.is_target_mounted(self.system_boot_path)
if not CMDHelperFuncs.is_target_mounted(
self.system_boot_path, raise_exception=False
):
_err_msg = "system-boot is not presented or not mounted!"
logger.error(_err_msg)
Expand All @@ -92,29 +92,34 @@ def _init_slots_info(self):
logger.debug("checking and initializing slots info...")
try:
# detect active slot
self._active_slot_dev = CMDHelperFuncs.get_dev_by_mount_point(
cfg.ACTIVE_ROOTFS_PATH
_active_slot_dev = CMDHelperFuncs.get_current_rootfs_dev()
assert _active_slot_dev
self._active_slot_dev = _active_slot_dev

_active_slot = CMDHelperFuncs.get_attrs_by_dev(
"LABEL", str(self._active_slot_dev)
)
if not (
_active_slot := CMDHelperFuncs.get_fslabel_by_dev(self._active_slot_dev)
):
raise ValueError(
f"failed to get slot_id(fslabel) for active slot dev({self._active_slot_dev})"
)
assert _active_slot
self._active_slot = _active_slot

# detect standby slot
# NOTE: using the similar logic like grub, detect the silibing dev
# of the active slot as standby slot
_parent = CMDHelperFuncs.get_parent_dev(self._active_slot_dev)
_parent = CMDHelperFuncs.get_parent_dev(str(self._active_slot_dev))
assert _parent

# list children device file from parent device
# exclude parent dev(always in the front)
# expected raw result from lsblk:
# NAME="/dev/sdx"
# NAME="/dev/sdx1" # system-boot
# NAME="/dev/sdx2" # slot_a
# NAME="/dev/sdx3" # slot_b
_raw_child_partitions = CMDHelperFuncs._lsblk(f"-Pp -o NAME {_parent}")
_check_dev_family_cmd = ["lsblk", "-Ppo", "NAME", _parent]
_raw_child_partitions = subprocess_check_output(
_check_dev_family_cmd, raise_exception=True
)

try:
# NOTE: exclude the first 2 lines(parent and system-boot)
_child_partitions = list(
Expand Down Expand Up @@ -315,38 +320,12 @@ def finalize_switching_boot(self) -> bool:
# reboot to the same slot to apply the new firmware

logger.info("reboot to apply new firmware...")
CMDHelperFuncs.reboot()

# NOTE(20230614): after calling reboot, otaclient SHOULD be terminated or exception raised
# on failed reboot command subprocess call. But if somehow it doesn't,
# it should be treated as failure and return False here.
return False

CMDHelperFuncs.reboot() # this function will make otaclient exit immediately
except Exception as e:
_err_msg = f"failed to finalize boot switching: {e!r}"
logger.error(_err_msg)
return False

def prepare_standby_dev(self, *, erase_standby: bool):
# try umount and dev
if CMDHelperFuncs.is_target_mounted(self.standby_slot_dev):
CMDHelperFuncs.umount(self.standby_slot_dev)
try:
if erase_standby:
CMDHelperFuncs.mkfs_ext4(
self.standby_slot_dev,
fslabel=self.standby_slot,
)
else:
# TODO: check the standby file system status
# if not erase the standby slot
# set the standby file system label with standby slot id
CMDHelperFuncs.set_dev_fslabel(self.active_slot_dev, self.standby_slot)
except Exception as e:
_err_msg = f"failed to prepare standby dev: {e!r}"
logger.error(_err_msg)
raise _RPIBootControllerError(_err_msg) from e

def prepare_tryboot_txt(self):
"""Copy the standby slot's config.txt as tryboot.txt."""
logger.debug("prepare tryboot.txt...")
Expand All @@ -364,8 +343,7 @@ def reboot_tryboot(self):
"""Reboot with tryboot flag."""
logger.info(f"tryboot reboot to standby slot({self.standby_slot})...")
try:
_cmd = "reboot '0 tryboot'"
subprocess_call(_cmd, raise_exception=True)
CMDHelperFuncs.reboot(args=["0 tryboot"])
except Exception as e:
_err_msg = "failed to reboot"
logger.exception(_err_msg)
Expand Down Expand Up @@ -496,7 +474,10 @@ def pre_update(self, version: str, *, standby_as_ref: bool, erase_standby: bool)
self._ota_status_control.pre_update_current()

### mount slots ###
self._rpiboot_control.prepare_standby_dev(erase_standby=erase_standby)
self._mp_control.prepare_standby_dev(
erase_standby=erase_standby,
fslabel=self._rpiboot_control.standby_slot,
)
self._mp_control.mount_standby()
self._mp_control.mount_active()

Expand Down
7 changes: 1 addition & 6 deletions otaclient/app/boot_control/selecter.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from typing_extensions import deprecated

from .configs import BootloaderType
from ._errors import BootControlError
from .protocol import BootControllerProtocol

from ..common import read_str_from_file
Expand Down Expand Up @@ -82,13 +81,9 @@ def get_boot_controller(
from ._grub import GrubController

return GrubController
if bootloader_type == BootloaderType.CBOOT:
from ._cboot import CBootController

return CBootController
if bootloader_type == BootloaderType.RPI_BOOT:
from ._rpi_boot import RPIBootController

return RPIBootController

raise BootControlError from NotImplementedError(f"unsupported: {bootloader_type=}")
raise NotImplementedError(f"unsupported: {bootloader_type=}")
Loading
Loading