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
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
),
72 def find_associated_devices(osd_id
=None, osd_fsid
=None):
74 From an ``osd_id`` and/or an ``osd_fsid``, filter out all the LVs in the
75 system that match those tag values, further detect if any partitions are
76 part of the OSD, and then return the set of LVs and partitions (if any).
80 lv_tags
['ceph.osd_id'] = osd_id
82 lv_tags
['ceph.osd_fsid'] = osd_fsid
84 lvs
.filter(lv_tags
=lv_tags
)
86 raise RuntimeError('Unable to find any LV for zapping OSD: %s' % osd_id
or osd_fsid
)
88 devices_to_zap
= ensure_associated_lvs(lvs
)
90 return [Device(path
) for path
in set(devices_to_zap
) if path
]
93 def ensure_associated_lvs(lvs
):
95 Go through each LV and ensure if backing devices (journal, wal, block)
96 are LVs or partitions, so that they can be accurately reported.
98 # look for many LVs for each backing type, because it is possible to
99 # receive a filtering for osd.1, and have multiple failed deployments
100 # leaving many journals with osd.1 - usually, only a single LV will be
102 journal_lvs
= lvs
._filter
(lv_tags
={'ceph.type': 'journal'})
103 db_lvs
= lvs
._filter
(lv_tags
={'ceph.type': 'db'})
104 wal_lvs
= lvs
._filter
(lv_tags
={'ceph.type': 'wal'})
106 (journal_lvs
, 'journal'),
111 verified_devices
= []
114 # go through each lv and append it, otherwise query `blkid` to find
115 # a physical device. Do this for each type (journal,db,wal) regardless
116 # if they have been processed in the previous LV, so that bad devices
117 # with the same ID can be caught
118 for ceph_lvs
, _type
in backing_devices
:
120 verified_devices
.extend([l
.lv_path
for l
in ceph_lvs
])
123 # must be a disk partition, by querying blkid by the uuid we are
124 # ensuring that the device path is always correct
126 device_uuid
= lv
.tags
['ceph.%s_uuid' % _type
]
128 # Bluestore will not have ceph.journal_uuid, and Filestore
129 # will not not have ceph.db_uuid
132 osd_device
= disk
.get_device_from_partuuid(device_uuid
)
134 # if the osd_device is not found by the partuuid, then it is
135 # not possible to ensure this device exists anymore, so skip it
137 verified_devices
.append(osd_device
)
139 verified_devices
.append(lv
.lv_path
)
141 # reduce the list from all the duplicates that were added
142 return list(set(verified_devices
))
147 help = 'Removes all data and filesystems from a logical volume or partition.'
149 def __init__(self
, argv
):
152 def unmount_lv(self
, lv
):
153 if lv
.tags
.get('ceph.cluster_name') and lv
.tags
.get('ceph.osd_id'):
154 lv_path
= "/var/lib/ceph/osd/{}-{}".format(lv
.tags
['ceph.cluster_name'], lv
.tags
['ceph.osd_id'])
157 dmcrypt_uuid
= lv
.lv_uuid
158 dmcrypt
= lv
.encrypted
159 if system
.path_is_mounted(lv_path
):
160 mlogger
.info("Unmounting %s", lv_path
)
161 system
.unmount(lv_path
)
162 if dmcrypt
and dmcrypt_uuid
:
163 self
.dmcrypt_close(dmcrypt_uuid
)
165 def zap_lv(self
, device
):
167 Device examples: vg-name/lv-name, /dev/vg-name/lv-name
168 Requirements: Must be a logical volume (LV)
170 lv
= api
.get_lv(lv_name
=device
.lv_name
, vg_name
=device
.vg_name
)
173 wipefs(device
.abspath
)
174 zap_data(device
.abspath
)
176 if self
.args
.destroy
:
178 lvs
.filter(vg_name
=device
.vg_name
)
180 mlogger
.info('Only 1 LV left in VG, will proceed to destroy volume group %s', device
.vg_name
)
181 api
.remove_vg(device
.vg_name
)
183 mlogger
.info('More than 1 LV left in VG, will proceed to destroy LV only')
184 mlogger
.info('Removing LV because --destroy was given: %s', device
.abspath
)
185 api
.remove_lv(device
.abspath
)
187 # just remove all lvm metadata, leaving the LV around
190 def zap_partition(self
, device
):
192 Device example: /dev/sda1
193 Requirements: Must be a partition
195 if device
.is_encrypted
:
198 '/dev/%s' % holder
for holder
in device
.sys_api
.get('holders', [])
200 for mapper_uuid
in os
.listdir('/dev/mapper'):
201 mapper_path
= os
.path
.join('/dev/mapper', mapper_uuid
)
202 if os
.path
.realpath(mapper_path
) in holders
:
203 self
.dmcrypt_close(mapper_uuid
)
205 if system
.device_is_mounted(device
.abspath
):
206 mlogger
.info("Unmounting %s", device
.abspath
)
207 system
.unmount(device
.abspath
)
209 wipefs(device
.abspath
)
210 zap_data(device
.abspath
)
212 if self
.args
.destroy
:
213 mlogger
.info("Destroying partition since --destroy was used: %s" % device
.abspath
)
214 disk
.remove_partition(device
)
216 def zap_lvm_member(self
, device
):
218 An LVM member may have more than one LV and or VG, for example if it is
219 a raw device with multiple partitions each belonging to a different LV
221 Device example: /dev/sda
222 Requirements: An LV or VG present in the device, making it an LVM member
224 for lv
in device
.lvs
:
225 self
.zap_lv(Device(lv
.lv_path
))
228 def zap_raw_device(self
, device
):
230 Any whole (raw) device passed in as input will be processed here,
231 checking for LVM membership and partitions (if any).
233 Device example: /dev/sda
236 if not self
.args
.destroy
:
237 # the use of dd on a raw device causes the partition table to be
240 '--destroy was not specified, but zapping a whole device will remove the partition table'
243 # look for partitions and zap those
244 for part_name
in device
.sys_api
.get('partitions', {}).keys():
245 self
.zap_partition(Device('/dev/%s' % part_name
))
247 wipefs(device
.abspath
)
248 zap_data(device
.abspath
)
250 @decorators.needs_root
251 def zap(self
, devices
=None):
252 devices
= devices
or self
.args
.devices
254 for device
in devices
:
255 mlogger
.info("Zapping: %s", device
.abspath
)
257 terminal
.error("Refusing to zap the mapper device: {}".format(device
))
259 if device
.is_lvm_member
:
260 self
.zap_lvm_member(device
)
263 if device
.is_partition
:
264 self
.zap_partition(device
)
266 self
.zap_raw_device(device
)
268 if self
.args
.devices
:
270 "Zapping successful for: %s" % ", ".join([str(d
) for d
in self
.args
.devices
])
273 identifier
= self
.args
.osd_id
or self
.args
.osd_fsid
275 "Zapping successful for OSD: %s" % identifier
278 @decorators.needs_root
281 osd_is_running
= systemctl
.osd_is_active(self
.args
.osd_id
)
283 mlogger
.error("OSD ID %s is running, stop it with:" % self
.args
.osd_id
)
284 mlogger
.error("systemctl stop ceph-osd@%s" % self
.args
.osd_id
)
285 raise SystemExit("Unable to zap devices associated with OSD ID: %s" % self
.args
.osd_id
)
286 devices
= find_associated_devices(self
.args
.osd_id
, self
.args
.osd_fsid
)
289 def dmcrypt_close(self
, dmcrypt_uuid
):
290 dmcrypt_path
= "/dev/mapper/{}".format(dmcrypt_uuid
)
291 mlogger
.info("Closing encrypted path %s", dmcrypt_path
)
292 encryption
.dmcrypt_close(dmcrypt_path
)
295 sub_command_help
= dedent("""
296 Zaps the given logical volume(s), raw device(s) or partition(s) for reuse by ceph-volume.
297 If given a path to a logical volume it must be in the format of vg/lv. Any
298 filesystems present on the given device, vg/lv, or partition will be removed and
299 all data will be purged.
301 If the logical volume, raw device or partition is being used for any ceph related
302 mount points they will be unmounted.
304 However, the lv or partition will be kept intact.
306 Example calls for supported scenarios:
308 Zapping a logical volume:
310 ceph-volume lvm zap {vg name/lv name}
314 ceph-volume lvm zap /dev/sdc1
316 Zapping many raw devices:
318 ceph-volume lvm zap /dev/sda /dev/sdb /db/sdc
320 Zapping devices associated with an OSD ID:
322 ceph-volume lvm zap --osd-id 1
324 Optionally include the OSD FSID
326 ceph-volume lvm zap --osd-id 1 --osd-fsid 55BD4219-16A7-4037-BC20-0F158EFCC83D
328 If the --destroy flag is given and you are zapping a raw device or partition
329 then all vgs and lvs that exist on that raw device or partition will be destroyed.
331 This is especially useful if a raw device or partition was used by ceph-volume lvm create
332 or ceph-volume lvm prepare commands previously and now you want to reuse that device.
336 ceph-volume lvm zap /dev/sda --destroy
338 If the --destroy flag is given and you are zapping an lv then the lv is still
339 kept intact for reuse.
342 parser
= argparse
.ArgumentParser(
343 prog
='ceph-volume lvm zap',
344 formatter_class
=argparse
.RawDescriptionHelpFormatter
,
345 description
=sub_command_help
,
352 type=arg_validators
.ValidDevice(gpt_ok
=True),
354 help='Path to one or many lv (as vg/lv), partition (as /dev/sda1) or device (as /dev/sda)'
361 help='Destroy all volume groups and logical volumes if you are zapping a raw device or partition',
366 help='Specify an OSD ID to detect associated devices for zapping',
371 help='Specify an OSD FSID to detect associated devices for zapping',
374 if len(self
.argv
) == 0:
375 print(sub_command_help
)
378 self
.args
= parser
.parse_args(self
.argv
)
380 if self
.args
.osd_id
or self
.args
.osd_fsid
: