1
from unittest import TestCase
2
from mock import call, patch
3
from curtin.block import dev_short
4
from curtin.block import mdadm
9
class MdadmTestBase(TestCase):
11
super(MdadmTestBase, self).setUp()
13
def add_patch(self, target, attr):
14
"""Patches specified target object and sets it as attr on test
15
instance also schedules cleanup"""
16
m = patch(target, autospec=True)
18
self.addCleanup(m.stop)
19
setattr(self, attr, p)
22
class TestBlockMdadmAssemble(MdadmTestBase):
24
super(TestBlockMdadmAssemble, self).setUp()
25
self.add_patch('curtin.block.mdadm.util', 'mock_util')
26
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
28
# Common mock settings
29
self.mock_valid.return_value = True
30
self.mock_util.lsb_release.return_value = {'codename': 'precise'}
31
self.mock_util.subp.side_effect = [
32
("", ""), # mdadm assemble
33
("", ""), # udevadm settle
36
def test_mdadm_assemble_scan(self):
37
mdadm.mdadm_assemble(scan=True)
39
call(["mdadm", "--assemble", "--scan"], capture=True,
41
call(["udevadm", "settle"]),
43
self.mock_util.subp.assert_has_calls(expected_calls)
45
def test_mdadm_assemble_md_devname(self):
46
md_devname = "/dev/md0"
47
mdadm.mdadm_assemble(md_devname=md_devname)
50
call(["mdadm", "--assemble", md_devname, "--run"], capture=True,
52
call(["udevadm", "settle"]),
54
self.mock_util.subp.assert_has_calls(expected_calls)
56
def test_mdadm_assemble_md_devname_short(self):
57
with self.assertRaises(ValueError):
59
mdadm.mdadm_assemble(md_devname=md_devname)
61
def test_mdadm_assemble_md_devname_none(self):
62
with self.assertRaises(ValueError):
64
mdadm.mdadm_assemble(md_devname=md_devname)
66
def test_mdadm_assemble_md_devname_devices(self):
67
md_devname = "/dev/md0"
68
devices = ["/dev/vdc1", "/dev/vdd1"]
69
mdadm.mdadm_assemble(md_devname=md_devname, devices=devices)
71
call(["mdadm", "--assemble", md_devname, "--run"] + devices,
72
capture=True, rcs=[0, 1, 2]),
73
call(["udevadm", "settle"]),
75
self.mock_util.subp.assert_has_calls(expected_calls)
78
class TestBlockMdadmCreate(MdadmTestBase):
80
super(TestBlockMdadmCreate, self).setUp()
81
self.add_patch('curtin.block.mdadm.util', 'mock_util')
82
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
84
# Common mock settings
85
self.mock_valid.return_value = True
86
self.mock_util.lsb_release.return_value = {'codename': 'precise'}
88
def prepare_mock(self, md_devname, raidlevel, devices, spares):
92
# don't mock anything if raidlevel and spares mismatch
93
if spares and raidlevel not in mdadm.SPARE_RAID_LEVELS:
94
return (side_effects, expected_calls)
96
# prepare side-effects
97
for d in devices + spares:
98
side_effects.append(("", "")) # mdadm --zero-superblock
99
expected_calls.append(
100
call(["mdadm", "--zero-superblock", d], capture=True))
102
side_effects.append(("", "")) # udevadm settle
103
expected_calls.append(call(["udevadm", "settle"]))
104
side_effects.append(("", "")) # udevadm control --stop-exec-queue
105
expected_calls.append(call(["udevadm", "control",
106
"--stop-exec-queue"]))
107
side_effects.append(("", "")) # mdadm create
108
# build command how mdadm_create does
109
cmd = (["mdadm", "--create", md_devname, "--run",
110
"--level=%s" % raidlevel, "--raid-devices=%s" % len(devices)] +
113
cmd += ["--spare-devices=%s" % len(spares)] + spares
115
expected_calls.append(call(cmd, capture=True))
116
side_effects.append(("", "")) # udevadm control --start-exec-queue
117
expected_calls.append(call(["udevadm", "control",
118
"--start-exec-queue"]))
119
side_effects.append(("", "")) # udevadm settle
120
expected_calls.append(call(["udevadm", "settle",
121
"--exit-if-exists=%s" % md_devname]))
123
return (side_effects, expected_calls)
125
def test_mdadm_create_raid0(self):
126
md_devname = "/dev/md0"
128
devices = ["/dev/vdc1", "/dev/vdd1"]
130
(side_effects, expected_calls) = self.prepare_mock(md_devname,
135
self.mock_util.subp.side_effect = side_effects
136
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
137
devices=devices, spares=spares)
138
self.mock_util.subp.assert_has_calls(expected_calls)
140
def test_mdadm_create_raid0_devshort(self):
143
devices = ["/dev/vdc1", "/dev/vdd1"]
145
with self.assertRaises(ValueError):
146
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
147
devices=devices, spares=spares)
149
def test_mdadm_create_raid0_with_spares(self):
150
md_devname = "/dev/md0"
152
devices = ["/dev/vdc1", "/dev/vdd1"]
153
spares = ["/dev/vde1"]
154
(side_effects, expected_calls) = self.prepare_mock(md_devname,
159
self.mock_util.subp.side_effect = side_effects
160
with self.assertRaises(ValueError):
161
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
162
devices=devices, spares=spares)
163
self.mock_util.subp.assert_has_calls(expected_calls)
165
def test_mdadm_create_md_devname_none(self):
168
devices = ["/dev/vdc1", "/dev/vdd1"]
169
spares = ["/dev/vde1"]
170
with self.assertRaises(ValueError):
171
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
172
devices=devices, spares=spares)
174
def test_mdadm_create_md_devname_missing(self):
175
self.mock_valid.return_value = False
176
md_devname = "/dev/wark"
178
devices = ["/dev/vdc1", "/dev/vdd1"]
179
spares = ["/dev/vde1"]
180
with self.assertRaises(ValueError):
181
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
182
devices=devices, spares=spares)
184
def test_mdadm_create_invalid_raidlevel(self):
185
md_devname = "/dev/md0"
187
devices = ["/dev/vdc1", "/dev/vdd1"]
188
spares = ["/dev/vde1"]
189
with self.assertRaises(ValueError):
190
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
191
devices=devices, spares=spares)
193
def test_mdadm_create_check_min_devices(self):
194
md_devname = "/dev/md0"
196
devices = ["/dev/vdc1", "/dev/vdd1"]
197
spares = ["/dev/vde1"]
198
with self.assertRaises(ValueError):
199
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
200
devices=devices, spares=spares)
202
def test_mdadm_create_raid5(self):
203
md_devname = "/dev/md0"
205
devices = ['/dev/vdc1', '/dev/vdd1', '/dev/vde1']
206
spares = ['/dev/vdg1']
207
(side_effects, expected_calls) = self.prepare_mock(md_devname,
212
self.mock_util.subp.side_effect = side_effects
213
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
214
devices=devices, spares=spares)
215
self.mock_util.subp.assert_has_calls(expected_calls)
218
class TestBlockMdadmExamine(MdadmTestBase):
220
super(TestBlockMdadmExamine, self).setUp()
221
self.add_patch('curtin.block.mdadm.util', 'mock_util')
222
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
224
# Common mock settings
225
self.mock_valid.return_value = True
226
self.mock_util.lsb_release.return_value = {'codename': 'precise'}
228
def test_mdadm_examine_export(self):
229
self.mock_util.lsb_release.return_value = {'codename': 'xenial'}
230
self.mock_util.subp.return_value = (
235
MD_UUID=93a73e10:427f280b:b7076c02:204b8f7a
239
data = mdadm.mdadm_examine(device, export=True)
242
call(["mdadm", "--examine", "--export", device], capture=True),
244
self.mock_util.subp.assert_has_calls(expected_calls)
245
self.assertEqual(data['MD_UUID'],
246
'93a73e10:427f280b:b7076c02:204b8f7a')
248
def test_mdadm_examine_no_export(self):
249
self.mock_util.subp.return_value = ("""/dev/vde:
253
Array UUID : 93a73e10:427f280b:b7076c02:204b8f7a
254
Name : wily-foobar:0 (local to host wily-foobar)
255
Creation Time : Sat Dec 12 16:06:05 2015
259
Avail Dev Size : 20955136 (9.99 GiB 10.73 GB)
260
Used Dev Size : 20955136 (9.99 GiB 10.73 GB)
261
Array Size : 10477568 (9.99 GiB 10.73 GB)
262
Data Offset : 16384 sectors
263
Super Offset : 8 sectors
264
Unused Space : before=16296 sectors, after=0 sectors
266
Device UUID : 8fcd62e6:991acc6e:6cb71ee3:7c956919
268
Update Time : Sat Dec 12 16:09:09 2015
269
Bad Block Log : 512 entries available at offset 72 sectors
270
Checksum : 65b57c2e - correct
275
Array State : AA ('A' == active, '.' == missing, 'R' == replacing)
276
""", "") # mdadm --examine /dev/vde
279
data = mdadm.mdadm_examine(device, export=False)
282
call(["mdadm", "--examine", device], capture=True),
284
self.mock_util.subp.assert_has_calls(expected_calls)
285
self.assertEqual(data['MD_UUID'],
286
'93a73e10:427f280b:b7076c02:204b8f7a')
288
def test_mdadm_examine_no_raid(self):
289
self.mock_util.subp.side_effect = subprocess.CalledProcessError("", "")
292
data = mdadm.mdadm_examine(device, export=False)
295
call(["mdadm", "--examine", device], capture=True),
298
# don't mock anything if raidlevel and spares mismatch
299
self.mock_util.subp.assert_has_calls(expected_calls)
300
self.assertEqual(data, {})
303
class TestBlockMdadmStop(MdadmTestBase):
305
super(TestBlockMdadmStop, self).setUp()
306
self.add_patch('curtin.block.mdadm.util', 'mock_util')
307
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
309
# Common mock settings
310
self.mock_valid.return_value = True
311
self.mock_util.lsb_release.return_value = {'codename': 'xenial'}
312
self.mock_util.subp.side_effect = [
313
("", ""), # mdadm stop device
316
def test_mdadm_stop_no_devpath(self):
317
with self.assertRaises(ValueError):
318
mdadm.mdadm_stop(None)
320
def test_mdadm_stop(self):
322
mdadm.mdadm_stop(device)
324
call(["mdadm", "--stop", device], rcs=[0, 1], capture=True),
326
self.mock_util.subp.assert_has_calls(expected_calls)
329
class TestBlockMdadmRemove(MdadmTestBase):
331
super(TestBlockMdadmRemove, self).setUp()
332
self.add_patch('curtin.block.mdadm.util', 'mock_util')
333
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
335
# Common mock settings
336
self.mock_valid.return_value = True
337
self.mock_util.lsb_release.return_value = {'codename': 'xenial'}
338
self.mock_util.subp.side_effect = [
339
("", ""), # mdadm remove device
342
def test_mdadm_remove_no_devpath(self):
343
with self.assertRaises(ValueError):
344
mdadm.mdadm_remove(None)
346
def test_mdadm_remove(self):
348
mdadm.mdadm_remove(device)
350
call(["mdadm", "--remove", device], rcs=[0, 1], capture=True),
352
self.mock_util.subp.assert_has_calls(expected_calls)
355
class TestBlockMdadmQueryDetail(MdadmTestBase):
357
super(TestBlockMdadmQueryDetail, self).setUp()
358
self.add_patch('curtin.block.mdadm.util', 'mock_util')
359
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
361
# Common mock settings
362
self.mock_valid.return_value = True
363
self.mock_util.lsb_release.return_value = {'codename': 'precise'}
365
def test_mdadm_query_detail_export(self):
366
self.mock_util.lsb_release.return_value = {'codename': 'xenial'}
367
self.mock_util.subp.return_value = (
372
MD_UUID=93a73e10:427f280b:b7076c02:204b8f7a
373
MD_NAME=wily-foobar:0
375
MD_DEVICE_vdc_DEV=/dev/vdc
377
MD_DEVICE_vdd_DEV=/dev/vdd
378
MD_DEVICE_vde_ROLE=spare
379
MD_DEVICE_vde_DEV=/dev/vde
383
self.mock_valid.return_value = True
384
data = mdadm.mdadm_query_detail(device, export=True)
387
call(["mdadm", "--query", "--detail", "--export", device],
390
self.mock_util.subp.assert_has_calls(expected_calls)
391
self.assertEqual(data['MD_UUID'],
392
'93a73e10:427f280b:b7076c02:204b8f7a')
394
def test_mdadm_query_detail_no_export(self):
395
self.mock_util.subp.return_value = ("""/dev/md0:
397
Creation Time : Sat Dec 12 16:06:05 2015
399
Array Size : 10477568 (9.99 GiB 10.73 GB)
400
Used Dev Size : 10477568 (9.99 GiB 10.73 GB)
403
Persistence : Superblock is persistent
405
Update Time : Sat Dec 12 16:09:09 2015
412
Name : wily-foobar:0 (local to host wily-foobar)
413
UUID : 93a73e10:427f280b:b7076c02:204b8f7a
416
Number Major Minor RaidDevice State
417
0 253 32 0 active sync /dev/vdc
418
1 253 48 1 active sync /dev/vdd
420
2 253 64 - spare /dev/vde
421
""", "") # mdadm --query --detail /dev/md0
424
data = mdadm.mdadm_query_detail(device, export=False)
426
call(["mdadm", "--query", "--detail", device], capture=True),
428
self.mock_util.subp.assert_has_calls(expected_calls)
429
self.assertEqual(data['MD_UUID'],
430
'93a73e10:427f280b:b7076c02:204b8f7a')
433
class TestBlockMdadmDetailScan(MdadmTestBase):
435
super(TestBlockMdadmDetailScan, self).setUp()
436
self.add_patch('curtin.block.mdadm.util', 'mock_util')
437
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
439
# Common mock settings
440
self.scan_output = ("ARRAY /dev/md0 metadata=1.2 spares=2 name=0 " +
441
"UUID=b1eae2ff:69b6b02e:1d63bb53:ddfa6e4a")
442
self.mock_valid.return_value = True
443
self.mock_util.lsb_release.return_value = {'codename': 'xenial'}
444
self.mock_util.subp.side_effect = [
445
(self.scan_output, ""), # mdadm --detail --scan
448
def test_mdadm_remove(self):
449
data = mdadm.mdadm_detail_scan()
451
call(["mdadm", "--detail", "--scan"], capture=True),
453
self.mock_util.subp.assert_has_calls(expected_calls)
454
self.assertEqual(self.scan_output, data)
456
def test_mdadm_remove_error(self):
457
self.mock_util.subp.side_effect = [
458
("wark", "error"), # mdadm --detail --scan
460
data = mdadm.mdadm_detail_scan()
462
call(["mdadm", "--detail", "--scan"], capture=True),
464
self.mock_util.subp.assert_has_calls(expected_calls)
465
self.assertEqual(None, data)
468
class TestBlockMdadmMdHelpers(MdadmTestBase):
470
super(TestBlockMdadmMdHelpers, self).setUp()
471
self.add_patch('curtin.block.mdadm.util', 'mock_util')
472
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
474
self.mock_valid.return_value = True
475
self.mock_util.lsb_release.return_value = {'codename': 'xenial'}
477
def test_valid_mdname(self):
479
result = mdadm.valid_mdname(mdname)
483
self.mock_valid.assert_has_calls(expected_calls)
484
self.assertTrue(result)
486
def test_valid_mdname_short(self):
488
with self.assertRaises(ValueError):
489
mdadm.valid_mdname(mdname)
491
def test_valid_mdname_none(self):
493
with self.assertRaises(ValueError):
494
mdadm.valid_mdname(mdname)
496
def test_valid_mdname_not_valid_device(self):
497
self.mock_valid.return_value = False
499
with self.assertRaises(ValueError):
500
mdadm.valid_mdname(mdname)
502
@patch('curtin.block.mdadm.sys_block_path')
503
@patch('curtin.block.mdadm.os.path.isfile')
504
def test_md_sysfs_attr(self, mock_isfile, mock_sysblock):
506
attr_name = 'array_state'
507
sysfs_path = '/sys/class/block/{}/md/{}'.format(dev_short(mdname),
509
mock_sysblock.side_effect = ['/sys/class/block/md0/md']
510
mock_isfile.side_effect = [True]
511
mdadm.md_sysfs_attr(mdname, attr_name)
512
self.mock_util.load_file.assert_called_with(sysfs_path)
513
mock_sysblock.assert_called_with(mdname, 'md')
514
mock_isfile.assert_called_with(sysfs_path)
516
def test_md_sysfs_attr_devname_none(self):
518
attr_name = 'array_state'
519
with self.assertRaises(ValueError):
520
mdadm.md_sysfs_attr(mdname, attr_name)
522
def test_md_raidlevel_short(self):
523
for rl in [0, 1, 5, 6, 10, 'linear', 'stripe']:
524
self.assertEqual(rl, mdadm.md_raidlevel_short(rl))
525
if isinstance(rl, int):
526
long_rl = 'raid%d' % rl
527
self.assertEqual(rl, mdadm.md_raidlevel_short(long_rl))
529
def test_md_minimum_devices(self):
531
2: [0, 1, 'linear', 'stripe'],
536
for rl in [0, 1, 5, 6, 10, 'linear', 'stripe']:
537
min_devs = mdadm.md_minimum_devices(rl)
538
self.assertTrue(rl in min_to_rl[min_devs])
540
def test_md_minimum_devices_invalid_rl(self):
541
min_devs = mdadm.md_minimum_devices(27)
542
self.assertEqual(min_devs, -1)
544
@patch('curtin.block.mdadm.md_sysfs_attr')
545
def test_md_check_array_state_rw(self, mock_attr):
547
mock_attr.return_value = 'clean'
548
self.assertTrue(mdadm.md_check_array_state_rw(mdname))
550
@patch('curtin.block.mdadm.md_sysfs_attr')
551
def test_md_check_array_state_rw_false(self, mock_attr):
553
mock_attr.return_value = 'inactive'
554
self.assertFalse(mdadm.md_check_array_state_rw(mdname))
556
@patch('curtin.block.mdadm.md_sysfs_attr')
557
def test_md_check_array_state_ro(self, mock_attr):
559
mock_attr.return_value = 'readonly'
560
self.assertTrue(mdadm.md_check_array_state_ro(mdname))
562
@patch('curtin.block.mdadm.md_sysfs_attr')
563
def test_md_check_array_state_ro_false(self, mock_attr):
565
mock_attr.return_value = 'inactive'
566
self.assertFalse(mdadm.md_check_array_state_ro(mdname))
568
@patch('curtin.block.mdadm.md_sysfs_attr')
569
def test_md_check_array_state_error(self, mock_attr):
571
mock_attr.return_value = 'inactive'
572
self.assertTrue(mdadm.md_check_array_state_error(mdname))
574
@patch('curtin.block.mdadm.md_sysfs_attr')
575
def test_md_check_array_state_error_false(self, mock_attr):
577
mock_attr.return_value = 'active'
578
self.assertFalse(mdadm.md_check_array_state_error(mdname))
580
def test_md_device_key_role(self):
582
rolekey = mdadm.md_device_key_role(devname)
583
self.assertEqual('MD_DEVICE_vda_ROLE', rolekey)
585
def test_md_device_key_role_no_dev(self):
587
with self.assertRaises(ValueError):
588
mdadm.md_device_key_role(devname)
590
def test_md_device_key_dev(self):
592
devkey = mdadm.md_device_key_dev(devname)
593
self.assertEqual('MD_DEVICE_vda_DEV', devkey)
595
def test_md_device_key_dev_no_dev(self):
597
with self.assertRaises(ValueError):
598
mdadm.md_device_key_dev(devname)
600
@patch('curtin.block.mdadm.os.path.exists')
601
@patch('curtin.block.mdadm.os.listdir')
602
def tests_md_get_spares_list(self, mock_listdir, mock_exists):
604
devices = ['dev-vda', 'dev-vdb', 'dev-vdc']
605
states = ['in-sync', 'in-sync', 'spare']
607
mock_exists.return_value = True
608
mock_listdir.return_value = devices
609
self.mock_util.load_file.side_effect = states
611
sysfs_path = '/sys/class/block/md0/md/'
615
expected_calls.append(call(os.path.join(sysfs_path, d, 'state')))
617
spares = mdadm.md_get_spares_list(mdname)
618
self.mock_util.load_file.assert_has_calls(expected_calls)
619
self.assertEqual(['/dev/vdc'], spares)
621
@patch('curtin.block.mdadm.os.path.exists')
622
def tests_md_get_spares_list_nomd(self, mock_exists):
624
mock_exists.return_value = False
625
with self.assertRaises(OSError):
626
mdadm.md_get_spares_list(mdname)
628
@patch('curtin.block.mdadm.os.path.exists')
629
@patch('curtin.block.mdadm.os.listdir')
630
def tests_md_get_devices_list(self, mock_listdir, mock_exists):
632
devices = ['dev-vda', 'dev-vdb', 'dev-vdc']
633
states = ['in-sync', 'in-sync', 'spare']
635
mock_exists.return_value = True
636
mock_listdir.return_value = devices
637
self.mock_util.load_file.side_effect = states
639
sysfs_path = '/sys/class/block/md0/md/'
643
expected_calls.append(call(os.path.join(sysfs_path, d, 'state')))
645
devs = mdadm.md_get_devices_list(mdname)
646
self.mock_util.load_file.assert_has_calls(expected_calls)
647
self.assertEqual(sorted(['/dev/vda', '/dev/vdb']), sorted(devs))
649
@patch('curtin.block.mdadm.os.path.exists')
650
def tests_md_get_devices_list_nomd(self, mock_exists):
652
mock_exists.return_value = False
653
with self.assertRaises(OSError):
654
mdadm.md_get_devices_list(mdname)
656
@patch('curtin.block.mdadm.os')
657
def test_md_check_array_uuid(self, mock_os):
659
md_uuid = '93a73e10:427f280b:b7076c02:204b8f7a'
660
mock_os.path.realpath.return_value = devname
661
rv = mdadm.md_check_array_uuid(devname, md_uuid)
664
@patch('curtin.block.mdadm.os')
665
def test_md_check_array_uuid_mismatch(self, mock_os):
667
md_uuid = '93a73e10:427f280b:b7076c02:204b8f7a'
668
mock_os.path.realpath.return_value = '/dev/md1'
670
with self.assertRaises(ValueError):
671
mdadm.md_check_array_uuid(devname, md_uuid)
673
@patch('curtin.block.mdadm.mdadm_query_detail')
674
def test_md_get_uuid(self, mock_query):
676
md_uuid = '93a73e10:427f280b:b7076c02:204b8f7a'
677
mock_query.return_value = {'MD_UUID': md_uuid}
678
uuid = mdadm.md_get_uuid(mdname)
679
self.assertEqual(md_uuid, uuid)
681
@patch('curtin.block.mdadm.mdadm_query_detail')
682
def test_md_get_uuid_dev_none(self, mock_query):
684
with self.assertRaises(ValueError):
685
mdadm.md_get_uuid(mdname)
687
def test_md_check_raid_level(self):
688
for rl in mdadm.VALID_RAID_LEVELS:
689
self.assertTrue(mdadm.md_check_raidlevel(rl))
691
def test_md_check_raid_level_bad(self):
693
self.assertTrue(bogus not in mdadm.VALID_RAID_LEVELS)
694
with self.assertRaises(ValueError):
695
mdadm.md_check_raidlevel(bogus)
697
@patch('curtin.block.mdadm.md_sysfs_attr')
698
def test_md_check_array_state(self, mock_attr):
700
mock_attr.side_effect = [
701
'clean', # array_state
703
'idle', # sync_action
705
self.assertTrue(mdadm.md_check_array_state(mdname))
707
@patch('curtin.block.mdadm.md_sysfs_attr')
708
def test_md_check_array_state_norw(self, mock_attr):
710
mock_attr.side_effect = [
711
'suspended', # array_state
713
'idle', # sync_action
715
with self.assertRaises(ValueError):
716
mdadm.md_check_array_state(mdname)
718
@patch('curtin.block.mdadm.md_sysfs_attr')
719
def test_md_check_array_state_degraded(self, mock_attr):
721
mock_attr.side_effect = [
722
'clean', # array_state
724
'idle', # sync_action
726
with self.assertRaises(ValueError):
727
mdadm.md_check_array_state(mdname)
729
@patch('curtin.block.mdadm.md_sysfs_attr')
730
def test_md_check_array_state_degraded_empty(self, mock_attr):
732
mock_attr.side_effect = [
733
'clean', # array_state
735
'idle', # sync_action
737
with self.assertRaises(ValueError):
738
mdadm.md_check_array_state(mdname)
740
@patch('curtin.block.mdadm.md_sysfs_attr')
741
def test_md_check_array_state_sync(self, mock_attr):
743
mock_attr.side_effect = [
744
'clean', # array_state
746
'recovery', # sync_action
748
with self.assertRaises(ValueError):
749
mdadm.md_check_array_state(mdname)
751
@patch('curtin.block.mdadm.md_check_array_uuid')
752
@patch('curtin.block.mdadm.md_get_uuid')
753
def test_md_check_uuid(self, mock_guuid, mock_ckuuid):
755
mock_guuid.return_value = '93a73e10:427f280b:b7076c02:204b8f7a'
756
mock_ckuuid.return_value = True
758
rv = mdadm.md_check_uuid(mdname)
761
@patch('curtin.block.mdadm.md_check_array_uuid')
762
@patch('curtin.block.mdadm.md_get_uuid')
763
def test_md_check_uuid_nouuid(self, mock_guuid, mock_ckuuid):
765
mock_guuid.return_value = None
766
with self.assertRaises(ValueError):
767
mdadm.md_check_uuid(mdname)
769
@patch('curtin.block.mdadm.md_get_devices_list')
770
def test_md_check_devices(self, mock_devlist):
772
devices = ['/dev/vdc', '/dev/vdd']
774
mock_devlist.return_value = devices
775
rv = mdadm.md_check_devices(mdname, devices)
776
self.assertEqual(rv, None)
778
@patch('curtin.block.mdadm.md_get_devices_list')
779
def test_md_check_devices_wrong_devs(self, mock_devlist):
781
devices = ['/dev/vdc', '/dev/vdd']
783
mock_devlist.return_value = ['/dev/sda']
784
with self.assertRaises(ValueError):
785
mdadm.md_check_devices(mdname, devices)
787
def test_md_check_devices_no_devs(self):
791
with self.assertRaises(ValueError):
792
mdadm.md_check_devices(mdname, devices)
794
@patch('curtin.block.mdadm.md_get_spares_list')
795
def test_md_check_spares(self, mock_devlist):
797
spares = ['/dev/vdc', '/dev/vdd']
799
mock_devlist.return_value = spares
800
rv = mdadm.md_check_spares(mdname, spares)
801
self.assertEqual(rv, None)
803
@patch('curtin.block.mdadm.md_get_spares_list')
804
def test_md_check_spares_wrong_devs(self, mock_devlist):
806
spares = ['/dev/vdc', '/dev/vdd']
808
mock_devlist.return_value = ['/dev/sda']
809
with self.assertRaises(ValueError):
810
mdadm.md_check_spares(mdname, spares)
812
@patch('curtin.block.mdadm.mdadm_examine')
813
@patch('curtin.block.mdadm.mdadm_query_detail')
814
@patch('curtin.block.mdadm.md_get_uuid')
815
def test_md_check_array_membership(self, mock_uuid, mock_query,
818
devices = ['/dev/vda', '/dev/vdb', '/dev/vdc', '/dev/vdd']
819
md_uuid = '93a73e10:427f280b:b7076c02:204b8f7a'
820
md_dict = {'MD_UUID': md_uuid}
821
mock_query.return_value = md_dict
822
mock_uuid.return_value = md_uuid
823
mock_examine.side_effect = [md_dict] * len(devices)
826
expected_calls.append(call(dev, export=False))
828
rv = mdadm.md_check_array_membership(mdname, devices)
830
self.assertEqual(rv, None)
831
mock_uuid.assert_has_calls([call(mdname)])
832
mock_examine.assert_has_calls(expected_calls)
834
@patch('curtin.block.mdadm.mdadm_examine')
835
@patch('curtin.block.mdadm.mdadm_query_detail')
836
@patch('curtin.block.mdadm.md_get_uuid')
837
def test_md_check_array_membership_bad_dev(self, mock_uuid, mock_query,
840
devices = ['/dev/vda', '/dev/vdb', '/dev/vdc', '/dev/vdd']
841
md_uuid = '93a73e10:427f280b:b7076c02:204b8f7a'
842
md_dict = {'MD_UUID': md_uuid}
843
mock_query.return_value = md_dict
844
mock_uuid.return_value = md_uuid
845
mock_examine.side_effect = [
850
] # one device isn't a member
852
with self.assertRaises(ValueError):
853
mdadm.md_check_array_membership(mdname, devices)
855
@patch('curtin.block.mdadm.mdadm_examine')
856
@patch('curtin.block.mdadm.mdadm_query_detail')
857
@patch('curtin.block.mdadm.md_get_uuid')
858
def test_md_check_array_membership_wrong_array(self, mock_uuid, mock_query,
861
devices = ['/dev/vda', '/dev/vdb', '/dev/vdc', '/dev/vdd']
862
md_uuid = '93a73e10:427f280b:b7076c02:204b8f7a'
863
md_dict = {'MD_UUID': '11111111:427f280b:b7076c02:204b8f7a'}
864
mock_query.return_value = md_dict
865
mock_uuid.return_value = md_uuid
866
mock_examine.side_effect = [md_dict] * len(devices)
868
with self.assertRaises(ValueError):
869
mdadm.md_check_array_membership(mdname, devices)
871
@patch('curtin.block.mdadm.md_check_array_membership')
872
@patch('curtin.block.mdadm.md_check_spares')
873
@patch('curtin.block.mdadm.md_check_devices')
874
@patch('curtin.block.mdadm.md_check_uuid')
875
@patch('curtin.block.mdadm.md_check_raidlevel')
876
@patch('curtin.block.mdadm.md_check_array_state')
877
def test_md_check_all_good(self, mock_array, mock_raid, mock_uuid,
878
mock_dev, mock_spare, mock_member):
879
md_devname = '/dev/md0'
881
devices = ['/dev/vda', '/dev/vdb']
882
spares = ['/dev/vdc']
884
mock_array.return_value = None
885
mock_raid.return_value = None
886
mock_uuid.return_value = None
887
mock_dev.return_value = None
888
mock_spare.return_value = None
889
mock_member.return_value = None
891
mdadm.md_check(md_devname, raidlevel, devices=devices, spares=spares)
893
mock_array.assert_has_calls([call(md_devname)])
894
mock_raid.assert_has_calls([call(raidlevel)])
895
mock_uuid.assert_has_calls([call(md_devname)])
896
mock_dev.assert_has_calls([call(md_devname, devices)])
897
mock_spare.assert_has_calls([call(md_devname, spares)])
898
mock_member.assert_has_calls([call(md_devname, devices + spares)])
900
def test_md_check_all_good_devshort(self):
903
devices = ['/dev/vda', '/dev/vdb']
904
spares = ['/dev/vdc']
906
with self.assertRaises(ValueError):
907
mdadm.md_check(md_devname, raidlevel, devices=devices,
910
# vi: ts=4 expandtab syntax=python