]>
Commit | Line | Data |
---|---|---|
494da23a | 1 | import pytest |
f6b5b4d7 | 2 | import json |
f91f0fd5 TL |
3 | import random |
4 | ||
5 | from argparse import ArgumentError | |
6 | from mock import MagicMock, patch | |
7 | ||
1e59de90 | 8 | from ceph_volume.api import lvm |
91327a77 | 9 | from ceph_volume.devices.lvm import batch |
f91f0fd5 | 10 | from ceph_volume.util import arg_validators |
91327a77 AA |
11 | |
12 | ||
11fdf7f2 | 13 | class TestBatch(object): |
f64942e4 AA |
14 | |
15 | def test_batch_instance(self, is_root): | |
16 | b = batch.Batch([]) | |
17 | b.main() | |
18 | ||
a4b75251 TL |
19 | def test_invalid_osd_ids_passed(self): |
20 | with pytest.raises(SystemExit): | |
21 | batch.Batch(argv=['--osd-ids', '1', 'foo']).main() | |
22 | ||
494da23a TL |
23 | def test_disjoint_device_lists(self, factory): |
24 | device1 = factory(used_by_ceph=False, available=True, abspath="/dev/sda") | |
25 | device2 = factory(used_by_ceph=False, available=True, abspath="/dev/sdb") | |
f91f0fd5 TL |
26 | devices = [device1, device2] |
27 | db_devices = [device2] | |
494da23a | 28 | with pytest.raises(Exception) as disjoint_ex: |
f91f0fd5 | 29 | batch.ensure_disjoint_device_lists(devices, db_devices) |
494da23a TL |
30 | assert 'Device lists are not disjoint' in str(disjoint_ex.value) |
31 | ||
f91f0fd5 TL |
32 | @patch('ceph_volume.util.arg_validators.Device') |
33 | def test_reject_partition(self, mocked_device): | |
34 | mocked_device.return_value = MagicMock( | |
35 | is_partition=True, | |
33c7a0ef TL |
36 | has_fs=False, |
37 | is_lvm_member=False, | |
f91f0fd5 | 38 | has_gpt_headers=False, |
1d09f67e | 39 | has_partitions=False, |
f91f0fd5 TL |
40 | ) |
41 | with pytest.raises(ArgumentError): | |
42 | arg_validators.ValidBatchDevice()('foo') | |
f64942e4 | 43 | |
f91f0fd5 TL |
44 | @pytest.mark.parametrize('format_', ['pretty', 'json', 'json-pretty']) |
45 | def test_report(self, format_, factory, conf_ceph_stub, mock_device_generator): | |
46 | # just ensure reporting works | |
47 | conf_ceph_stub('[global]\nfsid=asdf-lkjh') | |
48 | devs = [mock_device_generator() for _ in range(5)] | |
49 | args = factory(data_slots=1, | |
50 | osds_per_device=1, | |
51 | osd_ids=[], | |
52 | report=True, | |
53 | format=format_, | |
54 | devices=devs, | |
55 | db_devices=[], | |
56 | wal_devices=[], | |
57 | bluestore=True, | |
58 | block_db_size="1G", | |
59 | dmcrypt=True, | |
20effc67 | 60 | data_allocate_fraction=1.0, |
f91f0fd5 TL |
61 | ) |
62 | b = batch.Batch([]) | |
63 | plan = b.get_plan(args) | |
64 | b.args = args | |
65 | b.report(plan) | |
91327a77 | 66 | |
f91f0fd5 TL |
67 | @pytest.mark.parametrize('format_', ['json', 'json-pretty']) |
68 | def test_json_report_valid_empty(self, format_, factory, conf_ceph_stub, mock_device_generator): | |
69 | # ensure json reports are valid when empty | |
70 | conf_ceph_stub('[global]\nfsid=asdf-lkjh') | |
71 | devs = [] | |
72 | args = factory(data_slots=1, | |
73 | osds_per_device=1, | |
74 | osd_ids=[], | |
75 | report=True, | |
76 | format=format_, | |
77 | devices=devs, | |
78 | db_devices=[], | |
79 | wal_devices=[], | |
80 | bluestore=True, | |
81 | block_db_size="1G", | |
82 | dmcrypt=True, | |
20effc67 | 83 | data_allocate_fraction=1.0, |
f91f0fd5 TL |
84 | ) |
85 | b = batch.Batch([]) | |
86 | plan = b.get_plan(args) | |
87 | b.args = args | |
88 | report = b._create_report(plan) | |
89 | json.loads(report) | |
91327a77 | 90 | |
f91f0fd5 TL |
91 | @pytest.mark.parametrize('format_', ['json', 'json-pretty']) |
92 | def test_json_report_valid_empty_unavailable_fast(self, format_, factory, conf_ceph_stub, mock_device_generator): | |
93 | # ensure json reports are valid when empty | |
94 | conf_ceph_stub('[global]\nfsid=asdf-lkjh') | |
95 | devs = [mock_device_generator() for _ in range(5)] | |
96 | fast_devs = [mock_device_generator()] | |
97 | fast_devs[0].available_lvm = False | |
98 | args = factory(data_slots=1, | |
99 | osds_per_device=1, | |
100 | osd_ids=[], | |
101 | report=True, | |
102 | format=format_, | |
103 | devices=devs, | |
104 | db_devices=fast_devs, | |
105 | wal_devices=[], | |
106 | bluestore=True, | |
107 | block_db_size="1G", | |
108 | dmcrypt=True, | |
20effc67 | 109 | data_allocate_fraction=1.0, |
f91f0fd5 | 110 | ) |
92f5a8d4 | 111 | b = batch.Batch([]) |
f91f0fd5 | 112 | plan = b.get_plan(args) |
92f5a8d4 | 113 | b.args = args |
f91f0fd5 TL |
114 | report = b._create_report(plan) |
115 | json.loads(report) | |
116 | ||
117 | ||
118 | @pytest.mark.parametrize('format_', ['json', 'json-pretty']) | |
119 | def test_json_report_valid_empty_unavailable_very_fast(self, format_, factory, conf_ceph_stub, mock_device_generator): | |
120 | # ensure json reports are valid when empty | |
121 | conf_ceph_stub('[global]\nfsid=asdf-lkjh') | |
122 | devs = [mock_device_generator() for _ in range(5)] | |
123 | fast_devs = [mock_device_generator()] | |
124 | very_fast_devs = [mock_device_generator()] | |
125 | very_fast_devs[0].available_lvm = False | |
126 | args = factory(data_slots=1, | |
127 | osds_per_device=1, | |
128 | osd_ids=[], | |
129 | report=True, | |
130 | format=format_, | |
131 | devices=devs, | |
132 | db_devices=fast_devs, | |
133 | wal_devices=very_fast_devs, | |
134 | bluestore=True, | |
135 | block_db_size="1G", | |
136 | dmcrypt=True, | |
20effc67 | 137 | data_allocate_fraction=1.0, |
f91f0fd5 TL |
138 | ) |
139 | b = batch.Batch([]) | |
140 | plan = b.get_plan(args) | |
141 | b.args = args | |
142 | report = b._create_report(plan) | |
143 | json.loads(report) | |
144 | ||
145 | @pytest.mark.parametrize('rota', [0, 1]) | |
146 | def test_batch_sort_full(self, factory, rota): | |
147 | device1 = factory(used_by_ceph=False, available=True, rotational=rota, abspath="/dev/sda") | |
148 | device2 = factory(used_by_ceph=False, available=True, rotational=rota, abspath="/dev/sdb") | |
149 | device3 = factory(used_by_ceph=False, available=True, rotational=rota, abspath="/dev/sdc") | |
150 | devices = [device1, device2, device3] | |
151 | args = factory(report=True, | |
152 | devices=devices, | |
f91f0fd5 | 153 | ) |
f6b5b4d7 TL |
154 | b = batch.Batch([]) |
155 | b.args = args | |
f91f0fd5 TL |
156 | b._sort_rotational_disks() |
157 | assert len(b.args.devices) == 3 | |
158 | ||
05a536ef | 159 | @pytest.mark.parametrize('objectstore', ['bluestore']) |
f91f0fd5 TL |
160 | def test_batch_sort_mixed(self, factory, objectstore): |
161 | device1 = factory(used_by_ceph=False, available=True, rotational=1, abspath="/dev/sda") | |
162 | device2 = factory(used_by_ceph=False, available=True, rotational=1, abspath="/dev/sdb") | |
163 | device3 = factory(used_by_ceph=False, available=True, rotational=0, abspath="/dev/sdc") | |
164 | devices = [device1, device2, device3] | |
165 | args = factory(report=True, | |
166 | devices=devices, | |
f91f0fd5 TL |
167 | ) |
168 | b = batch.Batch([]) | |
169 | b.args = args | |
170 | b._sort_rotational_disks() | |
171 | assert len(b.args.devices) == 2 | |
05a536ef | 172 | assert len(b.args.db_devices) == 1 |
f91f0fd5 TL |
173 | |
174 | def test_get_physical_osds_return_len(self, factory, | |
175 | mock_devices_available, | |
176 | conf_ceph_stub, | |
177 | osds_per_device): | |
178 | conf_ceph_stub('[global]\nfsid=asdf-lkjh') | |
179 | args = factory(data_slots=1, osds_per_device=osds_per_device, | |
20effc67 TL |
180 | osd_ids=[], dmcrypt=False, |
181 | data_allocate_fraction=1.0) | |
f91f0fd5 TL |
182 | osds = batch.get_physical_osds(mock_devices_available, args) |
183 | assert len(osds) == len(mock_devices_available) * osds_per_device | |
184 | ||
185 | def test_get_physical_osds_rel_size(self, factory, | |
186 | mock_devices_available, | |
187 | conf_ceph_stub, | |
20effc67 TL |
188 | osds_per_device, |
189 | data_allocate_fraction): | |
f91f0fd5 | 190 | args = factory(data_slots=1, osds_per_device=osds_per_device, |
20effc67 TL |
191 | osd_ids=[], dmcrypt=False, |
192 | data_allocate_fraction=data_allocate_fraction) | |
f91f0fd5 TL |
193 | osds = batch.get_physical_osds(mock_devices_available, args) |
194 | for osd in osds: | |
20effc67 | 195 | assert osd.data[1] == data_allocate_fraction / osds_per_device |
f91f0fd5 TL |
196 | |
197 | def test_get_physical_osds_abs_size(self, factory, | |
198 | mock_devices_available, | |
199 | conf_ceph_stub, | |
20effc67 TL |
200 | osds_per_device, |
201 | data_allocate_fraction): | |
f91f0fd5 TL |
202 | conf_ceph_stub('[global]\nfsid=asdf-lkjh') |
203 | args = factory(data_slots=1, osds_per_device=osds_per_device, | |
20effc67 TL |
204 | osd_ids=[], dmcrypt=False, |
205 | data_allocate_fraction=data_allocate_fraction) | |
f91f0fd5 TL |
206 | osds = batch.get_physical_osds(mock_devices_available, args) |
207 | for osd, dev in zip(osds, mock_devices_available): | |
20effc67 | 208 | assert osd.data[2] == int(dev.vg_size[0] * (data_allocate_fraction / osds_per_device)) |
f91f0fd5 TL |
209 | |
210 | def test_get_physical_osds_osd_ids(self, factory, | |
211 | mock_devices_available, | |
212 | osds_per_device): | |
213 | pass | |
214 | ||
215 | def test_get_physical_fast_allocs_length(self, factory, | |
216 | conf_ceph_stub, | |
217 | mock_devices_available): | |
218 | conf_ceph_stub('[global]\nfsid=asdf-lkjh') | |
219 | args = factory(block_db_slots=None, get_block_db_size=None) | |
220 | fast = batch.get_physical_fast_allocs(mock_devices_available, | |
221 | 'block_db', 2, 2, args) | |
222 | assert len(fast) == 2 | |
223 | ||
2a845540 TL |
224 | def test_get_physical_fast_allocs_abs_size(self, factory, |
225 | conf_ceph_stub, | |
226 | mock_devices_available): | |
227 | conf_ceph_stub('[global]\nfsid=asdf-lkjh') | |
228 | args = factory(block_db_slots=None, get_block_db_size=None) | |
229 | fasts = batch.get_physical_fast_allocs(mock_devices_available, | |
230 | 'block_db', 2, 2, args) | |
231 | for fast, dev in zip(fasts, mock_devices_available): | |
232 | assert fast[2] == int(dev.vg_size[0] / 2) | |
233 | ||
1e59de90 TL |
234 | def test_get_physical_fast_allocs_abs_size_unused_devs(self, factory, |
235 | conf_ceph_stub, | |
236 | mock_devices_available): | |
237 | conf_ceph_stub('[global]\nfsid=asdf-lkjh') | |
238 | args = factory(block_db_slots=None, get_block_db_size=None) | |
239 | dev_size = 21474836480 | |
240 | vg_size = dev_size | |
241 | for dev in mock_devices_available: | |
242 | dev.vg_name = None | |
243 | dev.vg_size = [vg_size] | |
244 | dev.vg_free = dev.vg_size | |
245 | dev.vgs = [] | |
246 | slots_per_device = 2 | |
247 | fasts = batch.get_physical_fast_allocs(mock_devices_available, | |
248 | 'block_db', slots_per_device, 2, args) | |
249 | expected_slot_size = int(dev_size / slots_per_device) | |
250 | for (_, _, slot_size, _) in fasts: | |
251 | assert slot_size == expected_slot_size | |
252 | ||
253 | def test_get_physical_fast_allocs_abs_size_multi_pvs_per_vg(self, factory, | |
254 | conf_ceph_stub, | |
255 | mock_devices_available): | |
256 | conf_ceph_stub('[global]\nfsid=asdf-lkjh') | |
257 | args = factory(block_db_slots=None, get_block_db_size=None) | |
258 | dev_size = 21474836480 | |
259 | num_devices = len(mock_devices_available) | |
260 | vg_size = dev_size * num_devices | |
261 | vg_name = 'vg_foo' | |
262 | for dev in mock_devices_available: | |
263 | dev.vg_name = vg_name | |
264 | dev.vg_size = [vg_size] | |
265 | dev.vg_free = dev.vg_size | |
266 | dev.vgs = [lvm.VolumeGroup(vg_name=dev.vg_name, lv_name=dev.lv_name)] | |
267 | slots_per_device = 2 | |
268 | slots_per_vg = slots_per_device * num_devices | |
269 | fasts = batch.get_physical_fast_allocs(mock_devices_available, | |
270 | 'block_db', slots_per_device, 2, args) | |
271 | expected_slot_size = int(vg_size / slots_per_vg) | |
272 | for (_, _, slot_size, _) in fasts: | |
273 | assert slot_size == expected_slot_size | |
274 | ||
522d829b TL |
275 | def test_batch_fast_allocations_one_block_db_length(self, factory, conf_ceph_stub, |
276 | mock_lv_device_generator): | |
277 | conf_ceph_stub('[global]\nfsid=asdf-lkjh') | |
278 | ||
279 | b = batch.Batch([]) | |
280 | db_lv_devices = [mock_lv_device_generator()] | |
281 | fast = b.fast_allocations(db_lv_devices, 1, 0, 'block_db') | |
282 | assert len(fast) == 1 | |
283 | ||
f91f0fd5 TL |
284 | @pytest.mark.parametrize('occupied_prior', range(7)) |
285 | @pytest.mark.parametrize('slots,num_devs', | |
286 | [l for sub in [list(zip([x]*x, range(1, x + 1))) for x in range(1,7)] for l in sub]) | |
287 | def test_get_physical_fast_allocs_length_existing(self, | |
288 | num_devs, | |
289 | slots, | |
290 | occupied_prior, | |
291 | factory, | |
292 | conf_ceph_stub, | |
293 | mock_device_generator): | |
294 | conf_ceph_stub('[global]\nfsid=asdf-lkjh') | |
295 | occupied_prior = min(occupied_prior, slots) | |
296 | devs = [mock_device_generator() for _ in range(num_devs)] | |
297 | already_assigned = 0 | |
298 | while already_assigned < occupied_prior: | |
299 | dev_i = random.randint(0, num_devs - 1) | |
300 | dev = devs[dev_i] | |
301 | if len(dev.lvs) < occupied_prior: | |
302 | dev.lvs.append('foo') | |
303 | dev.path = '/dev/bar' | |
304 | already_assigned = sum([len(d.lvs) for d in devs]) | |
305 | args = factory(block_db_slots=None, get_block_db_size=None) | |
306 | expected_num_osds = max(len(devs) * slots - occupied_prior, 0) | |
307 | fast = batch.get_physical_fast_allocs(devs, | |
308 | 'block_db', slots, | |
309 | expected_num_osds, args) | |
310 | assert len(fast) == expected_num_osds | |
311 | expected_assignment_on_used_devices = sum([slots - len(d.lvs) for d in devs if len(d.lvs) > 0]) | |
312 | assert len([f for f in fast if f[0] == '/dev/bar']) == expected_assignment_on_used_devices | |
313 | assert len([f for f in fast if f[0] != '/dev/bar']) == expected_num_osds - expected_assignment_on_used_devices | |
314 | ||
315 | def test_get_lvm_osds_return_len(self, factory, | |
316 | mock_lv_device_generator, | |
317 | conf_ceph_stub, | |
318 | osds_per_device): | |
319 | conf_ceph_stub('[global]\nfsid=asdf-lkjh') | |
320 | args = factory(data_slots=1, osds_per_device=osds_per_device, | |
321 | osd_ids=[], dmcrypt=False) | |
322 | mock_lvs = [mock_lv_device_generator()] | |
323 | osds = batch.get_lvm_osds(mock_lvs, args) | |
324 | assert len(osds) == 1 | |
325 | ||
326 | ||
327 | class TestBatchOsd(object): | |
328 | ||
329 | def test_osd_class_ctor(self): | |
330 | osd = batch.Batch.OSD('/dev/data', 1, '5G', 1, 1, None) | |
331 | assert osd.data == batch.Batch.OSD.VolSpec('/dev/data', | |
332 | 1, | |
333 | '5G', | |
334 | 1, | |
335 | 'data') | |
336 | def test_add_fast(self): | |
337 | osd = batch.Batch.OSD('/dev/data', 1, '5G', 1, 1, None) | |
338 | osd.add_fast_device('/dev/db', 1, '5G', 1, 'block_db') | |
339 | assert osd.fast == batch.Batch.OSD.VolSpec('/dev/db', | |
340 | 1, | |
341 | '5G', | |
342 | 1, | |
343 | 'block_db') | |
344 | ||
345 | def test_add_very_fast(self): | |
346 | osd = batch.Batch.OSD('/dev/data', 1, '5G', 1, 1, None) | |
347 | osd.add_very_fast_device('/dev/wal', 1, '5G', 1) | |
348 | assert osd.very_fast == batch.Batch.OSD.VolSpec('/dev/wal', | |
349 | 1, | |
350 | '5G', | |
351 | 1, | |
352 | 'block_wal') |