5 from textwrap
import dedent
7 from ceph_volume
import decorators
, terminal
, process
8 from ceph_volume
.api
import lvm
as api
9 from ceph_volume
.util
import system
, encryption
, disk
, arg_validators
10 from ceph_volume
.util
.device
import Device
11 from ceph_volume
.systemd
import systemctl
13 logger
= logging
.getLogger(__name__
)
14 mlogger
= terminal
.MultiLogger(__name__
)
19 Removes the filesystem from an lv or partition.
30 Clears all data from the given path. Path should be
31 an absolute path to an lv or partition.
33 10M of data is written to the path to make sure that
34 there is no trace left of any previous Filesystem.
39 'of={path}'.format(path
=path
),
45 def find_associated_devices(osd_id
=None, osd_fsid
=None):
47 From an ``osd_id`` and/or an ``osd_fsid``, filter out all the LVs in the
48 system that match those tag values, further detect if any partitions are
49 part of the OSD, and then return the set of LVs and partitions (if any).
53 lv_tags
['ceph.osd_id'] = osd_id
55 lv_tags
['ceph.osd_fsid'] = osd_fsid
57 lvs
.filter(lv_tags
=lv_tags
)
59 raise RuntimeError('Unable to find any LV for zapping OSD: %s' % osd_id
or osd_fsid
)
61 devices_to_zap
= ensure_associated_lvs(lvs
)
63 return [Device(path
) for path
in set(devices_to_zap
) if path
]
66 def ensure_associated_lvs(lvs
):
68 Go through each LV and ensure if backing devices (journal, wal, block)
69 are LVs or partitions, so that they can be accurately reported.
71 # look for many LVs for each backing type, because it is possible to
72 # receive a filtering for osd.1, and have multiple failed deployments
73 # leaving many journals with osd.1 - usually, only a single LV will be
75 journal_lvs
= lvs
._filter
(lv_tags
={'ceph.type': 'journal'})
76 db_lvs
= lvs
._filter
(lv_tags
={'ceph.type': 'db'})
77 wal_lvs
= lvs
._filter
(lv_tags
={'ceph.type': 'wal'})
79 (journal_lvs
, 'journal'),
87 # go through each lv and append it, otherwise query `blkid` to find
88 # a physical device. Do this for each type (journal,db,wal) regardless
89 # if they have been processed in the previous LV, so that bad devices
90 # with the same ID can be caught
91 for ceph_lvs
, _type
in backing_devices
:
93 verified_devices
.extend([l
.lv_path
for l
in ceph_lvs
])
96 # must be a disk partition, by querying blkid by the uuid we are
97 # ensuring that the device path is always correct
99 device_uuid
= lv
.tags
['ceph.%s_uuid' % _type
]
101 # Bluestore will not have ceph.journal_uuid, and Filestore
102 # will not not have ceph.db_uuid
105 osd_device
= disk
.get_device_from_partuuid(device_uuid
)
107 # if the osd_device is not found by the partuuid, then it is
108 # not possible to ensure this device exists anymore, so skip it
110 verified_devices
.append(osd_device
)
112 verified_devices
.append(lv
.lv_path
)
114 # reduce the list from all the duplicates that were added
115 return list(set(verified_devices
))
120 help = 'Removes all data and filesystems from a logical volume or partition.'
122 def __init__(self
, argv
):
125 def unmount_lv(self
, lv
):
126 if lv
.tags
.get('ceph.cluster_name') and lv
.tags
.get('ceph.osd_id'):
127 lv_path
= "/var/lib/ceph/osd/{}-{}".format(lv
.tags
['ceph.cluster_name'], lv
.tags
['ceph.osd_id'])
130 dmcrypt_uuid
= lv
.lv_uuid
131 dmcrypt
= lv
.encrypted
132 if system
.path_is_mounted(lv_path
):
133 mlogger
.info("Unmounting %s", lv_path
)
134 system
.unmount(lv_path
)
135 if dmcrypt
and dmcrypt_uuid
:
136 self
.dmcrypt_close(dmcrypt_uuid
)
138 def zap_lv(self
, device
):
140 Device examples: vg-name/lv-name, /dev/vg-name/lv-name
141 Requirements: Must be a logical volume (LV)
143 lv
= api
.get_lv(lv_name
=device
.lv_name
, vg_name
=device
.vg_name
)
146 wipefs(device
.abspath
)
147 zap_data(device
.abspath
)
149 if self
.args
.destroy
:
151 lvs
.filter(vg_name
=device
.vg_name
)
153 mlogger
.info('Only 1 LV left in VG, will proceed to destroy volume group %s', device
.vg_name
)
154 api
.remove_vg(device
.vg_name
)
156 mlogger
.info('More than 1 LV left in VG, will proceed to destroy LV only')
157 mlogger
.info('Removing LV because --destroy was given: %s', device
.abspath
)
158 api
.remove_lv(device
.abspath
)
160 # just remove all lvm metadata, leaving the LV around
163 def zap_partition(self
, device
):
165 Device example: /dev/sda1
166 Requirements: Must be a partition
168 if device
.is_encrypted
:
171 '/dev/%s' % holder
for holder
in device
.sys_api
.get('holders', [])
173 for mapper_uuid
in os
.listdir('/dev/mapper'):
174 mapper_path
= os
.path
.join('/dev/mapper', mapper_uuid
)
175 if os
.path
.realpath(mapper_path
) in holders
:
176 self
.dmcrypt_close(mapper_uuid
)
178 if system
.device_is_mounted(device
.abspath
):
179 mlogger
.info("Unmounting %s", device
.abspath
)
180 system
.unmount(device
.abspath
)
182 wipefs(device
.abspath
)
183 zap_data(device
.abspath
)
185 if self
.args
.destroy
:
186 mlogger
.info("Destroying partition since --destroy was used: %s" % device
.abspath
)
187 disk
.remove_partition(device
)
189 def zap_lvm_member(self
, device
):
191 An LVM member may have more than one LV and or VG, for example if it is
192 a raw device with multiple partitions each belonging to a different LV
194 Device example: /dev/sda
195 Requirements: An LV or VG present in the device, making it an LVM member
197 for lv
in device
.lvs
:
198 self
.zap_lv(Device(lv
.lv_path
))
201 def zap_raw_device(self
, device
):
203 Any whole (raw) device passed in as input will be processed here,
204 checking for LVM membership and partitions (if any).
206 Device example: /dev/sda
209 if not self
.args
.destroy
:
210 # the use of dd on a raw device causes the partition table to be
213 '--destroy was not specified, but zapping a whole device will remove the partition table'
216 # look for partitions and zap those
217 for part_name
in device
.sys_api
.get('partitions', {}).keys():
218 self
.zap_partition(Device('/dev/%s' % part_name
))
220 wipefs(device
.abspath
)
221 zap_data(device
.abspath
)
223 @decorators.needs_root
224 def zap(self
, devices
=None):
225 devices
= devices
or self
.args
.devices
227 for device
in devices
:
228 mlogger
.info("Zapping: %s", device
.abspath
)
230 terminal
.error("Refusing to zap the mapper device: {}".format(device
))
232 if device
.is_lvm_member
:
233 self
.zap_lvm_member(device
)
236 if device
.is_partition
:
237 self
.zap_partition(device
)
239 self
.zap_raw_device(device
)
241 if self
.args
.devices
:
243 "Zapping successful for: %s" % ", ".join([str(d
) for d
in self
.args
.devices
])
247 "Zapping successful for OSD: %s" % self
.args
.osd_id
or self
.args
.osd_fsid
250 @decorators.needs_root
253 osd_is_running
= systemctl
.osd_is_active(self
.args
.osd_id
)
255 mlogger
.error("OSD ID %s is running, stop it with:" % self
.args
.osd_id
)
256 mlogger
.error("systemctl stop ceph-osd@%s" % self
.args
.osd_id
)
257 raise SystemExit("Unable to zap devices associated with OSD ID: %s" % self
.args
.osd_id
)
258 devices
= find_associated_devices(self
.args
.osd_id
, self
.args
.osd_fsid
)
261 def dmcrypt_close(self
, dmcrypt_uuid
):
262 dmcrypt_path
= "/dev/mapper/{}".format(dmcrypt_uuid
)
263 mlogger
.info("Closing encrypted path %s", dmcrypt_path
)
264 encryption
.dmcrypt_close(dmcrypt_path
)
267 sub_command_help
= dedent("""
268 Zaps the given logical volume(s), raw device(s) or partition(s) for reuse by ceph-volume.
269 If given a path to a logical volume it must be in the format of vg/lv. Any
270 filesystems present on the given device, vg/lv, or partition will be removed and
271 all data will be purged.
273 If the logical volume, raw device or partition is being used for any ceph related
274 mount points they will be unmounted.
276 However, the lv or partition will be kept intact.
278 Example calls for supported scenarios:
280 Zapping a logical volume:
282 ceph-volume lvm zap {vg name/lv name}
286 ceph-volume lvm zap /dev/sdc1
288 Zapping many raw devices:
290 ceph-volume lvm zap /dev/sda /dev/sdb /db/sdc
292 Zapping devices associated with an OSD ID:
294 ceph-volume lvm zap --osd-id 1
296 Optionally include the OSD FSID
298 ceph-volume lvm zap --osd-id 1 --osd-fsid 55BD4219-16A7-4037-BC20-0F158EFCC83D
300 If the --destroy flag is given and you are zapping a raw device or partition
301 then all vgs and lvs that exist on that raw device or partition will be destroyed.
303 This is especially useful if a raw device or partition was used by ceph-volume lvm create
304 or ceph-volume lvm prepare commands previously and now you want to reuse that device.
308 ceph-volume lvm zap /dev/sda --destroy
310 If the --destroy flag is given and you are zapping an lv then the lv is still
311 kept intact for reuse.
314 parser
= argparse
.ArgumentParser(
315 prog
='ceph-volume lvm zap',
316 formatter_class
=argparse
.RawDescriptionHelpFormatter
,
317 description
=sub_command_help
,
324 type=arg_validators
.ValidDevice(gpt_ok
=True),
326 help='Path to one or many lv (as vg/lv), partition (as /dev/sda1) or device (as /dev/sda)'
333 help='Destroy all volume groups and logical volumes if you are zapping a raw device or partition',
338 help='Specify an OSD ID to detect associated devices for zapping',
343 help='Specify an OSD FSID to detect associated devices for zapping',
346 if len(self
.argv
) == 0:
347 print(sub_command_help
)
350 self
.args
= parser
.parse_args(self
.argv
)
352 if self
.args
.osd_id
or self
.args
.osd_fsid
: