devices/lvm2.lv: add support for lvm devices files

LVM2 introduced system.devices as an alternative way to filter
devices. Since we create devices in a stage the devices won't be
added to the /etc/lvm/devices/system.devices file since /etc/ is
inside the container. As a result the we can't see these devices
and will fail with "Could not find parent device".
Therefore we add support for managing our own per-service devices
file, iff a `system.devices` is present.
This commit is contained in:
Christian Kellner 2022-04-22 20:21:26 +02:00
parent 99abc1373d
commit 090f768544

View file

@ -58,13 +58,45 @@ class LVService(devices.DeviceService):
super().__init__(args)
self.fullname = None
self.target = None
self.devices_file = None
@staticmethod
def lv_set_active(fullname: str, status: bool):
def manage_devices_file(self, device: str):
# if LVM2 uses system.devices file, the LVM2 physical device we created
# inside the stage won't be seen since it wont be added to `system.devices`
# so we need to manage our own devices file
if not os.path.exists("/etc/lvm/devices/system.devices"):
return
self.devices_file = f"osbuild-{os.getpid()}.devices"
print(f"adding device to '{self.devices_file}'")
cmd = [
"lvmdevices",
"--adddev", device,
"--devicesfile", self.devices_file
]
res = subprocess.run(cmd,
check=False,
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
encoding="UTF-8")
if res.returncode != 0:
data = res.stderr.strip()
msg = f"Failed to add '{device}' to '{self.devices_file}': {data}"
raise RuntimeError(msg)
def lv_set_active(self, fullname: str, status: bool):
mode = "y" if status else "n"
cmd = [
"lvchange", "--activate", mode, fullname
]
if self.devices_file:
cmd += ["--devicesfile", self.devices_file]
res = subprocess.run(cmd,
check=False,
stdout=subprocess.DEVNULL,
@ -76,8 +108,7 @@ class LVService(devices.DeviceService):
msg = f"Failed to set LV device ({fullname}) status: {data}"
raise RuntimeError(msg)
@staticmethod
def volume_group_for_device(device: str) -> str:
def volume_group_for_device(self, device: str) -> str:
# Find the volume group that belongs to the device specified via `parent`
vg_name = None
count = 0
@ -86,6 +117,9 @@ class LVService(devices.DeviceService):
"pvdisplay", "-C", "--noheadings", "-o", "vg_name", device
]
if self.devices_file:
cmd += ["--devicesfile", self.devices_file]
while True:
res = subprocess.run(cmd,
check=False,
@ -110,8 +144,7 @@ class LVService(devices.DeviceService):
return vg_name
@staticmethod
def device_for_logical_volume(vg_name: str, volume: str) -> Tuple[int, int]:
def device_for_logical_volume(self, vg_name: str, volume: str) -> Tuple[int, int]:
# Now that we have the volume group, find the specified logical volume and its device path
cmd = [
@ -122,6 +155,9 @@ class LVService(devices.DeviceService):
vg_name
]
if self.devices_file:
cmd += ["--devicesfile", self.devices_file]
res = subprocess.run(cmd,
check=False,
stdout=subprocess.PIPE,
@ -144,6 +180,9 @@ class LVService(devices.DeviceService):
assert not parent.startswith("/")
parent_path = os.path.join("/dev", parent)
# Add the device to a lvm devices file on supported systems
self.manage_devices_file(parent_path)
# Find the volume group that belongs to the device specified
# via `parent`
vg = self.volume_group_for_device(parent_path)
@ -181,6 +220,10 @@ class LVService(devices.DeviceService):
self.lv_set_active(self.fullname, False)
self.fullname = None
if self.devices_file:
os.unlink(os.path.join("/etc/lvm/devices", self.devices_file))
self.devices_file = None
def main():
service = LVService.from_args(sys.argv[1:])