]> git.proxmox.com Git - ceph.git/blob - ceph/src/python-common/ceph/tests/test_drive_group.py
77e9b4083d495d44e0582878b092d97946c7c154
[ceph.git] / ceph / src / python-common / ceph / tests / test_drive_group.py
1 # flake8: noqa
2 import re
3
4 import pytest
5 import yaml
6
7 from ceph.deployment import drive_selection, translate
8 from ceph.deployment.hostspec import HostSpec, SpecValidationError
9 from ceph.deployment.inventory import Device
10 from ceph.deployment.service_spec import PlacementSpec
11 from ceph.tests.utils import _mk_inventory, _mk_device
12 from ceph.deployment.drive_group import DriveGroupSpec, DeviceSelection, \
13 DriveGroupValidationError
14
15 @pytest.mark.parametrize("test_input",
16 [
17 ( # new style json
18 """service_type: osd
19 service_id: testing_drivegroup
20 placement:
21 host_pattern: hostname
22 crush_device_class: ssd
23 data_devices:
24 paths:
25 - /dev/sda
26 """
27 ),
28 (
29 """service_type: osd
30 service_id: testing_drivegroup
31 placement:
32 host_pattern: hostname
33 data_devices:
34 paths:
35 - path: /dev/sda
36 crush_device_class: ssd"""
37 ),
38 (
39 """service_type: osd
40 service_id: testing_drivegroup
41 placement:
42 host_pattern: hostname
43 spec:
44 osds_per_device: 2
45 data_devices:
46 paths:
47 - path: /dev/sda
48 crush_device_class: hdd"""
49 ),
50 ])
51 def test_DriveGroup(test_input):
52
53 dg = DriveGroupSpec.from_json(yaml.safe_load(test_input))
54 assert dg.service_id == 'testing_drivegroup'
55 assert all([isinstance(x, Device) for x in dg.data_devices.paths])
56 if isinstance(dg.data_devices.paths[0].path, str):
57 assert dg.data_devices.paths[0].path == '/dev/sda'
58
59
60
61 @pytest.mark.parametrize("match,test_input",
62 [
63 (
64 re.escape('Service Spec is not an (JSON or YAML) object. got "None"'),
65 ''
66 ),
67 (
68 'Failed to validate OSD spec "<unnamed>": `placement` required',
69 """data_devices:
70 all: True
71 """
72 ),
73 (
74 'Failed to validate OSD spec "mydg.data_devices": device selection cannot be empty', """
75 service_type: osd
76 service_id: mydg
77 placement:
78 host_pattern: '*'
79 data_devices:
80 limit: 1
81 """
82 ),
83 (
84 'Failed to validate OSD spec "mydg": filter_logic must be either <AND> or <OR>', """
85 service_type: osd
86 service_id: mydg
87 placement:
88 host_pattern: '*'
89 data_devices:
90 all: True
91 filter_logic: XOR
92 """
93 ),
94 (
95 'Failed to validate OSD spec "mydg": `data_devices` element is required.', """
96 service_type: osd
97 service_id: mydg
98 placement:
99 host_pattern: '*'
100 spec:
101 db_devices:
102 model: model
103 """
104 ),
105 (
106 'Failed to validate OSD spec "mydg.db_devices": Filtering for `unknown_key` is not supported', """
107 service_type: osd
108 service_id: mydg
109 placement:
110 host_pattern: '*'
111 spec:
112 db_devices:
113 unknown_key: 1
114 """
115 ),
116 (
117 'Failed to validate OSD spec "mydg": Feature `unknown_key` is not supported', """
118 service_type: osd
119 service_id: mydg
120 placement:
121 host_pattern: '*'
122 spec:
123 db_devices:
124 all: true
125 unknown_key: 1
126 """
127 ),
128 ])
129 def test_DriveGroup_fail(match, test_input):
130 with pytest.raises(SpecValidationError, match=match):
131 osd_spec = DriveGroupSpec.from_json(yaml.safe_load(test_input))
132 osd_spec.validate()
133
134
135 def test_drivegroup_pattern():
136 dg = DriveGroupSpec(
137 PlacementSpec(host_pattern='node[1-3]'),
138 service_id='foobar',
139 data_devices=DeviceSelection(all=True))
140 assert dg.placement.filter_matching_hostspecs([HostSpec('node{}'.format(i)) for i in range(10)]) == ['node1', 'node2', 'node3']
141
142
143 def test_drive_selection():
144 devs = DeviceSelection(paths=['/dev/sda'])
145 spec = DriveGroupSpec(
146 PlacementSpec('node_name'),
147 service_id='foobar',
148 data_devices=devs)
149 assert all([isinstance(x, Device) for x in spec.data_devices.paths])
150 assert spec.data_devices.paths[0].path == '/dev/sda'
151
152 with pytest.raises(DriveGroupValidationError, match='exclusive'):
153 ds = DeviceSelection(paths=['/dev/sda'], rotational=False)
154 ds.validate('')
155
156
157 def test_ceph_volume_command_0():
158 spec = DriveGroupSpec(placement=PlacementSpec(host_pattern='*'),
159 service_id='foobar',
160 data_devices=DeviceSelection(all=True)
161 )
162 spec.validate()
163 inventory = _mk_inventory(_mk_device()*2)
164 sel = drive_selection.DriveSelection(spec, inventory)
165 cmds = translate.to_ceph_volume(sel, []).run()
166 assert all(cmd == 'lvm batch --no-auto /dev/sda /dev/sdb --yes --no-systemd' for cmd in cmds), f'Expected {cmd} in {cmds}'
167
168
169 def test_ceph_volume_command_1():
170 spec = DriveGroupSpec(placement=PlacementSpec(host_pattern='*'),
171 service_id='foobar',
172 data_devices=DeviceSelection(rotational=True),
173 db_devices=DeviceSelection(rotational=False)
174 )
175 spec.validate()
176 inventory = _mk_inventory(_mk_device(rotational=True)*2 + _mk_device(rotational=False)*2)
177 sel = drive_selection.DriveSelection(spec, inventory)
178 cmds = translate.to_ceph_volume(sel, []).run()
179 assert all(cmd == ('lvm batch --no-auto /dev/sda /dev/sdb '
180 '--db-devices /dev/sdc /dev/sdd --yes --no-systemd') for cmd in cmds), f'Expected {cmd} in {cmds}'
181
182
183 def test_ceph_volume_command_2():
184 spec = DriveGroupSpec(placement=PlacementSpec(host_pattern='*'),
185 service_id='foobar',
186 data_devices=DeviceSelection(size='200GB:350GB', rotational=True),
187 db_devices=DeviceSelection(size='200GB:350GB', rotational=False),
188 wal_devices=DeviceSelection(size='10G')
189 )
190 spec.validate()
191 inventory = _mk_inventory(_mk_device(rotational=True, size="300.00 GB")*2 +
192 _mk_device(rotational=False, size="300.00 GB")*2 +
193 _mk_device(size="10.0 GB", rotational=False)*2
194 )
195 sel = drive_selection.DriveSelection(spec, inventory)
196 cmds = translate.to_ceph_volume(sel, []).run()
197 assert all(cmd == ('lvm batch --no-auto /dev/sda /dev/sdb '
198 '--db-devices /dev/sdc /dev/sdd --wal-devices /dev/sde /dev/sdf '
199 '--yes --no-systemd') for cmd in cmds), f'Expected {cmd} in {cmds}'
200
201
202 def test_ceph_volume_command_3():
203 spec = DriveGroupSpec(placement=PlacementSpec(host_pattern='*'),
204 service_id='foobar',
205 data_devices=DeviceSelection(size='200GB:350GB', rotational=True),
206 db_devices=DeviceSelection(size='200GB:350GB', rotational=False),
207 wal_devices=DeviceSelection(size='10G'),
208 encrypted=True
209 )
210 spec.validate()
211 inventory = _mk_inventory(_mk_device(rotational=True, size="300.00 GB")*2 +
212 _mk_device(rotational=False, size="300.00 GB")*2 +
213 _mk_device(size="10.0 GB", rotational=False)*2
214 )
215 sel = drive_selection.DriveSelection(spec, inventory)
216 cmds = translate.to_ceph_volume(sel, []).run()
217 assert all(cmd == ('lvm batch --no-auto /dev/sda /dev/sdb '
218 '--db-devices /dev/sdc /dev/sdd '
219 '--wal-devices /dev/sde /dev/sdf --dmcrypt '
220 '--yes --no-systemd') for cmd in cmds), f'Expected {cmd} in {cmds}'
221
222
223 def test_ceph_volume_command_4():
224 spec = DriveGroupSpec(placement=PlacementSpec(host_pattern='*'),
225 service_id='foobar',
226 data_devices=DeviceSelection(size='200GB:350GB', rotational=True),
227 db_devices=DeviceSelection(size='200GB:350GB', rotational=False),
228 wal_devices=DeviceSelection(size='10G'),
229 block_db_size='500M',
230 block_wal_size='500M',
231 osds_per_device=3,
232 encrypted=True
233 )
234 spec.validate()
235 inventory = _mk_inventory(_mk_device(rotational=True, size="300.00 GB")*2 +
236 _mk_device(rotational=False, size="300.00 GB")*2 +
237 _mk_device(size="10.0 GB", rotational=False)*2
238 )
239 sel = drive_selection.DriveSelection(spec, inventory)
240 cmds = translate.to_ceph_volume(sel, []).run()
241 assert all(cmd == ('lvm batch --no-auto /dev/sda /dev/sdb '
242 '--db-devices /dev/sdc /dev/sdd --wal-devices /dev/sde /dev/sdf '
243 '--block-wal-size 500M --block-db-size 500M --dmcrypt '
244 '--osds-per-device 3 --yes --no-systemd') for cmd in cmds), f'Expected {cmd} in {cmds}'
245
246
247 def test_ceph_volume_command_5():
248 spec = DriveGroupSpec(placement=PlacementSpec(host_pattern='*'),
249 service_id='foobar',
250 data_devices=DeviceSelection(rotational=True),
251 objectstore='filestore'
252 )
253 with pytest.raises(DriveGroupValidationError):
254 spec.validate()
255 inventory = _mk_inventory(_mk_device(rotational=True)*2)
256 sel = drive_selection.DriveSelection(spec, inventory)
257 cmds = translate.to_ceph_volume(sel, []).run()
258 assert all(cmd == 'lvm batch --no-auto /dev/sda /dev/sdb --filestore --yes --no-systemd' for cmd in cmds), f'Expected {cmd} in {cmds}'
259
260
261 def test_ceph_volume_command_6():
262 spec = DriveGroupSpec(placement=PlacementSpec(host_pattern='*'),
263 service_id='foobar',
264 data_devices=DeviceSelection(rotational=False),
265 journal_devices=DeviceSelection(rotational=True),
266 journal_size='500M',
267 objectstore='filestore'
268 )
269 with pytest.raises(DriveGroupValidationError):
270 spec.validate()
271 inventory = _mk_inventory(_mk_device(rotational=True)*2 + _mk_device(rotational=False)*2)
272 sel = drive_selection.DriveSelection(spec, inventory)
273 cmds = translate.to_ceph_volume(sel, []).run()
274 assert all(cmd == ('lvm batch --no-auto /dev/sdc /dev/sdd '
275 '--journal-size 500M --journal-devices /dev/sda /dev/sdb '
276 '--filestore --yes --no-systemd') for cmd in cmds), f'Expected {cmd} in {cmds}'
277
278
279 def test_ceph_volume_command_7():
280 spec = DriveGroupSpec(placement=PlacementSpec(host_pattern='*'),
281 service_id='foobar',
282 data_devices=DeviceSelection(all=True),
283 osd_id_claims={'host1': ['0', '1']}
284 )
285 spec.validate()
286 inventory = _mk_inventory(_mk_device(rotational=True)*2)
287 sel = drive_selection.DriveSelection(spec, inventory)
288 cmds = translate.to_ceph_volume(sel, ['0', '1']).run()
289 assert all(cmd == 'lvm batch --no-auto /dev/sda /dev/sdb --osd-ids 0 1 --yes --no-systemd' for cmd in cmds), f'Expected {cmd} in {cmds}'
290
291
292 def test_ceph_volume_command_8():
293 spec = DriveGroupSpec(placement=PlacementSpec(host_pattern='*'),
294 service_id='foobar',
295 data_devices=DeviceSelection(rotational=True, model='INTEL SSDS'),
296 db_devices=DeviceSelection(model='INTEL SSDP'),
297 filter_logic='OR',
298 osd_id_claims={}
299 )
300 spec.validate()
301 inventory = _mk_inventory(_mk_device(rotational=True, size='1.82 TB', model='ST2000DM001-1ER1') + # data
302 _mk_device(rotational=False, size="223.0 GB", model='INTEL SSDSC2KG24') + # data
303 _mk_device(rotational=False, size="349.0 GB", model='INTEL SSDPED1K375GA') # wal/db
304 )
305 sel = drive_selection.DriveSelection(spec, inventory)
306 cmds = translate.to_ceph_volume(sel, []).run()
307 assert all(cmd == 'lvm batch --no-auto /dev/sda /dev/sdb --db-devices /dev/sdc --yes --no-systemd' for cmd in cmds), f'Expected {cmd} in {cmds}'
308
309
310 def test_ceph_volume_command_9():
311 spec = DriveGroupSpec(placement=PlacementSpec(host_pattern='*'),
312 service_id='foobar',
313 data_devices=DeviceSelection(all=True),
314 data_allocate_fraction=0.8
315 )
316 spec.validate()
317 inventory = _mk_inventory(_mk_device()*2)
318 sel = drive_selection.DriveSelection(spec, inventory)
319 cmds = translate.to_ceph_volume(sel, []).run()
320 assert all(cmd == 'lvm batch --no-auto /dev/sda /dev/sdb --data-allocate-fraction 0.8 --yes --no-systemd' for cmd in cmds), f'Expected {cmd} in {cmds}'
321
322
323 @pytest.mark.parametrize("test_input_base",
324 [
325 (
326 """service_type: osd
327 service_id: testing_drivegroup
328 placement:
329 host_pattern: hostname
330 crush_device_class: ssd
331 data_devices:
332 paths:
333 - /dev/sda
334 """
335 ),
336 ])
337 def test_ceph_volume_command_10(test_input_base):
338 spec = DriveGroupSpec.from_json(yaml.safe_load(test_input_base))
339 spec.validate()
340 drive = drive_selection.DriveSelection(spec, spec.data_devices.paths)
341 cmds = translate.to_ceph_volume(drive, []).run()
342
343 assert all(cmd == 'lvm batch --no-auto /dev/sda --crush-device-class ssd --yes --no-systemd' for cmd in cmds), f'Expected {cmd} in {cmds}'
344
345
346 @pytest.mark.parametrize("test_input1",
347 [
348 (
349 """service_type: osd
350 service_id: testing_drivegroup
351 placement:
352 host_pattern: hostname
353 crush_device_class: ssd
354 data_devices:
355 paths:
356 - path: /dev/sda
357 crush_device_class: hdd
358 - path: /dev/sdb
359 crush_device_class: hdd
360 """
361 ),
362 ])
363 def test_ceph_volume_command_11(test_input1):
364 spec = DriveGroupSpec.from_json(yaml.safe_load(test_input1))
365 spec.validate()
366 drive = drive_selection.DriveSelection(spec, spec.data_devices.paths)
367 cmds = translate.to_ceph_volume(drive, []).run()
368
369 assert all(cmd == 'lvm batch --no-auto /dev/sda /dev/sdb --crush-device-class hdd --yes --no-systemd' for cmd in cmds), f'Expected {cmd} in {cmds}'
370
371
372 @pytest.mark.parametrize("test_input2",
373 [
374 (
375 """service_type: osd
376 service_id: testing_drivegroup
377 placement:
378 host_pattern: hostname
379 crush_device_class: ssd
380 data_devices:
381 paths:
382 - path: /dev/sda
383 crush_device_class: hdd
384 - path: /dev/sdb
385 """
386 ),
387 ])
388 def test_ceph_volume_command_12(test_input2):
389
390 spec = DriveGroupSpec.from_json(yaml.safe_load(test_input2))
391 spec.validate()
392 drive = drive_selection.DriveSelection(spec, spec.data_devices.paths)
393 cmds = translate.to_ceph_volume(drive, []).run()
394
395 assert (cmds[0] == 'lvm batch --no-auto /dev/sdb --crush-device-class ssd --yes --no-systemd') # noqa E501
396 assert (cmds[1] == 'lvm batch --no-auto /dev/sda --crush-device-class hdd --yes --no-systemd') # noqa E501
397
398
399 @pytest.mark.parametrize("test_input3",
400 [
401 (
402 """service_type: osd
403 service_id: testing_drivegroup
404 placement:
405 host_pattern: hostname
406 data_devices:
407 paths:
408 - path: /dev/sda
409 crush_device_class: hdd
410 - path: /dev/sdb
411 """
412 ),
413 ])
414 def test_ceph_volume_command_13(test_input3):
415
416 spec = DriveGroupSpec.from_json(yaml.safe_load(test_input3))
417 spec.validate()
418 drive = drive_selection.DriveSelection(spec, spec.data_devices.paths)
419 cmds = translate.to_ceph_volume(drive, []).run()
420
421 assert (cmds[0] == 'lvm batch --no-auto /dev/sdb --yes --no-systemd') # noqa E501
422 assert (cmds[1] == 'lvm batch --no-auto /dev/sda --crush-device-class hdd --yes --no-systemd') # noqa E501
423
424
425 @pytest.mark.parametrize("test_input4",
426 [
427 (
428 """service_type: osd
429 service_id: testing_drivegroup
430 placement:
431 host_pattern: hostname
432 data_devices:
433 paths:
434 - crush_device_class: hdd
435 """
436 ),
437 ])
438 def test_ceph_volume_command_14(test_input4):
439
440 with pytest.raises(DriveGroupValidationError, match='Device path'):
441 spec = DriveGroupSpec.from_json(yaml.safe_load(test_input4))
442 spec.validate()
443
444
445 def test_raw_ceph_volume_command_0():
446 spec = DriveGroupSpec(placement=PlacementSpec(host_pattern='*'),
447 service_id='foobar',
448 data_devices=DeviceSelection(rotational=True),
449 db_devices=DeviceSelection(rotational=False),
450 method='raw',
451 )
452 spec.validate()
453 inventory = _mk_inventory(_mk_device(rotational=True) + # data
454 _mk_device(rotational=True) + # data
455 _mk_device(rotational=False) + # db
456 _mk_device(rotational=False) # db
457 )
458 exp_cmds = ['raw prepare --bluestore --data /dev/sda --block.db /dev/sdc', 'raw prepare --bluestore --data /dev/sdb --block.db /dev/sdd']
459 sel = drive_selection.DriveSelection(spec, inventory)
460 cmds = translate.to_ceph_volume(sel, []).run()
461 assert all(cmd in exp_cmds for cmd in cmds), f'Expected {exp_cmds} to match {cmds}'
462
463 def test_raw_ceph_volume_command_1():
464 spec = DriveGroupSpec(placement=PlacementSpec(host_pattern='*'),
465 service_id='foobar',
466 data_devices=DeviceSelection(rotational=True),
467 db_devices=DeviceSelection(rotational=False),
468 method='raw',
469 )
470 spec.validate()
471 inventory = _mk_inventory(_mk_device(rotational=True) + # data
472 _mk_device(rotational=True) + # data
473 _mk_device(rotational=False) # db
474 )
475 sel = drive_selection.DriveSelection(spec, inventory)
476 with pytest.raises(ValueError):
477 cmds = translate.to_ceph_volume(sel, []).run()
478
479 @pytest.mark.parametrize("test_input5",
480 [
481 (
482 """service_type: osd
483 service_id: testing_drivegroup
484 placement:
485 host_pattern: hostname
486 method: raw
487 data_devices:
488 paths:
489 - path: /dev/sda
490 crush_device_class: hdd
491 - path: /dev/sdb
492 crush_device_class: hdd
493 - path: /dev/sdc
494 crush_device_class: hdd
495 db_devices:
496 paths:
497 - /dev/sdd
498 - /dev/sde
499 - /dev/sdf
500
501 """
502 ),
503 ])
504 def test_raw_ceph_volume_command_2(test_input5):
505
506 spec = DriveGroupSpec.from_json(yaml.safe_load(test_input5))
507 spec.validate()
508 drive = drive_selection.DriveSelection(spec, spec.data_devices.paths)
509 cmds = translate.to_ceph_volume(drive, []).run()
510
511 assert cmds[0] == 'raw prepare --bluestore --data /dev/sda --block.db /dev/sdd --crush-device-class hdd'
512 assert cmds[1] == 'raw prepare --bluestore --data /dev/sdb --block.db /dev/sde --crush-device-class hdd'
513 assert cmds[2] == 'raw prepare --bluestore --data /dev/sdc --block.db /dev/sdf --crush-device-class hdd'
514
515
516 @pytest.mark.parametrize("test_input6",
517 [
518 (
519 """service_type: osd
520 service_id: testing_drivegroup
521 placement:
522 host_pattern: hostname
523 method: raw
524 data_devices:
525 paths:
526 - path: /dev/sda
527 crush_device_class: hdd
528 - path: /dev/sdb
529 crush_device_class: hdd
530 - path: /dev/sdc
531 crush_device_class: ssd
532 db_devices:
533 paths:
534 - /dev/sdd
535 - /dev/sde
536 - /dev/sdf
537
538 """
539 ),
540 ])
541 def test_raw_ceph_volume_command_3(test_input6):
542
543 spec = DriveGroupSpec.from_json(yaml.safe_load(test_input6))
544 spec.validate()
545 drive = drive_selection.DriveSelection(spec, spec.data_devices.paths)
546 cmds = translate.to_ceph_volume(drive, []).run()
547
548 assert cmds[0] == 'raw prepare --bluestore --data /dev/sda --block.db /dev/sdd --crush-device-class hdd'
549 assert cmds[1] == 'raw prepare --bluestore --data /dev/sdb --block.db /dev/sde --crush-device-class hdd'
550 assert cmds[2] == 'raw prepare --bluestore --data /dev/sdc --block.db /dev/sdf --crush-device-class ssd'
551
552
553 @pytest.mark.parametrize("test_input7",
554 [
555 (
556 """service_type: osd
557 service_id: testing_drivegroup
558 placement:
559 host_pattern: hostname
560 method: raw
561 data_devices:
562 paths:
563 - path: /dev/sda
564 crush_device_class: hdd
565 - path: /dev/sdb
566 crush_device_class: nvme
567 - path: /dev/sdc
568 crush_device_class: ssd
569 db_devices:
570 paths:
571 - /dev/sdd
572 - /dev/sde
573 - /dev/sdf
574 wal_devices:
575 paths:
576 - /dev/sdg
577 - /dev/sdh
578 - /dev/sdi
579
580 """
581 ),
582 ])
583 def test_raw_ceph_volume_command_4(test_input7):
584
585 spec = DriveGroupSpec.from_json(yaml.safe_load(test_input7))
586 spec.validate()
587 drive = drive_selection.DriveSelection(spec, spec.data_devices.paths)
588 cmds = translate.to_ceph_volume(drive, []).run()
589
590 assert cmds[0] == 'raw prepare --bluestore --data /dev/sda --block.db /dev/sdd --block.wal /dev/sdg --crush-device-class hdd'
591 assert cmds[1] == 'raw prepare --bluestore --data /dev/sdb --block.db /dev/sdf --block.wal /dev/sdi --crush-device-class nvme'
592 assert cmds[2] == 'raw prepare --bluestore --data /dev/sdc --block.db /dev/sde --block.wal /dev/sdh --crush-device-class ssd'