6 from textwrap
import dedent
8 from ceph_volume
import decorators
, terminal
, process
9 from ceph_volume
.api
import lvm
as api
10 from ceph_volume
.util
import system
, encryption
, disk
, arg_validators
, str_to_int
, merge_dict
11 from ceph_volume
.util
.device
import Device
12 from ceph_volume
.systemd
import systemctl
14 logger
= logging
.getLogger(__name__
)
15 mlogger
= terminal
.MultiLogger(__name__
)
20 Removes the filesystem from an lv or partition.
22 Environment variables supported::
24 * ``CEPH_VOLUME_WIPEFS_TRIES``: Defaults to 8
25 * ``CEPH_VOLUME_WIPEFS_INTERVAL``: Defaults to 5
29 os
.environ
.get('CEPH_VOLUME_WIPEFS_TRIES', 8)
31 interval
= str_to_int(
32 os
.environ
.get('CEPH_VOLUME_WIPEFS_INTERVAL', 5)
35 for trying
in range(tries
):
36 stdout
, stderr
, exit_code
= process
.call([
42 # this could narrow the retry by poking in the stderr of the output
43 # to verify that 'probing initialization failed' appears, but
44 # better to be broad in this retry to prevent missing on
45 # a different message that needs to be retried as well
47 'failed to wipefs device, will try again to workaround probable race condition'
52 raise RuntimeError("could not complete wipefs on device: %s" % path
)
57 Clears all data from the given path. Path should be
58 an absolute path to an lv or partition.
60 10M of data is written to the path to make sure that
61 there is no trace left of any previous Filesystem.
66 'of={path}'.format(path
=path
),
73 def find_associated_devices(osd_id
=None, osd_fsid
=None):
75 From an ``osd_id`` and/or an ``osd_fsid``, filter out all the LVs in the
76 system that match those tag values, further detect if any partitions are
77 part of the OSD, and then return the set of LVs and partitions (if any).
81 lv_tags
['ceph.osd_id'] = osd_id
83 lv_tags
['ceph.osd_fsid'] = osd_fsid
85 lvs
= api
.get_lvs(tags
=lv_tags
)
87 raise RuntimeError('Unable to find any LV for zapping OSD: '
88 '%s' % osd_id
or osd_fsid
)
90 devices_to_zap
= ensure_associated_lvs(lvs
, lv_tags
)
91 return [Device(path
) for path
in set(devices_to_zap
) if path
]
94 def ensure_associated_lvs(lvs
, lv_tags
={}):
96 Go through each LV and ensure if backing devices (journal, wal, block)
97 are LVs or partitions, so that they can be accurately reported.
99 # look for many LVs for each backing type, because it is possible to
100 # receive a filtering for osd.1, and have multiple failed deployments
101 # leaving many journals with osd.1 - usually, only a single LV will be
104 db_lvs
= api
.get_lvs(tags
=merge_dict(lv_tags
, {'ceph.type': 'db'}))
105 wal_lvs
= api
.get_lvs(tags
=merge_dict(lv_tags
, {'ceph.type': 'wal'}))
106 backing_devices
= [(db_lvs
, 'db'),
109 verified_devices
= []
112 # go through each lv and append it, otherwise query `blkid` to find
113 # a physical device. Do this for each type (journal,db,wal) regardless
114 # if they have been processed in the previous LV, so that bad devices
115 # with the same ID can be caught
116 for ceph_lvs
, _type
in backing_devices
:
118 verified_devices
.extend([l
.lv_path
for l
in ceph_lvs
])
121 # must be a disk partition, by querying blkid by the uuid we are
122 # ensuring that the device path is always correct
124 device_uuid
= lv
.tags
['ceph.%s_uuid' % _type
]
126 # Bluestore will not have ceph.journal_uuid, and Filestore
127 # will not not have ceph.db_uuid
130 osd_device
= disk
.get_device_from_partuuid(device_uuid
)
132 # if the osd_device is not found by the partuuid, then it is
133 # not possible to ensure this device exists anymore, so skip it
135 verified_devices
.append(osd_device
)
137 verified_devices
.append(lv
.lv_path
)
139 # reduce the list from all the duplicates that were added
140 return list(set(verified_devices
))
145 help = 'Removes all data and filesystems from a logical volume or partition.'
147 def __init__(self
, argv
):
150 def unmount_lv(self
, lv
):
151 if lv
.tags
.get('ceph.cluster_name') and lv
.tags
.get('ceph.osd_id'):
152 lv_path
= "/var/lib/ceph/osd/{}-{}".format(lv
.tags
['ceph.cluster_name'], lv
.tags
['ceph.osd_id'])
155 dmcrypt_uuid
= lv
.lv_uuid
156 dmcrypt
= lv
.encrypted
157 if system
.path_is_mounted(lv_path
):
158 mlogger
.info("Unmounting %s", lv_path
)
159 system
.unmount(lv_path
)
160 if dmcrypt
and dmcrypt_uuid
:
161 self
.dmcrypt_close(dmcrypt_uuid
)
163 def zap_lv(self
, device
):
165 Device examples: vg-name/lv-name, /dev/vg-name/lv-name
166 Requirements: Must be a logical volume (LV)
168 lv
= api
.get_single_lv(filters
={'lv_name': device
.lv_name
, 'vg_name':
173 zap_data(device
.path
)
175 if self
.args
.destroy
:
176 lvs
= api
.get_lvs(filters
={'vg_name': device
.vg_name
})
178 mlogger
.info('No LVs left, exiting', device
.vg_name
)
181 mlogger
.info('Only 1 LV left in VG, will proceed to destroy '
182 'volume group %s', device
.vg_name
)
183 pvs
= api
.get_pvs(filters
={'lv_uuid': lv
.lv_uuid
})
184 api
.remove_vg(device
.vg_name
)
186 api
.remove_pv(pv
.pv_name
)
188 mlogger
.info('More than 1 LV left in VG, will proceed to '
190 mlogger
.info('Removing LV because --destroy was given: %s',
192 api
.remove_lv(device
.path
)
194 # just remove all lvm metadata, leaving the LV around
197 def zap_partition(self
, device
):
199 Device example: /dev/sda1
200 Requirements: Must be a partition
202 if device
.is_encrypted
:
205 '/dev/%s' % holder
for holder
in device
.sys_api
.get('holders', [])
207 for mapper_uuid
in os
.listdir('/dev/mapper'):
208 mapper_path
= os
.path
.join('/dev/mapper', mapper_uuid
)
209 if os
.path
.realpath(mapper_path
) in holders
:
210 self
.dmcrypt_close(mapper_uuid
)
212 if system
.device_is_mounted(device
.path
):
213 mlogger
.info("Unmounting %s", device
.path
)
214 system
.unmount(device
.path
)
217 zap_data(device
.path
)
219 if self
.args
.destroy
:
220 mlogger
.info("Destroying partition since --destroy was used: %s" % device
.path
)
221 disk
.remove_partition(device
)
223 def zap_lvm_member(self
, device
):
225 An LVM member may have more than one LV and or VG, for example if it is
226 a raw device with multiple partitions each belonging to a different LV
228 Device example: /dev/sda
229 Requirements: An LV or VG present in the device, making it an LVM member
231 for lv
in device
.lvs
:
233 mlogger
.info('Zapping lvm member {}. lv_path is {}'.format(device
.path
, lv
.lv_path
))
234 self
.zap_lv(Device(lv
.lv_path
))
236 vg
= api
.get_single_vg(filters
={'vg_name': lv
.vg_name
})
238 mlogger
.info('Found empty VG {}, removing'.format(vg
.vg_name
))
239 api
.remove_vg(vg
.vg_name
)
243 def zap_raw_device(self
, device
):
245 Any whole (raw) device passed in as input will be processed here,
246 checking for LVM membership and partitions (if any).
248 Device example: /dev/sda
251 if not self
.args
.destroy
:
252 # the use of dd on a raw device causes the partition table to be
255 '--destroy was not specified, but zapping a whole device will remove the partition table'
258 # look for partitions and zap those
259 for part_name
in device
.sys_api
.get('partitions', {}).keys():
260 self
.zap_partition(Device('/dev/%s' % part_name
))
263 zap_data(device
.path
)
265 @decorators.needs_root
266 def zap(self
, devices
=None):
267 devices
= devices
or self
.args
.devices
269 for device
in devices
:
270 mlogger
.info("Zapping: %s", device
.path
)
271 if device
.is_mapper
and not device
.is_mpath
:
272 terminal
.error("Refusing to zap the mapper device: {}".format(device
))
274 if device
.is_lvm_member
:
275 self
.zap_lvm_member(device
)
278 if device
.is_partition
:
279 self
.zap_partition(device
)
281 self
.zap_raw_device(device
)
283 if self
.args
.devices
:
285 "Zapping successful for: %s" % ", ".join([str(d
) for d
in self
.args
.devices
])
288 identifier
= self
.args
.osd_id
or self
.args
.osd_fsid
290 "Zapping successful for OSD: %s" % identifier
293 @decorators.needs_root
295 if self
.args
.osd_id
and not self
.args
.no_systemd
:
296 osd_is_running
= systemctl
.osd_is_active(self
.args
.osd_id
)
298 mlogger
.error("OSD ID %s is running, stop it with:" % self
.args
.osd_id
)
299 mlogger
.error("systemctl stop ceph-osd@%s" % self
.args
.osd_id
)
300 raise SystemExit("Unable to zap devices associated with OSD ID: %s" % self
.args
.osd_id
)
301 devices
= find_associated_devices(self
.args
.osd_id
, self
.args
.osd_fsid
)
304 def dmcrypt_close(self
, dmcrypt_uuid
):
305 mlogger
.info("Closing encrypted volume %s", dmcrypt_uuid
)
306 encryption
.dmcrypt_close(mapping
=dmcrypt_uuid
, skip_path_check
=True)
309 sub_command_help
= dedent("""
310 Zaps the given logical volume(s), raw device(s) or partition(s) for reuse by ceph-volume.
311 If given a path to a logical volume it must be in the format of vg/lv. Any
312 filesystems present on the given device, vg/lv, or partition will be removed and
313 all data will be purged.
315 If the logical volume, raw device or partition is being used for any ceph related
316 mount points they will be unmounted.
318 However, the lv or partition will be kept intact.
320 Example calls for supported scenarios:
322 Zapping a logical volume:
324 ceph-volume lvm zap {vg name/lv name}
328 ceph-volume lvm zap /dev/sdc1
330 Zapping many raw devices:
332 ceph-volume lvm zap /dev/sda /dev/sdb /db/sdc
334 Zapping devices associated with an OSD ID:
336 ceph-volume lvm zap --osd-id 1
338 Optionally include the OSD FSID
340 ceph-volume lvm zap --osd-id 1 --osd-fsid 55BD4219-16A7-4037-BC20-0F158EFCC83D
342 If the --destroy flag is given and you are zapping a raw device or partition
343 then all vgs and lvs that exist on that raw device or partition will be destroyed.
345 This is especially useful if a raw device or partition was used by ceph-volume lvm create
346 or ceph-volume lvm prepare commands previously and now you want to reuse that device.
350 ceph-volume lvm zap /dev/sda --destroy
352 If the --destroy flag is given and you are zapping an lv then the lv is still
353 kept intact for reuse.
356 parser
= argparse
.ArgumentParser(
357 prog
='ceph-volume lvm zap',
358 formatter_class
=argparse
.RawDescriptionHelpFormatter
,
359 description
=sub_command_help
,
366 type=arg_validators
.ValidZapDevice(gpt_ok
=True),
368 help='Path to one or many lv (as vg/lv), partition (as /dev/sda1) or device (as /dev/sda)'
375 help='Destroy all volume groups and logical volumes if you are zapping a raw device or partition',
380 type=arg_validators
.valid_osd_id
,
381 help='Specify an OSD ID to detect associated devices for zapping',
386 help='Specify an OSD FSID to detect associated devices for zapping',
393 help='Skip systemd unit checks',
396 if len(self
.argv
) == 0:
397 print(sub_command_help
)
400 self
.args
= parser
.parse_args(self
.argv
)
402 if self
.args
.osd_id
or self
.args
.osd_fsid
: