summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2026-09-01 09:39:03 +0200
committerChristian Breunig <christian@breunig.cc>2026-09-01 17:27:07 +0200
commite94f0e47d742f3c8f228bf21907954008ade7ba0 (patch)
tree39f38d1f51e0d5325a39ea9ea81cd65ba5554a7e /python
parent6d2523edeed89e0d968df13b7ec656de5bbd2952 (diff)
downloadvyos-1x-e94f0e47d742f3c8f228bf21907954008ade7ba0.tar.gz
vyos-1x-e94f0e47d742f3c8f228bf21907954008ade7ba0.zip
image: T9269: skip GRUB updates on container and diskless systems
Kernel cmdline and serial console updates rewrite the GRUB configuration on the persistence partition. A container has no GRUB at all, and on a system where the persistence partition is not mounted disk.find_persistence() returns an empty string, so the code reads '//boot/grub/grub.cfg' and dies with FileNotFoundError. Extend is_running_as_container() to also honor /run/.containerenv (Podman) and the container= variable systemd sets for PID 1, and add if_not_container() plus if_persistence() decorators guarding all grub_util entry points.
Diffstat (limited to 'python')
-rw-r--r--python/vyos/system/grub_util.py8
-rw-r--r--python/vyos/system/image.py45
2 files changed, 52 insertions, 1 deletions
diff --git a/python/vyos/system/grub_util.py b/python/vyos/system/grub_util.py
index edaea8ad3..98d3c1ddc 100644
--- a/python/vyos/system/grub_util.py
+++ b/python/vyos/system/grub_util.py
@@ -18,6 +18,8 @@ from vyos.system import grub
from vyos.system import image
from vyos.system import compat
+@image.if_not_container
+@image.if_persistence
@compat.grub_cfg_update
def set_serial_console(console_type: str, console_num: str,
console_speed: str, root_dir: str = '') -> None:
@@ -34,6 +36,8 @@ def set_serial_console(console_type: str, console_num: str,
grub.set_serial_console(console_type, console_num, console_speed, root_dir)
@image.if_not_live_boot
+@image.if_not_container
+@image.if_persistence
def update_serial_console(console_type: str, console_num: str,
console_speed: str, root_dir: str = '') -> None:
"""Update console_speed if different from current value"""
@@ -53,6 +57,8 @@ def update_serial_console(console_type: str, console_num: str,
console_speed != console_speed_current:
set_serial_console(console_type, console_num, console_speed, root_dir)
+@image.if_not_container
+@image.if_persistence
@compat.grub_cfg_update
def set_kernel_cmdline_options(cmdline_options: str, version: str = '',
root_dir: str = '') -> None:
@@ -66,6 +72,8 @@ def set_kernel_cmdline_options(cmdline_options: str, version: str = '',
grub.set_kernel_cmdline_options(cmdline_options, version, root_dir)
@image.if_not_live_boot
+@image.if_not_container
+@image.if_persistence
def update_kernel_cmdline_options(cmdline_options: str,
root_dir: str = '',
version = image.get_running_image()) -> None:
diff --git a/python/vyos/system/image.py b/python/vyos/system/image.py
index 02f2303b2..0b2eccf6d 100644
--- a/python/vyos/system/image.py
+++ b/python/vyos/system/image.py
@@ -283,6 +283,49 @@ def if_not_live_boot(func):
return wrapper
def is_running_as_container() -> bool:
- if Path('/.dockerenv').exists():
+ """Detect if the system is running inside a container
+
+ Returns:
+ bool: True if running as container (Docker, Podman, LXC, ...)
+ """
+ # Docker and Podman drop a marker file into the container root
+ if Path('/.dockerenv').exists() or Path('/run/.containerenv').exists():
return True
+ # systemd sets container= in the environment of PID 1 for all container
+ # types it knows about - this is only readable by root
+ try:
+ environ = Path('/proc/1/environ').read_bytes().decode(errors='ignore')
+ if 'container=' in environ:
+ return True
+ except (OSError, PermissionError):
+ pass
return False
+
+def has_persistence() -> bool:
+ """Detect if a persistence storage partition is mounted
+
+ Returns:
+ bool: True if the persistence partition is available
+ """
+ return bool(disk.find_persistence())
+
+def if_persistence(func):
+ """Decorator to call function only if persistence storage is available.
+ Without it there is no writeable GRUB configuration to operate on"""
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ if has_persistence():
+ ret = func(*args, **kwargs)
+ return ret
+ return None
+ return wrapper
+
+def if_not_container(func):
+ """Decorator to call function only if not running inside a container"""
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ if not is_running_as_container():
+ ret = func(*args, **kwargs)
+ return ret
+ return None
+ return wrapper