]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
ceph-volume lvm.zap split zapping in methods per device type
authorAlfredo Deza <adeza@redhat.com>
Tue, 27 Nov 2018 13:23:19 +0000 (08:23 -0500)
committerAlfredo Deza <adeza@redhat.com>
Fri, 30 Nov 2018 19:06:31 +0000 (14:06 -0500)
Signed-off-by: Alfredo Deza <adeza@redhat.com>
(cherry picked from commit 0d2a9a40d2c301039d571101d1e92a82058a51e0)

src/ceph-volume/ceph_volume/devices/lvm/zap.py

index 8e0e3a3c5e974af2b24fcf636268f8a18d620783..a77ac5f907ae0c19efef0ae3b0e3d57e1d2a7357 100644 (file)
@@ -1,11 +1,13 @@
 import argparse
+import os
 import logging
 
 from textwrap import dedent
 
 from ceph_volume import decorators, terminal, process
 from ceph_volume.api import lvm as api
-from ceph_volume.util import system, encryption, disk
+from ceph_volume.util import system, encryption, disk, arg_validators
+from ceph_volume.util.device import Device
 
 logger = logging.getLogger(__name__)
 mlogger = terminal.MultiLogger(__name__)
@@ -59,70 +61,108 @@ class Zap(object):
         if dmcrypt and dmcrypt_uuid:
             self.dmcrypt_close(dmcrypt_uuid)
 
+    def zap_lv(self, device):
+        """
+        Device examples: vg-name/lv-name, /dev/vg-name/lv-name
+        Requirements: Must be a logical volume (LV)
+        """
+        lv = api.get_lv(lv_name=device.lv_name, vg_name=device.vg_name)
+        self.unmount_lv(lv)
+
+        wipefs(device.abspath)
+        zap_data(device.abspath)
+
+        if self.args.destroy:
+            lvs = api.Volumes()
+            lvs.filter(vg_name=device.vg_name)
+            if len(lvs) <= 1:
+                mlogger.info('Only 1 LV left in VG, will proceed to destroy volume group %s', device.vg_name)
+                api.remove_vg(device.vg_name)
+            else:
+                mlogger.info('More than 1 LV left in VG, will proceed to destroy LV only')
+                mlogger.info('Removing LV because --destroy was given: %s', device.abspath)
+                api.remove_lv(device.abspath)
+        elif lv:
+            # just remove all lvm metadata, leaving the LV around
+            lv.clear_tags()
+
+    def zap_partition(self, device):
+        """
+        Device example: /dev/sda1
+        Requirements: Must be a partition
+        """
+        if device.is_encrypted:
+            # find the holder
+            holders = [
+                '/dev/%s' % holder for holder in device.sys_api.get('holders', [])
+            ]
+            for mapper_uuid in os.listdir('/dev/mapper'):
+                mapper_path = os.path.join('/dev/mapper', mapper_uuid)
+                if os.path.realpath(mapper_path) in holders:
+                    self.dmcrypt_close(mapper_uuid)
+
+        if system.device_is_mounted(device.abspath):
+            mlogger.info("Unmounting %s", device.abspath)
+            system.unmount(device.abspath)
+
+        wipefs(device.abspath)
+        zap_data(device.abspath)
+
+        if self.args.destroy:
+            mlogger.info("Destroying partition since --destroy was used: %s" % device.abspath)
+            disk.remove_partition(device)
+
+    def zap_lvm_member(self, device):
+        """
+        An LVM member may have more than one LV and or VG, for example if it is
+        a raw device with multiple partitions each belonging to a different LV
+
+        Device example: /dev/sda
+        Requirements: An LV or VG present in the device, making it an LVM member
+        """
+        for lv in device.lvs:
+            self.zap_lv(Device(lv.lv_path))
+
+
+    def zap_raw_device(self, device):
+        """
+        Any whole (raw) device passed in as input will be processed here,
+        checking for LVM membership and partitions (if any).
+
+        Device example: /dev/sda
+        Requirements: None
+        """
+        if not self.args.destroy:
+            # the use of dd on a raw device causes the partition table to be
+            # destroyed
+            mlogger.warning(
+                '--destroy was not specified, but zapping a whole device will remove the partition table'
+            )
+
+        # look for partitions and zap those
+        for part_name in device.sys_api.get('partitions', {}).keys():
+            self.zap_partition(Device('/dev/%s' % part_name))
+
+        wipefs(device.abspath)
+        zap_data(device.abspath)
+
     @decorators.needs_root
-    def zap(self, args):
-        for device in args.devices:
-            if disk.is_mapper_device(device):
+    def zap(self):
+        for device in self.args.devices:
+            mlogger.info("Zapping: %s", device.abspath)
+            if device.is_mapper:
                 terminal.error("Refusing to zap the mapper device: {}".format(device))
                 raise SystemExit(1)
-            lv = api.get_lv_from_argument(device)
-            if lv:
-                # we are zapping a logical volume
-                path = lv.lv_path
-                self.unmount_lv(lv)
-            else:
-                # we are zapping a partition
-                #TODO: ensure device is a partition
-                path = device
-                # check to if it is encrypted to close
-                partuuid = disk.get_partuuid(device)
-                if encryption.status("/dev/mapper/{}".format(partuuid)):
-                    dmcrypt_uuid = partuuid
-                    self.dmcrypt_close(dmcrypt_uuid)
-
-            mlogger.info("Zapping: %s", path)
-
-            # check if there was a pv created with the
-            # name of device
-            pvs = api.PVolumes()
-            pvs.filter(pv_name=device)
-            vgs = set([pv.vg_name for pv in pvs])
-            for pv in pvs:
-                vg_name = pv.vg_name
-                lv = None
-                if pv.lv_uuid:
-                    lv = api.get_lv(vg_name=vg_name, lv_uuid=pv.lv_uuid)
-
-                if lv:
-                    self.unmount_lv(lv)
-
-            if args.destroy:
-                for vg_name in vgs:
-                    mlogger.info("Destroying volume group %s because --destroy was given", vg_name)
-                    api.remove_vg(vg_name)
-                if not lv:
-                    mlogger.info("Destroying physical volume %s because --destroy was given", device)
-                    api.remove_pv(device)
-
-            wipefs(path)
-            zap_data(path)
-
-            if lv and not pvs:
-                if args.destroy:
-                    lvs = api.Volumes()
-                    lvs.filter(vg_name=lv.vg_name)
-                    if len(lvs) <= 1:
-                        mlogger.info('Only 1 LV left in VG, will proceed to destroy volume group %s', lv.vg_name)
-                        api.remove_vg(lv.vg_name)
-                    else:
-                        mlogger.info('More than 1 LV left in VG, will proceed to destroy LV only')
-                        mlogger.info('Removing LV because --destroy was given: %s', lv)
-                        api.remove_lv(lv)
-                else:
-                    # just remove all lvm metadata, leaving the LV around
-                    lv.clear_tags()
-
-        terminal.success("Zapping successful for: %s" % ", ".join(args.devices))
+            if device.is_lvm_member:
+                self.zap_lvm_member(device)
+            if device.is_lv:
+                self.zap_lv(device)
+            if device.is_partition:
+                self.zap_partition(device)
+            if device.is_device:
+                self.zap_raw_device(device)
+
+        terminal.success("Zapping successful for: %s" % ", ".join([str(d) for d in self.args.devices]))
 
     def dmcrypt_close(self, dmcrypt_uuid):
         dmcrypt_path = "/dev/mapper/{}".format(dmcrypt_uuid)
@@ -179,6 +219,7 @@ class Zap(object):
             'devices',
             metavar='DEVICES',
             nargs='*',
+            type=arg_validators.ValidDevice(gpt_ok=True),
             default=[],
             help='Path to one or many lv (as vg/lv), partition (as /dev/sda1) or device (as /dev/sda)'
         )
@@ -191,5 +232,5 @@ class Zap(object):
         if len(self.argv) == 0:
             print(sub_command_help)
             return
-        args = parser.parse_args(self.argv)
-        self.zap(args)
+        self.args = parser.parse_args(self.argv)
+        self.zap()