From 5ec2e23a32f88bc0576eba9d8c077496b52d49f0 Mon Sep 17 00:00:00 2001 From: Mark Syms Date: Fri, 22 May 2020 16:23:08 +0100 Subject: [PATCH 1/7] CA-340203: remove name header from mpath map list Signed-off-by: Mark Syms --- drivers/mpath_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mpath_cli.py b/drivers/mpath_cli.py index ce04e18e4..95051f02b 100755 --- a/drivers/mpath_cli.py +++ b/drivers/mpath_cli.py @@ -101,7 +101,7 @@ def list_maps(): util.SMlog("mpath cmd: %s" % cmd) (rc,stdout,stderr) = util.doexec(mpathcmd,cmd) util.SMlog("mpath output: %s" % stdout) - return map(lambda x: x.split(' ')[0], stdout.split('\n')[1:-1]) + return map(lambda x: x.split(' ')[0], stdout.split('\n')[2:-1]) def ensure_map_gone(scsi_id): while True: From c1b97680c0ff94dd6b7c97fd77c97637ea835414 Mon Sep 17 00:00:00 2001 From: Mark Syms Date: Fri, 22 May 2020 16:23:53 +0100 Subject: [PATCH 2/7] Select specific version of mock as used by CentOS Signed-off-by: Mark Syms --- base_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base_requirements.txt b/base_requirements.txt index 76b5fc67d..112fbebae 100644 --- a/base_requirements.txt +++ b/base_requirements.txt @@ -1,4 +1,4 @@ -mock +mock==1.0.1 xenapi coverage astroid==1.4.9 From 0b9ce5229e6aa512ad26fcf01d4b2e6ef2f33b9f Mon Sep 17 00:00:00 2001 From: Mark Syms Date: Fri, 22 May 2020 14:56:24 +0100 Subject: [PATCH 3/7] CP-34042: Remove dead open-iscsi code Signed-off-by: Mark Syms --- drivers/iscsilib.py | 10 ++-------- drivers/mpath_dmp.py | 16 ---------------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/drivers/iscsilib.py b/drivers/iscsilib.py index ca9c02f09..75cb4934b 100644 --- a/drivers/iscsilib.py +++ b/drivers/iscsilib.py @@ -365,10 +365,7 @@ def is_iscsi_daemon_running(): def stop_daemon(): if is_iscsi_daemon_running(): - if os.path.exists("/etc/init.d/open-iscsi"): - cmd = ["/etc/init.d/open-iscsi", "stop"] - else: - cmd = ["service", "iscsid", "stop"] + cmd = ["service", "iscsid", "stop"] failuremessage = "Failed to stop iscsi daemon" exn_on_failure(cmd,failuremessage) @@ -383,10 +380,7 @@ def restart_daemon(): shutil.rmtree(os.path.join(_ISCSI_DB_PATH, 'send_targets')) except: pass - if os.path.exists("/etc/init.d/open-iscsi"): - cmd = ["/etc/init.d/open-iscsi", "start"] - else: - cmd = ["service", "iscsid", "start"] + cmd = ["service", "iscsid", "start"] failuremessage = "Failed to start iscsi daemon" exn_on_failure(cmd,failuremessage) diff --git a/drivers/mpath_dmp.py b/drivers/mpath_dmp.py index 75be1e700..b4b5fa78c 100755 --- a/drivers/mpath_dmp.py +++ b/drivers/mpath_dmp.py @@ -26,10 +26,6 @@ import wwid_conf import errno -iscsi_mpath_file = "/etc/iscsi/iscsid-mpath.conf" -iscsi_default_file = "/etc/iscsi/iscsid-default.conf" -iscsi_file = "/etc/iscsi/iscsid.conf" - DMPBIN = "/sbin/multipath" DEVMAPPERPATH = "/dev/mapper" DEVBYIDPATH = "/dev/disk/by-id" @@ -252,14 +248,6 @@ def _refresh_DMP(sid, npaths): def activate(): util.SMlog("MPATH: multipath activate called") - cmd = ['ln', '-sf', iscsi_mpath_file, iscsi_file] - try: - if os.path.exists(iscsi_mpath_file): - # Only do this if using our customized open-iscsi package - util.pread2(cmd) - except util.CommandException, ce: - if not ce.reason.endswith(': File exists'): - raise # If we've got no active sessions, and the deamon is already running, # we're ok to restart the daemon @@ -283,10 +271,6 @@ def activate(): def deactivate(): util.SMlog("MPATH: multipath deactivate called") - cmd = ['ln', '-sf', iscsi_default_file, iscsi_file] - if os.path.exists(iscsi_default_file): - # Only do this if using our customized open-iscsi package - util.pread2(cmd) if _is_mpath_daemon_running(): # Flush the multipath nodes From 10c72071397ff67962103d486142e8c2fd68faf5 Mon Sep 17 00:00:00 2001 From: Mark Syms Date: Fri, 22 May 2020 17:16:14 +0100 Subject: [PATCH 4/7] CP-34042: Remove dead map_by_scsibus method Signed-off-by: Mark Syms --- drivers/mpath_dmp.py | 51 -------------------------------------------- 1 file changed, 51 deletions(-) diff --git a/drivers/mpath_dmp.py b/drivers/mpath_dmp.py index b4b5fa78c..0783aeeb9 100755 --- a/drivers/mpath_dmp.py +++ b/drivers/mpath_dmp.py @@ -110,57 +110,6 @@ def __map_explicit(devices): except: util.SMlog("WARNING: exception raised while attempting to add path %s" % base) -def map_by_scsibus(sid,npaths=0): - # Synchronously creates/refreshs the MP map for a single SCSIid. - # Gathers the device vector from /dev/disk/by-scsibus - we expect - # there to be 'npaths' paths - - util.SMlog("map_by_scsibus: sid=%s" % sid) - - devices = [] - - # Wait for up to 60 seconds for n devices to appear - for attempt in range(0,60): - devices = scsiutil._genReverseSCSIidmap(sid) - - # If we've got the right number of paths, or we don't know - # how many devices there ought to be, tell multipathd about - # the paths, and return. - if(len(devices)>=npaths or npaths==0): - # Enable this device's sid: it could be blacklisted - # We expect devices to be blacklisted according to their - # wwid only. We go through the list of paths until we have - # a definite answer about the device's blacklist status. - # If the path we are checking is down, we cannot tell. - for dev in devices: - try: - if wwid_conf.is_blacklisted(dev): - try: - wwid_conf.edit_wwid(sid) - except: - util.SMlog("WARNING: exception raised while " - "attempting to modify multipath.conf") - try: - mpath_cli.reconfigure() - except: - util.SMlog("WARNING: exception raised while " - "attempting to reconfigure") - time.sleep(5) - - break - except wwid_conf.WWIDException as e: - util.SMlog(e.errstr) - else: - util.SMlog("Device 'SCSI_id: {}' is inaccessible; " - "All paths are down.".format(sid)) - - __map_explicit(devices) - return - - time.sleep(1) - - __map_explicit(devices) - def refresh(sid,npaths): # Refresh the multipath status util.SMlog("Refreshing LUN %s" % sid) From 4b79a33cd2d75017431a4f8b13664956da469f4c Mon Sep 17 00:00:00 2001 From: Mark Syms Date: Fri, 22 May 2020 17:07:29 +0100 Subject: [PATCH 5/7] CP-34042: Remove dead parameter from mpath reset Signed-off-by: Mark Syms --- drivers/BaseISCSI.py | 4 ++-- drivers/LVHDoHBASR.py | 10 +++++----- drivers/RawHBASR.py | 3 +-- drivers/mpath_dmp.py | 8 ++++---- drivers/mpath_null.py | 2 +- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/BaseISCSI.py b/drivers/BaseISCSI.py index 69d697d57..417a7ffa1 100755 --- a/drivers/BaseISCSI.py +++ b/drivers/BaseISCSI.py @@ -422,7 +422,7 @@ def detach(self, sr_uuid, delete=False): except: pass if self.dconf.has_key('SCSIid'): - self.mpathmodule.reset(self.dconf['SCSIid'], True) # explicitly unmap + self.mpathmodule.reset(self.dconf['SCSIid'], explicit_unmap=True) keys.append("mpath-" + self.dconf['SCSIid']) # Remove iscsi_sessions and multipathed keys @@ -598,7 +598,7 @@ def _detach_LUN_bylunid(self, lunid, SCSIid): if not self.attached: raise xs_errors.XenError('SRUnavailable') if self.mpath == 'true' and len(SCSIid): - self.mpathmodule.reset(SCSIid, True) + self.mpathmodule.reset(SCSIid, explicit_unmap=True) util.remove_mpathcount_field(self.session, self.host_ref, self.sr_ref, SCSIid) for val in self.adapter: if not self.pathdict.has_key(val): diff --git a/drivers/LVHDoHBASR.py b/drivers/LVHDoHBASR.py index 687fa5815..a71770196 100755 --- a/drivers/LVHDoHBASR.py +++ b/drivers/LVHDoHBASR.py @@ -112,7 +112,7 @@ def create(self, sr_uuid, size): LVHDSR.LVHDSR.create(self, sr_uuid, size) finally: if self.mpath == "true": - self.mpathmodule.reset(self.SCSIid, True) # explicit unmap + self.mpathmodule.reset(self.SCSIid, explicit_unmap=True) util.remove_mpathcount_field(self.session, self.host_ref, \ self.sr_ref, self.SCSIid) @@ -167,16 +167,16 @@ def probe(self): self._pathrefresh(LVHDoHBASR) result = LVHDSR.LVHDSR.probe(self) if self.mpath == "true": - self.mpathmodule.reset(self.SCSIid,True) + self.mpathmodule.reset(self.SCSIid, explicit_unmap=True) return result except: if self.mpath == "true": - self.mpathmodule.reset(self.SCSIid,True) + self.mpathmodule.reset(self.SCSIid, explicit_unmap=True) raise def detach(self, sr_uuid): LVHDSR.LVHDSR.detach(self, sr_uuid) - self.mpathmodule.reset(self.SCSIid,True,True) # explicit_unmap + self.mpathmodule.reset(self.SCSIid, explicit_unmap=True) try: pbdref = util.find_my_pbd(self.session, self.host_ref, self.sr_ref) except: @@ -192,7 +192,7 @@ def delete(self, sr_uuid): LVHDSR.LVHDSR.delete(self, sr_uuid) finally: if self.mpath == "true": - self.mpathmodule.reset(self.SCSIid, True) # explicit unmap + self.mpathmodule.reset(self.SCSIid, explicit_unmap=True) def vdi(self, uuid): return LVHDoHBAVDI(self, uuid) diff --git a/drivers/RawHBASR.py b/drivers/RawHBASR.py index a596685c8..08c335a35 100755 --- a/drivers/RawHBASR.py +++ b/drivers/RawHBASR.py @@ -399,8 +399,7 @@ def detach(self, sr_uuid, vdi_uuid): # Multipath disable if self.sr.mpath == "true": - #devices = scsiutil._genReverseSCSIidmap(scsi_id) - self.sr.mpathmodule.reset(scsi_id, True) + self.sr.mpathmodule.reset(scsi_id, explicit_unmap=True) util.remove_mpathcount_field(self.sr.session, self.sr.host_ref, self.sr.sr_ref, scsi_id) diff --git a/drivers/mpath_dmp.py b/drivers/mpath_dmp.py index 0783aeeb9..0726fb7c8 100755 --- a/drivers/mpath_dmp.py +++ b/drivers/mpath_dmp.py @@ -58,9 +58,9 @@ def deactivate_MPdev(sid): if os.path.exists(path): os.unlink(path) -def reset(sid,explicit_unmap=False,delete_nodes=False): +def reset(sid,explicit_unmap=False): util.SMlog("Resetting LUN %s" % sid) - _resetDMP(sid,explicit_unmap,delete_nodes) + _resetDMP(sid,explicit_unmap) def _delete_node(dev): try: @@ -70,8 +70,8 @@ def _delete_node(dev): os.close(f) except: util.SMlog("Failed to delete %s" % dev) - -def _resetDMP(sid,explicit_unmap=False,delete_nodes=False): + +def _resetDMP(sid, explicit_unmap=False): # If mpath has been turned on since the sr/vdi was attached, we # might be trying to unmap it before the daemon has been started # This is unnecessary (and will fail) so just return. diff --git a/drivers/mpath_null.py b/drivers/mpath_null.py index 211409e31..dc449b12d 100755 --- a/drivers/mpath_null.py +++ b/drivers/mpath_null.py @@ -20,7 +20,7 @@ def refresh(sid,npaths): return -def reset(sid,explicit_unmap=False,delete_nodes=False): +def reset(sid,explicit_unmap=False): return def activate(): From 7892ef0ee3e8029b3462e361461f16fc5801094a Mon Sep 17 00:00:00 2001 From: Mark Syms Date: Tue, 26 May 2020 11:59:47 +0100 Subject: [PATCH 6/7] CP-34042: Remove dead methods from mpath modules Signed-off-by: Mark Syms --- drivers/mpath_dmp.py | 28 ---------------------------- drivers/mpath_null.py | 3 --- 2 files changed, 31 deletions(-) diff --git a/drivers/mpath_dmp.py b/drivers/mpath_dmp.py index 0726fb7c8..2b2234a5f 100755 --- a/drivers/mpath_dmp.py +++ b/drivers/mpath_dmp.py @@ -62,15 +62,6 @@ def reset(sid,explicit_unmap=False): util.SMlog("Resetting LUN %s" % sid) _resetDMP(sid,explicit_unmap) -def _delete_node(dev): - try: - path = '/sys/block/' + dev + '/device/delete' - f = os.open(path, os.O_WRONLY) - os.write(f,'1') - os.close(f) - except: - util.SMlog("Failed to delete %s" % dev) - def _resetDMP(sid, explicit_unmap=False): # If mpath has been turned on since the sr/vdi was attached, we # might be trying to unmap it before the daemon has been started @@ -99,17 +90,6 @@ def _resetDMP(sid, explicit_unmap=False): else: util.SMlog("MPATH: path disappeared [%s]" % path) -# expecting e.g. ["/dev/sda","/dev/sdb"] or ["/dev/disk/by-scsibus/...whatever" (links to the real devices)] -def __map_explicit(devices): - for device in devices: - realpath = os.path.realpath(device) - base = os.path.basename(realpath) - util.SMlog("Adding mpath path '%s'" % base) - try: - mpath_cli.add_path(base) - except: - util.SMlog("WARNING: exception raised while attempting to add path %s" % base) - def refresh(sid,npaths): # Refresh the multipath status util.SMlog("Refreshing LUN %s" % sid) @@ -248,11 +228,3 @@ def path(SCSIid): return path else: return DEVBYIDPATH + "/scsi-" + SCSIid - -def status(SCSIid): - pass - -def get_TargetID_LunNUM(SCSIid): - devices = scsiutil._genReverseSCSIidmap(SCSIid) - cmd = [MPPGETAIDLNOBIN, devices[0]] - return util.pread2(cmd).split('\n')[0] diff --git a/drivers/mpath_null.py b/drivers/mpath_null.py index dc449b12d..e822fb919 100755 --- a/drivers/mpath_null.py +++ b/drivers/mpath_null.py @@ -31,6 +31,3 @@ def deactivate(): def path(SCSIid): return DEVBYIDPATH + "/scsi-" + SCSIid - -def status(SCSIid): - pass From 362856dbbe5ddc9c67379f394d5b18e3a4113e79 Mon Sep 17 00:00:00 2001 From: Mark Syms Date: Tue, 12 May 2020 19:09:52 +0100 Subject: [PATCH 7/7] CP-34042: Add unit tests for mpath utils Signed-off-by: Mark Syms --- tests/test_mpath_dmp.py | 334 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 331 insertions(+), 3 deletions(-) diff --git a/tests/test_mpath_dmp.py b/tests/test_mpath_dmp.py index 1c0b6bebb..51b88686c 100644 --- a/tests/test_mpath_dmp.py +++ b/tests/test_mpath_dmp.py @@ -6,8 +6,15 @@ import unittest import mock import testlib +import util + import mpath_dmp import SR +from SR import SROSError + +from Queue import Queue + +import xs_errors # pylint: disable=W0613; mocks don't need to be accessed @@ -18,6 +25,15 @@ class TestMpathDmp(unittest.TestCase): Unit tests for mpath dmp """ + def setUp(self): + time_patcher = mock.patch('mpath_dmp.time', autospec=True) + self.mock_time = time_patcher.start() + + mpath_cli_patcher = mock.patch('mpath_dmp.mpath_cli', autospec=True) + self.mock_mpath_cli = mpath_cli_patcher.start() + + self.addCleanup(mock.patch.stopall) + @testlib.with_context @mock.patch('mpath_dmp.util', autospec=True) @mock.patch('mpath_dmp.os', autospec=True) @@ -77,7 +93,7 @@ def test_is_valid_multipath_device(self, context, mock_os, util_mod): @mock.patch('mpath_dmp.os.mkdir', autospec=True) def test_activate_no_exception(self, mock_mkdir, pread2): """ - Test that activeate MPDev works if directory does not exist + Test that activate MPDev works if directory does not exist """ mpath_dmp.activate_MPdev("sid", "dst") pread2.assert_called_with(['ln', '-sf', "dst", os.path.join(mpath_dmp.MP_INUSEDIR, "sid")]) @@ -86,7 +102,7 @@ def test_activate_no_exception(self, mock_mkdir, pread2): @mock.patch('mpath_dmp.os.mkdir', autospec=True) def test_activate_exists_success(self, mock_mkdir, pread2): """ - Test that activeate MPDev works if directory exists + Test that activate MPDev works if directory exists """ mock_mkdir.side_effect = [OSError(errno.EEXIST, "Directory exists")] mpath_dmp.activate_MPdev("sid", "dst") @@ -95,10 +111,322 @@ def test_activate_exists_success(self, mock_mkdir, pread2): @mock.patch('mpath_dmp.os.mkdir', autospec=True) def test_activate_permission_denied(self, mock_mkdir): """ - Test that activeate MPDev works if mkdir returns permission denied + Test that activate MPDev works if mkdir returns permission denied """ mock_mkdir.side_effect = [OSError(errno.EPERM, "Permission denied")] with self.assertRaises(OSError) as context: mpath_dmp.activate_MPdev("sid", "dst") self.assertEqual(errno.EPERM, context.exception.errno) + + @testlib.with_context + @mock.patch('mpath_dmp._is_valid_multipath_device', autospec=True) + @mock.patch('mpath_dmp.util', autospec=True) + @mock.patch('mpath_dmp.os.path.exists', autospec=True) + def test_refresh_dmp_success(self, context, mock_exists, mock_util, mock_valid): + """ + Test refresh DMP in success case + """ + mock_valid.return_value = True + + test_id='360871234' + + mock_exists.return_value = True + + with mock.patch('mpath_dmp.activate_MPdev') as mock_activate: + mpath_dmp._refresh_DMP(test_id, 4) + + # util retry around multipath should not be called + self.assertEqual(0, mock_util.retry.call_count) + + self.assertTrue( + mock_util.wait_for_path.call_args_list[0][0][0].endswith( + '%s/mapper' % test_id), + msg='wait_for_path not called with expected mapper path') + mock_activate.assert_called_with(test_id, mock.ANY) + + @testlib.with_context + @mock.patch('mpath_dmp._is_valid_multipath_device', autospec=True) + @mock.patch('mpath_dmp.util', autospec=True) + def test_refresh_dmp_device_not_found(self, context, mock_util, mock_valid): + """ + Test refresh DMP device not found + """ + # Setup error codes + context.setup_error_codes() + + mock_valid.return_value = True + + test_id='360871234' + + with self.assertRaises(SROSError): + mpath_dmp._refresh_DMP(test_id, 4) + + self.assertEqual(1, mock_util.wait_for_path.call_count) + self.assertTrue( + mock_util.wait_for_path.call_args_list[0][0][0].endswith(test_id)) + + @mock.patch('mpath_dmp._is_valid_multipath_device', autospec=True) + @mock.patch('mpath_dmp.util.pread2', autospec=True) + @mock.patch('mpath_dmp.util.wait_for_path', autospec=True) + @mock.patch('mpath_dmp.os.path.exists', autospec=True) + def test_refresh_dmp_reload_required( + self, mock_exists, mock_wait_for_path, mock_pread, mock_valid): + """ + Test refresh DMP device reload + """ + mock_valid.return_value = True + + test_id='360871234' + + mapper_exists = Queue() + mapper_exists.put(False) + mapper_exists.put(True) + + exists_data = {'/dev/mapper/%s' % test_id: mapper_exists} + + def exists(path): + assert(path in exists_data) + return exists_data[path].get() + + mock_exists.side_effect = exists + + mock_pread.return_value = 0 + + with mock.patch('mpath_dmp.activate_MPdev') as mock_activate: + mpath_dmp._refresh_DMP(test_id, 4) + + self.assertTrue( + mock_wait_for_path.call_args_list[0][0][0].endswith(test_id)) + + @mock.patch('mpath_dmp.iscsilib', autospec=True) + @mock.patch('mpath_dmp.util', autospec=True) + def test_activate_noiscsi_success( + self, mock_util, mock_iscsilib): + """ + MPATH activate, no iscsi, success + """ + mock_iscsilib.is_iscsi_daemon_running.return_value = False + mock_util.doexec.return_value = (0, "", "") + self.mock_mpath_cli.is_working.side_effect = [False, False, True] + + mpath_dmp.activate() + + self.assertEqual(0, mock_util.pread2.call_count) + + @mock.patch('mpath_dmp.iscsilib', autospec=True) + @mock.patch('mpath_dmp.util', autospec=True) + def test_activate_noiscsi_start_mpath( + self, mock_util, mock_iscsilib): + """ + MPATH activate, no iscsi, start mpath + """ + mock_iscsilib.is_iscsi_daemon_running.return_value = False + mock_util.doexec.return_value = (1, "", "") + self.mock_mpath_cli.is_working.side_effect = [False, False, True] + + mpath_dmp.activate() + + self.assertEqual(1, mock_util.pread2.call_count) + mock_util.pread2.assert_called_once_with( + ['service', 'multipathd', 'start']) + + @testlib.with_context + @mock.patch('mpath_dmp.iscsilib', autospec=True) + @mock.patch('mpath_dmp.util', autospec=True) + def test_activate_noiscsi_mpath_not_working( + self, context, mock_util, mock_iscsilib): + """ + MPATH activate, mpath not running + """ + # Setup error codes + context.setup_error_codes() + + mock_iscsilib.is_iscsi_daemon_running.return_value = False + mock_util.doexec.return_value = (0, "", "") + self.mock_mpath_cli.is_working.side_effect = [False] * 120 + + with self.assertRaises(SROSError) as soe: + mpath_dmp.activate() + + self.assertEqual(430, soe.exception.errno) + + @mock.patch('mpath_dmp.iscsilib', autospec=True) + @mock.patch('mpath_dmp.util', autospec=True) + def test_activate_active_iscsi_success( + self, mock_util, mock_iscsilib): + """ + MPATH activate, active iscsi, success + """ + mock_iscsilib.is_iscsi_daemon_running.return_value = True + mock_iscsilib._checkAnyTGT.return_value = True + mock_util.doexec.return_value = (0, "", "") + self.mock_mpath_cli.is_working.side_effect = [False, False, True] + + mpath_dmp.activate() + + self.assertEqual(0, mock_iscsilib.restart_daemon.call_count) + + @mock.patch('mpath_dmp.iscsilib', autospec=True) + @mock.patch('mpath_dmp.util', autospec=True) + def test_activate_iscsi_no_targets_success( + self, mock_util, mock_iscsilib): + """ + MPATH activate, iscsi, no_targets, success + """ + mock_iscsilib.is_iscsi_daemon_running.return_value = True + mock_iscsilib._checkAnyTGT.return_value = False + mock_util.doexec.return_value = (0, "", "") + self.mock_mpath_cli.is_working.side_effect = [False, False, True] + + mpath_dmp.activate() + + self.assertEqual(0, mock_util.pread2.call_count) + self.assertEqual(1, mock_iscsilib.restart_daemon.call_count) + + @mock.patch('mpath_dmp.glob.glob', autospec=True) + @mock.patch('mpath_dmp.os.path.realpath', autospec=True) + @mock.patch('mpath_dmp.iscsilib', autospec=True) + @mock.patch('mpath_dmp.util', autospec=True) + def test_deactivate_mpath_running( + self, mock_util, mock_iscsilib, mock_realpath, mock_glob): + """ + MPATH deactivate, running, success + """ + mock_util.doexec.return_value = (0, "", "") + self.mock_mpath_cli.list_maps.return_value = [ + '360a98000534b4f4e46704f5270674d70', + '3600140582622313e8dc4270a4a897b4e'] + mock_realpath.return_value = '/dev/disk/by-id/scsi-34564' + mock_util.retry.side_effect = util.retry + + mpath_dmp.deactivate() + + # Check that the mpath maps were removed + mock_util.pread2.assert_has_calls([ + mock.call(['/usr/sbin/multipath', '-f', '360a98000534b4f4e46704f5270674d70']), + mock.call(['/usr/sbin/multipath', '-W']), + mock.call(['/usr/sbin/multipath', '-f', '3600140582622313e8dc4270a4a897b4e']), + mock.call(['/usr/sbin/multipath', '-W'])]) + + @mock.patch('mpath_dmp.glob.glob', autospec=True) + @mock.patch('mpath_dmp.os.path.realpath', autospec=True) + @mock.patch('mpath_dmp.iscsilib', autospec=True) + @mock.patch('mpath_dmp.util', autospec=True) + def test_deactivate_mpath_root( + self, mock_util, mock_iscsilib, mock_realpath, mock_glob): + """ + MPATH deactivate, mpathed root + """ + mock_util.doexec.return_value = (0, "", "") + self.mock_mpath_cli.list_maps.return_value = [ + '360a98000534b4f4e46704f5270674d70', + '3600140582622313e8dc4270a4a897b4e'] + mock_realpath.side_effect = iter( + ['/dev/mapper/34564', '/dev/mapper/34564', + '/dev/mapper/360a98000534b4f4e46704f5270674d70']) + mock_glob.return_value = [ + '/dev/mapper/34564', + '/dev/mapper/360a98000534b4f4e46704f5270674d70'] + mock_util.retry.side_effect = util.retry + + mpath_dmp.deactivate() + + # Check that the mpath maps were removed + mock_util.pread2.assert_has_calls([ + mock.call(['/usr/sbin/multipath', '-f', '360a98000534b4f4e46704f5270674d70']), + mock.call(['/usr/sbin/multipath', '-W']), + mock.call(['/usr/sbin/multipath', '-f', '3600140582622313e8dc4270a4a897b4e']), + mock.call(['/usr/sbin/multipath', '-W'])]) + + @mock.patch('mpath_dmp.glob.glob', autospec=True) + @mock.patch('mpath_dmp.os.path.realpath', autospec=True) + @mock.patch('mpath_dmp.iscsilib', autospec=True) + @mock.patch('mpath_dmp.util', autospec=True) + def test_deactivate_mpath_no_iscsi_targets( + self, mock_util, mock_iscsilib, mock_realpath, mock_glob): + """ + MPATH deactivate, running, success + """ + mock_util.doexec.return_value = (0, "", "") + self.mock_mpath_cli.list_maps.return_value = [ + '360a98000534b4f4e46704f5270674d70', + '3600140582622313e8dc4270a4a897b4e'] + mock_realpath.return_value = '/dev/disk/by-id/scsi-34564' + mock_util.retry.side_effect = util.retry + mock_iscsilib.is_iscsi_daemon_running.return_value = True + mock_iscsilib._checkAnyTGT.return_value = False + + mpath_dmp.deactivate() + + # Check that the mpath maps were removed + mock_util.pread2.assert_has_calls([ + mock.call(['/usr/sbin/multipath', '-f', '360a98000534b4f4e46704f5270674d70']), + mock.call(['/usr/sbin/multipath', '-W']), + mock.call(['/usr/sbin/multipath', '-f', '3600140582622313e8dc4270a4a897b4e']), + mock.call(['/usr/sbin/multipath', '-W'])]) + + self.assertEqual(1, mock_iscsilib.restart_daemon.call_count) + + @testlib.with_context + def test_refresh_no_sid(self, context): + # Setup error codes + context.setup_error_codes() + + with self.assertRaises(SR.SROSError): + mpath_dmp.refresh("", 0) + + @mock.patch('mpath_dmp._refresh_DMP', autospec=True) + @mock.patch('mpath_dmp.os.path.exists', autospec=True) + def test_refresh_path_exists(self, mock_exists, mock_refresh): + + mock_exists.return_value = True + + mpath_dmp.refresh('360a98000534b4f4e46704f5270674d70', 0) + + mock_refresh.assert_called_once_with( + '360a98000534b4f4e46704f5270674d70', 0) + + mock_exists.assert_called_once_with( + '/dev/disk/by-id/scsi-360a98000534b4f4e46704f5270674d70') + + @mock.patch('mpath_dmp.util.wait_for_path', autospec=True) + @mock.patch('mpath_dmp.scsiutil', autospec=True) + @mock.patch('mpath_dmp._refresh_DMP', autospec=True) + @mock.patch('mpath_dmp.os.path.exists', autospec=True) + def test_refresh_refresh_scsi( + self, mock_exists, mock_refresh, mock_scsiutil, mock_wait): + + mock_exists.return_value = False + mock_wait.return_value = True + + mpath_dmp.refresh('360a98000534b4f4e46704f5270674d70', 0) + + mock_refresh.assert_called_once_with( + '360a98000534b4f4e46704f5270674d70', 0) + + mock_exists.assert_called_once_with( + '/dev/disk/by-id/scsi-360a98000534b4f4e46704f5270674d70') + + @testlib.with_context + @mock.patch('mpath_dmp.util.wait_for_path', autospec=True) + @mock.patch('mpath_dmp.scsiutil', autospec=True) + @mock.patch('mpath_dmp.os.path.exists', autospec=True) + def test_refresh_refresh_error( + self, context, mock_exists, mock_scsiutil, mock_wait): + + # Setup error codes + context.setup_error_codes() + + def exists(path): + print 'Exists %s' % path + if path.startswith('/dev/'): + return False + + return True + + mock_exists.side_effect = exists + mock_wait.return_value = False + + with self.assertRaises(SR.SROSError): + mpath_dmp.refresh('360a98000534b4f4e46704f5270674d70', 0)