]> git.proxmox.com Git - ceph.git/blame - ceph/src/ceph-volume/ceph_volume/tests/api/test_lvm.py
import 15.2.4
[ceph.git] / ceph / src / ceph-volume / ceph_volume / tests / api / test_lvm.py
CommitLineData
94b18763 1import os
d2e6a577 2import pytest
92f5a8d4 3from mock.mock import patch
d2e6a577 4from ceph_volume import process, exceptions
3efd9988 5from ceph_volume.api import lvm as api
d2e6a577
FG
6
7
8class 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
34class 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
61class 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
82def 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
93def 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
100class 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
121class 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
182class 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
203class 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
216class 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
238class 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
335class 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
342class 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
396class 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
403class 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
441class 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
494class 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
519class 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
541class 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
629class 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
708class 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
732class 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
756class 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
800def disable_kvdo_path(monkeypatch):
91327a77 801 monkeypatch.setattr('os.path.isdir', lambda x, **kw: False)
94b18763
FG
802
803
804@pytest.fixture
805def 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
812class 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')
831def 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')
844def 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
853class 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
890class 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
911class 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
935class 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
950class 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
974class 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
984class 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 == []