557
559
with self.assertRaises(ValueError):
558
560
iscsi.volpath_is_iscsi(None)
563
class TestBlockIscsiDiskFromConfig(CiTestCase):
564
# Test iscsi parsing of storage config for iscsi configure disks
567
super(TestBlockIscsiDiskFromConfig, self).setUp()
568
self.add_patch('curtin.block.iscsi.util.subp', 'mock_subp')
570
def test_parse_iscsi_disk_from_config(self):
571
"""Test parsing iscsi volume path creates the same iscsi disk"""
572
target = 'curtin-659d5f45-4f23-46cb-b826-f2937b896e09'
573
iscsi_path = 'iscsi:10.245.168.20::20112:1:' + target
576
'config': [{'type': 'disk',
579
'name': 'iscsi_disk1',
581
'wipe': 'superblock'}]
584
expected_iscsi_disk = iscsi.IscsiDisk(iscsi_path)
585
iscsi_disk = iscsi.get_iscsi_disks_from_config(cfg).pop()
586
# utilize IscsiDisk str method for equality check
587
self.assertEqual(str(expected_iscsi_disk), str(iscsi_disk))
589
def test_parse_iscsi_disk_from_config_no_iscsi(self):
590
"""Test parsing storage config with no iscsi disks included"""
593
'config': [{'type': 'disk',
595
'path': 'dev/slash/foo1',
596
'name': 'the-fast-one',
598
'wipe': 'superblock'}]
601
expected_iscsi_disks = []
602
iscsi_disks = iscsi.get_iscsi_disks_from_config(cfg)
603
self.assertEqual(expected_iscsi_disks, iscsi_disks)
605
def test_parse_iscsi_disk_from_config_invalid_iscsi(self):
606
"""Test parsing storage config with no iscsi disks included"""
609
'config': [{'type': 'disk',
611
'path': 'iscsi:garbage',
614
'wipe': 'superblock'}]
617
with self.assertRaises(ValueError):
618
iscsi.get_iscsi_disks_from_config(cfg)
620
def test_parse_iscsi_disk_from_config_empty(self):
621
"""Test parse_iscsi_disks handles empty/invalid config"""
622
expected_iscsi_disks = []
623
iscsi_disks = iscsi.get_iscsi_disks_from_config({})
624
self.assertEqual(expected_iscsi_disks, iscsi_disks)
626
cfg = {'storage': {'config': []}}
627
iscsi_disks = iscsi.get_iscsi_disks_from_config(cfg)
628
self.assertEqual(expected_iscsi_disks, iscsi_disks)
630
def test_parse_iscsi_disk_from_config_none(self):
631
"""Test parse_iscsi_disks handles no config"""
632
expected_iscsi_disks = []
633
iscsi_disks = iscsi.get_iscsi_disks_from_config({})
634
self.assertEqual(expected_iscsi_disks, iscsi_disks)
637
iscsi_disks = iscsi.get_iscsi_disks_from_config(cfg)
638
self.assertEqual(expected_iscsi_disks, iscsi_disks)
641
class TestBlockIscsiDisconnect(CiTestCase):
642
# test that when disconnecting iscsi targets we
643
# check that the target has an active session before
644
# issuing a disconnect command
647
super(TestBlockIscsiDisconnect, self).setUp()
648
self.add_patch('curtin.block.iscsi.util.subp', 'mock_subp')
649
self.add_patch('curtin.block.iscsi.iscsiadm_sessions',
650
'mock_iscsi_sessions')
651
# fake target_root + iscsi nodes dir
652
self.target_path = self.tmp_dir()
653
self.iscsi_nodes = os.path.join(self.target_path, 'etc/iscsi/nodes')
654
util.ensure_dir(self.iscsi_nodes)
656
def _fmt_disconnect(self, target, portal):
657
return ['iscsiadm', '--mode=node', '--targetname=%s' % target,
658
'--portal=%s' % portal, '--logout']
660
def _setup_nodes(self, sessions, connection):
661
# setup iscsi_nodes dir (<fakeroot>/etc/iscsi/nodes) with content
663
sdir = os.path.join(self.iscsi_nodes, s)
664
connpath = os.path.join(sdir, connection)
665
util.ensure_dir(sdir)
666
util.write_file(connpath, content="")
668
def test_disconnect_target_disk(self):
669
"""Test iscsi disconnecting multiple sessions, all present"""
672
'curtin-53ab23ff-a887-449a-80a8-288151208091',
673
'curtin-94b62de1-c579-42c0-879e-8a28178e64c5',
674
'curtin-556aeecd-a227-41b7-83d7-2bb471c574b4',
675
'curtin-fd0f644b-7858-420f-9997-3ea2aefe87b9'
677
connection = '10.245.168.20,16395,1'
678
self._setup_nodes(sessions, connection)
680
self.mock_iscsi_sessions.return_value = "\n".join(sessions)
682
iscsi.disconnect_target_disks(self.target_path)
685
for session in sessions:
686
(host, port, _) = connection.split(',')
687
disconnect = self._fmt_disconnect(session, "%s:%s" % (host, port))
690
mock.call(disconnect, capture=True, log_captured=True),
691
mock.call(['udevadm', 'settle']),
693
expected_calls.extend(calls)
695
self.mock_subp.assert_has_calls(expected_calls, any_order=True)
697
def test_disconnect_target_disk_skip_disconnected(self):
698
"""Test iscsi does not attempt to disconnect already closed sessions"""
700
'curtin-53ab23ff-a887-449a-80a8-288151208091',
701
'curtin-94b62de1-c579-42c0-879e-8a28178e64c5',
702
'curtin-556aeecd-a227-41b7-83d7-2bb471c574b4',
703
'curtin-fd0f644b-7858-420f-9997-3ea2aefe87b9'
705
connection = '10.245.168.20,16395,1'
706
self._setup_nodes(sessions, connection)
707
# Test with all sessions are already disconnected
708
self.mock_iscsi_sessions.return_value = ""
710
iscsi.disconnect_target_disks(self.target_path)
712
self.mock_subp.assert_has_calls([], any_order=True)
714
@mock.patch('curtin.block.iscsi.iscsiadm_logout')
715
def test_disconnect_target_disk_raises_runtime_error(self, mock_logout):
716
"""Test iscsi raises RuntimeError if we fail to logout"""
718
'curtin-53ab23ff-a887-449a-80a8-288151208091',
720
connection = '10.245.168.20,16395,1'
721
self._setup_nodes(sessions, connection)
722
self.mock_iscsi_sessions.return_value = "\n".join(sessions)
723
mock_logout.side_effect = util.ProcessExecutionError()
725
with self.assertRaises(RuntimeError):
726
iscsi.disconnect_target_disks(self.target_path)
729
for session in sessions:
730
(host, port, _) = connection.split(',')
731
disconnect = self._fmt_disconnect(session, "%s:%s" % (host, port))
734
mock.call(disconnect, capture=True, log_captured=True),
735
mock.call(['udevadm', 'settle']),
737
expected_calls.extend(calls)
739
self.mock_subp.assert_has_calls([], any_order=True)
560
741
# vi: ts=4 expandtab syntax=python