]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/ceph-volume/ceph_volume/devices/lvm/zap.py
import 15.2.5
[ceph.git] / ceph / src / ceph-volume / ceph_volume / devices / lvm / zap.py
index 328a036152e2c1ebb5242938524247e84d36c160..63624e55f5bd228b912bc5eaf09fa39944ea115f 100644 (file)
@@ -1,12 +1,13 @@
 import argparse
 import os
 import logging
+import time
 
 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, arg_validators
+from ceph_volume.util import system, encryption, disk, arg_validators, str_to_int, merge_dict
 from ceph_volume.util.device import Device
 from ceph_volume.systemd import systemctl
 
@@ -17,12 +18,38 @@ mlogger = terminal.MultiLogger(__name__)
 def wipefs(path):
     """
     Removes the filesystem from an lv or partition.
+
+    Environment variables supported::
+
+    * ``CEPH_VOLUME_WIPEFS_TRIES``: Defaults to 8
+    * ``CEPH_VOLUME_WIPEFS_INTERVAL``: Defaults to 5
+
     """
-    process.run([
-        'wipefs',
-        '--all',
-        path
-    ])
+    tries = str_to_int(
+        os.environ.get('CEPH_VOLUME_WIPEFS_TRIES', 8)
+    )
+    interval = str_to_int(
+        os.environ.get('CEPH_VOLUME_WIPEFS_INTERVAL', 5)
+    )
+
+    for trying in range(tries):
+        stdout, stderr, exit_code = process.call([
+            'wipefs',
+            '--all',
+            path
+        ])
+        if exit_code != 0:
+            # this could narrow the retry by poking in the stderr of the output
+            # to verify that 'probing initialization failed' appears, but
+            # better to be broad in this retry to prevent missing on
+            # a different message that needs to be retried as well
+            terminal.warning(
+                'failed to wipefs device, will try again to workaround probable race condition'
+            )
+            time.sleep(interval)
+        else:
+            return
+    raise RuntimeError("could not complete wipefs on device: %s" % path)
 
 
 def zap_data(path):
@@ -39,6 +66,7 @@ def zap_data(path):
         'of={path}'.format(path=path),
         'bs=1M',
         'count=10',
+        'conv=fsync'
     ])
 
 
@@ -53,17 +81,17 @@ def find_associated_devices(osd_id=None, osd_fsid=None):
         lv_tags['ceph.osd_id'] = osd_id
     if osd_fsid:
         lv_tags['ceph.osd_fsid'] = osd_fsid
-    lvs = api.Volumes()
-    lvs.filter(lv_tags=lv_tags)
-    if not lvs:
-        raise RuntimeError('Unable to find any LV for zapping OSD: %s' % osd_id or osd_fsid)
 
-    devices_to_zap = ensure_associated_lvs(lvs)
+    lvs = api.get_lvs(tags=lv_tags)
+    if not lvs:
+        raise RuntimeError('Unable to find any LV for zapping OSD: '
+                           '%s' % osd_id or osd_fsid)
 
+    devices_to_zap = ensure_associated_lvs(lvs, lv_tags)
     return [Device(path) for path in set(devices_to_zap) if path]
 
 
-def ensure_associated_lvs(lvs):
+def ensure_associated_lvs(lvs, lv_tags={}):
     """
     Go through each LV and ensure if backing devices (journal, wal, block)
     are LVs or partitions, so that they can be accurately reported.
@@ -72,14 +100,12 @@ def ensure_associated_lvs(lvs):
     # receive a filtering for osd.1, and have multiple failed deployments
     # leaving many journals with osd.1 - usually, only a single LV will be
     # returned
-    journal_lvs = lvs._filter(lv_tags={'ceph.type': 'journal'})
-    db_lvs = lvs._filter(lv_tags={'ceph.type': 'db'})
-    wal_lvs = lvs._filter(lv_tags={'ceph.type': 'wal'})
-    backing_devices = [
-        (journal_lvs, 'journal'),
-        (db_lvs, 'block'),
-        (wal_lvs, 'wal')
-    ]
+
+    journal_lvs = api.get_lvs(tags=merge_dict(lv_tags, {'ceph.type': 'journal'}))
+    db_lvs = api.get_lvs(tags=merge_dict(lv_tags, {'ceph.type': 'db'}))
+    wal_lvs = api.get_lvs(tags=merge_dict(lv_tags, {'ceph.type': 'wal'}))
+    backing_devices = [(journal_lvs, 'journal'), (db_lvs, 'db'),
+                       (wal_lvs, 'wal')]
 
     verified_devices = []
 
@@ -140,21 +166,27 @@ class Zap(object):
         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)
+        lv = api.get_first_lv(filters={'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)
+            lvs = api.get_lvs(filters={'vg_name': device.vg_name})
+            if lvs == []:
+                mlogger.info('No LVs left, exiting', device.vg_name)
+                return
+            elif 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)
+                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
@@ -195,7 +227,15 @@ class Zap(object):
         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))
+            if lv.lv_name:
+                mlogger.info('Zapping lvm member {}. lv_path is {}'.format(device.abspath, lv.lv_path))
+                self.zap_lv(Device(lv.lv_path))
+            else:
+                vg = api.get_first_vg(filters={'vg_name': lv.vg_name})
+                if vg:
+                    mlogger.info('Found empty VG {}, removing'.format(vg.vg_name))
+                    api.remove_vg(vg.vg_name)
+
 
 
     def zap_raw_device(self, device):
@@ -243,8 +283,9 @@ class Zap(object):
                 "Zapping successful for: %s" % ", ".join([str(d) for d in self.args.devices])
             )
         else:
+            identifier = self.args.osd_id or self.args.osd_fsid
             terminal.success(
-                "Zapping successful for OSD: %s" % self.args.osd_id or self.args.osd_fsid
+                "Zapping successful for OSD: %s" % identifier
             )
 
     @decorators.needs_root