]>
Commit | Line | Data |
---|---|---|
94b18763 | 1 | import os |
d2e6a577 | 2 | import pytest |
92f5a8d4 | 3 | from mock.mock import patch |
d2e6a577 | 4 | from ceph_volume import process, exceptions |
3efd9988 | 5 | from ceph_volume.api import lvm as api |
d2e6a577 FG |
6 | |
7 | ||
8 | class TestParseTags(object): | |
9 | ||
10 | def test_no_tags_means_empty_dict(self): | |
11 | result = api.parse_tags('') | |
12 | assert result == {} | |
13 | ||
14 | def test_single_tag_gets_parsed(self): | |
15 | result = api.parse_tags('ceph.osd_something=1') | |
16 | assert result == {'ceph.osd_something': '1'} | |
17 | ||
b32b8144 FG |
18 | def test_non_ceph_tags_are_skipped(self): |
19 | result = api.parse_tags('foo') | |
20 | assert result == {} | |
21 | ||
22 | def test_mixed_non_ceph_tags(self): | |
23 | result = api.parse_tags('foo,ceph.bar=1') | |
24 | assert result == {'ceph.bar': '1'} | |
25 | ||
d2e6a577 FG |
26 | def test_multiple_csv_expands_in_dict(self): |
27 | result = api.parse_tags('ceph.osd_something=1,ceph.foo=2,ceph.fsid=0000') | |
28 | # assert them piecemeal to avoid the un-ordered dict nature | |
29 | assert result['ceph.osd_something'] == '1' | |
30 | assert result['ceph.foo'] == '2' | |
31 | assert result['ceph.fsid'] == '0000' | |
32 | ||
33 | ||
34 | class TestGetAPIVgs(object): | |
35 | ||
36 | def test_report_is_emtpy(self, monkeypatch): | |
91327a77 | 37 | monkeypatch.setattr(api.process, 'call', lambda x,**kw: ('\n\n', '', 0)) |
d2e6a577 FG |
38 | assert api.get_api_vgs() == [] |
39 | ||
40 | def test_report_has_stuff(self, monkeypatch): | |
b5b8bbf5 | 41 | report = [' VolGroup00'] |
91327a77 | 42 | monkeypatch.setattr(api.process, 'call', lambda x, **kw: (report, '', 0)) |
d2e6a577 FG |
43 | assert api.get_api_vgs() == [{'vg_name': 'VolGroup00'}] |
44 | ||
b5b8bbf5 | 45 | def test_report_has_stuff_with_empty_attrs(self, monkeypatch): |
92f5a8d4 | 46 | report = [' VolGroup00 ;;;;;;4194304'] |
91327a77 | 47 | monkeypatch.setattr(api.process, 'call', lambda x, **kw: (report, '', 0)) |
b5b8bbf5 FG |
48 | result = api.get_api_vgs()[0] |
49 | assert len(result.keys()) == 7 | |
50 | assert result['vg_name'] == 'VolGroup00' | |
92f5a8d4 | 51 | assert result['vg_extent_size'] == '4194304' |
d2e6a577 | 52 | |
b5b8bbf5 FG |
53 | def test_report_has_multiple_items(self, monkeypatch): |
54 | report = [' VolGroup00;;;;;;;', ' ceph_vg;;;;;;;'] | |
91327a77 | 55 | monkeypatch.setattr(api.process, 'call', lambda x, **kw: (report, '', 0)) |
b5b8bbf5 FG |
56 | result = api.get_api_vgs() |
57 | assert result[0]['vg_name'] == 'VolGroup00' | |
58 | assert result[1]['vg_name'] == 'ceph_vg' | |
d2e6a577 FG |
59 | |
60 | ||
61 | class TestGetAPILvs(object): | |
62 | ||
63 | def test_report_is_emtpy(self, monkeypatch): | |
91327a77 | 64 | monkeypatch.setattr(api.process, 'call', lambda x, **kw: ('', '', 0)) |
d2e6a577 FG |
65 | assert api.get_api_lvs() == [] |
66 | ||
67 | def test_report_has_stuff(self, monkeypatch): | |
b5b8bbf5 | 68 | report = [' ;/path;VolGroup00;root'] |
91327a77 | 69 | monkeypatch.setattr(api.process, 'call', lambda x, **kw: (report, '', 0)) |
b5b8bbf5 FG |
70 | result = api.get_api_lvs() |
71 | assert result[0]['lv_name'] == 'VolGroup00' | |
d2e6a577 FG |
72 | |
73 | def test_report_has_multiple_items(self, monkeypatch): | |
b5b8bbf5 | 74 | report = [' ;/path;VolName;root', ';/dev/path;ceph_lv;ceph_vg'] |
91327a77 | 75 | monkeypatch.setattr(api.process, 'call', lambda x, **kw: (report, '', 0)) |
b5b8bbf5 FG |
76 | result = api.get_api_lvs() |
77 | assert result[0]['lv_name'] == 'VolName' | |
78 | assert result[1]['lv_name'] == 'ceph_lv' | |
d2e6a577 FG |
79 | |
80 | ||
81 | @pytest.fixture | |
82 | def volumes(monkeypatch): | |
91327a77 | 83 | monkeypatch.setattr(process, 'call', lambda x, **kw: ('', '', 0)) |
d2e6a577 FG |
84 | volumes = api.Volumes() |
85 | volumes._purge() | |
3efd9988 FG |
86 | # also patch api.Volumes so that when it is called, it will use the newly |
87 | # created fixture, with whatever the test method wants to append to it | |
88 | monkeypatch.setattr(api, 'Volumes', lambda: volumes) | |
d2e6a577 FG |
89 | return volumes |
90 | ||
91 | ||
92 | @pytest.fixture | |
93 | def volume_groups(monkeypatch): | |
91327a77 | 94 | monkeypatch.setattr(process, 'call', lambda x, **kw: ('', '', 0)) |
d2e6a577 FG |
95 | vgs = api.VolumeGroups() |
96 | vgs._purge() | |
97 | return vgs | |
98 | ||
99 | ||
100 | class TestGetLV(object): | |
101 | ||
102 | def test_nothing_is_passed_in(self): | |
103 | # so we return a None | |
104 | assert api.get_lv() is None | |
105 | ||
106 | def test_single_lv_is_matched(self, volumes, monkeypatch): | |
107 | FooVolume = api.Volume(lv_name='foo', lv_path='/dev/vg/foo', lv_tags="ceph.type=data") | |
108 | volumes.append(FooVolume) | |
109 | monkeypatch.setattr(api, 'Volumes', lambda: volumes) | |
110 | assert api.get_lv(lv_name='foo') == FooVolume | |
111 | ||
181888fb FG |
112 | def test_single_lv_is_matched_by_uuid(self, volumes, monkeypatch): |
113 | FooVolume = api.Volume( | |
114 | lv_name='foo', lv_path='/dev/vg/foo', | |
115 | lv_uuid='1111', lv_tags="ceph.type=data") | |
116 | volumes.append(FooVolume) | |
117 | monkeypatch.setattr(api, 'Volumes', lambda: volumes) | |
118 | assert api.get_lv(lv_uuid='1111') == FooVolume | |
119 | ||
120 | ||
121 | class TestGetPV(object): | |
122 | ||
123 | def test_nothing_is_passed_in(self): | |
124 | # so we return a None | |
125 | assert api.get_pv() is None | |
126 | ||
127 | def test_single_pv_is_not_matched(self, pvolumes, monkeypatch): | |
b32b8144 | 128 | FooPVolume = api.PVolume(pv_name='/dev/sda', pv_uuid="0000", pv_tags={}, vg_name="vg") |
181888fb FG |
129 | pvolumes.append(FooPVolume) |
130 | monkeypatch.setattr(api, 'PVolumes', lambda: pvolumes) | |
131 | assert api.get_pv(pv_uuid='foo') is None | |
132 | ||
133 | def test_single_pv_is_matched(self, pvolumes, monkeypatch): | |
b32b8144 | 134 | FooPVolume = api.PVolume(vg_name="vg", pv_name='/dev/sda', pv_uuid="0000", pv_tags={}) |
181888fb FG |
135 | pvolumes.append(FooPVolume) |
136 | monkeypatch.setattr(api, 'PVolumes', lambda: pvolumes) | |
137 | assert api.get_pv(pv_uuid='0000') == FooPVolume | |
138 | ||
1adf2230 AA |
139 | def test_multiple_pvs_is_matched_by_uuid(self, pvolumes, monkeypatch): |
140 | FooPVolume = api.PVolume(vg_name="vg", pv_name='/dev/sda', pv_uuid="0000", pv_tags={}, lv_uuid="0000000") | |
141 | BarPVolume = api.PVolume(vg_name="vg", pv_name='/dev/sda', pv_uuid="0000", pv_tags={}) | |
142 | pvolumes.append(FooPVolume) | |
143 | pvolumes.append(BarPVolume) | |
144 | monkeypatch.setattr(api, 'PVolumes', lambda: pvolumes) | |
145 | assert api.get_pv(pv_uuid='0000') == FooPVolume | |
146 | ||
147 | def test_multiple_pvs_is_matched_by_name(self, pvolumes, monkeypatch): | |
148 | FooPVolume = api.PVolume(vg_name="vg", pv_name='/dev/sda', pv_uuid="0000", pv_tags={}, lv_uuid="0000000") | |
149 | BarPVolume = api.PVolume(vg_name="vg", pv_name='/dev/sda', pv_uuid="0000", pv_tags={}) | |
150 | pvolumes.append(FooPVolume) | |
151 | pvolumes.append(BarPVolume) | |
152 | monkeypatch.setattr(api, 'PVolumes', lambda: pvolumes) | |
153 | assert api.get_pv(pv_name='/dev/sda') == FooPVolume | |
154 | ||
155 | def test_multiple_pvs_is_matched_by_tags(self, pvolumes, monkeypatch): | |
156 | FooPVolume = api.PVolume(vg_name="vg1", pv_name='/dev/sdc', pv_uuid="1000", pv_tags="ceph.foo=bar", lv_uuid="0000000") | |
157 | BarPVolume = api.PVolume(vg_name="vg", pv_name='/dev/sda', pv_uuid="0000", pv_tags="ceph.foo=bar") | |
158 | pvolumes.append(FooPVolume) | |
159 | pvolumes.append(BarPVolume) | |
160 | monkeypatch.setattr(api, 'PVolumes', lambda: pvolumes) | |
161 | with pytest.raises(exceptions.MultiplePVsError): | |
162 | api.get_pv(pv_tags={"ceph.foo": "bar"}) | |
163 | ||
181888fb FG |
164 | def test_single_pv_is_matched_by_uuid(self, pvolumes, monkeypatch): |
165 | FooPVolume = api.PVolume( | |
166 | pv_name='/dev/vg/foo', | |
b32b8144 | 167 | pv_uuid='1111', pv_tags="ceph.type=data", vg_name="vg") |
181888fb FG |
168 | pvolumes.append(FooPVolume) |
169 | monkeypatch.setattr(api, 'PVolumes', lambda: pvolumes) | |
170 | assert api.get_pv(pv_uuid='1111') == FooPVolume | |
171 | ||
b32b8144 FG |
172 | def test_vg_name_is_set(self, pvolumes, monkeypatch): |
173 | FooPVolume = api.PVolume( | |
174 | pv_name='/dev/vg/foo', | |
175 | pv_uuid='1111', pv_tags="ceph.type=data", vg_name="vg") | |
176 | pvolumes.append(FooPVolume) | |
177 | monkeypatch.setattr(api, 'PVolumes', lambda: pvolumes) | |
178 | pv = api.get_pv(pv_name="/dev/vg/foo") | |
179 | assert pv.vg_name == "vg" | |
180 | ||
181888fb FG |
181 | |
182 | class TestPVolumes(object): | |
183 | ||
184 | def test_filter_by_tag_does_not_match_one(self, pvolumes, monkeypatch): | |
185 | pv_tags = "ceph.type=journal,ceph.osd_id=1,ceph.fsid=000-aaa" | |
186 | FooPVolume = api.PVolume( | |
187 | pv_name='/dev/vg/foo', | |
b32b8144 | 188 | pv_uuid='1111', pv_tags=pv_tags, vg_name='vg') |
181888fb | 189 | pvolumes.append(FooPVolume) |
eafe8130 TL |
190 | assert pvolumes.filter(pv_tags={'ceph.type': 'journal', |
191 | 'ceph.osd_id': '2'}) == [] | |
181888fb FG |
192 | |
193 | def test_filter_by_tags_matches(self, pvolumes, monkeypatch): | |
194 | pv_tags = "ceph.type=journal,ceph.osd_id=1" | |
195 | FooPVolume = api.PVolume( | |
196 | pv_name='/dev/vg/foo', | |
b32b8144 | 197 | pv_uuid='1111', pv_tags=pv_tags, vg_name="vg") |
181888fb | 198 | pvolumes.append(FooPVolume) |
eafe8130 TL |
199 | assert pvolumes.filter(pv_tags={'ceph.type': 'journal', |
200 | 'ceph.osd_id': '1'}) == [FooPVolume] | |
181888fb | 201 | |
d2e6a577 FG |
202 | |
203 | class TestGetVG(object): | |
204 | ||
205 | def test_nothing_is_passed_in(self): | |
206 | # so we return a None | |
207 | assert api.get_vg() is None | |
208 | ||
209 | def test_single_vg_is_matched(self, volume_groups, monkeypatch): | |
210 | FooVG = api.VolumeGroup(vg_name='foo') | |
211 | volume_groups.append(FooVG) | |
212 | monkeypatch.setattr(api, 'VolumeGroups', lambda: volume_groups) | |
213 | assert api.get_vg(vg_name='foo') == FooVG | |
214 | ||
215 | ||
92f5a8d4 TL |
216 | class TestVolume(object): |
217 | ||
218 | def test_is_ceph_device(self): | |
219 | lv_tags = "ceph.type=data,ceph.osd_id=0" | |
220 | osd = api.Volume(lv_name='osd/volume', lv_tags=lv_tags) | |
221 | assert api.is_ceph_device(osd) | |
222 | ||
223 | @pytest.mark.parametrize('dev',[ | |
224 | '/dev/sdb', | |
225 | api.VolumeGroup(vg_name='foo'), | |
226 | api.Volume(lv_name='vg/no_osd', lv_tags=''), | |
227 | api.Volume(lv_name='vg/no_osd', lv_tags='ceph.osd_id=null'), | |
228 | None, | |
229 | ]) | |
230 | def test_is_not_ceph_device(self, dev): | |
231 | assert not api.is_ceph_device(dev) | |
232 | ||
9f95a23c TL |
233 | def test_no_empty_lv_name(self): |
234 | with pytest.raises(ValueError): | |
235 | api.Volume(lv_name='', lv_tags='') | |
236 | ||
92f5a8d4 | 237 | |
d2e6a577 FG |
238 | class TestVolumes(object): |
239 | ||
240 | def test_volume_get_has_no_volumes(self, volumes): | |
241 | assert volumes.get() is None | |
242 | ||
243 | def test_volume_get_filtered_has_no_volumes(self, volumes): | |
244 | assert volumes.get(lv_name='ceph') is None | |
245 | ||
246 | def test_volume_has_multiple_matches(self, volumes): | |
247 | volume1 = volume2 = api.Volume(lv_name='foo', lv_path='/dev/vg/lv', lv_tags='') | |
248 | volumes.append(volume1) | |
249 | volumes.append(volume2) | |
250 | with pytest.raises(exceptions.MultipleLVsError): | |
251 | volumes.get(lv_name='foo') | |
252 | ||
3efd9988 FG |
253 | def test_as_dict_infers_type_from_tags(self, volumes): |
254 | lv_tags = "ceph.type=data,ceph.fsid=000-aaa" | |
255 | osd = api.Volume(lv_name='volume1', lv_path='/dev/vg/lv', lv_tags=lv_tags) | |
256 | volumes.append(osd) | |
257 | result = volumes.get(lv_tags={'ceph.type': 'data'}).as_dict() | |
258 | assert result['type'] == 'data' | |
259 | ||
260 | def test_as_dict_populates_path_from_lv_api(self, volumes): | |
261 | lv_tags = "ceph.type=data,ceph.fsid=000-aaa" | |
262 | osd = api.Volume(lv_name='volume1', lv_path='/dev/vg/lv', lv_tags=lv_tags) | |
263 | volumes.append(osd) | |
264 | result = volumes.get(lv_tags={'ceph.type': 'data'}).as_dict() | |
265 | assert result['path'] == '/dev/vg/lv' | |
266 | ||
d2e6a577 FG |
267 | def test_find_the_correct_one(self, volumes): |
268 | volume1 = api.Volume(lv_name='volume1', lv_path='/dev/vg/lv', lv_tags='') | |
269 | volume2 = api.Volume(lv_name='volume2', lv_path='/dev/vg/lv', lv_tags='') | |
270 | volumes.append(volume1) | |
271 | volumes.append(volume2) | |
272 | assert volumes.get(lv_name='volume1') == volume1 | |
273 | ||
274 | def test_filter_by_tag(self, volumes): | |
275 | lv_tags = "ceph.type=data,ceph.fsid=000-aaa" | |
276 | osd = api.Volume(lv_name='volume1', lv_path='/dev/vg/lv', lv_tags=lv_tags) | |
277 | journal = api.Volume(lv_name='volume2', lv_path='/dev/vg/lv', lv_tags='ceph.type=journal') | |
278 | volumes.append(osd) | |
279 | volumes.append(journal) | |
280 | volumes.filter(lv_tags={'ceph.type': 'data'}) | |
281 | assert len(volumes) == 1 | |
282 | assert volumes[0].lv_name == 'volume1' | |
283 | ||
181888fb FG |
284 | def test_filter_by_tag_does_not_match_one(self, volumes): |
285 | lv_tags = "ceph.type=data,ceph.fsid=000-aaa" | |
286 | osd = api.Volume(lv_name='volume1', lv_path='/dev/vg/lv', lv_tags=lv_tags) | |
287 | journal = api.Volume(lv_name='volume2', lv_path='/dev/vg/lv', lv_tags='ceph.osd_id=1,ceph.type=journal') | |
288 | volumes.append(osd) | |
289 | volumes.append(journal) | |
290 | # note the different osd_id! | |
291 | volumes.filter(lv_tags={'ceph.type': 'data', 'ceph.osd_id': '2'}) | |
292 | assert volumes == [] | |
293 | ||
d2e6a577 FG |
294 | def test_filter_by_vg_name(self, volumes): |
295 | lv_tags = "ceph.type=data,ceph.fsid=000-aaa" | |
296 | osd = api.Volume(lv_name='volume1', vg_name='ceph_vg', lv_tags=lv_tags) | |
297 | journal = api.Volume(lv_name='volume2', vg_name='system_vg', lv_tags='ceph.type=journal') | |
298 | volumes.append(osd) | |
299 | volumes.append(journal) | |
300 | volumes.filter(vg_name='ceph_vg') | |
301 | assert len(volumes) == 1 | |
302 | assert volumes[0].lv_name == 'volume1' | |
303 | ||
304 | def test_filter_by_lv_path(self, volumes): | |
305 | osd = api.Volume(lv_name='volume1', lv_path='/dev/volume1', lv_tags='') | |
306 | journal = api.Volume(lv_name='volume2', lv_path='/dev/volume2', lv_tags='') | |
307 | volumes.append(osd) | |
308 | volumes.append(journal) | |
309 | volumes.filter(lv_path='/dev/volume1') | |
310 | assert len(volumes) == 1 | |
311 | assert volumes[0].lv_name == 'volume1' | |
312 | ||
181888fb FG |
313 | def test_filter_by_lv_uuid(self, volumes): |
314 | osd = api.Volume(lv_name='volume1', lv_path='/dev/volume1', lv_uuid='1111', lv_tags='') | |
315 | journal = api.Volume(lv_name='volume2', lv_path='/dev/volume2', lv_uuid='', lv_tags='') | |
316 | volumes.append(osd) | |
317 | volumes.append(journal) | |
318 | volumes.filter(lv_uuid='1111') | |
319 | assert len(volumes) == 1 | |
320 | assert volumes[0].lv_name == 'volume1' | |
321 | ||
322 | def test_filter_by_lv_uuid_nothing_found(self, volumes): | |
323 | osd = api.Volume(lv_name='volume1', lv_path='/dev/volume1', lv_uuid='1111', lv_tags='') | |
324 | journal = api.Volume(lv_name='volume2', lv_path='/dev/volume2', lv_uuid='', lv_tags='') | |
325 | volumes.append(osd) | |
326 | volumes.append(journal) | |
327 | volumes.filter(lv_uuid='22222') | |
328 | assert volumes == [] | |
329 | ||
d2e6a577 FG |
330 | def test_filter_requires_params(self, volumes): |
331 | with pytest.raises(TypeError): | |
332 | volumes.filter() | |
333 | ||
334 | ||
9f95a23c TL |
335 | class TestVolumeGroup(object): |
336 | ||
337 | def test_volume_group_no_empty_name(self): | |
338 | with pytest.raises(ValueError): | |
339 | api.VolumeGroup(vg_name='') | |
340 | ||
341 | ||
d2e6a577 FG |
342 | class TestVolumeGroups(object): |
343 | ||
344 | def test_volume_get_has_no_volume_groups(self, volume_groups): | |
345 | assert volume_groups.get() is None | |
346 | ||
347 | def test_volume_get_filtered_has_no_volumes(self, volume_groups): | |
348 | assert volume_groups.get(vg_name='ceph') is None | |
349 | ||
350 | def test_volume_has_multiple_matches(self, volume_groups): | |
351 | volume1 = volume2 = api.VolumeGroup(vg_name='foo', lv_path='/dev/vg/lv', lv_tags='') | |
352 | volume_groups.append(volume1) | |
353 | volume_groups.append(volume2) | |
354 | with pytest.raises(exceptions.MultipleVGsError): | |
355 | volume_groups.get(vg_name='foo') | |
356 | ||
357 | def test_find_the_correct_one(self, volume_groups): | |
358 | volume1 = api.VolumeGroup(vg_name='volume1', lv_tags='') | |
359 | volume2 = api.VolumeGroup(vg_name='volume2', lv_tags='') | |
360 | volume_groups.append(volume1) | |
361 | volume_groups.append(volume2) | |
362 | assert volume_groups.get(vg_name='volume1') == volume1 | |
363 | ||
364 | def test_filter_by_tag(self, volume_groups): | |
365 | vg_tags = "ceph.group=dmcache" | |
366 | osd = api.VolumeGroup(vg_name='volume1', vg_tags=vg_tags) | |
367 | journal = api.VolumeGroup(vg_name='volume2', vg_tags='ceph.group=plain') | |
368 | volume_groups.append(osd) | |
369 | volume_groups.append(journal) | |
eafe8130 | 370 | volume_groups = volume_groups.filter(vg_tags={'ceph.group': 'dmcache'}) |
d2e6a577 FG |
371 | assert len(volume_groups) == 1 |
372 | assert volume_groups[0].vg_name == 'volume1' | |
373 | ||
181888fb FG |
374 | def test_filter_by_tag_does_not_match_one(self, volume_groups): |
375 | vg_tags = "ceph.group=dmcache,ceph.disk_type=ssd" | |
376 | osd = api.VolumeGroup(vg_name='volume1', vg_path='/dev/vg/lv', vg_tags=vg_tags) | |
377 | volume_groups.append(osd) | |
eafe8130 | 378 | volume_groups = volume_groups.filter(vg_tags={'ceph.group': 'data', 'ceph.disk_type': 'ssd'}) |
181888fb FG |
379 | assert volume_groups == [] |
380 | ||
d2e6a577 FG |
381 | def test_filter_by_vg_name(self, volume_groups): |
382 | vg_tags = "ceph.type=data,ceph.fsid=000-aaa" | |
383 | osd = api.VolumeGroup(vg_name='ceph_vg', vg_tags=vg_tags) | |
384 | journal = api.VolumeGroup(vg_name='volume2', vg_tags='ceph.type=journal') | |
385 | volume_groups.append(osd) | |
386 | volume_groups.append(journal) | |
eafe8130 | 387 | volume_groups = volume_groups.filter(vg_name='ceph_vg') |
d2e6a577 FG |
388 | assert len(volume_groups) == 1 |
389 | assert volume_groups[0].vg_name == 'ceph_vg' | |
390 | ||
391 | def test_filter_requires_params(self, volume_groups): | |
392 | with pytest.raises(TypeError): | |
eafe8130 | 393 | volume_groups = volume_groups.filter() |
d2e6a577 FG |
394 | |
395 | ||
1adf2230 AA |
396 | class TestVolumeGroupFree(object): |
397 | ||
1adf2230 | 398 | def test_integer_gets_produced(self): |
92f5a8d4 TL |
399 | vg = api.VolumeGroup(vg_name='nosize', vg_free_count=100, vg_extent_size=4194304) |
400 | assert vg.free == 100 * 4194304 | |
1adf2230 AA |
401 | |
402 | ||
403 | class TestCreateLVs(object): | |
404 | ||
92f5a8d4 TL |
405 | def setup(self): |
406 | self.vg = api.VolumeGroup(vg_name='ceph', | |
407 | vg_extent_size=1073741824, | |
408 | vg_extent_count=99999999, | |
409 | vg_free_count=999) | |
410 | ||
1adf2230 AA |
411 | def test_creates_correct_lv_number_from_parts(self, monkeypatch): |
412 | monkeypatch.setattr('ceph_volume.api.lvm.create_lv', lambda *a, **kw: (a, kw)) | |
92f5a8d4 | 413 | lvs = api.create_lvs(self.vg, parts=4) |
1adf2230 AA |
414 | assert len(lvs) == 4 |
415 | ||
416 | def test_suffixes_the_size_arg(self, monkeypatch): | |
417 | monkeypatch.setattr('ceph_volume.api.lvm.create_lv', lambda *a, **kw: (a, kw)) | |
92f5a8d4 | 418 | lvs = api.create_lvs(self.vg, parts=4) |
1adf2230 AA |
419 | assert lvs[0][1]['extents'] == 249 |
420 | ||
421 | def test_only_uses_free_size(self, monkeypatch): | |
422 | monkeypatch.setattr('ceph_volume.api.lvm.create_lv', lambda *a, **kw: (a, kw)) | |
92f5a8d4 TL |
423 | vg = api.VolumeGroup(vg_name='ceph', |
424 | vg_extent_size=1073741824, | |
425 | vg_extent_count=99999999, | |
426 | vg_free_count=1000) | |
1adf2230 AA |
427 | lvs = api.create_lvs(vg, parts=4) |
428 | assert lvs[0][1]['extents'] == 250 | |
429 | ||
430 | def test_null_tags_are_set_by_default(self, monkeypatch): | |
431 | monkeypatch.setattr('ceph_volume.api.lvm.create_lv', lambda *a, **kw: (a, kw)) | |
92f5a8d4 | 432 | kwargs = api.create_lvs(self.vg, parts=4)[0][1] |
1adf2230 AA |
433 | assert list(kwargs['tags'].values()) == ['null', 'null', 'null', 'null'] |
434 | ||
435 | def test_fallback_to_one_part(self, monkeypatch): | |
436 | monkeypatch.setattr('ceph_volume.api.lvm.create_lv', lambda *a, **kw: (a, kw)) | |
92f5a8d4 | 437 | lvs = api.create_lvs(self.vg) |
1adf2230 AA |
438 | assert len(lvs) == 1 |
439 | ||
440 | ||
441 | class TestVolumeGroupSizing(object): | |
442 | ||
443 | def setup(self): | |
92f5a8d4 TL |
444 | self.vg = api.VolumeGroup(vg_name='ceph', |
445 | vg_extent_size=1073741824, | |
446 | vg_free_count=1024) | |
1adf2230 AA |
447 | |
448 | def test_parts_and_size_errors(self): | |
449 | with pytest.raises(ValueError) as error: | |
450 | self.vg.sizing(parts=4, size=10) | |
81eedcae | 451 | assert "Cannot process sizing" in str(error.value) |
1adf2230 AA |
452 | |
453 | def test_zero_parts_produces_100_percent(self): | |
454 | result = self.vg.sizing(parts=0) | |
455 | assert result['percentages'] == 100 | |
456 | ||
457 | def test_two_parts_produces_50_percent(self): | |
458 | result = self.vg.sizing(parts=2) | |
459 | assert result['percentages'] == 50 | |
460 | ||
461 | def test_two_parts_produces_half_size(self): | |
462 | result = self.vg.sizing(parts=2) | |
463 | assert result['sizes'] == 512 | |
464 | ||
465 | def test_half_size_produces_round_sizes(self): | |
466 | result = self.vg.sizing(size=512) | |
467 | assert result['sizes'] == 512 | |
468 | assert result['percentages'] == 50 | |
469 | assert result['parts'] == 2 | |
470 | ||
471 | def test_bit_more_than_half_size_allocates_full_size(self): | |
472 | # 513 can't allocate more than 1, so it just fallsback to using the | |
473 | # whole device | |
474 | result = self.vg.sizing(size=513) | |
475 | assert result['sizes'] == 1024 | |
476 | assert result['percentages'] == 100 | |
477 | assert result['parts'] == 1 | |
478 | ||
479 | def test_extents_are_halfed_rounded_down(self): | |
480 | result = self.vg.sizing(size=512) | |
92f5a8d4 | 481 | assert result['extents'] == 512 |
1adf2230 AA |
482 | |
483 | def test_bit_less_size_rounds_down(self): | |
484 | result = self.vg.sizing(size=129) | |
485 | assert result['sizes'] == 146 | |
486 | assert result['percentages'] == 14 | |
487 | assert result['parts'] == 7 | |
488 | ||
489 | def test_unable_to_allocate_past_free_size(self): | |
490 | with pytest.raises(exceptions.SizeAllocationError): | |
491 | self.vg.sizing(size=2048) | |
492 | ||
493 | ||
3efd9988 FG |
494 | class TestGetLVFromArgument(object): |
495 | ||
496 | def setup(self): | |
497 | self.foo_volume = api.Volume( | |
498 | lv_name='foo', lv_path='/path/to/lv', | |
499 | vg_name='foo_group', lv_tags='' | |
500 | ) | |
501 | ||
502 | def test_non_absolute_path_is_not_valid(self, volumes): | |
503 | volumes.append(self.foo_volume) | |
504 | assert api.get_lv_from_argument('foo') is None | |
505 | ||
506 | def test_too_many_slashes_is_invalid(self, volumes): | |
507 | volumes.append(self.foo_volume) | |
508 | assert api.get_lv_from_argument('path/to/lv') is None | |
509 | ||
510 | def test_absolute_path_is_not_lv(self, volumes): | |
511 | volumes.append(self.foo_volume) | |
512 | assert api.get_lv_from_argument('/path') is None | |
513 | ||
514 | def test_absolute_path_is_lv(self, volumes): | |
515 | volumes.append(self.foo_volume) | |
516 | assert api.get_lv_from_argument('/path/to/lv') == self.foo_volume | |
517 | ||
518 | ||
519 | class TestRemoveLV(object): | |
520 | ||
521 | def test_removes_lv(self, monkeypatch): | |
522 | def mock_call(cmd, **kw): | |
523 | return ('', '', 0) | |
524 | monkeypatch.setattr(process, 'call', mock_call) | |
525 | assert api.remove_lv("vg/lv") | |
526 | ||
91327a77 AA |
527 | def test_removes_lv_object(self, fake_call): |
528 | foo_volume = api.Volume(lv_name='foo', lv_path='/path', vg_name='foo_group', lv_tags='') | |
529 | api.remove_lv(foo_volume) | |
530 | # last argument from the list passed to process.call | |
531 | assert fake_call.calls[0]['args'][0][-1] == '/path' | |
532 | ||
3efd9988 FG |
533 | def test_fails_to_remove_lv(self, monkeypatch): |
534 | def mock_call(cmd, **kw): | |
535 | return ('', '', 1) | |
536 | monkeypatch.setattr(process, 'call', mock_call) | |
537 | with pytest.raises(RuntimeError): | |
538 | api.remove_lv("vg/lv") | |
539 | ||
540 | ||
d2e6a577 FG |
541 | class TestCreateLV(object): |
542 | ||
543 | def setup(self): | |
544 | self.foo_volume = api.Volume(lv_name='foo', lv_path='/path', vg_name='foo_group', lv_tags='') | |
92f5a8d4 TL |
545 | self.foo_group = api.VolumeGroup(vg_name='foo_group', |
546 | vg_extent_size=4194304, | |
547 | vg_free_count=100) | |
548 | ||
549 | @patch('ceph_volume.api.lvm.process.run') | |
550 | @patch('ceph_volume.api.lvm.process.call') | |
551 | @patch('ceph_volume.api.lvm.get_lv') | |
552 | def test_uses_size(self, m_get_lv, m_call, m_run, monkeypatch): | |
553 | m_get_lv.return_value = self.foo_volume | |
554 | api.create_lv('foo', 0, vg=self.foo_group, size=5368709120, tags={'ceph.type': 'data'}) | |
555 | expected = ['lvcreate', '--yes', '-l', '1280', '-n', 'foo-0', 'foo_group'] | |
556 | m_run.assert_called_with(expected) | |
557 | ||
558 | @patch('ceph_volume.api.lvm.process.run') | |
559 | @patch('ceph_volume.api.lvm.process.call') | |
560 | @patch('ceph_volume.api.lvm.get_lv') | |
561 | def test_uses_extents(self, m_get_lv, m_call, m_run, monkeypatch): | |
562 | m_get_lv.return_value = self.foo_volume | |
563 | api.create_lv('foo', 0, vg=self.foo_group, extents='50', tags={'ceph.type': 'data'}) | |
564 | expected = ['lvcreate', '--yes', '-l', '50', '-n', 'foo-0', 'foo_group'] | |
565 | m_run.assert_called_with(expected) | |
566 | ||
567 | @pytest.mark.parametrize("test_input,expected", | |
568 | [(2, 50), | |
569 | (3, 33),]) | |
570 | @patch('ceph_volume.api.lvm.process.run') | |
571 | @patch('ceph_volume.api.lvm.process.call') | |
572 | @patch('ceph_volume.api.lvm.get_lv') | |
573 | def test_uses_slots(self, m_get_lv, m_call, m_run, monkeypatch, test_input, expected): | |
574 | m_get_lv.return_value = self.foo_volume | |
575 | api.create_lv('foo', 0, vg=self.foo_group, slots=test_input, tags={'ceph.type': 'data'}) | |
576 | expected = ['lvcreate', '--yes', '-l', str(expected), '-n', 'foo-0', 'foo_group'] | |
577 | m_run.assert_called_with(expected) | |
578 | ||
579 | @patch('ceph_volume.api.lvm.process.run') | |
580 | @patch('ceph_volume.api.lvm.process.call') | |
581 | @patch('ceph_volume.api.lvm.get_lv') | |
582 | def test_uses_all(self, m_get_lv, m_call, m_run, monkeypatch): | |
583 | m_get_lv.return_value = self.foo_volume | |
584 | api.create_lv('foo', 0, vg=self.foo_group, tags={'ceph.type': 'data'}) | |
585 | expected = ['lvcreate', '--yes', '-l', '100%FREE', '-n', 'foo-0', 'foo_group'] | |
586 | m_run.assert_called_with(expected) | |
587 | ||
588 | @patch('ceph_volume.api.lvm.process.run') | |
589 | @patch('ceph_volume.api.lvm.process.call') | |
590 | @patch('ceph_volume.api.lvm.Volume.set_tags') | |
591 | @patch('ceph_volume.api.lvm.get_lv') | |
592 | def test_calls_to_set_tags_default(self, m_get_lv, m_set_tags, m_call, m_run, monkeypatch): | |
593 | m_get_lv.return_value = self.foo_volume | |
594 | api.create_lv('foo', 0, vg=self.foo_group) | |
595 | tags = { | |
596 | "ceph.osd_id": "null", | |
597 | "ceph.type": "null", | |
598 | "ceph.cluster_fsid": "null", | |
599 | "ceph.osd_fsid": "null", | |
600 | } | |
601 | m_set_tags.assert_called_with(tags) | |
602 | ||
603 | @patch('ceph_volume.api.lvm.process.run') | |
604 | @patch('ceph_volume.api.lvm.process.call') | |
605 | @patch('ceph_volume.api.lvm.Volume.set_tags') | |
606 | @patch('ceph_volume.api.lvm.get_lv') | |
607 | def test_calls_to_set_tags_arg(self, m_get_lv, m_set_tags, m_call, m_run, monkeypatch): | |
608 | m_get_lv.return_value = self.foo_volume | |
609 | api.create_lv('foo', 0, vg=self.foo_group, tags={'ceph.type': 'data'}) | |
610 | tags = { | |
611 | "ceph.type": "data", | |
612 | "ceph.data_device": "/path" | |
613 | } | |
614 | m_set_tags.assert_called_with(tags) | |
615 | ||
616 | @patch('ceph_volume.api.lvm.process.run') | |
617 | @patch('ceph_volume.api.lvm.process.call') | |
618 | @patch('ceph_volume.api.lvm.get_device_vgs') | |
619 | @patch('ceph_volume.api.lvm.create_vg') | |
620 | @patch('ceph_volume.api.lvm.get_lv') | |
621 | def test_create_vg(self, m_get_lv, m_create_vg, m_get_device_vgs, m_call, | |
622 | m_run, monkeypatch): | |
623 | m_get_lv.return_value = self.foo_volume | |
624 | m_get_device_vgs.return_value = [] | |
625 | api.create_lv('foo', 0, device='dev/foo', size='5G', tags={'ceph.type': 'data'}) | |
626 | m_create_vg.assert_called_with('dev/foo', name_prefix='ceph') | |
1adf2230 AA |
627 | |
628 | ||
81eedcae TL |
629 | class TestTags(object): |
630 | ||
631 | def setup(self): | |
632 | self.foo_volume_clean = api.Volume(lv_name='foo_clean', lv_path='/pathclean', | |
633 | vg_name='foo_group', | |
634 | lv_tags='') | |
635 | self.foo_volume = api.Volume(lv_name='foo', lv_path='/path', | |
636 | vg_name='foo_group', | |
637 | lv_tags='ceph.foo0=bar0,ceph.foo1=bar1,ceph.foo2=bar2') | |
638 | ||
639 | def test_set_tag(self, monkeypatch, capture): | |
640 | monkeypatch.setattr(process, 'run', capture) | |
641 | monkeypatch.setattr(process, 'call', capture) | |
642 | self.foo_volume_clean.set_tag('foo', 'bar') | |
643 | expected = ['lvchange', '--addtag', 'foo=bar', '/pathclean'] | |
644 | assert capture.calls[0]['args'][0] == expected | |
645 | assert self.foo_volume_clean.tags == {'foo': 'bar'} | |
646 | ||
647 | def test_set_clear_tag(self, monkeypatch, capture): | |
648 | monkeypatch.setattr(process, 'run', capture) | |
649 | monkeypatch.setattr(process, 'call', capture) | |
650 | self.foo_volume_clean.set_tag('foo', 'bar') | |
651 | assert self.foo_volume_clean.tags == {'foo': 'bar'} | |
652 | self.foo_volume_clean.clear_tag('foo') | |
653 | expected = ['lvchange', '--deltag', 'foo=bar', '/pathclean'] | |
654 | assert self.foo_volume_clean.tags == {} | |
655 | assert capture.calls[1]['args'][0] == expected | |
656 | ||
657 | def test_set_tags(self, monkeypatch, capture): | |
658 | monkeypatch.setattr(process, 'run', capture) | |
659 | monkeypatch.setattr(process, 'call', capture) | |
660 | tags = {'ceph.foo0': 'bar0', 'ceph.foo1': 'bar1', 'ceph.foo2': 'bar2'} | |
661 | assert self.foo_volume.tags == tags | |
662 | ||
663 | tags = {'ceph.foo0': 'bar0', 'ceph.foo1': 'baz1', 'ceph.foo2': 'baz2'} | |
664 | self.foo_volume.set_tags(tags) | |
665 | assert self.foo_volume.tags == tags | |
666 | ||
667 | self.foo_volume.set_tag('ceph.foo1', 'other1') | |
668 | tags['ceph.foo1'] = 'other1' | |
669 | assert self.foo_volume.tags == tags | |
670 | ||
671 | expected = [ | |
e306af50 TL |
672 | sorted(['lvchange', '--deltag', 'ceph.foo0=bar0', '--deltag', |
673 | 'ceph.foo1=bar1', '--deltag', 'ceph.foo2=bar2', '/path']), | |
674 | sorted(['lvchange', '--deltag', 'ceph.foo1=baz1', '/path']), | |
675 | sorted(['lvchange', '--addtag', 'ceph.foo0=bar0', '--addtag', | |
676 | 'ceph.foo1=baz1', '--addtag', 'ceph.foo2=baz2', '/path']), | |
677 | sorted(['lvchange', '--addtag', 'ceph.foo1=other1', '/path']), | |
81eedcae TL |
678 | ] |
679 | # The order isn't guaranted | |
680 | for call in capture.calls: | |
e306af50 | 681 | assert sorted(call['args'][0]) in expected |
81eedcae TL |
682 | assert len(capture.calls) == len(expected) |
683 | ||
684 | def test_clear_tags(self, monkeypatch, capture): | |
685 | monkeypatch.setattr(process, 'run', capture) | |
686 | monkeypatch.setattr(process, 'call', capture) | |
687 | tags = {'ceph.foo0': 'bar0', 'ceph.foo1': 'bar1', 'ceph.foo2': 'bar2'} | |
688 | ||
689 | self.foo_volume_clean.set_tags(tags) | |
690 | assert self.foo_volume_clean.tags == tags | |
691 | self.foo_volume_clean.clear_tags() | |
692 | assert self.foo_volume_clean.tags == {} | |
693 | ||
694 | expected = [ | |
e306af50 TL |
695 | sorted(['lvchange', '--addtag', 'ceph.foo0=bar0', '--addtag', |
696 | 'ceph.foo1=bar1', '--addtag', 'ceph.foo2=bar2', | |
697 | '/pathclean']), | |
698 | sorted(['lvchange', '--deltag', 'ceph.foo0=bar0', '--deltag', | |
699 | 'ceph.foo1=bar1', '--deltag', 'ceph.foo2=bar2', | |
700 | '/pathclean']), | |
81eedcae TL |
701 | ] |
702 | # The order isn't guaranted | |
703 | for call in capture.calls: | |
e306af50 | 704 | assert sorted(call['args'][0]) in expected |
81eedcae TL |
705 | assert len(capture.calls) == len(expected) |
706 | ||
707 | ||
1adf2230 AA |
708 | class TestExtendVG(object): |
709 | ||
710 | def setup(self): | |
711 | self.foo_volume = api.VolumeGroup(vg_name='foo', lv_tags='') | |
712 | ||
713 | def test_uses_single_device_in_list(self, monkeypatch, fake_run): | |
714 | monkeypatch.setattr(api, 'get_vg', lambda **kw: True) | |
715 | api.extend_vg(self.foo_volume, ['/dev/sda']) | |
716 | expected = ['vgextend', '--force', '--yes', 'foo', '/dev/sda'] | |
717 | assert fake_run.calls[0]['args'][0] == expected | |
718 | ||
719 | def test_uses_single_device(self, monkeypatch, fake_run): | |
720 | monkeypatch.setattr(api, 'get_vg', lambda **kw: True) | |
721 | api.extend_vg(self.foo_volume, '/dev/sda') | |
722 | expected = ['vgextend', '--force', '--yes', 'foo', '/dev/sda'] | |
723 | assert fake_run.calls[0]['args'][0] == expected | |
724 | ||
725 | def test_uses_multiple_devices(self, monkeypatch, fake_run): | |
726 | monkeypatch.setattr(api, 'get_vg', lambda **kw: True) | |
727 | api.extend_vg(self.foo_volume, ['/dev/sda', '/dev/sdb']) | |
728 | expected = ['vgextend', '--force', '--yes', 'foo', '/dev/sda', '/dev/sdb'] | |
81eedcae TL |
729 | assert fake_run.calls[0]['args'][0] == expected |
730 | ||
731 | ||
732 | class TestReduceVG(object): | |
733 | ||
734 | def setup(self): | |
735 | self.foo_volume = api.VolumeGroup(vg_name='foo', lv_tags='') | |
736 | ||
737 | def test_uses_single_device_in_list(self, monkeypatch, fake_run): | |
738 | monkeypatch.setattr(api, 'get_vg', lambda **kw: True) | |
739 | api.reduce_vg(self.foo_volume, ['/dev/sda']) | |
740 | expected = ['vgreduce', '--force', '--yes', 'foo', '/dev/sda'] | |
741 | assert fake_run.calls[0]['args'][0] == expected | |
742 | ||
743 | def test_uses_single_device(self, monkeypatch, fake_run): | |
744 | monkeypatch.setattr(api, 'get_vg', lambda **kw: True) | |
745 | api.reduce_vg(self.foo_volume, '/dev/sda') | |
746 | expected = ['vgreduce', '--force', '--yes', 'foo', '/dev/sda'] | |
747 | assert fake_run.calls[0]['args'][0] == expected | |
748 | ||
749 | def test_uses_multiple_devices(self, monkeypatch, fake_run): | |
750 | monkeypatch.setattr(api, 'get_vg', lambda **kw: True) | |
751 | api.reduce_vg(self.foo_volume, ['/dev/sda', '/dev/sdb']) | |
752 | expected = ['vgreduce', '--force', '--yes', 'foo', '/dev/sda', '/dev/sdb'] | |
1adf2230 AA |
753 | assert fake_run.calls[0]['args'][0] == expected |
754 | ||
755 | ||
756 | class TestCreateVG(object): | |
757 | ||
758 | def setup(self): | |
759 | self.foo_volume = api.VolumeGroup(vg_name='foo', lv_tags='') | |
760 | ||
761 | def test_no_name(self, monkeypatch, fake_run): | |
762 | monkeypatch.setattr(api, 'get_vg', lambda **kw: True) | |
763 | api.create_vg('/dev/sda') | |
764 | result = fake_run.calls[0]['args'][0] | |
765 | assert '/dev/sda' in result | |
766 | assert result[-2].startswith('ceph-') | |
767 | ||
768 | def test_devices_list(self, monkeypatch, fake_run): | |
769 | monkeypatch.setattr(api, 'get_vg', lambda **kw: True) | |
770 | api.create_vg(['/dev/sda', '/dev/sdb'], name='ceph') | |
771 | result = fake_run.calls[0]['args'][0] | |
92f5a8d4 | 772 | expected = ['vgcreate', '--force', '--yes', 'ceph', '/dev/sda', '/dev/sdb'] |
1adf2230 AA |
773 | assert result == expected |
774 | ||
775 | def test_name_prefix(self, monkeypatch, fake_run): | |
776 | monkeypatch.setattr(api, 'get_vg', lambda **kw: True) | |
777 | api.create_vg('/dev/sda', name_prefix='master') | |
778 | result = fake_run.calls[0]['args'][0] | |
779 | assert '/dev/sda' in result | |
780 | assert result[-2].startswith('master-') | |
781 | ||
782 | def test_specific_name(self, monkeypatch, fake_run): | |
783 | monkeypatch.setattr(api, 'get_vg', lambda **kw: True) | |
784 | api.create_vg('/dev/sda', name='master') | |
785 | result = fake_run.calls[0]['args'][0] | |
786 | assert '/dev/sda' in result | |
787 | assert result[-2] == 'master' | |
94b18763 FG |
788 | |
789 | # | |
790 | # The following tests are pretty gnarly. VDO detection is very convoluted and | |
791 | # involves correlating information from device mappers, realpaths, slaves of | |
792 | # those mappers, and parents or related mappers. This makes it very hard to | |
793 | # patch nicely or keep tests short and readable. These tests are trying to | |
794 | # ensure correctness, the better approach will be to do some functional testing | |
795 | # with VDO. | |
796 | # | |
797 | ||
798 | ||
799 | @pytest.fixture | |
800 | def disable_kvdo_path(monkeypatch): | |
91327a77 | 801 | monkeypatch.setattr('os.path.isdir', lambda x, **kw: False) |
94b18763 FG |
802 | |
803 | ||
804 | @pytest.fixture | |
805 | def enable_kvdo_path(monkeypatch): | |
91327a77 | 806 | monkeypatch.setattr('os.path.isdir', lambda x, **kw: True) |
94b18763 FG |
807 | |
808 | ||
809 | # Stub for os.listdir | |
810 | ||
811 | ||
812 | class ListDir(object): | |
813 | ||
814 | def __init__(self, paths): | |
815 | self.paths = paths | |
816 | self._normalize_paths() | |
817 | self.listdir = os.listdir | |
818 | ||
819 | def _normalize_paths(self): | |
820 | for k, v in self.paths.items(): | |
821 | self.paths[k.rstrip('/')] = v.rstrip('/') | |
822 | ||
823 | def add(self, original, fake): | |
824 | self.paths[original.rstrip('/')] = fake.rstrip('/') | |
825 | ||
826 | def __call__(self, path): | |
827 | return self.listdir(self.paths[path.rstrip('/')]) | |
828 | ||
829 | ||
830 | @pytest.fixture(scope='function') | |
831 | def listdir(monkeypatch): | |
832 | def apply(paths=None, stub=None): | |
833 | if not stub: | |
834 | stub = ListDir(paths) | |
835 | if paths: | |
836 | for original, fake in paths.items(): | |
837 | stub.add(original, fake) | |
838 | ||
839 | monkeypatch.setattr('os.listdir', stub) | |
840 | return apply | |
841 | ||
842 | ||
843 | @pytest.fixture(scope='function') | |
844 | def makedirs(tmpdir): | |
845 | def create(directory): | |
846 | path = os.path.join(str(tmpdir), directory) | |
847 | os.makedirs(path) | |
848 | return path | |
849 | create.base = str(tmpdir) | |
850 | return create | |
851 | ||
852 | ||
853 | class TestIsVdo(object): | |
854 | ||
855 | def test_no_vdo_dir(self, disable_kvdo_path): | |
856 | assert api._is_vdo('/path') is False | |
857 | ||
858 | def test_exceptions_return_false(self, monkeypatch): | |
859 | def throw(): | |
860 | raise Exception() | |
861 | monkeypatch.setattr('ceph_volume.api.lvm._is_vdo', throw) | |
862 | assert api.is_vdo('/path') == '0' | |
863 | ||
864 | def test_is_vdo_returns_a_string(self, monkeypatch): | |
91327a77 | 865 | monkeypatch.setattr('ceph_volume.api.lvm._is_vdo', lambda x, **kw: True) |
94b18763 FG |
866 | assert api.is_vdo('/path') == '1' |
867 | ||
868 | def test_kvdo_dir_no_devices(self, makedirs, enable_kvdo_path, listdir, monkeypatch): | |
869 | kvdo_path = makedirs('sys/kvdo') | |
870 | listdir(paths={'/sys/kvdo': kvdo_path}) | |
91327a77 AA |
871 | monkeypatch.setattr('ceph_volume.api.lvm._vdo_slaves', lambda x, **kw: []) |
872 | monkeypatch.setattr('ceph_volume.api.lvm._vdo_parents', lambda x, **kw: []) | |
94b18763 FG |
873 | assert api._is_vdo('/dev/mapper/vdo0') is False |
874 | ||
875 | def test_vdo_slaves_found_and_matched(self, makedirs, enable_kvdo_path, listdir, monkeypatch): | |
876 | kvdo_path = makedirs('sys/kvdo') | |
877 | listdir(paths={'/sys/kvdo': kvdo_path}) | |
91327a77 AA |
878 | monkeypatch.setattr('ceph_volume.api.lvm._vdo_slaves', lambda x, **kw: ['/dev/dm-3']) |
879 | monkeypatch.setattr('ceph_volume.api.lvm._vdo_parents', lambda x, **kw: []) | |
94b18763 FG |
880 | assert api._is_vdo('/dev/dm-3') is True |
881 | ||
882 | def test_vdo_parents_found_and_matched(self, makedirs, enable_kvdo_path, listdir, monkeypatch): | |
883 | kvdo_path = makedirs('sys/kvdo') | |
884 | listdir(paths={'/sys/kvdo': kvdo_path}) | |
91327a77 AA |
885 | monkeypatch.setattr('ceph_volume.api.lvm._vdo_slaves', lambda x, **kw: []) |
886 | monkeypatch.setattr('ceph_volume.api.lvm._vdo_parents', lambda x, **kw: ['/dev/dm-4']) | |
94b18763 FG |
887 | assert api._is_vdo('/dev/dm-4') is True |
888 | ||
889 | ||
890 | class TestVdoSlaves(object): | |
891 | ||
892 | def test_slaves_are_not_found(self, makedirs, listdir, monkeypatch): | |
893 | slaves_path = makedirs('sys/block/vdo0/slaves') | |
894 | listdir(paths={'/sys/block/vdo0/slaves': slaves_path}) | |
91327a77 | 895 | monkeypatch.setattr('ceph_volume.api.lvm.os.path.exists', lambda x, **kw: True) |
94b18763 FG |
896 | result = sorted(api._vdo_slaves(['vdo0'])) |
897 | assert '/dev/mapper/vdo0' in result | |
898 | assert 'vdo0' in result | |
899 | ||
900 | def test_slaves_are_found(self, makedirs, listdir, monkeypatch): | |
901 | slaves_path = makedirs('sys/block/vdo0/slaves') | |
902 | makedirs('sys/block/vdo0/slaves/dm-4') | |
903 | makedirs('dev/mapper/vdo0') | |
904 | listdir(paths={'/sys/block/vdo0/slaves': slaves_path}) | |
91327a77 | 905 | monkeypatch.setattr('ceph_volume.api.lvm.os.path.exists', lambda x, **kw: True) |
94b18763 FG |
906 | result = sorted(api._vdo_slaves(['vdo0'])) |
907 | assert '/dev/dm-4' in result | |
908 | assert 'dm-4' in result | |
909 | ||
910 | ||
911 | class TestVDOParents(object): | |
912 | ||
913 | def test_parents_are_found(self, makedirs, listdir): | |
914 | block_path = makedirs('sys/block') | |
915 | slaves_path = makedirs('sys/block/dm-4/slaves') | |
916 | makedirs('sys/block/dm-4/slaves/dm-3') | |
917 | listdir(paths={ | |
918 | '/sys/block/dm-4/slaves': slaves_path, | |
919 | '/sys/block': block_path}) | |
920 | result = api._vdo_parents(['dm-3']) | |
921 | assert '/dev/dm-4' in result | |
922 | assert 'dm-4' in result | |
923 | ||
924 | def test_parents_are_not_found(self, makedirs, listdir): | |
925 | block_path = makedirs('sys/block') | |
926 | slaves_path = makedirs('sys/block/dm-4/slaves') | |
927 | makedirs('sys/block/dm-4/slaves/dm-5') | |
928 | listdir(paths={ | |
929 | '/sys/block/dm-4/slaves': slaves_path, | |
930 | '/sys/block': block_path}) | |
931 | result = api._vdo_parents(['dm-3']) | |
932 | assert result == [] | |
1adf2230 AA |
933 | |
934 | ||
935 | class TestSplitNameParser(object): | |
936 | ||
937 | def test_keys_are_parsed_without_prefix(self): | |
938 | line = ["DM_VG_NAME='/dev/mapper/vg';DM_LV_NAME='lv';DM_LV_LAYER=''"] | |
939 | result = api._splitname_parser(line) | |
940 | assert result['VG_NAME'] == 'vg' | |
941 | assert result['LV_NAME'] == 'lv' | |
942 | assert result['LV_LAYER'] == '' | |
943 | ||
944 | def test_vg_name_sans_mapper(self): | |
945 | line = ["DM_VG_NAME='/dev/mapper/vg';DM_LV_NAME='lv';DM_LV_LAYER=''"] | |
946 | result = api._splitname_parser(line) | |
947 | assert '/dev/mapper' not in result['VG_NAME'] | |
948 | ||
949 | ||
950 | class TestIsLV(object): | |
951 | ||
952 | def test_is_not_an_lv(self, monkeypatch): | |
eafe8130 | 953 | monkeypatch.setattr(api.process, 'call', lambda x, **kw: ('', '', 0)) |
91327a77 | 954 | monkeypatch.setattr(api, 'dmsetup_splitname', lambda x, **kw: {}) |
1adf2230 AA |
955 | assert api.is_lv('/dev/sda1', lvs=[]) is False |
956 | ||
957 | def test_lvs_not_found(self, monkeypatch, volumes): | |
958 | CephVolume = api.Volume(lv_name='foo', lv_path='/dev/vg/foo', lv_tags="ceph.type=data") | |
959 | volumes.append(CephVolume) | |
960 | splitname = {'LV_NAME': 'data', 'VG_NAME': 'ceph'} | |
91327a77 | 961 | monkeypatch.setattr(api, 'dmsetup_splitname', lambda x, **kw: splitname) |
1adf2230 AA |
962 | assert api.is_lv('/dev/sda1', lvs=volumes) is False |
963 | ||
964 | def test_is_lv(self, monkeypatch, volumes): | |
965 | CephVolume = api.Volume( | |
966 | vg_name='ceph', lv_name='data', | |
967 | lv_path='/dev/vg/foo', lv_tags="ceph.type=data" | |
968 | ) | |
969 | volumes.append(CephVolume) | |
970 | splitname = {'LV_NAME': 'data', 'VG_NAME': 'ceph'} | |
91327a77 | 971 | monkeypatch.setattr(api, 'dmsetup_splitname', lambda x, **kw: splitname) |
1adf2230 | 972 | assert api.is_lv('/dev/sda1', lvs=volumes) is True |
9f95a23c TL |
973 | |
974 | class TestGetDeviceVgs(object): | |
975 | ||
976 | @patch('ceph_volume.process.call') | |
977 | @patch('ceph_volume.api.lvm._output_parser') | |
978 | def test_get_device_vgs_with_empty_pv(self, patched_output_parser, pcall): | |
979 | patched_output_parser.return_value = [{'vg_name': ''}] | |
980 | pcall.return_value = ('', '', '') | |
981 | vgs = api.get_device_vgs('/dev/foo') | |
982 | assert vgs == [] | |
983 | ||
984 | class TestGetDeviceLvs(object): | |
985 | ||
986 | @patch('ceph_volume.process.call') | |
987 | @patch('ceph_volume.api.lvm._output_parser') | |
988 | def test_get_device_lvs_with_empty_vg(self, patched_output_parser, pcall): | |
989 | patched_output_parser.return_value = [{'lv_name': ''}] | |
990 | pcall.return_value = ('', '', '') | |
991 | vgs = api.get_device_lvs('/dev/foo') | |
992 | assert vgs == [] |