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
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):
'of={path}'.format(path=path),
'bs=1M',
'count=10',
+ 'conv=fsync'
])
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.
# 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 = []
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
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):
"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