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
),
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
.filter(lv_tags
=lv_tags
)
87 raise RuntimeError('Unable to find any LV for zapping OSD: %s' % osd_id
or osd_fsid
)
89 devices_to_zap
= ensure_associated_lvs(lvs
)
91 return [Device(path
) for path
in set(devices_to_zap
) if path
]
94 def ensure_associated_lvs(lvs
):
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
103 journal_lvs
= lvs
._filter
(lv_tags
={'ceph.type': 'journal'})
104 db_lvs
= lvs
._filter
(lv_tags
={'ceph.type': 'db'})
105 wal_lvs
= lvs
._filter
(lv_tags
={'ceph.type': 'wal'})
107 (journal_lvs
, 'journal'),
112 verified_devices
= []
115 # go through each lv and append it, otherwise query `blkid` to find
116 # a physical device. Do this for each type (journal,db,wal) regardless
117 # if they have been processed in the previous LV, so that bad devices
118 # with the same ID can be caught
119 for ceph_lvs
, _type
in backing_devices
:
121 verified_devices
.extend([l
.lv_path
for l
in ceph_lvs
])
124 # must be a disk partition, by querying blkid by the uuid we are
125 # ensuring that the device path is always correct
127 device_uuid
= lv
.tags
['ceph.%s_uuid' % _type
]
129 # Bluestore will not have ceph.journal_uuid, and Filestore
130 # will not not have ceph.db_uuid
133 osd_device
= disk
.get_device_from_partuuid(device_uuid
)
135 # if the osd_device is not found by the partuuid, then it is
136 # not possible to ensure this device exists anymore, so skip it
138 verified_devices
.append(osd_device
)
140 verified_devices
.append(lv
.lv_path
)
142 # reduce the list from all the duplicates that were added
143 return list(set(verified_devices
))
148 help = 'Removes all data and filesystems from a logical volume or partition.'
150 def __init__(self
, argv
):
153 def unmount_lv(self
, lv
):
154 if lv
.tags
.get('ceph.cluster_name') and lv
.tags
.get('ceph.osd_id'):
155 lv_path
= "/var/lib/ceph/osd/{}-{}".format(lv
.tags
['ceph.cluster_name'], lv
.tags
['ceph.osd_id'])
158 dmcrypt_uuid
= lv
.lv_uuid
159 dmcrypt
= lv
.encrypted
160 if system
.path_is_mounted(lv_path
):
161 mlogger
.info("Unmounting %s", lv_path
)
162 system
.unmount(lv_path
)
163 if dmcrypt
and dmcrypt_uuid
:
164 self
.dmcrypt_close(dmcrypt_uuid
)
166 def zap_lv(self
, device
):
168 Device examples: vg-name/lv-name, /dev/vg-name/lv-name
169 Requirements: Must be a logical volume (LV)
171 lv
= api
.get_lv(lv_name
=device
.lv_name
, vg_name
=device
.vg_name
)
174 wipefs(device
.abspath
)
175 zap_data(device
.abspath
)
177 if self
.args
.destroy
:
179 lvs
.filter(vg_name
=device
.vg_name
)
181 mlogger
.info('Only 1 LV left in VG, will proceed to destroy volume group %s', device
.vg_name
)
182 api
.remove_vg(device
.vg_name
)
184 mlogger
.info('More than 1 LV left in VG, will proceed to destroy LV only')
185 mlogger
.info('Removing LV because --destroy was given: %s', device
.abspath
)
186 api
.remove_lv(device
.abspath
)
188 # just remove all lvm metadata, leaving the LV around
191 def zap_partition(self
, device
):
193 Device example: /dev/sda1
194 Requirements: Must be a partition
196 if device
.is_encrypted
:
199 '/dev/%s' % holder
for holder
in device
.sys_api
.get('holders', [])
201 for mapper_uuid
in os
.listdir('/dev/mapper'):
202 mapper_path
= os
.path
.join('/dev/mapper', mapper_uuid
)
203 if os
.path
.realpath(mapper_path
) in holders
:
204 self
.dmcrypt_close(mapper_uuid
)
206 if system
.device_is_mounted(device
.abspath
):
207 mlogger
.info("Unmounting %s", device
.abspath
)
208 system
.unmount(device
.abspath
)
210 wipefs(device
.abspath
)
211 zap_data(device
.abspath
)
213 if self
.args
.destroy
:
214 mlogger
.info("Destroying partition since --destroy was used: %s" % device
.abspath
)
215 disk
.remove_partition(device
)
217 def zap_lvm_member(self
, device
):
219 An LVM member may have more than one LV and or VG, for example if it is
220 a raw device with multiple partitions each belonging to a different LV
222 Device example: /dev/sda
223 Requirements: An LV or VG present in the device, making it an LVM member
225 for lv
in device
.lvs
:
227 mlogger
.info('Zapping lvm member {}. lv_path is {}'.format(device
.abspath
, lv
.lv_path
))
228 self
.zap_lv(Device(lv
.lv_path
))
230 vg
= api
.get_first_vg(filters
={'vg_name': lv
.vg_name
})
232 mlogger
.info('Found empty VG {}, removing'.format(vg
.vg_name
))
233 api
.remove_vg(vg
.vg_name
)
237 def zap_raw_device(self
, device
):
239 Any whole (raw) device passed in as input will be processed here,
240 checking for LVM membership and partitions (if any).
242 Device example: /dev/sda
245 if not self
.args
.destroy
:
246 # the use of dd on a raw device causes the partition table to be
249 '--destroy was not specified, but zapping a whole device will remove the partition table'
252 # look for partitions and zap those
253 for part_name
in device
.sys_api
.get('partitions', {}).keys():
254 self
.zap_partition(Device('/dev/%s' % part_name
))
256 wipefs(device
.abspath
)
257 zap_data(device
.abspath
)
259 @decorators.needs_root
260 def zap(self
, devices
=None):
261 devices
= devices
or self
.args
.devices
263 for device
in devices
:
264 mlogger
.info("Zapping: %s", device
.abspath
)
266 terminal
.error("Refusing to zap the mapper device: {}".format(device
))
268 if device
.is_lvm_member
:
269 self
.zap_lvm_member(device
)
272 if device
.is_partition
:
273 self
.zap_partition(device
)
275 self
.zap_raw_device(device
)
277 if self
.args
.devices
:
279 "Zapping successful for: %s" % ", ".join([str(d
) for d
in self
.args
.devices
])
282 identifier
= self
.args
.osd_id
or self
.args
.osd_fsid
284 "Zapping successful for OSD: %s" % identifier
287 @decorators.needs_root
290 osd_is_running
= systemctl
.osd_is_active(self
.args
.osd_id
)
292 mlogger
.error("OSD ID %s is running, stop it with:" % self
.args
.osd_id
)
293 mlogger
.error("systemctl stop ceph-osd@%s" % self
.args
.osd_id
)
294 raise SystemExit("Unable to zap devices associated with OSD ID: %s" % self
.args
.osd_id
)
295 devices
= find_associated_devices(self
.args
.osd_id
, self
.args
.osd_fsid
)
298 def dmcrypt_close(self
, dmcrypt_uuid
):
299 dmcrypt_path
= "/dev/mapper/{}".format(dmcrypt_uuid
)
300 mlogger
.info("Closing encrypted path %s", dmcrypt_path
)
301 encryption
.dmcrypt_close(dmcrypt_path
)
304 sub_command_help
= dedent("""
305 Zaps the given logical volume(s), raw device(s) or partition(s) for reuse by ceph-volume.
306 If given a path to a logical volume it must be in the format of vg/lv. Any
307 filesystems present on the given device, vg/lv, or partition will be removed and
308 all data will be purged.
310 If the logical volume, raw device or partition is being used for any ceph related
311 mount points they will be unmounted.
313 However, the lv or partition will be kept intact.
315 Example calls for supported scenarios:
317 Zapping a logical volume:
319 ceph-volume lvm zap {vg name/lv name}
323 ceph-volume lvm zap /dev/sdc1
325 Zapping many raw devices:
327 ceph-volume lvm zap /dev/sda /dev/sdb /db/sdc
329 Zapping devices associated with an OSD ID:
331 ceph-volume lvm zap --osd-id 1
333 Optionally include the OSD FSID
335 ceph-volume lvm zap --osd-id 1 --osd-fsid 55BD4219-16A7-4037-BC20-0F158EFCC83D
337 If the --destroy flag is given and you are zapping a raw device or partition
338 then all vgs and lvs that exist on that raw device or partition will be destroyed.
340 This is especially useful if a raw device or partition was used by ceph-volume lvm create
341 or ceph-volume lvm prepare commands previously and now you want to reuse that device.
345 ceph-volume lvm zap /dev/sda --destroy
347 If the --destroy flag is given and you are zapping an lv then the lv is still
348 kept intact for reuse.
351 parser
= argparse
.ArgumentParser(
352 prog
='ceph-volume lvm zap',
353 formatter_class
=argparse
.RawDescriptionHelpFormatter
,
354 description
=sub_command_help
,
361 type=arg_validators
.ValidDevice(gpt_ok
=True),
363 help='Path to one or many lv (as vg/lv), partition (as /dev/sda1) or device (as /dev/sda)'
370 help='Destroy all volume groups and logical volumes if you are zapping a raw device or partition',
375 help='Specify an OSD ID to detect associated devices for zapping',
380 help='Specify an OSD FSID to detect associated devices for zapping',
383 if len(self
.argv
) == 0:
384 print(sub_command_help
)
387 self
.args
= parser
.parse_args(self
.argv
)
389 if self
.args
.osd_id
or self
.args
.osd_fsid
: