1
from mock import call, patch
2
from curtin.block import dev_short
3
from curtin.block import mdadm
4
from curtin import util
5
from .helpers import CiTestCase
11
class TestBlockMdadmAssemble(CiTestCase):
14
super(TestBlockMdadmAssemble, self).setUp()
15
self.add_patch('curtin.block.mdadm.util', 'mock_util')
16
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
17
self.add_patch('curtin.block.mdadm.udev', 'mock_udev')
19
# Common mock settings
20
self.mock_valid.return_value = True
21
self.mock_util.lsb_release.return_value = {'codename': 'precise'}
22
self.mock_util.subp.return_value = ('', '')
24
def test_mdadm_assemble_scan(self):
25
mdadm.mdadm_assemble(scan=True)
27
call(["mdadm", "--assemble", "--scan", "-v"], capture=True,
29
call(["mdadm", "--detail", "--scan", "-v"], capture=True,
32
self.mock_util.subp.assert_has_calls(assemble_calls)
33
self.assertTrue(self.mock_udev.udevadm_settle.called)
35
def test_mdadm_assemble_md_devname(self):
36
md_devname = "/dev/md0"
37
mdadm.mdadm_assemble(md_devname=md_devname)
40
call(["mdadm", "--assemble", md_devname, "--run"],
41
capture=True, rcs=[0, 1, 2]),
42
call(["mdadm", "--detail", "--scan", "-v"], capture=True,
45
self.mock_util.subp.assert_has_calls(assemble_calls)
46
self.assertTrue(self.mock_udev.udevadm_settle.called)
48
def test_mdadm_assemble_md_devname_short(self):
49
with self.assertRaises(ValueError):
51
mdadm.mdadm_assemble(md_devname=md_devname)
53
def test_mdadm_assemble_md_devname_none(self):
54
with self.assertRaises(ValueError):
56
mdadm.mdadm_assemble(md_devname=md_devname)
58
def test_mdadm_assemble_md_devname_devices(self):
59
md_devname = "/dev/md0"
60
devices = ["/dev/vdc1", "/dev/vdd1"]
61
mdadm.mdadm_assemble(md_devname=md_devname, devices=devices)
63
call(["mdadm", "--assemble", md_devname, "--run"] + devices,
64
capture=True, rcs=[0, 1, 2]),
65
call(["mdadm", "--detail", "--scan", "-v"], capture=True,
68
self.mock_util.subp.assert_has_calls(assemble_calls)
69
self.assertTrue(self.mock_udev.udevadm_settle.called)
71
def test_mdadm_assemble_exec_error(self):
73
def _raise_pexec_error(*args, **kwargs):
74
raise util.ProcessExecutionError()
76
self.mock_util.ProcessExecutionError = util.ProcessExecutionError
77
self.mock_util.subp.side_effect = _raise_pexec_error
78
with self.assertRaises(util.ProcessExecutionError):
79
mdadm.mdadm_assemble(scan=True, ignore_errors=False)
80
self.mock_util.subp.assert_called_with(
81
['mdadm', '--assemble', '--scan', '-v'], capture=True,
85
class TestBlockMdadmCreate(CiTestCase):
87
super(TestBlockMdadmCreate, self).setUp()
88
self.add_patch('curtin.block.mdadm.util', 'mock_util')
89
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
90
self.add_patch('curtin.block.mdadm.get_holders', 'mock_holders')
92
# Common mock settings
93
self.mock_valid.return_value = True
94
self.mock_util.lsb_release.return_value = {'codename': 'precise'}
95
self.mock_holders.return_value = []
97
def prepare_mock(self, md_devname, raidlevel, devices, spares):
102
# don't mock anything if raidlevel and spares mismatch
103
if spares and raidlevel not in mdadm.SPARE_RAID_LEVELS:
104
return (side_effects, expected_calls)
106
side_effects.append((hostname, "")) # hostname -s
107
expected_calls.append(call(["hostname", "-s"],
108
capture=True, rcs=[0]))
110
# prepare side-effects
111
for d in devices + spares:
112
side_effects.append(("", "")) # mdadm --zero-superblock
113
expected_calls.append(
114
call(["mdadm", "--zero-superblock", d], capture=True))
116
side_effects.append(("", "")) # udevadm settle
117
expected_calls.append(call(["udevadm", "settle"]))
118
side_effects.append(("", "")) # udevadm control --stop-exec-queue
119
expected_calls.append(call(["udevadm", "control",
120
"--stop-exec-queue"]))
122
side_effects.append(("", "")) # mdadm create
123
# build command how mdadm_create does
124
cmd = (["mdadm", "--create", md_devname, "--run",
125
"--homehost=%s" % hostname, "--level=%s" % raidlevel,
126
"--raid-devices=%s" % len(devices)] +
129
cmd += ["--spare-devices=%s" % len(spares)] + spares
131
expected_calls.append(call(cmd, capture=True))
132
side_effects.append(("", "")) # udevadm control --start-exec-queue
133
expected_calls.append(call(["udevadm", "control",
134
"--start-exec-queue"]))
135
side_effects.append(("", "")) # udevadm settle
136
expected_calls.append(call(["udevadm", "settle",
137
"--exit-if-exists=%s" % md_devname]))
139
return (side_effects, expected_calls)
141
def test_mdadm_create_raid0(self):
142
md_devname = "/dev/md0"
144
devices = ["/dev/vdc1", "/dev/vdd1"]
146
(side_effects, expected_calls) = self.prepare_mock(md_devname,
151
self.mock_util.subp.side_effect = side_effects
152
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
153
devices=devices, spares=spares)
154
self.mock_util.subp.assert_has_calls(expected_calls)
156
def test_mdadm_create_raid0_devshort(self):
159
devices = ["/dev/vdc1", "/dev/vdd1"]
161
with self.assertRaises(ValueError):
162
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
163
devices=devices, spares=spares)
165
def test_mdadm_create_raid0_with_spares(self):
166
md_devname = "/dev/md0"
168
devices = ["/dev/vdc1", "/dev/vdd1"]
169
spares = ["/dev/vde1"]
170
(side_effects, expected_calls) = self.prepare_mock(md_devname,
175
self.mock_util.subp.side_effect = side_effects
176
with self.assertRaises(ValueError):
177
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
178
devices=devices, spares=spares)
179
self.mock_util.subp.assert_has_calls(expected_calls)
181
def test_mdadm_create_md_devname_none(self):
184
devices = ["/dev/vdc1", "/dev/vdd1"]
185
spares = ["/dev/vde1"]
186
with self.assertRaises(ValueError):
187
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
188
devices=devices, spares=spares)
190
def test_mdadm_create_md_devname_missing(self):
191
self.mock_valid.return_value = False
192
md_devname = "/dev/wark"
194
devices = ["/dev/vdc1", "/dev/vdd1"]
195
spares = ["/dev/vde1"]
196
with self.assertRaises(ValueError):
197
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
198
devices=devices, spares=spares)
200
def test_mdadm_create_invalid_raidlevel(self):
201
md_devname = "/dev/md0"
203
devices = ["/dev/vdc1", "/dev/vdd1"]
204
spares = ["/dev/vde1"]
205
with self.assertRaises(ValueError):
206
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
207
devices=devices, spares=spares)
209
def test_mdadm_create_check_min_devices(self):
210
md_devname = "/dev/md0"
212
devices = ["/dev/vdc1", "/dev/vdd1"]
213
spares = ["/dev/vde1"]
214
with self.assertRaises(ValueError):
215
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
216
devices=devices, spares=spares)
218
def test_mdadm_create_raid5(self):
219
md_devname = "/dev/md0"
221
devices = ['/dev/vdc1', '/dev/vdd1', '/dev/vde1']
222
spares = ['/dev/vdg1']
223
(side_effects, expected_calls) = self.prepare_mock(md_devname,
228
self.mock_util.subp.side_effect = side_effects
229
mdadm.mdadm_create(md_devname=md_devname, raidlevel=raidlevel,
230
devices=devices, spares=spares)
231
self.mock_util.subp.assert_has_calls(expected_calls)
234
class TestBlockMdadmExamine(CiTestCase):
236
super(TestBlockMdadmExamine, self).setUp()
237
self.add_patch('curtin.block.mdadm.util', 'mock_util')
238
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
240
# Common mock settings
241
self.mock_valid.return_value = True
242
self.mock_util.lsb_release.return_value = {'codename': 'precise'}
244
def test_mdadm_examine_export(self):
245
self.mock_util.lsb_release.return_value = {'codename': 'xenial'}
246
self.mock_util.subp.return_value = (
251
MD_UUID=93a73e10:427f280b:b7076c02:204b8f7a
255
data = mdadm.mdadm_examine(device, export=True)
258
call(["mdadm", "--examine", "--export", device], capture=True),
260
self.mock_util.subp.assert_has_calls(expected_calls)
261
self.assertEqual(data['MD_UUID'],
262
'93a73e10:427f280b:b7076c02:204b8f7a')
264
def test_mdadm_examine_no_export(self):
265
self.mock_util.subp.return_value = ("""/dev/vde:
269
Array UUID : 93a73e10:427f280b:b7076c02:204b8f7a
270
Name : wily-foobar:0 (local to host wily-foobar)
271
Creation Time : Sat Dec 12 16:06:05 2015
275
Avail Dev Size : 20955136 (9.99 GiB 10.73 GB)
276
Used Dev Size : 20955136 (9.99 GiB 10.73 GB)
277
Array Size : 10477568 (9.99 GiB 10.73 GB)
278
Data Offset : 16384 sectors
279
Super Offset : 8 sectors
280
Unused Space : before=16296 sectors, after=0 sectors
282
Device UUID : 8fcd62e6:991acc6e:6cb71ee3:7c956919
284
Update Time : Sat Dec 12 16:09:09 2015
285
Bad Block Log : 512 entries available at offset 72 sectors
286
Checksum : 65b57c2e - correct
291
Array State : AA ('A' == active, '.' == missing, 'R' == replacing)
292
""", "") # mdadm --examine /dev/vde
295
data = mdadm.mdadm_examine(device, export=False)
298
call(["mdadm", "--examine", device], capture=True),
300
self.mock_util.subp.assert_has_calls(expected_calls)
301
self.assertEqual(data['MD_UUID'],
302
'93a73e10:427f280b:b7076c02:204b8f7a')
304
def test_mdadm_examine_no_raid(self):
305
self.mock_util.subp.side_effect = subprocess.CalledProcessError("", "")
308
data = mdadm.mdadm_examine(device, export=False)
311
call(["mdadm", "--examine", device], capture=True),
314
# don't mock anything if raidlevel and spares mismatch
315
self.mock_util.subp.assert_has_calls(expected_calls)
316
self.assertEqual(data, {})
319
class TestBlockMdadmStop(CiTestCase):
321
super(TestBlockMdadmStop, self).setUp()
322
self.add_patch('curtin.block.mdadm.util.lsb_release', 'mock_util_lsb')
323
self.add_patch('curtin.block.mdadm.util.subp', 'mock_util_subp')
324
self.add_patch('curtin.block.mdadm.util.write_file',
325
'mock_util_write_file')
326
self.add_patch('curtin.block.mdadm.util.load_file',
327
'mock_util_load_file')
328
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
329
self.add_patch('curtin.block.mdadm.sys_block_path',
330
'mock_sys_block_path')
331
self.add_patch('curtin.block.mdadm.os.path.isfile', 'mock_path_isfile')
333
# Common mock settings
334
self.mock_valid.return_value = True
335
self.mock_util_lsb.return_value = {'codename': 'xenial'}
336
self.mock_util_subp.side_effect = iter([
337
("", ""), # mdadm stop device
339
self.mock_path_isfile.return_value = True
340
self.mock_util_load_file.side_effect = iter([
344
def _set_sys_path(self, md_device):
345
self.sys_path = '/sys/class/block/%s/md' % md_device.split("/")[-1]
346
self.mock_sys_block_path.return_value = self.sys_path
348
def test_mdadm_stop_no_devpath(self):
349
with self.assertRaises(ValueError):
350
mdadm.mdadm_stop(None)
352
def test_mdadm_stop(self):
354
self._set_sys_path(device)
356
mdadm.mdadm_stop(device)
359
call(["mdadm", "--manage", "--stop", device], capture=True)
361
self.mock_util_subp.assert_has_calls(expected_calls)
364
call(self.sys_path + '/sync_action'),
365
call(self.sys_path + '/sync_max'),
367
self.mock_util_load_file.assert_has_calls(expected_reads)
369
@patch('curtin.block.mdadm.time.sleep')
370
def test_mdadm_stop_retry(self, mock_sleep):
372
self._set_sys_path(device)
373
self.mock_util_load_file.side_effect = iter([
375
"proc/mdstat output",
378
self.mock_util_subp.side_effect = iter([
379
util.ProcessExecutionError(),
380
("mdadm stopped %s" % device, ''),
383
mdadm.mdadm_stop(device)
386
call(["mdadm", "--manage", "--stop", device], capture=True),
387
call(["mdadm", "--manage", "--stop", device], capture=True)
389
self.mock_util_subp.assert_has_calls(expected_calls)
392
call(self.sys_path + '/sync_action'),
393
call(self.sys_path + '/sync_max'),
394
call('/proc/mdstat'),
395
call(self.sys_path + '/sync_action'),
396
call(self.sys_path + '/sync_max'),
398
self.mock_util_load_file.assert_has_calls(expected_reads)
401
call(self.sys_path + '/sync_action', content='idle'),
402
call(self.sys_path + '/sync_max', content='0'),
403
call(self.sys_path + '/sync_min', content='0'),
405
self.mock_util_write_file.assert_has_calls(expected_writes)
407
@patch('curtin.block.mdadm.time.sleep')
408
def test_mdadm_stop_retry_sysfs_write_fail(self, mock_sleep):
409
device = "/dev/md126"
410
self._set_sys_path(device)
411
self.mock_util_load_file.side_effect = iter([
413
"proc/mdstat output",
416
self.mock_util_subp.side_effect = iter([
417
util.ProcessExecutionError(),
418
("mdadm stopped %s" % device, ''),
420
# sometimes we fail to modify sysfs attrs
421
self.mock_util_write_file.side_effect = iter([
422
"", # write to sync_action OK
423
IOError(), # write to sync_max FAIL
426
mdadm.mdadm_stop(device)
429
call(["mdadm", "--manage", "--stop", device], capture=True),
430
call(["mdadm", "--manage", "--stop", device], capture=True)
432
self.mock_util_subp.assert_has_calls(expected_calls)
435
call(self.sys_path + '/sync_action'),
436
call(self.sys_path + '/sync_max'),
437
call('/proc/mdstat'),
438
call(self.sys_path + '/sync_action'),
439
call(self.sys_path + '/sync_max'),
441
self.mock_util_load_file.assert_has_calls(expected_reads)
444
call(self.sys_path + '/sync_action', content='idle'),
446
self.mock_util_write_file.assert_has_calls(expected_writes)
448
@patch('curtin.block.mdadm.time.sleep')
449
def test_mdadm_stop_retry_exhausted(self, mock_sleep):
450
device = "/dev/md/37"
452
self._set_sys_path(device)
453
self.mock_util_load_file.side_effect = iter([
455
"proc/mdstat output",
457
self.mock_util_subp.side_effect = iter([
458
util.ProcessExecutionError(),
460
# sometimes we fail to modify sysfs attrs
461
self.mock_util_write_file.side_effect = iter([
462
"", IOError()] * retries)
464
with self.assertRaises(OSError):
465
mdadm.mdadm_stop(device)
468
call(["mdadm", "--manage", "--stop", device], capture=True),
470
self.mock_util_subp.assert_has_calls(expected_calls)
473
call(self.sys_path + '/sync_action'),
474
call(self.sys_path + '/sync_max'),
475
call('/proc/mdstat'),
477
self.mock_util_load_file.assert_has_calls(expected_reads)
480
call(self.sys_path + '/sync_action', content='idle'),
481
call(self.sys_path + '/sync_max', content='0'),
483
self.mock_util_write_file.assert_has_calls(expected_writes)
486
class TestBlockMdadmRemove(CiTestCase):
488
super(TestBlockMdadmRemove, self).setUp()
489
self.add_patch('curtin.block.mdadm.util', 'mock_util')
490
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
492
# Common mock settings
493
self.mock_valid.return_value = True
494
self.mock_util.lsb_release.return_value = {'codename': 'xenial'}
495
self.mock_util.subp.side_effect = [
496
("", ""), # mdadm remove device
499
def test_mdadm_remove_no_devpath(self):
500
with self.assertRaises(ValueError):
501
mdadm.mdadm_remove(None)
503
def test_mdadm_remove(self):
505
mdadm.mdadm_remove(device)
507
call(["mdadm", "--remove", device], rcs=[0], capture=True),
509
self.mock_util.subp.assert_has_calls(expected_calls)
512
class TestBlockMdadmQueryDetail(CiTestCase):
514
super(TestBlockMdadmQueryDetail, self).setUp()
515
self.add_patch('curtin.block.mdadm.util', 'mock_util')
516
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
518
# Common mock settings
519
self.mock_valid.return_value = True
520
self.mock_util.lsb_release.return_value = {'codename': 'precise'}
522
def test_mdadm_query_detail_export(self):
523
self.mock_util.lsb_release.return_value = {'codename': 'xenial'}
524
self.mock_util.subp.return_value = (
529
MD_UUID=93a73e10:427f280b:b7076c02:204b8f7a
530
MD_NAME=wily-foobar:0
532
MD_DEVICE_vdc_DEV=/dev/vdc
534
MD_DEVICE_vdd_DEV=/dev/vdd
535
MD_DEVICE_vde_ROLE=spare
536
MD_DEVICE_vde_DEV=/dev/vde
540
self.mock_valid.return_value = True
541
data = mdadm.mdadm_query_detail(device, export=True)
544
call(["mdadm", "--query", "--detail", "--export", device],
547
self.mock_util.subp.assert_has_calls(expected_calls)
548
self.assertEqual(data['MD_UUID'],
549
'93a73e10:427f280b:b7076c02:204b8f7a')
551
def test_mdadm_query_detail_no_export(self):
552
self.mock_util.subp.return_value = ("""/dev/md0:
554
Creation Time : Sat Dec 12 16:06:05 2015
556
Array Size : 10477568 (9.99 GiB 10.73 GB)
557
Used Dev Size : 10477568 (9.99 GiB 10.73 GB)
560
Persistence : Superblock is persistent
562
Update Time : Sat Dec 12 16:09:09 2015
569
Name : wily-foobar:0 (local to host wily-foobar)
570
UUID : 93a73e10:427f280b:b7076c02:204b8f7a
573
Number Major Minor RaidDevice State
574
0 253 32 0 active sync /dev/vdc
575
1 253 48 1 active sync /dev/vdd
577
2 253 64 - spare /dev/vde
578
""", "") # mdadm --query --detail /dev/md0
581
data = mdadm.mdadm_query_detail(device, export=False)
583
call(["mdadm", "--query", "--detail", device], capture=True),
585
self.mock_util.subp.assert_has_calls(expected_calls)
586
self.assertEqual(data['MD_UUID'],
587
'93a73e10:427f280b:b7076c02:204b8f7a')
590
class TestBlockMdadmDetailScan(CiTestCase):
592
super(TestBlockMdadmDetailScan, self).setUp()
593
self.add_patch('curtin.block.mdadm.util', 'mock_util')
594
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
596
# Common mock settings
597
self.scan_output = ("ARRAY /dev/md0 metadata=1.2 spares=2 name=0 " +
598
"UUID=b1eae2ff:69b6b02e:1d63bb53:ddfa6e4a")
599
self.mock_valid.return_value = True
600
self.mock_util.lsb_release.return_value = {'codename': 'xenial'}
601
self.mock_util.subp.side_effect = [
602
(self.scan_output, ""), # mdadm --detail --scan
605
def test_mdadm_remove(self):
606
data = mdadm.mdadm_detail_scan()
608
call(["mdadm", "--detail", "--scan"], capture=True),
610
self.mock_util.subp.assert_has_calls(expected_calls)
611
self.assertEqual(self.scan_output, data)
613
def test_mdadm_remove_error(self):
614
self.mock_util.subp.side_effect = [
615
("wark", "error"), # mdadm --detail --scan
617
data = mdadm.mdadm_detail_scan()
619
call(["mdadm", "--detail", "--scan"], capture=True),
621
self.mock_util.subp.assert_has_calls(expected_calls)
622
self.assertEqual(None, data)
625
class TestBlockMdadmMdHelpers(CiTestCase):
627
super(TestBlockMdadmMdHelpers, self).setUp()
628
self.add_patch('curtin.block.mdadm.util', 'mock_util')
629
self.add_patch('curtin.block.mdadm.is_valid_device', 'mock_valid')
631
self.mock_valid.return_value = True
632
self.mock_util.lsb_release.return_value = {'codename': 'xenial'}
634
def test_valid_mdname(self):
636
result = mdadm.valid_mdname(mdname)
640
self.mock_valid.assert_has_calls(expected_calls)
641
self.assertTrue(result)
643
def test_valid_mdname_short(self):
645
with self.assertRaises(ValueError):
646
mdadm.valid_mdname(mdname)
648
def test_valid_mdname_none(self):
650
with self.assertRaises(ValueError):
651
mdadm.valid_mdname(mdname)
653
def test_valid_mdname_not_valid_device(self):
654
self.mock_valid.return_value = False
656
with self.assertRaises(ValueError):
657
mdadm.valid_mdname(mdname)
659
@patch('curtin.block.mdadm.sys_block_path')
660
@patch('curtin.block.mdadm.os.path.isfile')
661
def test_md_sysfs_attr(self, mock_isfile, mock_sysblock):
663
attr_name = 'array_state'
664
sysfs_path = '/sys/class/block/{}/md/{}'.format(dev_short(mdname),
666
mock_sysblock.side_effect = ['/sys/class/block/md0/md']
667
mock_isfile.side_effect = [True]
668
mdadm.md_sysfs_attr(mdname, attr_name)
669
self.mock_util.load_file.assert_called_with(sysfs_path)
670
mock_sysblock.assert_called_with(mdname, 'md')
671
mock_isfile.assert_called_with(sysfs_path)
673
def test_md_sysfs_attr_devname_none(self):
675
attr_name = 'array_state'
676
with self.assertRaises(ValueError):
677
mdadm.md_sysfs_attr(mdname, attr_name)
679
def test_md_raidlevel_short(self):
680
for rl in [0, 1, 5, 6, 10, 'linear', 'stripe']:
681
self.assertEqual(rl, mdadm.md_raidlevel_short(rl))
682
if isinstance(rl, int):
683
long_rl = 'raid%d' % rl
684
self.assertEqual(rl, mdadm.md_raidlevel_short(long_rl))
686
def test_md_minimum_devices(self):
688
2: [0, 1, 'linear', 'stripe'],
693
for rl in [0, 1, 5, 6, 10, 'linear', 'stripe']:
694
min_devs = mdadm.md_minimum_devices(rl)
695
self.assertTrue(rl in min_to_rl[min_devs])
697
def test_md_minimum_devices_invalid_rl(self):
698
min_devs = mdadm.md_minimum_devices(27)
699
self.assertEqual(min_devs, -1)
701
@patch('curtin.block.mdadm.md_sysfs_attr')
702
def test_md_check_array_state_rw(self, mock_attr):
704
mock_attr.return_value = 'clean'
705
self.assertTrue(mdadm.md_check_array_state_rw(mdname))
707
@patch('curtin.block.mdadm.md_sysfs_attr')
708
def test_md_check_array_state_rw_false(self, mock_attr):
710
mock_attr.return_value = 'inactive'
711
self.assertFalse(mdadm.md_check_array_state_rw(mdname))
713
@patch('curtin.block.mdadm.md_sysfs_attr')
714
def test_md_check_array_state_ro(self, mock_attr):
716
mock_attr.return_value = 'readonly'
717
self.assertTrue(mdadm.md_check_array_state_ro(mdname))
719
@patch('curtin.block.mdadm.md_sysfs_attr')
720
def test_md_check_array_state_ro_false(self, mock_attr):
722
mock_attr.return_value = 'inactive'
723
self.assertFalse(mdadm.md_check_array_state_ro(mdname))
725
@patch('curtin.block.mdadm.md_sysfs_attr')
726
def test_md_check_array_state_error(self, mock_attr):
728
mock_attr.return_value = 'inactive'
729
self.assertTrue(mdadm.md_check_array_state_error(mdname))
731
@patch('curtin.block.mdadm.md_sysfs_attr')
732
def test_md_check_array_state_error_false(self, mock_attr):
734
mock_attr.return_value = 'active'
735
self.assertFalse(mdadm.md_check_array_state_error(mdname))
737
def test_md_device_key_role(self):
739
rolekey = mdadm.md_device_key_role(devname)
740
self.assertEqual('MD_DEVICE_vda_ROLE', rolekey)
742
def test_md_device_key_role_no_dev(self):
744
with self.assertRaises(ValueError):
745
mdadm.md_device_key_role(devname)
747
def test_md_device_key_dev(self):
749
devkey = mdadm.md_device_key_dev(devname)
750
self.assertEqual('MD_DEVICE_vda_DEV', devkey)
752
def test_md_device_key_dev_no_dev(self):
754
with self.assertRaises(ValueError):
755
mdadm.md_device_key_dev(devname)
757
@patch('curtin.block.get_blockdev_for_partition')
758
@patch('curtin.block.mdadm.os.path.exists')
759
@patch('curtin.block.mdadm.os.listdir')
760
def tests_md_get_spares_list(self, mock_listdir, mock_exists,
763
devices = ['dev-vda', 'dev-vdb', 'dev-vdc']
764
states = ['in-sync', 'in-sync', 'spare']
766
mock_exists.return_value = True
767
mock_listdir.return_value = devices
768
self.mock_util.load_file.side_effect = states
769
mock_getbdev.return_value = ('md0', None)
771
sysfs_path = '/sys/class/block/md0/md/'
775
expected_calls.append(call(os.path.join(sysfs_path, d, 'state')))
777
spares = mdadm.md_get_spares_list(mdname)
778
self.mock_util.load_file.assert_has_calls(expected_calls)
779
self.assertEqual(['/dev/vdc'], spares)
781
@patch('curtin.block.get_blockdev_for_partition')
782
@patch('curtin.block.mdadm.os.path.exists')
783
def tests_md_get_spares_list_nomd(self, mock_exists, mock_getbdev):
785
mock_exists.return_value = False
786
mock_getbdev.return_value = ('md0', None)
787
with self.assertRaises(OSError):
788
mdadm.md_get_spares_list(mdname)
790
@patch('curtin.block.get_blockdev_for_partition')
791
@patch('curtin.block.mdadm.os.path.exists')
792
@patch('curtin.block.mdadm.os.listdir')
793
def tests_md_get_devices_list(self, mock_listdir, mock_exists,
796
devices = ['dev-vda', 'dev-vdb', 'dev-vdc']
797
states = ['in-sync', 'in-sync', 'spare']
799
mock_exists.return_value = True
800
mock_listdir.return_value = devices
801
self.mock_util.load_file.side_effect = states
802
mock_getbdev.return_value = ('md0', None)
804
sysfs_path = '/sys/class/block/md0/md/'
808
expected_calls.append(call(os.path.join(sysfs_path, d, 'state')))
810
devs = mdadm.md_get_devices_list(mdname)
811
self.mock_util.load_file.assert_has_calls(expected_calls)
812
self.assertEqual(sorted(['/dev/vda', '/dev/vdb']), sorted(devs))
814
@patch('curtin.block.get_blockdev_for_partition')
815
@patch('curtin.block.mdadm.os.path.exists')
816
def tests_md_get_devices_list_nomd(self, mock_exists, mock_getbdev):
818
mock_exists.return_value = False
819
mock_getbdev.return_value = ('md0', None)
820
with self.assertRaises(OSError):
821
mdadm.md_get_devices_list(mdname)
823
@patch('curtin.block.mdadm.os')
824
def test_md_check_array_uuid(self, mock_os):
826
md_uuid = '93a73e10:427f280b:b7076c02:204b8f7a'
827
mock_os.path.realpath.return_value = devname
828
rv = mdadm.md_check_array_uuid(devname, md_uuid)
831
@patch('curtin.block.mdadm.os')
832
def test_md_check_array_uuid_mismatch(self, mock_os):
834
md_uuid = '93a73e10:427f280b:b7076c02:204b8f7a'
835
mock_os.path.realpath.return_value = '/dev/md1'
837
with self.assertRaises(ValueError):
838
mdadm.md_check_array_uuid(devname, md_uuid)
840
@patch('curtin.block.mdadm.mdadm_query_detail')
841
def test_md_get_uuid(self, mock_query):
843
md_uuid = '93a73e10:427f280b:b7076c02:204b8f7a'
844
mock_query.return_value = {'MD_UUID': md_uuid}
845
uuid = mdadm.md_get_uuid(mdname)
846
self.assertEqual(md_uuid, uuid)
848
@patch('curtin.block.mdadm.mdadm_query_detail')
849
def test_md_get_uuid_dev_none(self, mock_query):
851
with self.assertRaises(ValueError):
852
mdadm.md_get_uuid(mdname)
854
def test_md_check_raid_level(self):
855
for rl in mdadm.VALID_RAID_LEVELS:
856
self.assertTrue(mdadm.md_check_raidlevel(rl))
858
def test_md_check_raid_level_bad(self):
860
self.assertTrue(bogus not in mdadm.VALID_RAID_LEVELS)
861
with self.assertRaises(ValueError):
862
mdadm.md_check_raidlevel(bogus)
864
@patch('curtin.block.mdadm.md_sysfs_attr')
865
def test_md_check_array_state(self, mock_attr):
867
mock_attr.side_effect = [
868
'clean', # array_state
870
'idle', # sync_action
872
self.assertTrue(mdadm.md_check_array_state(mdname))
874
@patch('curtin.block.mdadm.md_sysfs_attr')
875
def test_md_check_array_state_norw(self, mock_attr):
877
mock_attr.side_effect = [
878
'suspended', # array_state
880
'idle', # sync_action
882
with self.assertRaises(ValueError):
883
mdadm.md_check_array_state(mdname)
885
@patch('curtin.block.mdadm.md_sysfs_attr')
886
def test_md_check_array_state_degraded(self, mock_attr):
888
mock_attr.side_effect = [
889
'clean', # array_state
891
'idle', # sync_action
893
with self.assertRaises(ValueError):
894
mdadm.md_check_array_state(mdname)
896
@patch('curtin.block.mdadm.md_sysfs_attr')
897
def test_md_check_array_state_degraded_empty(self, mock_attr):
899
mock_attr.side_effect = [
900
'clean', # array_state
902
'idle', # sync_action
904
with self.assertRaises(ValueError):
905
mdadm.md_check_array_state(mdname)
907
@patch('curtin.block.mdadm.md_sysfs_attr')
908
def test_md_check_array_state_sync(self, mock_attr):
910
mock_attr.side_effect = [
911
'clean', # array_state
913
'recovery', # sync_action
915
with self.assertRaises(ValueError):
916
mdadm.md_check_array_state(mdname)
918
@patch('curtin.block.mdadm.md_check_array_uuid')
919
@patch('curtin.block.mdadm.md_get_uuid')
920
def test_md_check_uuid(self, mock_guuid, mock_ckuuid):
922
mock_guuid.return_value = '93a73e10:427f280b:b7076c02:204b8f7a'
923
mock_ckuuid.return_value = True
925
rv = mdadm.md_check_uuid(mdname)
928
@patch('curtin.block.mdadm.md_check_array_uuid')
929
@patch('curtin.block.mdadm.md_get_uuid')
930
def test_md_check_uuid_nouuid(self, mock_guuid, mock_ckuuid):
932
mock_guuid.return_value = None
933
with self.assertRaises(ValueError):
934
mdadm.md_check_uuid(mdname)
936
@patch('curtin.block.mdadm.md_get_devices_list')
937
def test_md_check_devices(self, mock_devlist):
939
devices = ['/dev/vdc', '/dev/vdd']
941
mock_devlist.return_value = devices
942
rv = mdadm.md_check_devices(mdname, devices)
943
self.assertEqual(rv, None)
945
@patch('curtin.block.mdadm.md_get_devices_list')
946
def test_md_check_devices_wrong_devs(self, mock_devlist):
948
devices = ['/dev/vdc', '/dev/vdd']
950
mock_devlist.return_value = ['/dev/sda']
951
with self.assertRaises(ValueError):
952
mdadm.md_check_devices(mdname, devices)
954
def test_md_check_devices_no_devs(self):
958
with self.assertRaises(ValueError):
959
mdadm.md_check_devices(mdname, devices)
961
@patch('curtin.block.mdadm.md_get_spares_list')
962
def test_md_check_spares(self, mock_devlist):
964
spares = ['/dev/vdc', '/dev/vdd']
966
mock_devlist.return_value = spares
967
rv = mdadm.md_check_spares(mdname, spares)
968
self.assertEqual(rv, None)
970
@patch('curtin.block.mdadm.md_get_spares_list')
971
def test_md_check_spares_wrong_devs(self, mock_devlist):
973
spares = ['/dev/vdc', '/dev/vdd']
975
mock_devlist.return_value = ['/dev/sda']
976
with self.assertRaises(ValueError):
977
mdadm.md_check_spares(mdname, spares)
979
@patch('curtin.block.mdadm.mdadm_examine')
980
@patch('curtin.block.mdadm.mdadm_query_detail')
981
@patch('curtin.block.mdadm.md_get_uuid')
982
def test_md_check_array_membership(self, mock_uuid, mock_query,
985
devices = ['/dev/vda', '/dev/vdb', '/dev/vdc', '/dev/vdd']
986
md_uuid = '93a73e10:427f280b:b7076c02:204b8f7a'
987
md_dict = {'MD_UUID': md_uuid}
988
mock_query.return_value = md_dict
989
mock_uuid.return_value = md_uuid
990
mock_examine.side_effect = [md_dict] * len(devices)
993
expected_calls.append(call(dev, export=False))
995
rv = mdadm.md_check_array_membership(mdname, devices)
997
self.assertEqual(rv, None)
998
mock_uuid.assert_has_calls([call(mdname)])
999
mock_examine.assert_has_calls(expected_calls)
1001
@patch('curtin.block.mdadm.mdadm_examine')
1002
@patch('curtin.block.mdadm.mdadm_query_detail')
1003
@patch('curtin.block.mdadm.md_get_uuid')
1004
def test_md_check_array_membership_bad_dev(self, mock_uuid, mock_query,
1007
devices = ['/dev/vda', '/dev/vdb', '/dev/vdc', '/dev/vdd']
1008
md_uuid = '93a73e10:427f280b:b7076c02:204b8f7a'
1009
md_dict = {'MD_UUID': md_uuid}
1010
mock_query.return_value = md_dict
1011
mock_uuid.return_value = md_uuid
1012
mock_examine.side_effect = [
1017
] # one device isn't a member
1019
with self.assertRaises(ValueError):
1020
mdadm.md_check_array_membership(mdname, devices)
1022
@patch('curtin.block.mdadm.mdadm_examine')
1023
@patch('curtin.block.mdadm.mdadm_query_detail')
1024
@patch('curtin.block.mdadm.md_get_uuid')
1025
def test_md_check_array_membership_wrong_array(self, mock_uuid, mock_query,
1028
devices = ['/dev/vda', '/dev/vdb', '/dev/vdc', '/dev/vdd']
1029
md_uuid = '93a73e10:427f280b:b7076c02:204b8f7a'
1030
md_dict = {'MD_UUID': '11111111:427f280b:b7076c02:204b8f7a'}
1031
mock_query.return_value = md_dict
1032
mock_uuid.return_value = md_uuid
1033
mock_examine.side_effect = [md_dict] * len(devices)
1035
with self.assertRaises(ValueError):
1036
mdadm.md_check_array_membership(mdname, devices)
1038
@patch('curtin.block.mdadm.md_check_array_membership')
1039
@patch('curtin.block.mdadm.md_check_spares')
1040
@patch('curtin.block.mdadm.md_check_devices')
1041
@patch('curtin.block.mdadm.md_check_uuid')
1042
@patch('curtin.block.mdadm.md_check_raidlevel')
1043
@patch('curtin.block.mdadm.md_check_array_state')
1044
def test_md_check_all_good(self, mock_array, mock_raid, mock_uuid,
1045
mock_dev, mock_spare, mock_member):
1046
md_devname = '/dev/md0'
1048
devices = ['/dev/vda', '/dev/vdb']
1049
spares = ['/dev/vdc']
1051
mock_array.return_value = None
1052
mock_raid.return_value = None
1053
mock_uuid.return_value = None
1054
mock_dev.return_value = None
1055
mock_spare.return_value = None
1056
mock_member.return_value = None
1058
mdadm.md_check(md_devname, raidlevel, devices=devices, spares=spares)
1060
mock_array.assert_has_calls([call(md_devname)])
1061
mock_raid.assert_has_calls([call(raidlevel)])
1062
mock_uuid.assert_has_calls([call(md_devname)])
1063
mock_dev.assert_has_calls([call(md_devname, devices)])
1064
mock_spare.assert_has_calls([call(md_devname, spares)])
1065
mock_member.assert_has_calls([call(md_devname, devices + spares)])
1067
def test_md_check_all_good_devshort(self):
1070
devices = ['/dev/vda', '/dev/vdb']
1071
spares = ['/dev/vdc']
1073
with self.assertRaises(ValueError):
1074
mdadm.md_check(md_devname, raidlevel, devices=devices,
1077
def test_md_present(self):
1079
self.mock_util.load_file.return_value = textwrap.dedent("""
1080
Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5]
1082
md0 : active raid1 vdc1[1] vda2[0]
1083
3143680 blocks super 1.2 [2/2] [UU]
1085
unused devices: <none>
1088
md_is_present = mdadm.md_present(mdname)
1090
self.assertTrue(md_is_present)
1091
self.mock_util.load_file.assert_called_with('/proc/mdstat')
1093
def test_md_present_not_found(self):
1095
self.mock_util.load_file.return_value = textwrap.dedent("""
1096
Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5]
1098
md0 : active raid1 vdc1[1] vda2[0]
1099
3143680 blocks super 1.2 [2/2] [UU]
1101
unused devices: <none>
1104
md_is_present = mdadm.md_present(mdname)
1106
self.assertFalse(md_is_present)
1107
self.mock_util.load_file.assert_called_with('/proc/mdstat')
1109
def test_md_present_not_found_check_matching(self):
1111
found_mdname = 'md10'
1112
self.mock_util.load_file.return_value = textwrap.dedent("""
1113
Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5]
1115
md10 : active raid1 vdc1[1] vda2[0]
1116
3143680 blocks super 1.2 [2/2] [UU]
1118
unused devices: <none>
1121
md_is_present = mdadm.md_present(mdname)
1123
self.assertFalse(md_is_present,
1124
"%s mistakenly matched %s" % (mdname, found_mdname))
1125
self.mock_util.load_file.assert_called_with('/proc/mdstat')
1127
def test_md_present_with_dev_path(self):
1129
self.mock_util.load_file.return_value = textwrap.dedent("""
1130
Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5]
1132
md0 : active raid1 vdc1[1] vda2[0]
1133
3143680 blocks super 1.2 [2/2] [UU]
1135
unused devices: <none>
1138
md_is_present = mdadm.md_present(mdname)
1140
self.assertTrue(md_is_present)
1141
self.mock_util.load_file.assert_called_with('/proc/mdstat')
1143
def test_md_present_none(self):
1145
self.mock_util.load_file.return_value = textwrap.dedent("""
1146
Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5]
1148
md0 : active raid1 vdc1[1] vda2[0]
1149
3143680 blocks super 1.2 [2/2] [UU]
1151
unused devices: <none>
1154
with self.assertRaises(ValueError):
1155
mdadm.md_present(mdname)
1157
# util.load_file should NOT have been called
1158
self.assertEqual([], self.mock_util.call_args_list)
1160
def test_md_present_no_proc_mdstat(self):
1162
self.mock_util.side_effect = IOError
1164
md_is_present = mdadm.md_present(mdname)
1165
self.assertFalse(md_is_present)
1166
self.mock_util.load_file.assert_called_with('/proc/mdstat')
1169
# vi: ts=4 expandtab syntax=python