]>
Commit | Line | Data |
---|---|---|
1 | import argparse | |
2 | import logging | |
3 | ||
4 | from textwrap import dedent | |
5 | ||
6 | from ceph_volume import decorators, terminal, process | |
7 | from ceph_volume.api import lvm as api | |
8 | from ceph_volume.util import system, encryption, disk | |
9 | ||
10 | logger = logging.getLogger(__name__) | |
11 | mlogger = terminal.MultiLogger(__name__) | |
12 | ||
13 | ||
14 | def wipefs(path): | |
15 | """ | |
16 | Removes the filesystem from an lv or partition. | |
17 | """ | |
18 | process.run([ | |
19 | 'wipefs', | |
20 | '--all', | |
21 | path | |
22 | ]) | |
23 | ||
24 | ||
25 | def zap_data(path): | |
26 | """ | |
27 | Clears all data from the given path. Path should be | |
28 | an absolute path to an lv or partition. | |
29 | ||
30 | 10M of data is written to the path to make sure that | |
31 | there is no trace left of any previous Filesystem. | |
32 | """ | |
33 | process.run([ | |
34 | 'dd', | |
35 | 'if=/dev/zero', | |
36 | 'of={path}'.format(path=path), | |
37 | 'bs=1M', | |
38 | 'count=10', | |
39 | ]) | |
40 | ||
41 | ||
42 | class Zap(object): | |
43 | ||
44 | help = 'Removes all data and filesystems from a logical volume or partition.' | |
45 | ||
46 | def __init__(self, argv): | |
47 | self.argv = argv | |
48 | ||
49 | def unmount_lv(self, lv): | |
50 | if lv.tags.get('ceph.cluster_name') and lv.tags.get('ceph.osd_id'): | |
51 | lv_path = "/var/lib/ceph/osd/{}-{}".format(lv.tags['ceph.cluster_name'], lv.tags['ceph.osd_id']) | |
52 | else: | |
53 | lv_path = lv.lv_path | |
54 | dmcrypt_uuid = lv.lv_uuid | |
55 | dmcrypt = lv.encrypted | |
56 | if system.path_is_mounted(lv_path): | |
57 | mlogger.info("Unmounting %s", lv_path) | |
58 | system.unmount(lv_path) | |
59 | if dmcrypt and dmcrypt_uuid: | |
60 | self.dmcrypt_close(dmcrypt_uuid) | |
61 | ||
62 | @decorators.needs_root | |
63 | def zap(self, args): | |
64 | for device in args.devices: | |
65 | if disk.is_mapper_device(device): | |
66 | terminal.error("Refusing to zap the mapper device: {}".format(device)) | |
67 | raise SystemExit(1) | |
68 | lv = api.get_lv_from_argument(device) | |
69 | if lv: | |
70 | # we are zapping a logical volume | |
71 | path = lv.lv_path | |
72 | self.unmount_lv(lv) | |
73 | else: | |
74 | # we are zapping a partition | |
75 | #TODO: ensure device is a partition | |
76 | path = device | |
77 | # check to if it is encrypted to close | |
78 | partuuid = disk.get_partuuid(device) | |
79 | if encryption.status("/dev/mapper/{}".format(partuuid)): | |
80 | dmcrypt_uuid = partuuid | |
81 | self.dmcrypt_close(dmcrypt_uuid) | |
82 | ||
83 | mlogger.info("Zapping: %s", path) | |
84 | ||
85 | # check if there was a pv created with the | |
86 | # name of device | |
87 | pvs = api.PVolumes() | |
88 | pvs.filter(pv_name=device) | |
89 | vgs = set([pv.vg_name for pv in pvs]) | |
90 | for pv in pvs: | |
91 | vg_name = pv.vg_name | |
92 | lv = None | |
93 | if pv.lv_uuid: | |
94 | lv = api.get_lv(vg_name=vg_name, lv_uuid=pv.lv_uuid) | |
95 | ||
96 | if lv: | |
97 | self.unmount_lv(lv) | |
98 | ||
99 | if args.destroy: | |
100 | for vg_name in vgs: | |
101 | mlogger.info("Destroying volume group %s because --destroy was given", vg_name) | |
102 | api.remove_vg(vg_name) | |
103 | mlogger.info("Destroying physical volume %s because --destroy was given", device) | |
104 | api.remove_pv(device) | |
105 | ||
106 | wipefs(path) | |
107 | zap_data(path) | |
108 | ||
109 | if lv and not pvs: | |
110 | # remove all lvm metadata | |
111 | lv.clear_tags() | |
112 | ||
113 | terminal.success("Zapping successful for: %s" % ", ".join(args.devices)) | |
114 | ||
115 | def dmcrypt_close(self, dmcrypt_uuid): | |
116 | dmcrypt_path = "/dev/mapper/{}".format(dmcrypt_uuid) | |
117 | mlogger.info("Closing encrypted path %s", dmcrypt_path) | |
118 | encryption.dmcrypt_close(dmcrypt_path) | |
119 | ||
120 | def main(self): | |
121 | sub_command_help = dedent(""" | |
122 | Zaps the given logical volume(s), raw device(s) or partition(s) for reuse by ceph-volume. | |
123 | If given a path to a logical volume it must be in the format of vg/lv. Any | |
124 | filesystems present on the given device, vg/lv, or partition will be removed and | |
125 | all data will be purged. | |
126 | ||
127 | If the logical volume, raw device or partition is being used for any ceph related | |
128 | mount points they will be unmounted. | |
129 | ||
130 | However, the lv or partition will be kept intact. | |
131 | ||
132 | Example calls for supported scenarios: | |
133 | ||
134 | Zapping a logical volume: | |
135 | ||
136 | ceph-volume lvm zap {vg name/lv name} | |
137 | ||
138 | Zapping a partition: | |
139 | ||
140 | ceph-volume lvm zap /dev/sdc1 | |
141 | ||
142 | Zapping many raw devices: | |
143 | ||
144 | ceph-volume lvm zap /dev/sda /dev/sdb /db/sdc | |
145 | ||
146 | If the --destroy flag is given and you are zapping a raw device or partition | |
147 | then all vgs and lvs that exist on that raw device or partition will be destroyed. | |
148 | ||
149 | This is especially useful if a raw device or partition was used by ceph-volume lvm create | |
150 | or ceph-volume lvm prepare commands previously and now you want to reuse that device. | |
151 | ||
152 | For example: | |
153 | ||
154 | ceph-volume lvm zap /dev/sda --destroy | |
155 | ||
156 | If the --destroy flag is given and you are zapping an lv then the lv is still | |
157 | kept intact for reuse. | |
158 | ||
159 | """) | |
160 | parser = argparse.ArgumentParser( | |
161 | prog='ceph-volume lvm zap', | |
162 | formatter_class=argparse.RawDescriptionHelpFormatter, | |
163 | description=sub_command_help, | |
164 | ) | |
165 | ||
166 | parser.add_argument( | |
167 | 'devices', | |
168 | metavar='DEVICES', | |
169 | nargs='*', | |
170 | default=[], | |
171 | help='Path to one or many lv (as vg/lv), partition (as /dev/sda1) or device (as /dev/sda)' | |
172 | ) | |
173 | parser.add_argument( | |
174 | '--destroy', | |
175 | action='store_true', | |
176 | default=False, | |
177 | help='Destroy all volume groups and logical volumes if you are zapping a raw device or partition', | |
178 | ) | |
179 | if len(self.argv) == 0: | |
180 | print(sub_command_help) | |
181 | return | |
182 | args = parser.parse_args(self.argv) | |
183 | self.zap(args) |