From 33fb9611275b297dfe4f83980618ff4958e32751 Mon Sep 17 00:00:00 2001
From: Steve Baker <sbaker@redhat.com>
Date: Mon, 2 Sep 2024 13:50:09 +1200
Subject: [PATCH] Add block device rollback for lvm volumes and groups

If mkfs fails then the rollback won't delete logical volumes or the
volume group. This prevents unmounting the loop back, and subsequent
image build runs will fail because the volume group still exists.

This change adds lvremove and vgremove rollback actions.

Change-Id: Ib93dfc43f5b3ef90fcec38538e828f135e514f8b
---
 diskimage_builder/block_device/level1/lvm.py | 25 ++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/diskimage_builder/block_device/level1/lvm.py b/diskimage_builder/block_device/level1/lvm.py
index 1b6bfa297..16842c989 100644
--- a/diskimage_builder/block_device/level1/lvm.py
+++ b/diskimage_builder/block_device/level1/lvm.py
@@ -20,7 +20,6 @@ from diskimage_builder.block_device.plugin import NodeBase
 from diskimage_builder.block_device.plugin import PluginBase
 from diskimage_builder.block_device.utils import exec_sudo
 from diskimage_builder.block_device.utils import parse_abs_size_spec
-from diskimage_builder.block_device.utils import remove_device
 
 PHYSICAL_EXTENT_BYTES = parse_abs_size_spec('4MiB')
 LVS_TYPES = ['thin', 'thin-pool']
@@ -70,6 +69,27 @@ logger = logging.getLogger(__name__)
 # call that is driven by the LVSNode object.
 
 
+def lvremove(device_name):
+    logger.debug('Removing logical volume %s', device_name)
+    try:
+        exec_sudo(["lvchange", "--activate", "n", device_name])
+        exec_sudo(["lvremove", "--yes", "/dev/%s" % device_name])
+    except BlockDeviceSetupException as e:
+        # Do not raise an error - maybe other cleanup methods
+        # can at least do some more work.
+        logger.warning("Removing logical volume failed (%s)", e.returncode)
+
+
+def vgremove(vg_name):
+    logger.debug('Removing volume group %s', vg_name)
+    try:
+        exec_sudo(["vgremove", "--yes", "--force", vg_name])
+    except BlockDeviceSetupException as e:
+        # Do not raise an error - maybe other cleanup methods
+        # can at least do some more work.
+        logger.warning("Removing volume group failed (%s)", e.returncode)
+
+
 class PvsNode(NodeBase):
     def __init__(self, name, state, base, options):
         """Physical volume
@@ -150,6 +170,7 @@ class VgsNode(NodeBase):
         logger.debug("Creating vg command [%s]", cmd)
         exec_sudo(cmd)
 
+        self.add_rollback(vgremove, self.name)
         # save state
         if 'vgs' not in self.state:
             self.state['vgs'] = {}
@@ -232,7 +253,7 @@ class LvsNode(NodeBase):
             'opts': self.options,
             'device': '/dev/mapper/%s' % device_name
         }
-        self.add_rollback(remove_device, device_name)
+        self.add_rollback(lvremove, "%s/%s" % (self.base, self.name))
 
     def _umount(self):
         exec_sudo(['lvchange', '-an',