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 journal_lvs
= api
.get_lvs(tags
=merge_dict(lv_tags
, {'ceph.type': 'journal'}))
105 db_lvs
= api
.get_lvs(tags
=merge_dict(lv_tags
, {'ceph.type': 'db'}))
106 wal_lvs
= api
.get_lvs(tags
=merge_dict(lv_tags
, {'ceph.type': 'wal'}))
107 backing_devices
= [(journal_lvs
, 'journal'), (db_lvs
, 'db'),
110 verified_devices
= []
113 # go through each lv and append it, otherwise query `blkid` to find
114 # a physical device. Do this for each type (journal,db,wal) regardless
115 # if they have been processed in the previous LV, so that bad devices
116 # with the same ID can be caught
117 for ceph_lvs
, _type
in backing_devices
:
119 verified_devices
.extend([l
.lv_path
for l
in ceph_lvs
])
122 # must be a disk partition, by querying blkid by the uuid we are
123 # ensuring that the device path is always correct
125 device_uuid
= lv
.tags
['ceph.%s_uuid' % _type
]
127 # Bluestore will not have ceph.journal_uuid, and Filestore
128 # will not not have ceph.db_uuid
131 osd_device
= disk
.get_device_from_partuuid(device_uuid
)
133 # if the osd_device is not found by the partuuid, then it is
134 # not possible to ensure this device exists anymore, so skip it
136 verified_devices
.append(osd_device
)
138 verified_devices
.append(lv
.lv_path
)
140 # reduce the list from all the duplicates that were added
141 return list(set(verified_devices
))
146 help = 'Removes all data and filesystems from a logical volume or partition.'
148 def __init__(self
, argv
):
151 def unmount_lv(self
, lv
):
152 if lv
.tags
.get('ceph.cluster_name') and lv
.tags
.get('ceph.osd_id'):
153 lv_path
= "/var/lib/ceph/osd/{}-{}".format(lv
.tags
['ceph.cluster_name'], lv
.tags
['ceph.osd_id'])
156 dmcrypt_uuid
= lv
.lv_uuid
157 dmcrypt
= lv
.encrypted
158 if system
.path_is_mounted(lv_path
):
159 mlogger
.info("Unmounting %s", lv_path
)
160 system
.unmount(lv_path
)
161 if dmcrypt
and dmcrypt_uuid
:
162 self
.dmcrypt_close(dmcrypt_uuid
)
164 def zap_lv(self
, device
):
166 Device examples: vg-name/lv-name, /dev/vg-name/lv-name
167 Requirements: Must be a logical volume (LV)
169 lv
= api
.get_first_lv(filters
={'lv_name': device
.lv_name
, 'vg_name':
173 wipefs(device
.abspath
)
174 zap_data(device
.abspath
)
176 if self
.args
.destroy
:
177 lvs
= api
.get_lvs(filters
={'vg_name': device
.vg_name
})
179 mlogger
.info('No LVs left, exiting', device
.vg_name
)
182 mlogger
.info('Only 1 LV left in VG, will proceed to destroy '
183 'volume group %s', device
.vg_name
)
184 api
.remove_vg(device
.vg_name
)
186 mlogger
.info('More than 1 LV left in VG, will proceed to '
188 mlogger
.info('Removing LV because --destroy was given: %s',
190 api
.remove_lv(device
.abspath
)
192 # just remove all lvm metadata, leaving the LV around
195 def zap_partition(self
, device
):
197 Device example: /dev/sda1
198 Requirements: Must be a partition
200 if device
.is_encrypted
:
203 '/dev/%s' % holder
for holder
in device
.sys_api
.get('holders', [])
205 for mapper_uuid
in os
.listdir('/dev/mapper'):
206 mapper_path
= os
.path
.join('/dev/mapper', mapper_uuid
)
207 if os
.path
.realpath(mapper_path
) in holders
:
208 self
.dmcrypt_close(mapper_uuid
)
210 if system
.device_is_mounted(device
.abspath
):
211 mlogger
.info("Unmounting %s", device
.abspath
)
212 system
.unmount(device
.abspath
)
214 wipefs(device
.abspath
)
215 zap_data(device
.abspath
)
217 if self
.args
.destroy
:
218 mlogger
.info("Destroying partition since --destroy was used: %s" % device
.abspath
)
219 disk
.remove_partition(device
)
221 def zap_lvm_member(self
, device
):
223 An LVM member may have more than one LV and or VG, for example if it is
224 a raw device with multiple partitions each belonging to a different LV
226 Device example: /dev/sda
227 Requirements: An LV or VG present in the device, making it an LVM member
229 for lv
in device
.lvs
:
231 mlogger
.info('Zapping lvm member {}. lv_path is {}'.format(device
.abspath
, lv
.lv_path
))
232 self
.zap_lv(Device(lv
.lv_path
))
234 vg
= api
.get_first_vg(filters
={'vg_name': lv
.vg_name
})
236 mlogger
.info('Found empty VG {}, removing'.format(vg
.vg_name
))
237 api
.remove_vg(vg
.vg_name
)
241 def zap_raw_device(self
, device
):
243 Any whole (raw) device passed in as input will be processed here,
244 checking for LVM membership and partitions (if any).
246 Device example: /dev/sda
249 if not self
.args
.destroy
:
250 # the use of dd on a raw device causes the partition table to be
253 '--destroy was not specified, but zapping a whole device will remove the partition table'
256 # look for partitions and zap those
257 for part_name
in device
.sys_api
.get('partitions', {}).keys():
258 self
.zap_partition(Device('/dev/%s' % part_name
))
260 wipefs(device
.abspath
)
261 zap_data(device
.abspath
)
263 @decorators.needs_root
264 def zap(self
, devices
=None):
265 devices
= devices
or self
.args
.devices
267 for device
in devices
:
268 mlogger
.info("Zapping: %s", device
.abspath
)
270 terminal
.error("Refusing to zap the mapper device: {}".format(device
))
272 if device
.is_lvm_member
:
273 self
.zap_lvm_member(device
)
276 if device
.is_partition
:
277 self
.zap_partition(device
)
279 self
.zap_raw_device(device
)
281 if self
.args
.devices
:
283 "Zapping successful for: %s" % ", ".join([str(d
) for d
in self
.args
.devices
])
286 identifier
= self
.args
.osd_id
or self
.args
.osd_fsid
288 "Zapping successful for OSD: %s" % identifier
291 @decorators.needs_root
294 osd_is_running
= systemctl
.osd_is_active(self
.args
.osd_id
)
296 mlogger
.error("OSD ID %s is running, stop it with:" % self
.args
.osd_id
)
297 mlogger
.error("systemctl stop ceph-osd@%s" % self
.args
.osd_id
)
298 raise SystemExit("Unable to zap devices associated with OSD ID: %s" % self
.args
.osd_id
)
299 devices
= find_associated_devices(self
.args
.osd_id
, self
.args
.osd_fsid
)
302 def dmcrypt_close(self
, dmcrypt_uuid
):
303 dmcrypt_path
= "/dev/mapper/{}".format(dmcrypt_uuid
)
304 mlogger
.info("Closing encrypted path %s", dmcrypt_path
)
305 encryption
.dmcrypt_close(dmcrypt_path
)
308 sub_command_help
= dedent("""
309 Zaps the given logical volume(s), raw device(s) or partition(s) for reuse by ceph-volume.
310 If given a path to a logical volume it must be in the format of vg/lv. Any
311 filesystems present on the given device, vg/lv, or partition will be removed and
312 all data will be purged.
314 If the logical volume, raw device or partition is being used for any ceph related
315 mount points they will be unmounted.
317 However, the lv or partition will be kept intact.
319 Example calls for supported scenarios:
321 Zapping a logical volume:
323 ceph-volume lvm zap {vg name/lv name}
327 ceph-volume lvm zap /dev/sdc1
329 Zapping many raw devices:
331 ceph-volume lvm zap /dev/sda /dev/sdb /db/sdc
333 Zapping devices associated with an OSD ID:
335 ceph-volume lvm zap --osd-id 1
337 Optionally include the OSD FSID
339 ceph-volume lvm zap --osd-id 1 --osd-fsid 55BD4219-16A7-4037-BC20-0F158EFCC83D
341 If the --destroy flag is given and you are zapping a raw device or partition
342 then all vgs and lvs that exist on that raw device or partition will be destroyed.
344 This is especially useful if a raw device or partition was used by ceph-volume lvm create
345 or ceph-volume lvm prepare commands previously and now you want to reuse that device.
349 ceph-volume lvm zap /dev/sda --destroy
351 If the --destroy flag is given and you are zapping an lv then the lv is still
352 kept intact for reuse.
355 parser
= argparse
.ArgumentParser(
356 prog
='ceph-volume lvm zap',
357 formatter_class
=argparse
.RawDescriptionHelpFormatter
,
358 description
=sub_command_help
,
365 type=arg_validators
.ValidDevice(gpt_ok
=True),
367 help='Path to one or many lv (as vg/lv), partition (as /dev/sda1) or device (as /dev/sda)'
374 help='Destroy all volume groups and logical volumes if you are zapping a raw device or partition',
379 help='Specify an OSD ID to detect associated devices for zapping',
384 help='Specify an OSD FSID to detect associated devices for zapping',
387 if len(self
.argv
) == 0:
388 print(sub_command_help
)
391 self
.args
= parser
.parse_args(self
.argv
)
393 if self
.args
.osd_id
or self
.args
.osd_fsid
: