From 9ef47c280b03b971f84b2d12918e55be0f8804a3 Mon Sep 17 00:00:00 2001 From: Anthony Lee Date: Wed, 15 Oct 2014 14:16:32 -0700 Subject: [PATCH] Removing locks from 3PAR FC and iSCSI drivers Removed locks from the 3PAR FC and iSCSI drivers. In high load environments where many simultaneous volume creations/deletions and attaches/detaches are happening errors occur periodically. By changing the drivers to create a new connection to the 3PAR backend whenever a volume request is made the errors are avoided and performance is improved. Closes-Bug: 1381190 Change-Id: Ie588a1d87cf5a22ddf2e890c440582e1fe67f2cb --- cinder/tests/test_hp3par.py | 3540 ++++++++++------- .../volume/drivers/san/hp/hp_3par_common.py | 55 +- cinder/volume/drivers/san/hp/hp_3par_fc.py | 236 +- cinder/volume/drivers/san/hp/hp_3par_iscsi.py | 311 +- 4 files changed, 2296 insertions(+), 1846 deletions(-) diff --git a/cinder/tests/test_hp3par.py b/cinder/tests/test_hp3par.py index ed2f9048956..d9e090bbe01 100644 --- a/cinder/tests/test_hp3par.py +++ b/cinder/tests/test_hp3par.py @@ -359,6 +359,19 @@ class HP3PARBaseDriver(object): # intentionally removed to make _retype more usable for other use cases. RETYPE_DIFF = None + standard_login = [ + mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), + mock.call.setSSHOptions( + HP3PAR_SAN_IP, + HP3PAR_USER_NAME, + HP3PAR_USER_PASS, + privatekey=HP3PAR_SAN_SSH_PRIVATE, + port=HP3PAR_SAN_SSH_PORT, + conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT)] + + standard_logout = [ + mock.call.logout()] + def setup_configuration(self): configuration = mock.Mock() configuration.hp3par_debug = False @@ -414,18 +427,12 @@ class HP3PARBaseDriver(object): self.ctxt = context.get_admin_context() mock_client = self.setup_mock_client(driver=hpfcdriver.HP3PARFCDriver) expected = [ - mock.call.setSSHOptions( - HP3PAR_SAN_IP, - HP3PAR_USER_NAME, - HP3PAR_USER_PASS, - privatekey=HP3PAR_SAN_SSH_PRIVATE, - port=HP3PAR_SAN_SSH_PORT, - conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT), - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), mock.call.getCPG(HP3PAR_CPG), - mock.call.getCPG(HP3PAR_CPG2), - mock.call.logout()] - mock_client.assert_has_calls(expected) + mock.call.getCPG(HP3PAR_CPG2)] + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) @mock.patch('hp3parclient.version', "3.1.1") def test_ssh_options(self): @@ -443,6 +450,7 @@ class HP3PARBaseDriver(object): CONF.strict_ssh_host_key_policy = orig_strict_ssh_host_key_policy expected = [ + mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), mock.call.setSSHOptions( HP3PAR_SAN_IP, HP3PAR_USER_NAME, @@ -452,11 +460,11 @@ class HP3PARBaseDriver(object): missing_key_policy="AutoAddPolicy", port=HP3PAR_SAN_SSH_PORT, conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT), - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), mock.call.getCPG(HP3PAR_CPG), - mock.call.getCPG(HP3PAR_CPG2), - mock.call.logout()] - mock_client.assert_has_calls(expected) + mock.call.getCPG(HP3PAR_CPG2)] + mock_client.assert_has_calls( + expected + + self.standard_logout) @mock.patch('hp3parclient.version', "3.1.1") def test_ssh_options_strict(self): @@ -474,6 +482,7 @@ class HP3PARBaseDriver(object): CONF.strict_ssh_host_key_policy = orig_strict_ssh_host_key_policy expected = [ + mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), mock.call.setSSHOptions( HP3PAR_SAN_IP, HP3PAR_USER_NAME, @@ -483,11 +492,9 @@ class HP3PARBaseDriver(object): missing_key_policy="RejectPolicy", port=HP3PAR_SAN_SSH_PORT, conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT), - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), mock.call.getCPG(HP3PAR_CPG), - mock.call.getCPG(HP3PAR_CPG2), - mock.call.logout()] - mock_client.assert_has_calls(expected) + mock.call.getCPG(HP3PAR_CPG2)] + mock_client.assert_has_calls(expected + self.standard_logout) def test_task_waiter(self): @@ -501,68 +508,81 @@ class HP3PARBaseDriver(object): task_id = 1234 interval = .001 - waiter = self.driver.common.TaskWaiter(mock_client, task_id, interval) - status = waiter.wait_for_task() - expected = [ - mock.call.getTask(task_id), - mock.call.getTask(task_id), - mock.call.getTask(task_id) - ] - mock_client.assert_has_calls(expected) - self.assertEqual(status, self.STATUS_DONE) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + waiter = common.TaskWaiter(mock_client, task_id, interval) + status = waiter.wait_for_task() + + expected = [ + mock.call.getTask(task_id), + mock.call.getTask(task_id), + mock.call.getTask(task_id) + ] + mock_client.assert_has_calls(expected) + self.assertEqual(status, self.STATUS_DONE) def test_create_volume(self): - # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() - self.driver.create_volume(self.volume) - comment = ( - '{"display_name": "Foo Volume", "type": "OpenStack",' - ' "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",' - ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.createVolume( - self.VOLUME_3PAR_NAME, - HP3PAR_CPG, - 1907, { - 'comment': comment, - 'tpvv': True, - 'snapCPG': HP3PAR_CPG_SNAP}), - mock.call.logout()] + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.driver.create_volume(self.volume) + comment = ( + '{"display_name": "Foo Volume", "type": "OpenStack",' + ' "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",' + ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') + expected = [ + mock.call.createVolume( + self.VOLUME_3PAR_NAME, + HP3PAR_CPG, + 1907, { + 'comment': comment, + 'tpvv': True, + 'snapCPG': HP3PAR_CPG_SNAP})] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) def test_create_volume_in_pool(self): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() - return_model = self.driver.create_volume(self.volume_pool) - comment = ( - '{"display_name": "Foo Volume", "type": "OpenStack",' - ' "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",' - ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.createVolume( - self.VOLUME_3PAR_NAME, - HP3PAR_CPG2, - 1907, { - 'comment': comment, - 'tpvv': True, - 'snapCPG': HP3PAR_CPG_SNAP}), - mock.call.logout()] + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client - mock_client.assert_has_calls(expected) - self.assertEqual(return_model, None) + return_model = self.driver.create_volume(self.volume_pool) + comment = ( + '{"display_name": "Foo Volume", "type": "OpenStack",' + ' "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7",' + ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') + expected = [ + mock.call.createVolume( + self.VOLUME_3PAR_NAME, + HP3PAR_CPG2, + 1907, { + 'comment': comment, + 'tpvv': True, + 'snapCPG': HP3PAR_CPG_SNAP})] + + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + self.assertEqual(return_model, None) @mock.patch.object(volume_types, 'get_volume_type') def test_get_snap_cpg_from_volume_type(self, _mock_volume_types): - self.setup_driver() + mock_client = self.setup_driver() expected_type_snap_cpg = "type_snap_cpg" _mock_volume_types.return_value = { 'name': 'gold', @@ -571,15 +591,20 @@ class HP3PARBaseDriver(object): 'snap_cpg': expected_type_snap_cpg, 'volume_type': self.volume_type}} - result = self.driver.common.get_volume_settings_from_type_id( - "mock", self.driver.configuration.hp3par_cpg) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - self.assertEqual(expected_type_snap_cpg, result['snap_cpg']) + result = common.get_volume_settings_from_type_id( + "mock", self.driver.configuration.hp3par_cpg) + + self.assertEqual(expected_type_snap_cpg, result['snap_cpg']) @mock.patch.object(volume_types, 'get_volume_type') def test_get_snap_cpg_from_volume_type_cpg(self, _mock_volume_types): - self.setup_driver() + mock_client = self.setup_driver() expected_cpg = 'use_extra_specs_cpg' _mock_volume_types.return_value = { 'name': 'gold', @@ -587,10 +612,15 @@ class HP3PARBaseDriver(object): 'cpg': expected_cpg, 'volume_type': self.volume_type}} - result = self.driver.common.get_volume_settings_from_type_id( - "mock", self.driver.configuration.hp3par_cpg) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - self.assertEqual(expected_cpg, result['snap_cpg']) + result = common.get_volume_settings_from_type_id( + "mock", self.driver.configuration.hp3par_cpg) + + self.assertEqual(expected_cpg, result['snap_cpg']) @mock.patch.object(volume_types, 'get_volume_type') def test_get_snap_cpg_from_volume_type_conf_snap_cpg( @@ -602,9 +632,14 @@ class HP3PARBaseDriver(object): conf = self.setup_configuration() expected_snap_cpg = conf.hp3par_cpg_snap - self.setup_driver(config=conf) - result = self.driver.common.get_volume_settings_from_type_id( - "mock", self.driver.configuration.hp3par_cpg) + mock_client = self.setup_driver(config=conf) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + + result = common.get_volume_settings_from_type_id( + "mock", self.driver.configuration.hp3par_cpg) self.assertEqual(expected_snap_cpg, result['snap_cpg']) @@ -619,11 +654,16 @@ class HP3PARBaseDriver(object): conf = self.setup_configuration() conf.hp3par_cpg_snap = None expected_cpg = conf.hp3par_cpg - self.setup_driver(config=conf) - result = self.driver.common.get_volume_settings_from_type_id( - "mock", self.driver.configuration.hp3par_cpg) + mock_client = self.setup_driver(config=conf) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - self.assertEqual(expected_cpg, result['snap_cpg']) + result = common.get_volume_settings_from_type_id( + "mock", self.driver.configuration.hp3par_cpg) + + self.assertEqual(expected_cpg, result['snap_cpg']) @mock.patch.object(volume_types, 'get_volume_type') def test_create_volume_qos(self, _mock_volume_types): @@ -641,48 +681,57 @@ class HP3PARBaseDriver(object): 'tpvv': True, 'volume_type': self.volume_type}} - return_model = self.driver.create_volume(self.volume_qos) - comment = ( - '{"volume_type_name": "gold", "display_name": "Foo Volume"' - ', "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7' - '", "volume_type_id": "gold", "volume_id": "d03338a9-91' - '15-48a3-8dfc-35cdfcdc15a7", "qos": {}, "type": "OpenStack"}') + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getCPG(HP3PAR_CPG_QOS), - mock.call.createVolume( - self.VOLUME_3PAR_NAME, - HP3PAR_CPG_QOS, - 1907, { - 'comment': comment, - 'tpvv': True, - 'snapCPG': HP3PAR_CPG_SNAP}), - mock.call.logout()] + return_model = self.driver.create_volume(self.volume_qos) + comment = ( + '{"volume_type_name": "gold", "display_name": "Foo Volume"' + ', "name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7' + '", "volume_type_id": "gold", "volume_id": "d03338a9-91' + '15-48a3-8dfc-35cdfcdc15a7", "qos": {}, "type": "OpenStack"}') - mock_client.assert_has_calls(expected) - self.assertEqual(return_model, - {'host': volume_utils.append_host(self.FAKE_HOST, - HP3PAR_CPG_QOS)}) + expected = [ + mock.call.getCPG(HP3PAR_CPG_QOS), + mock.call.createVolume( + self.VOLUME_3PAR_NAME, + HP3PAR_CPG_QOS, + 1907, { + 'comment': comment, + 'tpvv': True, + 'snapCPG': HP3PAR_CPG_SNAP})] + + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + self.assertEqual(return_model, + {'host': volume_utils.append_host( + self.FAKE_HOST, + HP3PAR_CPG_QOS)}) @mock.patch.object(volume_types, 'get_volume_type') def test_retype_not_3par(self, _mock_volume_types): _mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_1 mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF) - self.assertRaises(exception.InvalidHost, - self.driver.retype, - self.ctxt, - self.RETYPE_VOLUME_INFO_0, - self.RETYPE_VOLUME_TYPE_1, - self.RETYPE_DIFF, - self.RETYPE_HOST_NOT3PAR) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.assertRaises(exception.InvalidHost, + self.driver.retype, + self.ctxt, + self.RETYPE_VOLUME_INFO_0, + self.RETYPE_VOLUME_TYPE_1, + self.RETYPE_DIFF, + self.RETYPE_HOST_NOT3PAR) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(self.VOLUME_3PAR_NAME), - mock.call.logout()] - mock_client.assert_has_calls(expected) + expected = [mock.call.getVolume(self.VOLUME_3PAR_NAME)] + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_retype_volume_not_found(self, _mock_volume_types): @@ -690,19 +739,22 @@ class HP3PARBaseDriver(object): mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF) mock_client.getVolume.side_effect = hpexceptions.HTTPNotFound - self.assertRaises(hpexceptions.HTTPNotFound, - self.driver.retype, - self.ctxt, - self.RETYPE_VOLUME_INFO_0, - self.RETYPE_VOLUME_TYPE_1, - self.RETYPE_DIFF, - self.RETYPE_HOST) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.assertRaises(hpexceptions.HTTPNotFound, + self.driver.retype, + self.ctxt, + self.RETYPE_VOLUME_INFO_0, + self.RETYPE_VOLUME_TYPE_1, + self.RETYPE_DIFF, + self.RETYPE_HOST) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(self.VOLUME_3PAR_NAME), - mock.call.logout()] - mock_client.assert_has_calls(expected) + expected = [mock.call.getVolume(self.VOLUME_3PAR_NAME)] + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_retype_specs_error_reverts_snap_cpg(self, _mock_volume_types): @@ -715,29 +767,33 @@ class HP3PARBaseDriver(object): mock_client.addVolumeToVolumeSet.side_effect = \ hpexceptions.HTTPForbidden - self.assertRaises(hpexceptions.HTTPForbidden, - self.driver.retype, - self.ctxt, - {'id': self.VOLUME_ID}, - self.RETYPE_VOLUME_TYPE_0, - self.RETYPE_DIFF, - self.RETYPE_HOST) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.assertRaises(hpexceptions.HTTPForbidden, + self.driver.retype, + self.ctxt, + {'id': self.VOLUME_ID}, + self.RETYPE_VOLUME_TYPE_0, + self.RETYPE_DIFF, + self.RETYPE_HOST) - old_settings = { - 'snapCPG': self.RETYPE_VOLUME_INFO_0['snapCPG'], - 'comment': self.RETYPE_VOLUME_INFO_0['comment']} - new_settings = { - 'snapCPG': self.RETYPE_VOLUME_TYPE_1['extra_specs']['snap_cpg'], - 'comment': mock.ANY} + old_settings = { + 'snapCPG': self.RETYPE_VOLUME_INFO_0['snapCPG'], + 'comment': self.RETYPE_VOLUME_INFO_0['comment']} + new_settings = { + 'snapCPG': ( + self.RETYPE_VOLUME_TYPE_1['extra_specs']['snap_cpg']), + 'comment': mock.ANY} - expected = [ - mock.call.modifyVolume(self.VOLUME_3PAR_NAME, new_settings) - ] - mock_client.assert_has_calls(expected) - expected = [ - mock.call.modifyVolume(self.VOLUME_3PAR_NAME, old_settings), - mock.call.logout()] - mock_client.assert_has_calls(expected) + expected = [ + mock.call.modifyVolume(self.VOLUME_3PAR_NAME, new_settings) + ] + mock_client.assert_has_calls(expected) + expected = [ + mock.call.modifyVolume(self.VOLUME_3PAR_NAME, old_settings) + ] + mock_client.assert_has_calls(expected + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_retype_revert_comment(self, _mock_volume_types): @@ -749,22 +805,24 @@ class HP3PARBaseDriver(object): # Fail the QOS setting to test the revert of the snap CPG rename. mock_client.deleteVolumeSet.side_effect = hpexceptions.HTTPForbidden - self.assertRaises(hpexceptions.HTTPForbidden, - self.driver.retype, - self.ctxt, - {'id': self.VOLUME_ID}, - self.RETYPE_VOLUME_TYPE_2, - self.RETYPE_DIFF, - self.RETYPE_HOST) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.assertRaises(hpexceptions.HTTPForbidden, + self.driver.retype, + self.ctxt, + {'id': self.VOLUME_ID}, + self.RETYPE_VOLUME_TYPE_2, + self.RETYPE_DIFF, + self.RETYPE_HOST) - original = { - 'snapCPG': self.RETYPE_VOLUME_INFO_1['snapCPG'], - 'comment': self.RETYPE_VOLUME_INFO_1['comment']} + original = { + 'snapCPG': self.RETYPE_VOLUME_INFO_1['snapCPG'], + 'comment': self.RETYPE_VOLUME_INFO_1['comment']} - expected = [ - mock.call.modifyVolume('osv-0DM4qZEVSKON-DXN-NwVpw', original), - mock.call.logout()] - mock_client.assert_has_calls(expected) + expected = [ + mock.call.modifyVolume('osv-0DM4qZEVSKON-DXN-NwVpw', original)] + mock_client.assert_has_calls(expected + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_retype_different_array(self, _mock_volume_types): @@ -774,21 +832,25 @@ class HP3PARBaseDriver(object): mock_client.getStorageSystemInfo.return_value = { 'serialNumber': 'XXXXXXX'} - self.assertRaises(exception.InvalidHost, - self.driver.retype, - self.ctxt, - self.RETYPE_VOLUME_INFO_0, - self.RETYPE_VOLUME_TYPE_1, - self.RETYPE_DIFF, - self.RETYPE_HOST) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.assertRaises(exception.InvalidHost, + self.driver.retype, + self.ctxt, + self.RETYPE_VOLUME_INFO_0, + self.RETYPE_VOLUME_TYPE_1, + self.RETYPE_DIFF, + self.RETYPE_HOST) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(self.VOLUME_3PAR_NAME), - mock.call.getStorageSystemInfo(), - mock.call.logout()] + expected = [ + mock.call.getVolume(self.VOLUME_3PAR_NAME), + mock.call.getStorageSystemInfo()] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_retype_across_cpg_domains(self, _mock_volume_types): @@ -800,23 +862,28 @@ class HP3PARBaseDriver(object): {'domain': 'domain2'}, ] - self.assertRaises(exception.Invalid3PARDomain, - self.driver.retype, - self.ctxt, - self.RETYPE_VOLUME_INFO_0, - self.RETYPE_VOLUME_TYPE_1, - self.RETYPE_DIFF, - self.RETYPE_HOST) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.assertRaises(exception.Invalid3PARDomain, + self.driver.retype, + self.ctxt, + self.RETYPE_VOLUME_INFO_0, + self.RETYPE_VOLUME_TYPE_1, + self.RETYPE_DIFF, + self.RETYPE_HOST) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(self.VOLUME_3PAR_NAME), - mock.call.getStorageSystemInfo(), - mock.call.getCPG(self.RETYPE_VOLUME_INFO_0['userCPG']), - mock.call.getCPG(self.RETYPE_VOLUME_TYPE_1['extra_specs']['cpg']), - mock.call.logout() - ] - mock_client.assert_has_calls(expected) + expected = [ + mock.call.getVolume(self.VOLUME_3PAR_NAME), + mock.call.getStorageSystemInfo(), + mock.call.getCPG(self.RETYPE_VOLUME_INFO_0['userCPG']), + mock.call.getCPG( + self.RETYPE_VOLUME_TYPE_1['extra_specs']['cpg']) + ] + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_retype_across_snap_cpg_domains(self, _mock_volume_types): @@ -829,45 +896,52 @@ class HP3PARBaseDriver(object): {'domain': 'snap_cpg_domain_1'}, ] - self.assertRaises(exception.Invalid3PARDomain, - self.driver.retype, - self.ctxt, - self.RETYPE_VOLUME_INFO_0, - self.RETYPE_VOLUME_TYPE_1, - self.RETYPE_DIFF, - self.RETYPE_HOST) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.assertRaises(exception.Invalid3PARDomain, + self.driver.retype, + self.ctxt, + self.RETYPE_VOLUME_INFO_0, + self.RETYPE_VOLUME_TYPE_1, + self.RETYPE_DIFF, + self.RETYPE_HOST) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(self.VOLUME_3PAR_NAME), - mock.call.getStorageSystemInfo(), - mock.call.getCPG(self.RETYPE_VOLUME_INFO_0['userCPG']), - mock.call.getCPG(self.RETYPE_VOLUME_TYPE_1['extra_specs']['cpg']), - mock.call.getCPG( - self.RETYPE_VOLUME_TYPE_1['extra_specs']['snap_cpg']), - mock.call.logout() - ] - mock_client.assert_has_calls(expected) + expected = [ + mock.call.getVolume(self.VOLUME_3PAR_NAME), + mock.call.getStorageSystemInfo(), + mock.call.getCPG(self.RETYPE_VOLUME_INFO_0['userCPG']), + mock.call.getCPG( + self.RETYPE_VOLUME_TYPE_1['extra_specs']['cpg']), + mock.call.getCPG( + self.RETYPE_VOLUME_TYPE_1['extra_specs']['snap_cpg']) + ] + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_retype_to_bad_persona(self, _mock_volume_types): _mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_BAD_PERSONA mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF) - self.assertRaises(exception.InvalidInput, - self.driver.retype, - self.ctxt, - self.RETYPE_VOLUME_INFO_0, - self.RETYPE_VOLUME_TYPE_BAD_PERSONA, - self.RETYPE_DIFF, - self.RETYPE_HOST) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.assertRaises(exception.InvalidInput, + self.driver.retype, + self.ctxt, + self.RETYPE_VOLUME_INFO_0, + self.RETYPE_VOLUME_TYPE_BAD_PERSONA, + self.RETYPE_DIFF, + self.RETYPE_HOST) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(self.VOLUME_3PAR_NAME), - mock.call.logout() - ] - mock_client.assert_has_calls(expected) + expected = [mock.call.getVolume(self.VOLUME_3PAR_NAME)] + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_retype_to_bad_cpg(self, _mock_volume_types): @@ -875,21 +949,25 @@ class HP3PARBaseDriver(object): mock_client = self.setup_driver(mock_conf=self.RETYPE_CONF) mock_client.getCPG.side_effect = hpexceptions.HTTPNotFound - self.assertRaises(exception.InvalidInput, - self.driver.retype, - self.ctxt, - self.RETYPE_VOLUME_INFO_0, - self.RETYPE_VOLUME_TYPE_BAD_CPG, - self.RETYPE_DIFF, - self.RETYPE_HOST) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.assertRaises(exception.InvalidInput, + self.driver.retype, + self.ctxt, + self.RETYPE_VOLUME_INFO_0, + self.RETYPE_VOLUME_TYPE_BAD_CPG, + self.RETYPE_DIFF, + self.RETYPE_HOST) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getCPG( - self.RETYPE_VOLUME_TYPE_BAD_CPG['extra_specs']['cpg']), - mock.call.logout() - ] - mock_client.assert_has_calls(expected) + expected = [ + mock.call.getCPG( + self.RETYPE_VOLUME_TYPE_BAD_CPG['extra_specs']['cpg']) + ] + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_retype_tune(self, _mock_volume_types): @@ -912,26 +990,29 @@ class HP3PARBaseDriver(object): volume = {'id': HP3PARBaseDriver.CLONE_ID} - retyped = self.driver.retype( - self.ctxt, volume, type_ref, None, self.RETYPE_HOST) - self.assertTrue(retyped) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client - expected = [ - mock.call.modifyVolume('osv-0DM4qZEVSKON-AAAAAAAAA', - {'comment': mock.ANY, - 'snapCPG': 'OpenStackCPGSnap'}), - mock.call.deleteVolumeSet('vvs-0DM4qZEVSKON-AAAAAAAAA'), - mock.call.addVolumeToVolumeSet('myvvs', - 'osv-0DM4qZEVSKON-AAAAAAAAA'), - mock.call.modifyVolume('osv-0DM4qZEVSKON-AAAAAAAAA', - {'action': 6, - 'userCPG': 'OpenStackCPG', - 'conversionOperation': 1, - 'tuneOperation': 1}), - mock.call.getTask(1), - mock.call.logout() - ] - mock_client.assert_has_calls(expected) + retyped = self.driver.retype( + self.ctxt, volume, type_ref, None, self.RETYPE_HOST) + self.assertTrue(retyped) + + expected = [ + mock.call.modifyVolume('osv-0DM4qZEVSKON-AAAAAAAAA', + {'comment': mock.ANY, + 'snapCPG': 'OpenStackCPGSnap'}), + mock.call.deleteVolumeSet('vvs-0DM4qZEVSKON-AAAAAAAAA'), + mock.call.addVolumeToVolumeSet('myvvs', + 'osv-0DM4qZEVSKON-AAAAAAAAA'), + mock.call.modifyVolume('osv-0DM4qZEVSKON-AAAAAAAAA', + {'action': 6, + 'userCPG': 'OpenStackCPG', + 'conversionOperation': 1, + 'tuneOperation': 1}), + mock.call.getTask(1) + ] + mock_client.assert_has_calls(expected + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_retype_qos_spec(self, _mock_volume_types): @@ -940,103 +1021,118 @@ class HP3PARBaseDriver(object): cpg = "any_cpg" snap_cpg = "any_cpg" - self.driver.common._retype(self.volume, - HP3PARBaseDriver.VOLUME_3PAR_NAME, - "old_type", "old_type_id", - HP3PARBaseDriver.RETYPE_HOST, - None, cpg, cpg, snap_cpg, snap_cpg, - True, True, None, None, - self.QOS_SPECS, self.RETYPE_QOS_SPECS, - "{}") - expected = [ - mock.call.createVolumeSet('vvs-0DM4qZEVSKON-DXN-NwVpw', None), - mock.call.createQoSRules( - 'vvs-0DM4qZEVSKON-DXN-NwVpw', - {'ioMinGoal': 100, 'ioMaxLimit': 1000, - 'bwMinGoalKB': 25600, 'bwMaxLimitKB': 51200, - 'priority': 3, - 'latencyGoal': 25} - ), - mock.call.addVolumeToVolumeSet( - 'vvs-0DM4qZEVSKON-DXN-NwVpw', 'osv-0DM4qZEVSKON-DXN-NwVpw')] - mock_client.assert_has_calls(expected) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + common._retype(self.volume, + HP3PARBaseDriver.VOLUME_3PAR_NAME, + "old_type", "old_type_id", + HP3PARBaseDriver.RETYPE_HOST, + None, cpg, cpg, snap_cpg, snap_cpg, + True, True, None, None, + self.QOS_SPECS, self.RETYPE_QOS_SPECS, + "{}") + + expected = [ + mock.call.createVolumeSet('vvs-0DM4qZEVSKON-DXN-NwVpw', None), + mock.call.createQoSRules( + 'vvs-0DM4qZEVSKON-DXN-NwVpw', + {'ioMinGoal': 100, 'ioMaxLimit': 1000, + 'bwMinGoalKB': 25600, 'bwMaxLimitKB': 51200, + 'priority': 3, + 'latencyGoal': 25} + ), + mock.call.addVolumeToVolumeSet( + 'vvs-0DM4qZEVSKON-DXN-NwVpw', + 'osv-0DM4qZEVSKON-DXN-NwVpw')] + mock_client.assert_has_calls(expected) def test_delete_volume(self): - # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() - self.driver.delete_volume(self.volume) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.driver.delete_volume(self.volume) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.deleteVolume(self.VOLUME_3PAR_NAME), - mock.call.logout()] + expected = [mock.call.deleteVolume(self.VOLUME_3PAR_NAME)] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) def test_create_cloned_volume(self): - # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() mock_client.copyVolume.return_value = {'taskid': 1} + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client - volume = {'name': HP3PARBaseDriver.VOLUME_NAME, - 'id': HP3PARBaseDriver.CLONE_ID, - 'display_name': 'Foo Volume', - 'size': 2, - 'host': volume_utils.append_host(self.FAKE_HOST, - HP3PAR_CPG2), - 'source_volid': HP3PARBaseDriver.VOLUME_ID} - src_vref = {} - model_update = self.driver.create_cloned_volume(volume, src_vref) - self.assertIsNone(model_update) + volume = {'name': HP3PARBaseDriver.VOLUME_NAME, + 'id': HP3PARBaseDriver.CLONE_ID, + 'display_name': 'Foo Volume', + 'size': 2, + 'host': volume_utils.append_host(self.FAKE_HOST, + HP3PAR_CPG2), + 'source_volid': HP3PARBaseDriver.VOLUME_ID} + src_vref = {} + model_update = self.driver.create_cloned_volume(volume, src_vref) + self.assertIsNone(model_update) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.copyVolume( - self.VOLUME_3PAR_NAME, - 'osv-0DM4qZEVSKON-AAAAAAAAA', - HP3PAR_CPG2, - {'snapCPG': 'OpenStackCPGSnap', 'tpvv': True, - 'online': True}), - mock.call.logout()] + expected = [ + mock.call.copyVolume( + self.VOLUME_3PAR_NAME, + 'osv-0DM4qZEVSKON-AAAAAAAAA', + HP3PAR_CPG2, + {'snapCPG': 'OpenStackCPGSnap', 'tpvv': True, + 'online': True})] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_create_cloned_qos_volume(self, _mock_volume_types): _mock_volume_types.return_value = self.RETYPE_VOLUME_TYPE_2 mock_client = self.setup_driver() mock_client.copyVolume.return_value = {'taskid': 1} + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client - src_vref = {} - volume = self.volume_qos.copy() - host = "TEST_HOST" - pool = "TEST_POOL" - volume_host = volume_utils.append_host(host, pool) - expected_cpg = self.RETYPE_VOLUME_TYPE_2['extra_specs']['cpg'] - expected_volume_host = volume_utils.append_host(host, expected_cpg) - volume['id'] = HP3PARBaseDriver.CLONE_ID - volume['host'] = volume_host - volume['source_volid'] = HP3PARBaseDriver.VOLUME_ID - model_update = self.driver.create_cloned_volume(volume, src_vref) - self.assertEqual(model_update, {'host': expected_volume_host}) + src_vref = {} + volume = self.volume_qos.copy() + host = "TEST_HOST" + pool = "TEST_POOL" + volume_host = volume_utils.append_host(host, pool) + expected_cpg = self.RETYPE_VOLUME_TYPE_2['extra_specs']['cpg'] + expected_volume_host = volume_utils.append_host(host, expected_cpg) + volume['id'] = HP3PARBaseDriver.CLONE_ID + volume['host'] = volume_host + volume['source_volid'] = HP3PARBaseDriver.VOLUME_ID + model_update = self.driver.create_cloned_volume(volume, src_vref) + self.assertEqual(model_update, {'host': expected_volume_host}) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getCPG(expected_cpg), - mock.call.copyVolume( - self.VOLUME_3PAR_NAME, - 'osv-0DM4qZEVSKON-AAAAAAAAA', - expected_cpg, - {'snapCPG': 'OpenStackCPGSnap', 'tpvv': True, - 'online': True}), - mock.call.logout()] + expected = [ + mock.call.getCPG(expected_cpg), + mock.call.copyVolume( + self.VOLUME_3PAR_NAME, + 'osv-0DM4qZEVSKON-AAAAAAAAA', + expected_cpg, + {'snapCPG': 'OpenStackCPGSnap', 'tpvv': True, + 'online': True})] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) def test_migrate_volume(self): @@ -1064,35 +1160,38 @@ class HP3PARBaseDriver(object): 'status': 'available', 'host': HP3PARBaseDriver.FAKE_HOST, 'source_volid': HP3PARBaseDriver.VOLUME_ID} + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - volume_name_3par = self.driver.common._encode_name(volume['id']) + volume_name_3par = common._encode_name(volume['id']) - loc_info = 'HP3PARDriver:1234:CPG-FC1' - host = {'host': 'stack@3parfc1#CPG-FC1', - 'capabilities': {'location_info': loc_info}} + loc_info = 'HP3PARDriver:1234:CPG-FC1' + host = {'host': 'stack@3parfc1#CPG-FC1', + 'capabilities': {'location_info': loc_info}} - result = self.driver.migrate_volume(context.get_admin_context(), - volume, host) - self.assertIsNotNone(result) - self.assertEqual((True, None), result) + result = self.driver.migrate_volume(context.get_admin_context(), + volume, host) + self.assertIsNotNone(result) + self.assertEqual((True, None), result) - osv_matcher = 'osv-' + volume_name_3par + osv_matcher = 'osv-' + volume_name_3par - expected = [ - mock.call.modifyVolume( - osv_matcher, - {'comment': '{"qos": {}, "display_name": "Foo Volume"}', - 'snapCPG': HP3PAR_CPG_SNAP}), - mock.call.modifyVolume(osv_matcher, - {'action': 6, - 'userCPG': 'CPG-FC1', - 'conversionOperation': 1, - 'tuneOperation': 1}), - mock.call.getTask(mock.ANY), - mock.call.logout() - ] + expected = [ + mock.call.modifyVolume( + osv_matcher, + {'comment': '{"qos": {}, "display_name": "Foo Volume"}', + 'snapCPG': HP3PAR_CPG_SNAP}), + mock.call.modifyVolume(osv_matcher, + {'action': 6, + 'userCPG': 'CPG-FC1', + 'conversionOperation': 1, + 'tuneOperation': 1}), + mock.call.getTask(mock.ANY) + ] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_migrate_volume_with_type(self, _mock_volume_types): @@ -1124,46 +1223,53 @@ class HP3PARBaseDriver(object): 'host': HP3PARBaseDriver.FAKE_HOST, 'source_volid': HP3PARBaseDriver.VOLUME_ID} - volume_name_3par = self.driver.common._encode_name(volume['id']) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - loc_info = 'HP3PARDriver:1234:CPG-FC1' - host = {'host': 'stack@3parfc1#CPG-FC1', - 'capabilities': {'location_info': loc_info}} + volume_name_3par = common._encode_name(volume['id']) - result = self.driver.migrate_volume(context.get_admin_context(), - volume, host) - self.assertIsNotNone(result) - expected_host = volume_utils.append_host( - "stack@3parfc1", - self.RETYPE_VOLUME_TYPE_2['extra_specs']['cpg']) - self.assertEqual((True, {'host': expected_host}), result) + loc_info = 'HP3PARDriver:1234:CPG-FC1' + host = {'host': 'stack@3parfc1#CPG-FC1', + 'capabilities': {'location_info': loc_info}} - osv_matcher = 'osv-' + volume_name_3par + result = self.driver.migrate_volume(context.get_admin_context(), + volume, host) + self.assertIsNotNone(result) + expected_host = volume_utils.append_host( + "stack@3parfc1", + self.RETYPE_VOLUME_TYPE_2['extra_specs']['cpg']) + self.assertEqual((True, {'host': expected_host}), result) - expected_comment = { - "display_name": display_name, - "volume_type_id": self.RETYPE_VOLUME_TYPE_2['id'], - "volume_type_name": self.RETYPE_VOLUME_TYPE_2['name'], - "vvs": self.RETYPE_VOLUME_TYPE_2['extra_specs']['vvs'] - } - expected = [ - mock.call.modifyVolume( - osv_matcher, - {'comment': self.CommentMatcher(self.assertEqual, - expected_comment), - 'snapCPG': self.RETYPE_VOLUME_TYPE_2 - ['extra_specs']['snap_cpg']}), - mock.call.modifyVolume( - osv_matcher, - {'action': 6, - 'userCPG': self.RETYPE_VOLUME_TYPE_2['extra_specs']['cpg'], - 'conversionOperation': 1, - 'tuneOperation': 1}), - mock.call.getTask(mock.ANY), - mock.call.logout() - ] + osv_matcher = 'osv-' + volume_name_3par - mock_client.assert_has_calls(expected) + expected_comment = { + "display_name": display_name, + "volume_type_id": self.RETYPE_VOLUME_TYPE_2['id'], + "volume_type_name": self.RETYPE_VOLUME_TYPE_2['name'], + "vvs": self.RETYPE_VOLUME_TYPE_2['extra_specs']['vvs'] + } + expected = [ + mock.call.modifyVolume( + osv_matcher, + {'comment': self.CommentMatcher(self.assertEqual, + expected_comment), + 'snapCPG': self.RETYPE_VOLUME_TYPE_2 + ['extra_specs']['snap_cpg']}), + mock.call.modifyVolume( + osv_matcher, + {'action': 6, + 'userCPG': self.RETYPE_VOLUME_TYPE_2 + ['extra_specs']['cpg'], + 'conversionOperation': 1, + 'tuneOperation': 1}), + mock.call.getTask(mock.ANY) + ] + + mock_client.assert_has_calls( + expected + + self.standard_logout) def test_migrate_volume_diff_host(self): conf = { @@ -1171,7 +1277,7 @@ class HP3PARBaseDriver(object): 'serialNumber': 'different'}, } - self.setup_driver(mock_conf=conf) + mock_client = self.setup_driver(mock_conf=conf) volume = {'name': HP3PARBaseDriver.VOLUME_NAME, 'id': HP3PARBaseDriver.CLONE_ID, @@ -1186,10 +1292,13 @@ class HP3PARBaseDriver(object): host = {'host': 'stack@3parfc1', 'capabilities': {'location_info': loc_info}} - result = self.driver.migrate_volume(context.get_admin_context(), - volume, host) - self.assertIsNotNone(result) - self.assertEqual((False, None), result) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + result = self.driver.migrate_volume(context.get_admin_context(), + volume, host) + self.assertIsNotNone(result) + self.assertEqual((False, None), result) @mock.patch.object(volume_types, 'get_volume_type') def test_migrate_volume_diff_domain(self, _mock_volume_types): @@ -1220,34 +1329,38 @@ class HP3PARBaseDriver(object): 'host': HP3PARBaseDriver.FAKE_HOST, 'source_volid': HP3PARBaseDriver.VOLUME_ID} - volume_name_3par = self.driver.common._encode_name(volume['id']) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - loc_info = 'HP3PARDriver:1234:CPG-FC1' - host = {'host': 'stack@3parfc1#CPG-FC1', - 'capabilities': {'location_info': loc_info}} + volume_name_3par = common._encode_name(volume['id']) - result = self.driver.migrate_volume(context.get_admin_context(), - volume, host) - self.assertIsNotNone(result) - self.assertEqual((True, None), result) + loc_info = 'HP3PARDriver:1234:CPG-FC1' + host = {'host': 'stack@3parfc1#CPG-FC1', + 'capabilities': {'location_info': loc_info}} - osv_matcher = 'osv-' + volume_name_3par + result = self.driver.migrate_volume(context.get_admin_context(), + volume, host) + self.assertIsNotNone(result) + self.assertEqual((True, None), result) - expected = [ - mock.call.modifyVolume( - osv_matcher, - {'comment': '{"qos": {}, "display_name": "Foo Volume"}', - 'snapCPG': HP3PAR_CPG_SNAP}), - mock.call.modifyVolume(osv_matcher, - {'action': 6, - 'userCPG': 'CPG-FC1', - 'conversionOperation': 1, - 'tuneOperation': 1}), - mock.call.getTask(mock.ANY), - mock.call.logout() - ] + osv_matcher = 'osv-' + volume_name_3par - mock_client.assert_has_calls(expected) + expected = [ + mock.call.modifyVolume( + osv_matcher, + {'comment': '{"qos": {}, "display_name": "Foo Volume"}', + 'snapCPG': HP3PAR_CPG_SNAP}), + mock.call.modifyVolume(osv_matcher, + {'action': 6, + 'userCPG': 'CPG-FC1', + 'conversionOperation': 1, + 'tuneOperation': 1}), + mock.call.getTask(mock.ANY), + ] + + mock_client.assert_has_calls(expected + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_migrate_volume_attached(self, _mock_volume_types): @@ -1263,42 +1376,47 @@ class HP3PARBaseDriver(object): 'host': HP3PARBaseDriver.FAKE_HOST, 'source_volid': HP3PARBaseDriver.VOLUME_ID} - volume_name_3par = self.driver.common._encode_name(volume['id']) - osv_matcher = 'osv-' + volume_name_3par + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - loc_info = 'HP3PARDriver:1234567:CPG-FC1' + volume_name_3par = common._encode_name(volume['id']) + osv_matcher = 'osv-' + volume_name_3par - protocol = "FC" - if self.properties['driver_volume_type'] == "iscsi": - protocol = "iSCSI" + loc_info = 'HP3PARDriver:1234567:CPG-FC1' - host = {'host': 'stack@3parfc1', - 'capabilities': {'location_info': loc_info, - 'storage_protocol': protocol}} + protocol = "FC" + if self.properties['driver_volume_type'] == "iscsi": + protocol = "iSCSI" - result = self.driver.migrate_volume(context.get_admin_context(), - volume, host) + host = {'host': 'stack@3parfc1', + 'capabilities': {'location_info': loc_info, + 'storage_protocol': protocol}} - new_comment = {"qos": {}, - "retype_test": "test comment"} - expected = [ - mock.call.modifyVolume(osv_matcher, - {'comment': self.CommentMatcher( - self.assertEqual, new_comment), - 'snapCPG': 'OpenStackCPGSnap'}), - mock.call.modifyVolume(osv_matcher, - {'action': 6, - 'userCPG': 'OpenStackCPG', - 'conversionOperation': 1, - 'tuneOperation': 1}), - mock.call.getTask(1), - mock.call.logout() - ] - mock_client.assert_has_calls(expected) + result = self.driver.migrate_volume(context.get_admin_context(), + volume, host) - self.assertIsNotNone(result) - self.assertEqual((True, {'host': 'stack@3parfc1#OpenStackCPG'}), - result) + new_comment = {"qos": {}, + "retype_test": "test comment"} + expected = [ + mock.call.modifyVolume(osv_matcher, + {'comment': self.CommentMatcher( + self.assertEqual, new_comment), + 'snapCPG': 'OpenStackCPGSnap'}), + mock.call.modifyVolume(osv_matcher, + {'action': 6, + 'userCPG': 'OpenStackCPG', + 'conversionOperation': 1, + 'tuneOperation': 1}), + mock.call.getTask(1), + mock.call.logout() + ] + mock_client.assert_has_calls(expected) + + self.assertIsNotNone(result) + self.assertEqual((True, {'host': 'stack@3parfc1#OpenStackCPG'}), + result) @mock.patch.object(volume_types, 'get_volume_type') def test_migrate_volume_attached_diff_protocol(self, _mock_volume_types): @@ -1330,106 +1448,122 @@ class HP3PARBaseDriver(object): mock_client.assert_has_calls(expected) def test_attach_volume(self): - # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() - self.driver.attach_volume(context.get_admin_context(), - self.volume, - 'abcdef', - 'newhost', - '/dev/vdb') + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.driver.attach_volume(context.get_admin_context(), + self.volume, + 'abcdef', + 'newhost', + '/dev/vdb') - expected = [ - mock.call.setVolumeMetaData( - self.VOLUME_3PAR_NAME, - 'HPQ-CS-instance_uuid', - 'abcdef')] + expected = [ + mock.call.setVolumeMetaData( + self.VOLUME_3PAR_NAME, + 'HPQ-CS-instance_uuid', + 'abcdef')] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected) - # test the exception - mock_client.setVolumeMetaData.side_effect = Exception('Custom ex') - self.assertRaises(exception.CinderException, - self.driver.attach_volume, - context.get_admin_context(), - self.volume, - 'abcdef', - 'newhost', - '/dev/vdb') + # test the exception + mock_client.setVolumeMetaData.side_effect = Exception('Custom ex') + self.assertRaises(exception.CinderException, + self.driver.attach_volume, + context.get_admin_context(), + self.volume, + 'abcdef', + 'newhost', + '/dev/vdb') def test_detach_volume(self): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() - self.driver.detach_volume(context.get_admin_context(), self.volume) - expected = [ - mock.call.removeVolumeMetaData( - self.VOLUME_3PAR_NAME, - 'HPQ-CS-instance_uuid')] + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.driver.detach_volume(context.get_admin_context(), self.volume) + expected = [ + mock.call.removeVolumeMetaData( + self.VOLUME_3PAR_NAME, + 'HPQ-CS-instance_uuid')] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected) - # test the exception - mock_client.removeVolumeMetaData.side_effect = Exception('Custom ex') - self.assertRaises(exception.CinderException, - self.driver.detach_volume, - context.get_admin_context(), - self.volume) + # test the exception + mock_client.removeVolumeMetaData.side_effect = Exception( + 'Custom ex') + self.assertRaises(exception.CinderException, + self.driver.detach_volume, + context.get_admin_context(), + self.volume) def test_create_snapshot(self): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() - self.driver.create_snapshot(self.snapshot) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.driver.create_snapshot(self.snapshot) - commet = ( - '{"volume_id": "761fc5e5-5191-4ec7-aeba-33e36de44156",' - ' "display_name": "fakesnap",' - ' "description": "test description name",' - ' "volume_name": "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') + comment = ( + '{"volume_id": "761fc5e5-5191-4ec7-aeba-33e36de44156",' + ' "display_name": "fakesnap",' + ' "description": "test description name",' + ' "volume_name":' + ' "volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.createSnapshot( - 'oss-L4I73ONuTci9Fd4ceij-MQ', - 'osv-dh-F5VGRTseuujPjbeRBVg', - { - 'comment': commet, - 'readOnly': True}), - mock.call.logout()] + expected = [ + mock.call.createSnapshot( + 'oss-L4I73ONuTci9Fd4ceij-MQ', + 'osv-dh-F5VGRTseuujPjbeRBVg', + { + 'comment': comment, + 'readOnly': True})] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) def test_delete_snapshot(self): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.driver.delete_snapshot(self.snapshot) - self.driver.delete_snapshot(self.snapshot) + expected = [ + mock.call.deleteVolume('oss-L4I73ONuTci9Fd4ceij-MQ')] - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.deleteVolume('oss-L4I73ONuTci9Fd4ceij-MQ'), - mock.call.logout()] - - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) def test_delete_snapshot_in_use(self): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.driver.create_snapshot(self.snapshot) + self.driver.create_volume_from_snapshot(self.volume, self.snapshot) - self.driver.create_snapshot(self.snapshot) - self.driver.create_volume_from_snapshot(self.volume, self.snapshot) + ex = hpexceptions.HTTPConflict("In use") + mock_client.deleteVolume = mock.Mock(side_effect=ex) - ex = hpexceptions.HTTPConflict("In use") - mock_client.deleteVolume = mock.Mock(side_effect=ex) - - # Deleting the snapshot that a volume is dependent on should fail - self.assertRaises(exception.SnapshotIsBusy, - self.driver.delete_snapshot, - self.snapshot) + # Deleting the snapshot that a volume is dependent on should fail + self.assertRaises(exception.SnapshotIsBusy, + self.driver.delete_snapshot, + self.snapshot) def test_delete_snapshot_not_found(self): # setup_mock_client drive with default configuration @@ -1450,32 +1584,38 @@ class HP3PARBaseDriver(object): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() - model_update = self.driver.create_volume_from_snapshot(self.volume, - self.snapshot) - self.assertIsNone(model_update) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client - comment = ( - '{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",' - ' "display_name": "Foo Volume",' - ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') + model_update = self.driver.create_volume_from_snapshot( + self.volume, + self.snapshot) + self.assertIsNone(model_update) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.createSnapshot( - self.VOLUME_3PAR_NAME, - 'oss-L4I73ONuTci9Fd4ceij-MQ', - { - 'comment': comment, - 'readOnly': False}), - mock.call.logout()] + comment = ( + '{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",' + ' "display_name": "Foo Volume",' + ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') - mock_client.assert_has_calls(expected) + expected = [ + mock.call.createSnapshot( + self.VOLUME_3PAR_NAME, + 'oss-L4I73ONuTci9Fd4ceij-MQ', + { + 'comment': comment, + 'readOnly': False})] - volume = self.volume.copy() - volume['size'] = 1 - self.assertRaises(exception.InvalidInput, - self.driver.create_volume_from_snapshot, - volume, self.snapshot) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + + volume = self.volume.copy() + volume['size'] = 1 + self.assertRaises(exception.InvalidInput, + self.driver.create_volume_from_snapshot, + volume, self.snapshot) def test_create_volume_from_snapshot_and_extend(self): # setup_mock_client drive with default configuration @@ -1488,42 +1628,48 @@ class HP3PARBaseDriver(object): } mock_client = self.setup_driver(mock_conf=conf) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - volume = self.volume.copy() - volume['size'] = self.volume['size'] + 10 - model_update = self.driver.create_volume_from_snapshot(volume, - self.snapshot) - self.assertEqual(model_update, - {'host': volume_utils.append_host(self.FAKE_HOST, - HP3PAR_CPG)}) + volume = self.volume.copy() + volume['size'] = self.volume['size'] + 10 + model_update = self.driver.create_volume_from_snapshot( + volume, + self.snapshot) + self.assertEqual(model_update, + {'host': volume_utils.append_host(self.FAKE_HOST, + HP3PAR_CPG)}) - comment = ( - '{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",' - ' "display_name": "Foo Volume",' - ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') + comment = ( + '{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",' + ' "display_name": "Foo Volume",' + ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') - volume_name_3par = self.driver.common._encode_name(volume['id']) - osv_matcher = 'osv-' + volume_name_3par - omv_matcher = 'omv-' + volume_name_3par + volume_name_3par = common._encode_name(volume['id']) + osv_matcher = 'osv-' + volume_name_3par + omv_matcher = 'omv-' + volume_name_3par - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.createSnapshot( - self.VOLUME_3PAR_NAME, - 'oss-L4I73ONuTci9Fd4ceij-MQ', - { - 'comment': comment, - 'readOnly': False}), - mock.call.copyVolume( - osv_matcher, omv_matcher, HP3PAR_CPG, mock.ANY), - mock.call.getTask(mock.ANY), - mock.call.getVolume(osv_matcher), - mock.call.deleteVolume(osv_matcher), - mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher}), - mock.call.growVolume(osv_matcher, 10 * 1024), - mock.call.logout()] + expected = [ + mock.call.createSnapshot( + self.VOLUME_3PAR_NAME, + 'oss-L4I73ONuTci9Fd4ceij-MQ', + { + 'comment': comment, + 'readOnly': False}), + mock.call.copyVolume( + osv_matcher, omv_matcher, HP3PAR_CPG, mock.ANY), + mock.call.getTask(mock.ANY), + mock.call.getVolume(osv_matcher), + mock.call.deleteVolume(osv_matcher), + mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher}), + mock.call.growVolume(osv_matcher, 10 * 1024)] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_create_volume_from_snapshot_and_extend_with_qos( @@ -1548,42 +1694,50 @@ class HP3PARBaseDriver(object): 'tpvv': True, 'volume_type': self.volume_type}} - volume = self.volume_qos.copy() - volume['size'] = self.volume['size'] + 10 - model_update = self.driver.create_volume_from_snapshot(volume, - self.snapshot) - self.assertEqual(model_update, - {'host': volume_utils.append_host(self.FAKE_HOST, - HP3PAR_CPG_QOS)}) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - comment = ( - '{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",' - ' "display_name": "Foo Volume",' - ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') + volume = self.volume_qos.copy() + volume['size'] = self.volume['size'] + 10 + model_update = self.driver.create_volume_from_snapshot( + volume, + self.snapshot) + self.assertEqual(model_update, + {'host': volume_utils.append_host( + self.FAKE_HOST, + HP3PAR_CPG_QOS)}) - volume_name_3par = self.driver.common._encode_name(volume['id']) - osv_matcher = 'osv-' + volume_name_3par - omv_matcher = 'omv-' + volume_name_3par + comment = ( + '{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",' + ' "display_name": "Foo Volume",' + ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.createSnapshot( - self.VOLUME_3PAR_NAME, - 'oss-L4I73ONuTci9Fd4ceij-MQ', - { - 'comment': comment, - 'readOnly': False}), - mock.call.getCPG(HP3PAR_CPG_QOS), - mock.call.copyVolume( - osv_matcher, omv_matcher, HP3PAR_CPG_QOS, mock.ANY), - mock.call.getTask(mock.ANY), - mock.call.getVolume(osv_matcher), - mock.call.deleteVolume(osv_matcher), - mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher}), - mock.call.growVolume(osv_matcher, 10 * 1024), - mock.call.logout()] + volume_name_3par = common._encode_name(volume['id']) + osv_matcher = 'osv-' + volume_name_3par + omv_matcher = 'omv-' + volume_name_3par - mock_client.assert_has_calls(expected) + expected = [ + mock.call.createSnapshot( + self.VOLUME_3PAR_NAME, + 'oss-L4I73ONuTci9Fd4ceij-MQ', + { + 'comment': comment, + 'readOnly': False}), + mock.call.getCPG(HP3PAR_CPG_QOS), + mock.call.copyVolume( + osv_matcher, omv_matcher, HP3PAR_CPG_QOS, mock.ANY), + mock.call.getTask(mock.ANY), + mock.call.getVolume(osv_matcher), + mock.call.deleteVolume(osv_matcher), + mock.call.modifyVolume(omv_matcher, {'newName': osv_matcher}), + mock.call.growVolume(osv_matcher, 10 * 1024)] + + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) def test_create_volume_from_snapshot_and_extend_copy_fail(self): # setup_mock_client drive with default configuration @@ -1610,38 +1764,44 @@ class HP3PARBaseDriver(object): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() - _mock_volume_types.return_value = { - 'name': 'gold', - 'extra_specs': { - 'cpg': HP3PAR_CPG, - 'snap_cpg': HP3PAR_CPG_SNAP, - 'vvs_name': self.VVS_NAME, - 'qos': self.QOS, - 'tpvv': True, - 'volume_type': self.volume_type}} - self.driver.create_volume_from_snapshot(self.volume_qos, self.snapshot) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + _mock_volume_types.return_value = { + 'name': 'gold', + 'extra_specs': { + 'cpg': HP3PAR_CPG, + 'snap_cpg': HP3PAR_CPG_SNAP, + 'vvs_name': self.VVS_NAME, + 'qos': self.QOS, + 'tpvv': True, + 'volume_type': self.volume_type}} + self.driver.create_volume_from_snapshot( + self.volume_qos, + self.snapshot) - comment = ( - '{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",' - ' "display_name": "Foo Volume",' - ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') + comment = ( + '{"snapshot_id": "2f823bdc-e36e-4dc8-bd15-de1c7a28ff31",' + ' "display_name": "Foo Volume",' + ' "volume_id": "d03338a9-9115-48a3-8dfc-35cdfcdc15a7"}') - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.createSnapshot( - self.VOLUME_3PAR_NAME, - 'oss-L4I73ONuTci9Fd4ceij-MQ', { - 'comment': comment, - 'readOnly': False}), - mock.call.logout()] + expected = [ + mock.call.createSnapshot( + self.VOLUME_3PAR_NAME, + 'oss-L4I73ONuTci9Fd4ceij-MQ', { + 'comment': comment, + 'readOnly': False})] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) - volume = self.volume.copy() - volume['size'] = 1 - self.assertRaises(exception.InvalidInput, - self.driver.create_volume_from_snapshot, - volume, self.snapshot) + volume = self.volume.copy() + volume['size'] = 1 + self.assertRaises(exception.InvalidInput, + self.driver.create_volume_from_snapshot, + volume, self.snapshot) def test_terminate_connection(self): # setup_mock_client drive with default configuration @@ -1652,26 +1812,30 @@ class HP3PARBaseDriver(object): 'volumeName': self.VOLUME_3PAR_NAME, 'lun': None, 'type': 0}] - self.driver.terminate_connection( - self.volume, - self.connector, - force=True) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + self.driver.terminate_connection( + self.volume, + self.connector, + force=True) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getHostVLUNs(self.FAKE_HOST), - mock.call.deleteVLUN( - self.VOLUME_3PAR_NAME, - None, - self.FAKE_HOST), - mock.call.deleteHost(self.FAKE_HOST), - mock.call.removeVolumeMetaData( - self.VOLUME_3PAR_NAME, CHAP_USER_KEY), - mock.call.removeVolumeMetaData( - self.VOLUME_3PAR_NAME, CHAP_PASS_KEY), - mock.call.logout()] + expected = [ + mock.call.getHostVLUNs(self.FAKE_HOST), + mock.call.deleteVLUN( + self.VOLUME_3PAR_NAME, + None, + self.FAKE_HOST), + mock.call.deleteHost(self.FAKE_HOST), + mock.call.removeVolumeMetaData( + self.VOLUME_3PAR_NAME, CHAP_USER_KEY), + mock.call.removeVolumeMetaData( + self.VOLUME_3PAR_NAME, CHAP_PASS_KEY)] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) def test_update_volume_key_value_pair(self): # setup_mock_client drive with default configuration @@ -1680,59 +1844,70 @@ class HP3PARBaseDriver(object): key = 'a' value = 'b' - self.driver.common.update_volume_key_value_pair( - self.volume, - key, - value) - expected = [ - mock.call.setVolumeMetaData(self.VOLUME_3PAR_NAME, key, value)] + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + common.update_volume_key_value_pair( + self.volume, + key, + value) - mock_client.assert_has_calls(expected) + expected = [ + mock.call.setVolumeMetaData(self.VOLUME_3PAR_NAME, key, value)] - # check exception - mock_client.setVolumeMetaData.side_effect = Exception('fake') - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.common.update_volume_key_value_pair, - self.volume, - None, - 'b') + mock_client.assert_has_calls(expected) + + # check exception + mock_client.setVolumeMetaData.side_effect = Exception('fake') + self.assertRaises(exception.VolumeBackendAPIException, + common.update_volume_key_value_pair, + self.volume, + None, + 'b') def test_clear_volume_key_value_pair(self): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + key = 'a' + common = self.driver._login() + common.clear_volume_key_value_pair(self.volume, key) - key = 'a' - self.driver.common.clear_volume_key_value_pair(self.volume, key) + expected = [ + mock.call.removeVolumeMetaData(self.VOLUME_3PAR_NAME, key)] - expected = [ - mock.call.removeVolumeMetaData(self.VOLUME_3PAR_NAME, key)] + mock_client.assert_has_calls(expected) - mock_client.assert_has_calls(expected) - - # check the exception - mock_client.removeVolumeMetaData.side_effect = Exception('fake') - self.assertRaises(exception.VolumeBackendAPIException, - self.driver.common.clear_volume_key_value_pair, - self.volume, - None) + # check the exception + mock_client.removeVolumeMetaData.side_effect = Exception('fake') + self.assertRaises(exception.VolumeBackendAPIException, + common.clear_volume_key_value_pair, + self.volume, + None) def test_extend_volume(self): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() - grow_size = 3 - old_size = self.volume['size'] - new_size = old_size + grow_size - self.driver.extend_volume(self.volume, str(new_size)) - growth_size_mib = grow_size * units.Ki + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + grow_size = 3 + old_size = self.volume['size'] + new_size = old_size + grow_size + self.driver.extend_volume(self.volume, str(new_size)) + growth_size_mib = grow_size * units.Ki - expected = [ - mock.call.growVolume(self.VOLUME_3PAR_NAME, growth_size_mib)] + expected = [ + mock.call.growVolume(self.VOLUME_3PAR_NAME, growth_size_mib)] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected) def test_extend_volume_non_base(self): extend_ex = hpexceptions.HTTPForbidden(error={'code': 150}) @@ -1748,12 +1923,15 @@ class HP3PARBaseDriver(object): } mock_client = self.setup_driver(mock_conf=conf) - grow_size = 3 - old_size = self.volume['size'] - new_size = old_size + grow_size - self.driver.extend_volume(self.volume, str(new_size)) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + grow_size = 3 + old_size = self.volume['size'] + new_size = old_size + grow_size + self.driver.extend_volume(self.volume, str(new_size)) - self.assertEqual(2, mock_client.growVolume.call_count) + self.assertEqual(2, mock_client.growVolume.call_count) def test_extend_volume_non_base_failure(self): extend_ex = hpexceptions.HTTPForbidden(error={'code': 150}) @@ -1767,14 +1945,17 @@ class HP3PARBaseDriver(object): 'growVolume.side_effect': extend_ex } - self.setup_driver(mock_conf=conf) - grow_size = 3 - old_size = self.volume['size'] - new_size = old_size + grow_size - self.assertRaises(hpexceptions.HTTPForbidden, - self.driver.extend_volume, - self.volume, - str(new_size)) + mock_client = self.setup_driver(mock_conf=conf) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + grow_size = 3 + old_size = self.volume['size'] + new_size = old_size + grow_size + self.assertRaises(hpexceptions.HTTPForbidden, + self.driver.extend_volume, + self.volume, + str(new_size)) def test_get_ports(self): # setup_mock_client drive with default configuration @@ -1809,111 +1990,143 @@ class HP3PARBaseDriver(object): 'type': 2, 'portPos': {'node': 0, 'slot': 6, 'cardPort': 3}}]} - ports = self.driver.common.get_ports()['members'] - self.assertEqual(len(ports), 3) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + ports = common.get_ports()['members'] + self.assertEqual(len(ports), 3) def test_get_by_qos_spec_with_scoping(self): - self.setup_driver() - qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', self.QOS) - type_ref = volume_types.create(self.ctxt, - "type1", {"qos:maxIOPS": "100", - "qos:maxBWS": "50", - "qos:minIOPS": "10", - "qos:minBWS": "20", - "qos:latency": "5", - "qos:priority": "high"}) - qos_specs.associate_qos_with_type(self.ctxt, - qos_ref['id'], - type_ref['id']) - type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id']) - qos = self.driver.common._get_qos_by_volume_type(type_ref) - self.assertEqual(qos, {'maxIOPS': '1000', 'maxBWS': '50', - 'minIOPS': '100', 'minBWS': '25', - 'latency': '25', 'priority': 'low'}) + mock_client = self.setup_driver() + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', self.QOS) + type_ref = volume_types.create(self.ctxt, + "type1", {"qos:maxIOPS": "100", + "qos:maxBWS": "50", + "qos:minIOPS": "10", + "qos:minBWS": "20", + "qos:latency": "5", + "qos:priority": "high"}) + qos_specs.associate_qos_with_type(self.ctxt, + qos_ref['id'], + type_ref['id']) + type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id']) + qos = common._get_qos_by_volume_type(type_ref) + self.assertEqual(qos, {'maxIOPS': '1000', 'maxBWS': '50', + 'minIOPS': '100', 'minBWS': '25', + 'latency': '25', 'priority': 'low'}) def test_get_by_qos_spec(self): - self.setup_driver() - qos_ref = qos_specs.create(self.ctxt, 'qos-specs-1', self.QOS_SPECS) - type_ref = volume_types.create(self.ctxt, - "type1", {"qos:maxIOPS": "100", - "qos:maxBWS": "50", - "qos:minIOPS": "10", - "qos:minBWS": "20", - "qos:latency": "5", - "qos:priority": "high"}) - qos_specs.associate_qos_with_type(self.ctxt, - qos_ref['id'], - type_ref['id']) - type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id']) - qos = self.driver.common._get_qos_by_volume_type(type_ref) - self.assertEqual(qos, {'maxIOPS': '1000', 'maxBWS': '50', - 'minIOPS': '100', 'minBWS': '25', - 'latency': '25', 'priority': 'low'}) + mock_client = self.setup_driver() + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + qos_ref = qos_specs.create( + self.ctxt, + 'qos-specs-1', + self.QOS_SPECS) + type_ref = volume_types.create(self.ctxt, + "type1", {"qos:maxIOPS": "100", + "qos:maxBWS": "50", + "qos:minIOPS": "10", + "qos:minBWS": "20", + "qos:latency": "5", + "qos:priority": "high"}) + qos_specs.associate_qos_with_type(self.ctxt, + qos_ref['id'], + type_ref['id']) + type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id']) + qos = common._get_qos_by_volume_type(type_ref) + self.assertEqual(qos, {'maxIOPS': '1000', 'maxBWS': '50', + 'minIOPS': '100', 'minBWS': '25', + 'latency': '25', 'priority': 'low'}) def test_get_by_qos_by_type_only(self): - self.setup_driver() - type_ref = volume_types.create(self.ctxt, - "type1", {"qos:maxIOPS": "100", - "qos:maxBWS": "50", - "qos:minIOPS": "10", - "qos:minBWS": "20", - "qos:latency": "5", - "qos:priority": "high"}) - type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id']) - qos = self.driver.common._get_qos_by_volume_type(type_ref) - self.assertEqual(qos, {'maxIOPS': '100', 'maxBWS': '50', - 'minIOPS': '10', 'minBWS': '20', - 'latency': '5', 'priority': 'high'}) + mock_client = self.setup_driver() + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + type_ref = volume_types.create(self.ctxt, + "type1", {"qos:maxIOPS": "100", + "qos:maxBWS": "50", + "qos:minIOPS": "10", + "qos:minBWS": "20", + "qos:latency": "5", + "qos:priority": "high"}) + type_ref = volume_types.get_volume_type(self.ctxt, type_ref['id']) + qos = common._get_qos_by_volume_type(type_ref) + self.assertEqual(qos, {'maxIOPS': '100', 'maxBWS': '50', + 'minIOPS': '10', 'minBWS': '20', + 'latency': '5', 'priority': 'high'}) def test_create_vlun(self): host = 'fake-host' lun_id = 11 nsp = '1:2:3' mock_client = self.setup_driver() - location = ("%(name)s,%(lunid)s,%(host)s,%(nsp)s" % - {'name': self.VOLUME_NAME, - 'lunid': lun_id, - 'host': host, - 'nsp': nsp}) - mock_client.createVLUN.return_value = location + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + location = ("%(name)s,%(lunid)s,%(host)s,%(nsp)s" % + {'name': self.VOLUME_NAME, + 'lunid': lun_id, + 'host': host, + 'nsp': nsp}) + mock_client.createVLUN.return_value = location - expected_info = {'volume_name': self.VOLUME_NAME, - 'lun_id': lun_id, - 'host_name': host, - 'nsp': nsp} - vlun_info = self.driver.common._create_3par_vlun(self.VOLUME_NAME, - host, nsp) - self.assertEqual(expected_info, vlun_info) + expected_info = {'volume_name': self.VOLUME_NAME, + 'lun_id': lun_id, + 'host_name': host, + 'nsp': nsp} + common = self.driver._login() + vlun_info = common._create_3par_vlun( + self.VOLUME_NAME, + host, + nsp) + self.assertEqual(expected_info, vlun_info) - location = ("%(name)s,%(lunid)s,%(host)s" % - {'name': self.VOLUME_NAME, - 'lunid': lun_id, - 'host': host}) - mock_client.createVLUN.return_value = location - expected_info = {'volume_name': self.VOLUME_NAME, - 'lun_id': lun_id, - 'host_name': host} - vlun_info = self.driver.common._create_3par_vlun(self.VOLUME_NAME, - host, None) - self.assertEqual(expected_info, vlun_info) + location = ("%(name)s,%(lunid)s,%(host)s" % + {'name': self.VOLUME_NAME, + 'lunid': lun_id, + 'host': host}) + mock_client.createVLUN.return_value = location + expected_info = {'volume_name': self.VOLUME_NAME, + 'lun_id': lun_id, + 'host_name': host} + vlun_info = common._create_3par_vlun( + self.VOLUME_NAME, + host, + None) + self.assertEqual(expected_info, vlun_info) def test__get_existing_volume_ref_name(self): - self.setup_driver() - unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) + mock_client = self.setup_driver() + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - existing_ref = {'source-name': unm_matcher} - result = self.driver.common._get_existing_volume_ref_name(existing_ref) - self.assertEqual(unm_matcher, result) + unm_matcher = common._get_3par_unm_name(self.volume['id']) - existing_ref = {'source-id': self.volume['id']} - result = self.driver.common._get_existing_volume_ref_name(existing_ref) - self.assertEqual(unm_matcher, result) + existing_ref = {'source-name': unm_matcher} + result = common._get_existing_volume_ref_name(existing_ref) + self.assertEqual(unm_matcher, result) - existing_ref = {'bad-key': 'foo'} - self.assertRaises( - exception.ManageExistingInvalidReference, - self.driver.common._get_existing_volume_ref_name, - existing_ref) + existing_ref = {'source-id': self.volume['id']} + result = common._get_existing_volume_ref_name(existing_ref) + self.assertEqual(unm_matcher, result) + + existing_ref = {'bad-key': 'foo'} + self.assertRaises( + exception.ManageExistingInvalidReference, + common._get_existing_volume_ref_name, + existing_ref) @mock.patch.object(volume_types, 'get_volume_type') def test_manage_existing(self, _mock_volume_types): @@ -1935,69 +2148,75 @@ class HP3PARBaseDriver(object): mock_client.modifyVolume.return_value = ("anyResponse", {'taskid': 1}) mock_client.getTask.return_value = self.STATUS_DONE - unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) - osv_matcher = self.driver.common._get_3par_vol_name(volume['id']) - vvs_matcher = self.driver.common._get_3par_vvs_name(volume['id']) - existing_ref = {'source-name': unm_matcher} + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - obj = self.driver.manage_existing(volume, existing_ref) + unm_matcher = common._get_3par_unm_name(self.volume['id']) + osv_matcher = common._get_3par_vol_name(volume['id']) + vvs_matcher = common._get_3par_vvs_name(volume['id']) + existing_ref = {'source-name': unm_matcher} - expected_obj = {'display_name': 'Foo Volume', - 'host': 'my-stack1@3parxxx#fakepool'} + expected_obj = {'display_name': 'Foo Volume', + 'host': 'my-stack1@3parxxx#fakepool'} - expected_manage = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['source-name']), - mock.call.modifyVolume(existing_ref['source-name'], - {'newName': osv_matcher, - 'comment': self.CommentMatcher( - self.assertEqual, new_comment)}), - ] + obj = self.driver.manage_existing(volume, existing_ref) - retype_comment_qos = { - "display_name": "Foo Volume", - "volume_type_name": self.volume_type['name'], - "volume_type_id": self.volume_type['id'], - "qos": { - 'maxIOPS': '1000', - 'maxBWS': '50', - 'minIOPS': '100', - 'minBWS': '25', - 'latency': '25', - 'priority': 'low' + expected_manage = [ + mock.call.getVolume(existing_ref['source-name']), + mock.call.modifyVolume(existing_ref['source-name'], + {'newName': osv_matcher, + 'comment': self.CommentMatcher( + self.assertEqual, new_comment)}), + ] + + retype_comment_qos = { + "display_name": "Foo Volume", + "volume_type_name": self.volume_type['name'], + "volume_type_id": self.volume_type['id'], + "qos": { + 'maxIOPS': '1000', + 'maxBWS': '50', + 'minIOPS': '100', + 'minBWS': '25', + 'latency': '25', + 'priority': 'low' + } } - } - expected_snap_cpg = self.volume_type['extra_specs']['cpg'] - expected_retype_modify = [ - mock.call.modifyVolume(osv_matcher, - {'comment': self.CommentMatcher( - self.assertEqual, retype_comment_qos), - 'snapCPG': expected_snap_cpg}), - mock.call.deleteVolumeSet(vvs_matcher), - ] + expected_snap_cpg = self.volume_type['extra_specs']['cpg'] + expected_retype_modify = [ + mock.call.modifyVolume(osv_matcher, + {'comment': self.CommentMatcher( + self.assertEqual, + retype_comment_qos), + 'snapCPG': expected_snap_cpg}), + mock.call.deleteVolumeSet(vvs_matcher), + ] - expected_retype_specs = [ - mock.call.createVolumeSet(vvs_matcher, None), - mock.call.createQoSRules( - vvs_matcher, - {'ioMinGoal': 100, 'ioMaxLimit': 1000, - 'bwMinGoalKB': 25600, 'priority': 1, 'latencyGoal': 25, - 'bwMaxLimitKB': 51200}), - mock.call.addVolumeToVolumeSet(vvs_matcher, osv_matcher), - mock.call.modifyVolume( - osv_matcher, - {'action': 6, - 'userCPG': self.volume_type['extra_specs']['cpg'], - 'conversionOperation': 1, 'tuneOperation': 1}), - mock.call.getTask(1), - mock.call.logout() - ] + expected_retype_specs = [ + mock.call.createVolumeSet(vvs_matcher, None), + mock.call.createQoSRules( + vvs_matcher, + {'ioMinGoal': 100, 'ioMaxLimit': 1000, + 'bwMinGoalKB': 25600, 'priority': 1, 'latencyGoal': 25, + 'bwMaxLimitKB': 51200}), + mock.call.addVolumeToVolumeSet(vvs_matcher, osv_matcher), + mock.call.modifyVolume( + osv_matcher, + {'action': 6, + 'userCPG': self.volume_type['extra_specs']['cpg'], + 'conversionOperation': 1, 'tuneOperation': 1}), + mock.call.getTask(1) + ] - mock_client.assert_has_calls(expected_manage) - mock_client.assert_has_calls(expected_retype_modify) - mock_client.assert_has_calls(expected_retype_specs) - self.assertEqual(expected_obj, obj) + mock_client.assert_has_calls(self.standard_login + expected_manage) + mock_client.assert_has_calls(expected_retype_modify) + mock_client.assert_has_calls( + expected_retype_specs + + self.standard_logout) + self.assertEqual(expected_obj, obj) @mock.patch.object(volume_types, 'get_volume_type') def test_manage_existing_vvs(self, _mock_volume_types): @@ -2022,51 +2241,57 @@ class HP3PARBaseDriver(object): 'volume_type_id': 'acfa9fa4-54a0-4340-a3d8-bfcf19aea65e', 'id': id} - unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) - osv_matcher = self.driver.common._get_3par_vol_name(volume['id']) - vvs_matcher = self.driver.common._get_3par_vvs_name(volume['id']) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - existing_ref = {'source-name': unm_matcher} + unm_matcher = common._get_3par_unm_name(self.volume['id']) + osv_matcher = common._get_3par_vol_name(volume['id']) + vvs_matcher = common._get_3par_vvs_name(volume['id']) - obj = self.driver.manage_existing(volume, existing_ref) + existing_ref = {'source-name': unm_matcher} - expected_obj = {'display_name': 'Test Volume', - 'host': 'my-stack1@3parxxx#qospool'} - expected_manage = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['source-name']), - mock.call.modifyVolume(existing_ref['source-name'], - {'newName': osv_matcher, - 'comment': self.CommentMatcher( - self.assertEqual, new_comment)}) - ] + obj = self.driver.manage_existing(volume, existing_ref) - retype_comment_vvs = { - "display_name": "Foo Volume", - "volume_type_name": test_volume_type['name'], - "volume_type_id": test_volume_type['id'], - "vvs": vvs - } + expected_obj = {'display_name': 'Test Volume', + 'host': 'my-stack1@3parxxx#qospool'} + expected_manage = [ + mock.call.getVolume(existing_ref['source-name']), + mock.call.modifyVolume(existing_ref['source-name'], + {'newName': osv_matcher, + 'comment': self.CommentMatcher( + self.assertEqual, new_comment)}) + ] - expected_retype = [ - mock.call.modifyVolume(osv_matcher, - {'comment': self.CommentMatcher( - self.assertEqual, retype_comment_vvs), - 'snapCPG': 'OpenStackCPGSnap'}), - mock.call.deleteVolumeSet(vvs_matcher), - mock.call.addVolumeToVolumeSet(vvs, osv_matcher), - mock.call.modifyVolume(osv_matcher, - {'action': 6, 'userCPG': - test_volume_type['extra_specs']['cpg'], - 'conversionOperation': 1, - 'tuneOperation': 1}), - mock.call.getTask(1), - mock.call.logout() - ] + retype_comment_vvs = { + "display_name": "Foo Volume", + "volume_type_name": test_volume_type['name'], + "volume_type_id": test_volume_type['id'], + "vvs": vvs + } - mock_client.assert_has_calls(expected_manage) - mock_client.assert_has_calls(expected_retype) - self.assertEqual(expected_obj, obj) + expected_retype = [ + mock.call.modifyVolume(osv_matcher, + {'comment': self.CommentMatcher( + self.assertEqual, + retype_comment_vvs), + 'snapCPG': 'OpenStackCPGSnap'}), + mock.call.deleteVolumeSet(vvs_matcher), + mock.call.addVolumeToVolumeSet(vvs, osv_matcher), + mock.call.modifyVolume(osv_matcher, + {'action': 6, 'userCPG': + test_volume_type['extra_specs']['cpg'], + 'conversionOperation': 1, + 'tuneOperation': 1}), + mock.call.getTask(1) + ] + + mock_client.assert_has_calls(self.standard_login + expected_manage) + mock_client.assert_has_calls( + expected_retype + + self.standard_logout) + self.assertEqual(expected_obj, obj) def test_manage_existing_no_volume_type(self): mock_client = self.setup_driver() @@ -2085,59 +2310,67 @@ class HP3PARBaseDriver(object): mock_client.getVolume.return_value = {'comment': comment} - unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) - osv_matcher = self.driver.common._get_3par_vol_name(volume['id']) - existing_ref = {'source-name': unm_matcher} + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + unm_matcher = common._get_3par_unm_name(self.volume['id']) + osv_matcher = common._get_3par_vol_name(volume['id']) + existing_ref = {'source-name': unm_matcher} - obj = self.driver.manage_existing(volume, existing_ref) + obj = self.driver.manage_existing(volume, existing_ref) - expected_obj = {'display_name': 'Foo Volume'} - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['source-name']), - mock.call.modifyVolume(existing_ref['source-name'], - {'newName': osv_matcher, - 'comment': new_comment}), - mock.call.logout() - ] + expected_obj = {'display_name': 'Foo Volume'} + expected = [ + mock.call.getVolume(existing_ref['source-name']), + mock.call.modifyVolume(existing_ref['source-name'], + {'newName': osv_matcher, + 'comment': new_comment}) + ] - mock_client.assert_has_calls(expected) - self.assertEqual(expected_obj, obj) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + self.assertEqual(expected_obj, obj) - volume['display_name'] = 'Test Volume' + volume['display_name'] = 'Test Volume' - obj = self.driver.manage_existing(volume, existing_ref) + obj = self.driver.manage_existing(volume, existing_ref) - expected_obj = {'display_name': 'Test Volume'} - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['source-name']), - mock.call.modifyVolume(existing_ref['source-name'], - {'newName': osv_matcher, - 'comment': new_comment}), - mock.call.logout() - ] + expected_obj = {'display_name': 'Test Volume'} + expected = [ + mock.call.getVolume(existing_ref['source-name']), + mock.call.modifyVolume(existing_ref['source-name'], + {'newName': osv_matcher, + 'comment': new_comment}) + ] - mock_client.assert_has_calls(expected) - self.assertEqual(expected_obj, obj) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + self.assertEqual(expected_obj, obj) - mock_client.getVolume.return_value = {} - volume['display_name'] = None + mock_client.getVolume.return_value = {} + volume['display_name'] = None + common = self.driver._login() - obj = self.driver.manage_existing(volume, existing_ref) + obj = self.driver.manage_existing(volume, existing_ref) - expected_obj = {'display_name': None} - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['source-name']), - mock.call.modifyVolume(existing_ref['source-name'], - {'newName': osv_matcher, - 'comment': new_comment}), - mock.call.logout() - ] + expected_obj = {'display_name': None} + expected = [ + mock.call.getVolume(existing_ref['source-name']), + mock.call.modifyVolume(existing_ref['source-name'], + {'newName': osv_matcher, + 'comment': new_comment}) + ] - mock_client.assert_has_calls(expected) - self.assertEqual(expected_obj, obj) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + self.assertEqual(expected_obj, obj) def test_manage_existing_invalid_input(self): mock_client = self.setup_driver() @@ -2148,21 +2381,24 @@ class HP3PARBaseDriver(object): mock_client.getVolume.side_effect = hpexceptions.HTTPNotFound('fake') - unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) - existing_ref = {'source-name': unm_matcher} + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + unm_matcher = common._get_3par_unm_name(self.volume['id']) + existing_ref = {'source-name': unm_matcher} - self.assertRaises(exception.InvalidInput, - self.driver.manage_existing, - volume=volume, - existing_ref=existing_ref) + self.assertRaises(exception.InvalidInput, + self.driver.manage_existing, + volume=volume, + existing_ref=existing_ref) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['source-name']), - mock.call.logout() - ] + expected = [mock.call.getVolume(existing_ref['source-name'])] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) def test_manage_existing_volume_type_exception(self): mock_client = self.setup_driver() @@ -2176,21 +2412,24 @@ class HP3PARBaseDriver(object): mock_client.getVolume.return_value = {'comment': comment} - unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) - existing_ref = {'source-name': unm_matcher} + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + unm_matcher = common._get_3par_unm_name(self.volume['id']) + existing_ref = {'source-name': unm_matcher} - self.assertRaises(exception.ManageExistingVolumeTypeMismatch, - self.driver.manage_existing, - volume=volume, - existing_ref=existing_ref) + self.assertRaises(exception.ManageExistingVolumeTypeMismatch, + self.driver.manage_existing, + volume=volume, + existing_ref=existing_ref) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['source-name']), - mock.call.logout() - ] + expected = [mock.call.getVolume(existing_ref['source-name'])] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) @mock.patch.object(volume_types, 'get_volume_type') def test_manage_existing_retype_exception(self, _mock_volume_types): @@ -2221,119 +2460,137 @@ class HP3PARBaseDriver(object): {'domain': 'domain3'}, ] - unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) - osv_matcher = self.driver.common._get_3par_vol_name(volume['id']) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - existing_ref = {'source-name': unm_matcher} + unm_matcher = common._get_3par_unm_name(self.volume['id']) + osv_matcher = common._get_3par_vol_name(volume['id']) - self.assertRaises(exception.Invalid3PARDomain, - self.driver.manage_existing, - volume=volume, - existing_ref=existing_ref) + existing_ref = {'source-name': unm_matcher} - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(unm_matcher), - mock.call.modifyVolume( - unm_matcher, {'newName': osv_matcher, 'comment': mock.ANY}), - mock.call.getCPG('OpenStackCPG'), - mock.call.getVolume(osv_matcher), - mock.call.getCPG('testUserCpg0'), - mock.call.getCPG('OpenStackCPG'), - mock.call.modifyVolume( - osv_matcher, {'newName': unm_matcher, - 'comment': self.MANAGE_VOLUME_INFO['comment']}), - mock.call.logout() - ] + self.assertRaises(exception.Invalid3PARDomain, + self.driver.manage_existing, + volume=volume, + existing_ref=existing_ref) - mock_client.assert_has_calls(expected) + expected = [ + + mock.call.getVolume(unm_matcher), + mock.call.modifyVolume( + unm_matcher, { + 'newName': osv_matcher, + 'comment': mock.ANY}), + mock.call.getCPG('OpenStackCPG'), + mock.call.getVolume(osv_matcher), + mock.call.getCPG('testUserCpg0'), + mock.call.getCPG('OpenStackCPG'), + mock.call.modifyVolume( + osv_matcher, {'newName': unm_matcher, + 'comment': self.MANAGE_VOLUME_INFO + ['comment']}) + ] + + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) def test_manage_existing_get_size(self): mock_client = self.setup_driver() mock_client.getVolume.return_value = {'sizeMiB': 2048} - unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) - volume = {} - existing_ref = {'source-name': unm_matcher} + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + unm_matcher = common._get_3par_unm_name(self.volume['id']) + volume = {} + existing_ref = {'source-name': unm_matcher} - size = self.driver.manage_existing_get_size(volume, existing_ref) + size = self.driver.manage_existing_get_size(volume, existing_ref) - expected_size = 2 - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['source-name']), - mock.call.logout() - ] + expected_size = 2 + expected = [mock.call.getVolume(existing_ref['source-name'])] - mock_client.assert_has_calls(expected, True) - self.assertEqual(expected_size, size) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + self.assertEqual(expected_size, size) def test_manage_existing_get_size_invalid_reference(self): mock_client = self.setup_driver() - volume = {} - existing_ref = {'source-name': self.VOLUME_3PAR_NAME} + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + volume = {} + existing_ref = {'source-name': self.VOLUME_3PAR_NAME} - self.assertRaises(exception.ManageExistingInvalidReference, - self.driver.manage_existing_get_size, - volume=volume, - existing_ref=existing_ref) + self.assertRaises(exception.ManageExistingInvalidReference, + self.driver.manage_existing_get_size, + volume=volume, + existing_ref=existing_ref) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.logout() - ] + mock_client.assert_has_calls( + self.standard_login + + self.standard_logout) - mock_client.assert_has_calls(expected) + existing_ref = {} - existing_ref = {} + self.assertRaises(exception.ManageExistingInvalidReference, + self.driver.manage_existing_get_size, + volume=volume, + existing_ref=existing_ref) - self.assertRaises(exception.ManageExistingInvalidReference, - self.driver.manage_existing_get_size, - volume=volume, - existing_ref=existing_ref) - - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.logout() - ] - - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + self.standard_logout) def test_manage_existing_get_size_invalid_input(self): mock_client = self.setup_driver() mock_client.getVolume.side_effect = hpexceptions.HTTPNotFound('fake') - unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) - volume = {} - existing_ref = {'source-name': unm_matcher} + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + unm_matcher = common._get_3par_unm_name(self.volume['id']) + volume = {} + existing_ref = {'source-name': unm_matcher} - self.assertRaises(exception.InvalidInput, - self.driver.manage_existing_get_size, - volume=volume, - existing_ref=existing_ref) + self.assertRaises(exception.InvalidInput, + self.driver.manage_existing_get_size, + volume=volume, + existing_ref=existing_ref) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(existing_ref['source-name']), - mock.call.logout() - ] + expected = [mock.call.getVolume(existing_ref['source-name'])] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) def test_unmanage(self): mock_client = self.setup_driver() + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + self.driver.unmanage(self.volume) - self.driver.unmanage(self.volume) + osv_matcher = common._get_3par_vol_name(self.volume['id']) + unm_matcher = common._get_3par_unm_name(self.volume['id']) - osv_matcher = self.driver.common._get_3par_vol_name(self.volume['id']) - unm_matcher = self.driver.common._get_3par_unm_name(self.volume['id']) + expected = [ + mock.call.modifyVolume(osv_matcher, {'newName': unm_matcher}) + ] - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.modifyVolume(osv_matcher, {'newName': unm_matcher}), - mock.call.logout()] - - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) def test__safe_hostname(self): long_hostname = "abc123abc123abc123abc123abc123abc123" @@ -2366,18 +2623,12 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): driver=hpfcdriver.HP3PARFCDriver) expected = [ - mock.call.setSSHOptions( - HP3PAR_SAN_IP, - HP3PAR_USER_NAME, - HP3PAR_USER_PASS, - privatekey=HP3PAR_SAN_SSH_PRIVATE, - port=HP3PAR_SAN_SSH_PORT, - conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT), - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), mock.call.getCPG(HP3PAR_CPG), - mock.call.getCPG(HP3PAR_CPG2), - mock.call.logout()] - mock_client.assert_has_calls(expected) + mock.call.getCPG(HP3PAR_CPG2)] + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) mock_client.reset_mock() return mock_client @@ -2406,7 +2657,11 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): 'slot': 2}, 'vendor': None, 'wwn': self.wwn[1]}]}] - mock_client.findHost.return_value = self.FAKE_HOST + mock_client.queryHost.return_value = { + 'members': [{ + 'name': self.FAKE_HOST + }] + } mock_client.getHostVLUNs.return_value = [ {'active': True, 'volumeName': self.VOLUME_3PAR_NAME, @@ -2418,26 +2673,32 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): 'nsp': 'something'}) mock_client.createVLUN.return_value = location - result = self.driver.initialize_connection(self.volume, self.connector) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + result = self.driver.initialize_connection( + self.volume, + self.connector) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(self.VOLUME_3PAR_NAME), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getHost(self.FAKE_HOST), - mock.ANY, - mock.call.getHost(self.FAKE_HOST), - mock.call.getPorts(), - mock.call.createVLUN( - self.VOLUME_3PAR_NAME, - auto=True, - hostname=self.FAKE_HOST), - mock.call.getHostVLUNs(self.FAKE_HOST), - mock.call.logout()] + expected = [ + mock.call.getVolume(self.VOLUME_3PAR_NAME), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getHost(self.FAKE_HOST), + mock.ANY, + mock.call.getHost(self.FAKE_HOST), + mock.call.getPorts(), + mock.call.createVLUN( + self.VOLUME_3PAR_NAME, + auto=True, + hostname=self.FAKE_HOST), + mock.call.getHostVLUNs(self.FAKE_HOST)] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) - self.assertDictMatch(result, self.properties) + self.assertDictMatch(result, self.properties) @mock.patch('cinder.zonemanager.utils.create_lookup_service') def test_initialize_connection_with_lookup_single_nsp(self, mock_lookup): @@ -2467,7 +2728,11 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): 'slot': 2}, 'vendor': None, 'wwn': self.wwn[0]}]}] - mock_client.findHost.return_value = self.FAKE_HOST + mock_client.queryHost.return_value = { + 'members': [{ + 'name': self.FAKE_HOST + }] + } mock_client.getHostVLUNs.return_value = [ {'active': True, 'volumeName': self.VOLUME_3PAR_NAME, @@ -2495,28 +2760,32 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): ['0987654321234'] }}} - result = self.driver.initialize_connection(self.volume, connector) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + result = self.driver.initialize_connection(self.volume, connector) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(self.VOLUME_3PAR_NAME), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getHost(self.FAKE_HOST), - mock.ANY, - mock.call.getHost(self.FAKE_HOST), - mock.call.getPorts(), - mock.call.getPorts(), - mock.call.createVLUN( - self.VOLUME_3PAR_NAME, - auto=True, - hostname=self.FAKE_HOST, - portPos={'node': 7, 'slot': 1, 'cardPort': 1}), - mock.call.getHostVLUNs(self.FAKE_HOST), - mock.call.logout()] + expected = [ + mock.call.getVolume(self.VOLUME_3PAR_NAME), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getHost(self.FAKE_HOST), + mock.ANY, + mock.call.getHost(self.FAKE_HOST), + mock.call.getPorts(), + mock.call.getPorts(), + mock.call.createVLUN( + self.VOLUME_3PAR_NAME, + auto=True, + hostname=self.FAKE_HOST, + portPos={'node': 7, 'slot': 1, 'cardPort': 1}), + mock.call.getHostVLUNs(self.FAKE_HOST)] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) - self.assertDictMatch(result, expected_properties) + self.assertDictMatch(result, expected_properties) def test_terminate_connection(self): # setup_mock_client drive with default configuration @@ -2531,7 +2800,6 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): mock_client.getHostVLUNs.side_effect = effects expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), mock.call.getHostVLUNs(self.FAKE_HOST), mock.call.deleteVLUN( self.VOLUME_3PAR_NAME, @@ -2539,35 +2807,46 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): self.FAKE_HOST), mock.call.deleteHost(self.FAKE_HOST), mock.call.getHostVLUNs(self.FAKE_HOST), - mock.call.getPorts(), - mock.call.logout()] + mock.call.getPorts()] - conn_info = self.driver.terminate_connection(self.volume, - self.connector) - mock_client.assert_has_calls(expected) - self.assertIn('data', conn_info) - self.assertIn('initiator_target_map', conn_info['data']) - mock_client.reset_mock() + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + conn_info = self.driver.terminate_connection(self.volume, + self.connector) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + self.assertIn('data', conn_info) + self.assertIn('initiator_target_map', conn_info['data']) + mock_client.reset_mock() - mock_client.getHostVLUNs.side_effect = effects + mock_client.getHostVLUNs.side_effect = effects - # mock some deleteHost exceptions that are handled - delete_with_vlun = hpexceptions.HTTPConflict( - error={'message': "has exported VLUN"}) - delete_with_hostset = hpexceptions.HTTPConflict( - error={'message': "host is a member of a set"}) - mock_client.deleteHost = mock.Mock( - side_effect=[delete_with_vlun, delete_with_hostset]) + # mock some deleteHost exceptions that are handled + delete_with_vlun = hpexceptions.HTTPConflict( + error={'message': "has exported VLUN"}) + delete_with_hostset = hpexceptions.HTTPConflict( + error={'message': "host is a member of a set"}) + mock_client.deleteHost = mock.Mock( + side_effect=[delete_with_vlun, delete_with_hostset]) - conn_info = self.driver.terminate_connection(self.volume, - self.connector) - mock_client.assert_has_calls(expected) - mock_client.reset_mock() - mock_client.getHostVLUNs.side_effect = effects + conn_info = self.driver.terminate_connection(self.volume, + self.connector) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + mock_client.reset_mock() + mock_client.getHostVLUNs.side_effect = effects - conn_info = self.driver.terminate_connection(self.volume, - self.connector) - mock_client.assert_has_calls(expected) + conn_info = self.driver.terminate_connection(self.volume, + self.connector) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) @mock.patch('cinder.zonemanager.utils.create_lookup_service') def test_terminate_connection_with_lookup(self, mock_lookup): @@ -2593,7 +2872,6 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): mock_client.getHostVLUNs.side_effect = effects expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), mock.call.getHostVLUNs(self.FAKE_HOST), mock.call.deleteVLUN( self.VOLUME_3PAR_NAME, @@ -2601,35 +2879,46 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): self.FAKE_HOST), mock.call.deleteHost(self.FAKE_HOST), mock.call.getHostVLUNs(self.FAKE_HOST), - mock.call.getPorts(), - mock.call.logout()] + mock.call.getPorts()] - conn_info = self.driver.terminate_connection(self.volume, - self.connector) - mock_client.assert_has_calls(expected) - self.assertIn('data', conn_info) - self.assertIn('initiator_target_map', conn_info['data']) - mock_client.reset_mock() + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + conn_info = self.driver.terminate_connection(self.volume, + self.connector) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + self.assertIn('data', conn_info) + self.assertIn('initiator_target_map', conn_info['data']) + mock_client.reset_mock() - mock_client.getHostVLUNs.side_effect = effects + mock_client.getHostVLUNs.side_effect = effects - # mock some deleteHost exceptions that are handled - delete_with_vlun = hpexceptions.HTTPConflict( - error={'message': "has exported VLUN"}) - delete_with_hostset = hpexceptions.HTTPConflict( - error={'message': "host is a member of a set"}) - mock_client.deleteHost = mock.Mock( - side_effect=[delete_with_vlun, delete_with_hostset]) + # mock some deleteHost exceptions that are handled + delete_with_vlun = hpexceptions.HTTPConflict( + error={'message': "has exported VLUN"}) + delete_with_hostset = hpexceptions.HTTPConflict( + error={'message': "host is a member of a set"}) + mock_client.deleteHost = mock.Mock( + side_effect=[delete_with_vlun, delete_with_hostset]) - conn_info = self.driver.terminate_connection(self.volume, - self.connector) - mock_client.assert_has_calls(expected) - mock_client.reset_mock() - mock_client.getHostVLUNs.side_effect = effects + conn_info = self.driver.terminate_connection(self.volume, + self.connector) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + mock_client.reset_mock() + mock_client.getHostVLUNs.side_effect = effects - conn_info = self.driver.terminate_connection(self.volume, - self.connector) - mock_client.assert_has_calls(expected) + conn_info = self.driver.terminate_connection(self.volume, + self.connector) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) def test_terminate_connection_more_vols(self): mock_client = self.setup_driver() @@ -2645,19 +2934,23 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): ] expect_less = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), mock.call.getHostVLUNs(self.FAKE_HOST), mock.call.deleteVLUN( self.VOLUME_3PAR_NAME, None, self.FAKE_HOST), - mock.call.getHostVLUNs(self.FAKE_HOST), - mock.call.logout()] + mock.call.getHostVLUNs(self.FAKE_HOST)] - conn_info = self.driver.terminate_connection(self.volume, - self.connector) - mock_client.assert_has_calls(expect_less) - self.assertNotIn('initiator_target_map', conn_info['data']) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + conn_info = self.driver.terminate_connection(self.volume, + self.connector) + mock_client.assert_has_calls( + self.standard_login + + expect_less + + self.standard_logout) + self.assertNotIn('initiator_target_map', conn_info['data']) def test_get_volume_stats(self): # setup_mock_client drive with default configuration @@ -2669,52 +2962,58 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): 'freeCapacityMiB': 1024.0 * 2, 'totalCapacityMiB': 1024.0 * 123 } - stats = self.driver.get_volume_stats(True) - const = 0.0009765625 - self.assertEqual(stats['storage_protocol'], 'FC') - self.assertEqual(stats['total_capacity_gb'], 0) - self.assertEqual(stats['free_capacity_gb'], 0) - self.assertEqual(stats['pools'][0]['total_capacity_gb'], 123.0) - self.assertEqual(stats['pools'][0]['free_capacity_gb'], 2.0) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getStorageSystemInfo(), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getCPG(HP3PAR_CPG2), - mock.call.logout()] + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - mock_client.assert_has_calls(expected) - stats = self.driver.get_volume_stats(True) - self.assertEqual(stats['storage_protocol'], 'FC') - self.assertEqual(stats['total_capacity_gb'], 0) - self.assertEqual(stats['free_capacity_gb'], 0) - self.assertEqual(stats['pools'][0]['total_capacity_gb'], 123.0) - self.assertEqual(stats['pools'][0]['free_capacity_gb'], 2.0) + stats = self.driver.get_volume_stats(True) + const = 0.0009765625 + self.assertEqual(stats['storage_protocol'], 'FC') + self.assertEqual(stats['total_capacity_gb'], 0) + self.assertEqual(stats['free_capacity_gb'], 0) + self.assertEqual(stats['pools'][0]['total_capacity_gb'], 123.0) + self.assertEqual(stats['pools'][0]['free_capacity_gb'], 2.0) - cpg2 = self.cpgs[0].copy() - cpg2.update({'SDGrowth': {'limitMiB': 8192}}) - mock_client.getCPG.return_value = cpg2 + expected = [ + mock.call.getStorageSystemInfo(), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getCPG(HP3PAR_CPG2)] - stats = self.driver.get_volume_stats(True) - self.assertEqual(stats['storage_protocol'], 'FC') - total_capacity_gb = 8192 * const - self.assertEqual(stats['total_capacity_gb'], 0) - self.assertEqual(stats['pools'][0]['total_capacity_gb'], - total_capacity_gb) - free_capacity_gb = int( - (8192 - self.cpgs[0]['UsrUsage']['usedMiB']) * const) - self.assertEqual(stats['free_capacity_gb'], 0) - self.assertEqual(stats['pools'][0]['free_capacity_gb'], - free_capacity_gb) - self.driver.common.client.deleteCPG(HP3PAR_CPG) - self.driver.common.client.createCPG(HP3PAR_CPG, {}) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + stats = self.driver.get_volume_stats(True) + self.assertEqual(stats['storage_protocol'], 'FC') + self.assertEqual(stats['total_capacity_gb'], 0) + self.assertEqual(stats['free_capacity_gb'], 0) + self.assertEqual(stats['pools'][0]['total_capacity_gb'], 123.0) + self.assertEqual(stats['pools'][0]['free_capacity_gb'], 2.0) + + cpg2 = self.cpgs[0].copy() + cpg2.update({'SDGrowth': {'limitMiB': 8192}}) + mock_client.getCPG.return_value = cpg2 + + stats = self.driver.get_volume_stats(True) + self.assertEqual(stats['storage_protocol'], 'FC') + total_capacity_gb = 8192 * const + self.assertEqual(stats['total_capacity_gb'], 0) + self.assertEqual(stats['pools'][0]['total_capacity_gb'], + total_capacity_gb) + free_capacity_gb = int( + (8192 - self.cpgs[0]['UsrUsage']['usedMiB']) * const) + self.assertEqual(stats['free_capacity_gb'], 0) + self.assertEqual(stats['pools'][0]['free_capacity_gb'], + free_capacity_gb) + common.client.deleteCPG(HP3PAR_CPG) + common.client.createCPG(HP3PAR_CPG, {}) def test_create_host(self): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() - mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG} mock_client.getCPG.return_value = {} mock_client.getHost.side_effect = [ @@ -2736,25 +3035,30 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): 'slot': 2}, 'vendor': None, 'wwn': self.wwn[1]}]}] - mock_client.findHost.return_value = None + mock_client.queryHost.return_value = None mock_client.getVLUN.return_value = {'lun': 186} + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + host = self.driver._create_host( + common, + self.volume, + self.connector) + expected = [ + mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getHost(self.FAKE_HOST), + mock.call.queryHost(['123456789012345', '123456789054321']), + mock.call.createHost( + self.FAKE_HOST, + FCWwns=['123456789012345', '123456789054321'], + optional={'domain': None, 'persona': 2}), + mock.call.getHost(self.FAKE_HOST)] - host = self.driver._create_host(self.volume, self.connector) - expected = [ - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getHost(self.FAKE_HOST), - mock.call.findHost(wwn='123456789012345'), - mock.call.findHost(wwn='123456789054321'), - mock.call.createHost( - self.FAKE_HOST, - FCWwns=['123456789012345', '123456789054321'], - optional={'domain': None, 'persona': 2}), - mock.call.getHost(self.FAKE_HOST)] + mock_client.assert_has_calls(expected) - mock_client.assert_has_calls(expected) - - self.assertEqual(host['name'], self.FAKE_HOST) + self.assertEqual(host['name'], self.FAKE_HOST) def test_create_invalid_host(self): # setup_mock_client drive with default configuration @@ -2768,20 +3072,31 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): 'name': 'fakehost.foo', 'FCPaths': [{'wwn': '123456789012345'}, { 'wwn': '123456789054321'}]}] - mock_client.findHost.return_value = 'fakehost.foo' + mock_client.queryHost.return_value = { + 'members': [{ + 'name': 'fakehost.foo' + }] + } - host = self.driver._create_host(self.volume, self.connector) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + host = self.driver._create_host( + common, + self.volume, + self.connector) - expected = [ - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getHost('fakehost'), - mock.call.findHost(wwn='123456789012345'), - mock.call.getHost('fakehost.foo')] + expected = [ + mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getHost('fakehost'), + mock.call.queryHost(['123456789012345', '123456789054321']), + mock.call.getHost('fakehost.foo')] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected) - self.assertEqual(host['name'], 'fakehost.foo') + self.assertEqual(host['name'], 'fakehost.foo') def test_create_modify_host(self): # setup_mock_client drive with default configuration @@ -2795,21 +3110,28 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): 'FCPaths': [{'wwn': '123456789012345'}, { 'wwn': '123456789054321'}]}] - host = self.driver._create_host(self.volume, self.connector) - expected = [ - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getHost('fakehost'), - mock.call.modifyHost( - 'fakehost', { - 'FCWWNs': ['123456789012345', '123456789054321'], - 'pathOperation': 1}), - mock.call.getHost('fakehost')] + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + host = self.driver._create_host( + common, + self.volume, + self.connector) + expected = [ + mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getHost('fakehost'), + mock.call.modifyHost( + 'fakehost', { + 'FCWWNs': ['123456789012345', '123456789054321'], + 'pathOperation': 1}), + mock.call.getHost('fakehost')] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected) - self.assertEqual(host['name'], self.FAKE_HOST) - self.assertEqual(len(host['FCPaths']), 2) + self.assertEqual(host['name'], self.FAKE_HOST) + self.assertEqual(len(host['FCPaths']), 2) def test_modify_host_with_new_wwn(self): # setup_mock_client drive with default configuration @@ -2826,21 +3148,28 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): {'wwn': '123456789054321'}]} mock_client.getHost.side_effect = [getHost_ret1, getHost_ret2] - host = self.driver._create_host(self.volume, self.connector) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + host = self.driver._create_host( + common, + self.volume, + self.connector) - expected = [ - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getHost('fakehost'), - mock.call.modifyHost( - 'fakehost', { - 'FCWWNs': ['123456789012345'], 'pathOperation': 1}), - mock.call.getHost('fakehost')] + expected = [ + mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getHost('fakehost'), + mock.call.modifyHost( + 'fakehost', { + 'FCWWNs': ['123456789012345'], 'pathOperation': 1}), + mock.call.getHost('fakehost')] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected) - self.assertEqual(host['name'], self.FAKE_HOST) - self.assertEqual(len(host['FCPaths']), 2) + self.assertEqual(host['name'], self.FAKE_HOST) + self.assertEqual(len(host['FCPaths']), 2) def test_modify_host_with_unknown_wwn_and_new_wwn(self): # setup_mock_client drive with default configuration @@ -2859,21 +3188,28 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase): {'wwn': 'xxxxxxxxxxxxxxx'}]} mock_client.getHost.side_effect = [getHost_ret1, getHost_ret2] - host = self.driver._create_host(self.volume, self.connector) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + host = self.driver._create_host( + common, + self.volume, + self.connector) - expected = [ - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getHost('fakehost'), - mock.call.modifyHost( - 'fakehost', { - 'FCWWNs': ['123456789012345'], 'pathOperation': 1}), - mock.call.getHost('fakehost')] + expected = [ + mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getHost('fakehost'), + mock.call.modifyHost( + 'fakehost', { + 'FCWWNs': ['123456789012345'], 'pathOperation': 1}), + mock.call.getHost('fakehost')] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected) - self.assertEqual(host['name'], self.FAKE_HOST) - self.assertEqual(len(host['FCPaths']), 3) + self.assertEqual(host['name'], self.FAKE_HOST) + self.assertEqual(len(host['FCPaths']), 3) class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): @@ -2898,22 +3234,17 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): m_conf=mock_conf, driver=hpdriver.HP3PARISCSIDriver) - expected = [ - mock.call.setSSHOptions( - HP3PAR_SAN_IP, - HP3PAR_USER_NAME, - HP3PAR_USER_PASS, - privatekey=HP3PAR_SAN_SSH_PRIVATE, - port=HP3PAR_SAN_SSH_PORT, - conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT), - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), + expected_get_cpgs = [ mock.call.getCPG(HP3PAR_CPG), - mock.call.getCPG(HP3PAR_CPG2), - mock.call.logout(), - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getPorts(), - mock.call.logout()] - mock_client.assert_has_calls(expected) + mock.call.getCPG(HP3PAR_CPG2)] + expected_get_ports = [mock.call.getPorts()] + mock_client.assert_has_calls( + self.standard_login + + expected_get_cpgs + + self.standard_logout + + self.standard_login + + expected_get_ports + + self.standard_logout) mock_client.reset_mock() return mock_client @@ -2939,26 +3270,32 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): 'nsp': 'something'}) mock_client.createVLUN.return_value = location - result = self.driver.initialize_connection(self.volume, self.connector) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + result = self.driver.initialize_connection( + self.volume, + self.connector) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume(self.VOLUME_3PAR_NAME), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getHost(self.FAKE_HOST), - mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'), - mock.call.getHost(self.FAKE_HOST), - mock.call.createVLUN( - self.VOLUME_3PAR_NAME, - auto=True, - hostname='fakehost', - portPos={'node': 8, 'slot': 1, 'cardPort': 1}), - mock.call.getHostVLUNs(self.FAKE_HOST), - mock.call.logout()] + expected = [ + mock.call.getVolume(self.VOLUME_3PAR_NAME), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getHost(self.FAKE_HOST), + mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'), + mock.call.getHost(self.FAKE_HOST), + mock.call.createVLUN( + self.VOLUME_3PAR_NAME, + auto=True, + hostname='fakehost', + portPos={'node': 8, 'slot': 1, 'cardPort': 1}), + mock.call.getHostVLUNs(self.FAKE_HOST)] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) - self.assertDictMatch(result, self.properties) + self.assertDictMatch(result, self.properties) def test_get_volume_stats(self): # setup_mock_client drive with default configuration @@ -2970,38 +3307,44 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): 'freeCapacityMiB': 1024.0 * 2, 'totalCapacityMiB': 1024.0 * 123 } - stats = self.driver.get_volume_stats(True) - const = 0.0009765625 - self.assertEqual(stats['storage_protocol'], 'iSCSI') - self.assertEqual(stats['total_capacity_gb'], 0) - self.assertEqual(stats['free_capacity_gb'], 0) - self.assertEqual(stats['pools'][0]['total_capacity_gb'], 123.0) - self.assertEqual(stats['pools'][0]['free_capacity_gb'], 2.0) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getStorageSystemInfo(), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getCPG(HP3PAR_CPG2), - mock.call.logout()] + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client - mock_client.assert_has_calls(expected) + stats = self.driver.get_volume_stats(True) + const = 0.0009765625 + self.assertEqual(stats['storage_protocol'], 'iSCSI') + self.assertEqual(stats['total_capacity_gb'], 0) + self.assertEqual(stats['free_capacity_gb'], 0) + self.assertEqual(stats['pools'][0]['total_capacity_gb'], 123.0) + self.assertEqual(stats['pools'][0]['free_capacity_gb'], 2.0) - cpg2 = self.cpgs[0].copy() - cpg2.update({'SDGrowth': {'limitMiB': 8192}}) - mock_client.getCPG.return_value = cpg2 + expected = [ + mock.call.getStorageSystemInfo(), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getCPG(HP3PAR_CPG2)] - stats = self.driver.get_volume_stats(True) - self.assertEqual(stats['storage_protocol'], 'iSCSI') - total_capacity_gb = 8192 * const - self.assertEqual(stats['total_capacity_gb'], 0) - self.assertEqual(stats['pools'][0]['total_capacity_gb'], - total_capacity_gb) - free_capacity_gb = int( - (8192 - self.cpgs[0]['UsrUsage']['usedMiB']) * const) - self.assertEqual(stats['free_capacity_gb'], 0) - self.assertEqual(stats['pools'][0]['free_capacity_gb'], - free_capacity_gb) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + + cpg2 = self.cpgs[0].copy() + cpg2.update({'SDGrowth': {'limitMiB': 8192}}) + mock_client.getCPG.return_value = cpg2 + + stats = self.driver.get_volume_stats(True) + self.assertEqual(stats['storage_protocol'], 'iSCSI') + total_capacity_gb = 8192 * const + self.assertEqual(stats['total_capacity_gb'], 0) + self.assertEqual(stats['pools'][0]['total_capacity_gb'], + total_capacity_gb) + free_capacity_gb = int( + (8192 - self.cpgs[0]['UsrUsage']['usedMiB']) * const) + self.assertEqual(stats['free_capacity_gb'], 0) + self.assertEqual(stats['pools'][0]['free_capacity_gb'], + free_capacity_gb) def test_create_host(self): # setup_mock_client drive with default configuration @@ -3016,24 +3359,28 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): mock_client.findHost.return_value = None mock_client.getVLUN.return_value = {'lun': self.TARGET_LUN} - host, auth_username, auth_password = self.driver._create_host( - self.volume, self.connector) - expected = [ - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getHost(self.FAKE_HOST), - mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'), - mock.call.createHost( - self.FAKE_HOST, - optional={'domain': None, 'persona': 2}, - iscsiNames=['iqn.1993-08.org.debian:01:222']), - mock.call.getHost(self.FAKE_HOST)] + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + host, auth_username, auth_password = self.driver._create_host( + common, self.volume, self.connector) + expected = [ + mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getHost(self.FAKE_HOST), + mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'), + mock.call.createHost( + self.FAKE_HOST, + optional={'domain': None, 'persona': 2}, + iscsiNames=['iqn.1993-08.org.debian:01:222']), + mock.call.getHost(self.FAKE_HOST)] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected) - self.assertEqual(host['name'], self.FAKE_HOST) - self.assertEqual(auth_username, None) - self.assertEqual(auth_password, None) + self.assertEqual(host['name'], self.FAKE_HOST) + self.assertEqual(auth_username, None) + self.assertEqual(auth_password, None) def test_create_host_chap_enabled(self): # setup_mock_client drive with CHAP enabled configuration @@ -3067,32 +3414,36 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): mock_client.getVolumeMetaData.side_effect = get_side_effect - host, auth_username, auth_password = self.driver._create_host( - self.volume, self.connector) - expected = [ - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getVolumeMetaData( - 'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY), - mock.call.getVolumeMetaData( - 'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY), - mock.call.getHost(self.FAKE_HOST), - mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'), - mock.call.createHost( - self.FAKE_HOST, - optional={'domain': None, 'persona': 2}, - iscsiNames=['iqn.1993-08.org.debian:01:222']), - mock.call.modifyHost( - 'fakehost', - expected_mod_request), - mock.call.getHost(self.FAKE_HOST) - ] + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + host, auth_username, auth_password = self.driver._create_host( + common, self.volume, self.connector) + expected = [ + mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getVolumeMetaData( + 'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY), + mock.call.getVolumeMetaData( + 'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY), + mock.call.getHost(self.FAKE_HOST), + mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'), + mock.call.createHost( + self.FAKE_HOST, + optional={'domain': None, 'persona': 2}, + iscsiNames=['iqn.1993-08.org.debian:01:222']), + mock.call.modifyHost( + 'fakehost', + expected_mod_request), + mock.call.getHost(self.FAKE_HOST) + ] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected) - self.assertEqual(host['name'], self.FAKE_HOST) - self.assertEqual(auth_username, 'test-user') - self.assertEqual(auth_password, 'test-pass') + self.assertEqual(host['name'], self.FAKE_HOST) + self.assertEqual(auth_username, 'test-user') + self.assertEqual(auth_password, 'test-pass') def test_create_invalid_host(self): # setup_mock_client drive with default configuration @@ -3105,21 +3456,25 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): {'name': 'fakehost.foo'}] mock_client.findHost.return_value = 'fakehost.foo' - host, auth_username, auth_password = self.driver._create_host( - self.volume, self.connector) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + host, auth_username, auth_password = self.driver._create_host( + common, self.volume, self.connector) - expected = [ - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getHost(self.FAKE_HOST), - mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'), - mock.call.getHost('fakehost.foo')] + expected = [ + mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getHost(self.FAKE_HOST), + mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'), + mock.call.getHost('fakehost.foo')] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected) - self.assertEqual(host['name'], 'fakehost.foo') - self.assertEqual(auth_username, None) - self.assertEqual(auth_password, None) + self.assertEqual(host['name'], 'fakehost.foo') + self.assertEqual(auth_username, None) + self.assertEqual(auth_password, None) def test_create_invalid_host_chap_enabled(self): # setup_mock_client drive with CHAP enabled configuration @@ -3152,29 +3507,33 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): 'chapSecret': 'test-pass' } - host, auth_username, auth_password = self.driver._create_host( - self.volume, self.connector) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + host, auth_username, auth_password = self.driver._create_host( + common, self.volume, self.connector) - expected = [ - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getVolumeMetaData( - 'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY), - mock.call.getVolumeMetaData( - 'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY), - mock.call.getHost(self.FAKE_HOST), - mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'), - mock.call.modifyHost( - 'fakehost.foo', - expected_mod_request), - mock.call.getHost('fakehost.foo') - ] + expected = [ + mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getVolumeMetaData( + 'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY), + mock.call.getVolumeMetaData( + 'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY), + mock.call.getHost(self.FAKE_HOST), + mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'), + mock.call.modifyHost( + 'fakehost.foo', + expected_mod_request), + mock.call.getHost('fakehost.foo') + ] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected) - self.assertEqual(host['name'], 'fakehost.foo') - self.assertEqual(auth_username, 'test-user') - self.assertEqual(auth_password, 'test-pass') + self.assertEqual(host['name'], 'fakehost.foo') + self.assertEqual(auth_username, 'test-user') + self.assertEqual(auth_password, 'test-pass') def test_create_modify_host(self): # setup_mock_client drive with default configuration @@ -3188,25 +3547,29 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): 'FCPaths': [{'wwn': '123456789012345'}, {'wwn': '123456789054321'}]}] - host, auth_username, auth_password = self.driver._create_host( - self.volume, self.connector) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + host, auth_username, auth_password = self.driver._create_host( + common, self.volume, self.connector) - expected = [ - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getHost(self.FAKE_HOST), - mock.call.modifyHost( - self.FAKE_HOST, - {'pathOperation': 1, - 'iSCSINames': ['iqn.1993-08.org.debian:01:222']}), - mock.call.getHost(self.FAKE_HOST)] + expected = [ + mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getHost(self.FAKE_HOST), + mock.call.modifyHost( + self.FAKE_HOST, + {'pathOperation': 1, + 'iSCSINames': ['iqn.1993-08.org.debian:01:222']}), + mock.call.getHost(self.FAKE_HOST)] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected) - self.assertEqual(host['name'], self.FAKE_HOST) - self.assertEqual(auth_username, None) - self.assertEqual(auth_password, None) - self.assertEqual(len(host['FCPaths']), 2) + self.assertEqual(host['name'], self.FAKE_HOST) + self.assertEqual(auth_username, None) + self.assertEqual(auth_password, None) + self.assertEqual(len(host['FCPaths']), 2) def test_create_modify_host_chap_enabled(self): # setup_mock_client drive with CHAP enabled configuration @@ -3240,33 +3603,37 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): 'chapSecret': 'test-pass' } - host, auth_username, auth_password = self.driver._create_host( - self.volume, self.connector) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + host, auth_username, auth_password = self.driver._create_host( + common, self.volume, self.connector) - expected = [ - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.getCPG(HP3PAR_CPG), - mock.call.getVolumeMetaData( - 'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY), - mock.call.getVolumeMetaData( - 'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY), - mock.call.getHost(self.FAKE_HOST), - mock.call.modifyHost( - self.FAKE_HOST, - {'pathOperation': 1, - 'iSCSINames': ['iqn.1993-08.org.debian:01:222']}), - mock.call.modifyHost( - self.FAKE_HOST, - expected_mod_request - ), - mock.call.getHost(self.FAKE_HOST)] + expected = [ + mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.getCPG(HP3PAR_CPG), + mock.call.getVolumeMetaData( + 'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_USER_KEY), + mock.call.getVolumeMetaData( + 'osv-0DM4qZEVSKON-DXN-NwVpw', CHAP_PASS_KEY), + mock.call.getHost(self.FAKE_HOST), + mock.call.modifyHost( + self.FAKE_HOST, + {'pathOperation': 1, + 'iSCSINames': ['iqn.1993-08.org.debian:01:222']}), + mock.call.modifyHost( + self.FAKE_HOST, + expected_mod_request + ), + mock.call.getHost(self.FAKE_HOST)] - mock_client.assert_has_calls(expected) + mock_client.assert_has_calls(expected) - self.assertEqual(host['name'], self.FAKE_HOST) - self.assertEqual(auth_username, 'test-user') - self.assertEqual(auth_password, 'test-pass') - self.assertEqual(len(host['FCPaths']), 2) + self.assertEqual(host['name'], self.FAKE_HOST) + self.assertEqual(auth_username, 'test-user') + self.assertEqual(auth_password, 'test-pass') + self.assertEqual(len(host['FCPaths']), 2) def test_get_least_used_nsp_for_host_single(self): # setup_mock_client drive with default configuration @@ -3276,14 +3643,19 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): mock_client.getPorts.return_value = PORTS_RET mock_client.getVLUNs.return_value = VLUNS1_RET - #Setup a single ISCSI IP - iscsi_ips = ["10.10.220.253"] - self.driver.configuration.hp3par_iscsi_ips = iscsi_ips + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - self.driver.initialize_iscsi_ports() + #Setup a single ISCSI IP + iscsi_ips = ["10.10.220.253"] + self.driver.configuration.hp3par_iscsi_ips = iscsi_ips - nsp = self.driver._get_least_used_nsp_for_host('newhost') - self.assertEqual(nsp, "1:8:1") + self.driver.initialize_iscsi_ports(common) + + nsp = self.driver._get_least_used_nsp_for_host(common, 'newhost') + self.assertEqual(nsp, "1:8:1") def test_get_least_used_nsp_for_host_new(self): # setup_mock_client drive with default configuration @@ -3293,16 +3665,21 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): mock_client.getPorts.return_value = PORTS_RET mock_client.getVLUNs.return_value = VLUNS1_RET - #Setup two ISCSI IPs - iscsi_ips = ["10.10.220.252", "10.10.220.253"] - self.driver.configuration.hp3par_iscsi_ips = iscsi_ips + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - self.driver.initialize_iscsi_ports() + #Setup two ISCSI IPs + iscsi_ips = ["10.10.220.252", "10.10.220.253"] + self.driver.configuration.hp3par_iscsi_ips = iscsi_ips - # Host 'newhost' does not yet have any iscsi paths, - # so the 'least used' is returned - nsp = self.driver._get_least_used_nsp_for_host('newhost') - self.assertEqual(nsp, "1:8:2") + self.driver.initialize_iscsi_ports(common) + + # Host 'newhost' does not yet have any iscsi paths, + # so the 'least used' is returned + nsp = self.driver._get_least_used_nsp_for_host(common, 'newhost') + self.assertEqual(nsp, "1:8:2") def test_get_least_used_nsp_for_host_reuse(self): # setup_mock_client drive with default configuration @@ -3312,19 +3689,24 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): mock_client.getPorts.return_value = PORTS_RET mock_client.getVLUNs.return_value = VLUNS1_RET - #Setup two ISCSI IPs - iscsi_ips = ["10.10.220.252", "10.10.220.253"] - self.driver.configuration.hp3par_iscsi_ips = iscsi_ips + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() - self.driver.initialize_iscsi_ports() + #Setup two ISCSI IPs + iscsi_ips = ["10.10.220.252", "10.10.220.253"] + self.driver.configuration.hp3par_iscsi_ips = iscsi_ips - # hosts 'foo' and 'bar' already have active iscsi paths - # the same one should be used - nsp = self.driver._get_least_used_nsp_for_host('foo') - self.assertEqual(nsp, "1:8:2") + self.driver.initialize_iscsi_ports(common) - nsp = self.driver._get_least_used_nsp_for_host('bar') - self.assertEqual(nsp, "1:8:1") + # hosts 'foo' and 'bar' already have active iscsi paths + # the same one should be used + nsp = self.driver._get_least_used_nsp_for_host(common, 'foo') + self.assertEqual(nsp, "1:8:2") + + nsp = self.driver._get_least_used_nsp_for_host(common, 'bar') + self.assertEqual(nsp, "1:8:1") def test_get_least_used_nps_for_host_fc(self): # setup_mock_client drive with default configuration @@ -3338,11 +3720,15 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): iscsi_ips = ["10.10.220.252", "10.10.220.253"] self.driver.configuration.hp3par_iscsi_ips = iscsi_ips - self.driver.initialize_iscsi_ports() + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + self.driver.initialize_iscsi_ports(common) - nsp = self.driver._get_least_used_nsp_for_host('newhost') - self.assertNotEqual(nsp, "0:6:3") - self.assertEqual(nsp, "1:8:1") + nsp = self.driver._get_least_used_nsp_for_host(common, 'newhost') + self.assertNotEqual(nsp, "0:6:3") + self.assertEqual(nsp, "1:8:1") def test_invalid_iscsi_ip(self): config = self.setup_configuration() @@ -3391,59 +3777,87 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}] mock_client.getVLUNs.return_value = {'members': ports} - # in use count - vluns = self.driver.common.client.getVLUNs() - nsp = self.driver._get_least_used_nsp(vluns['members'], - ['0:2:1', '1:8:1']) - self.assertEqual(nsp, '1:8:1') + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + # in use count + vluns = common.client.getVLUNs() + nsp = self.driver._get_least_used_nsp(common, vluns['members'], + ['0:2:1', '1:8:1']) + self.assertEqual(nsp, '1:8:1') - ports = [ - {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}] + ports = [ + {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, + 'active': True}] - mock_client.getVLUNs.return_value = {'members': ports} + mock_client.getVLUNs.return_value = {'members': ports} - # in use count - vluns = self.driver.common.client.getVLUNs() - nsp = self.driver._get_least_used_nsp(vluns['members'], - ['0:2:1', '1:2:1']) - self.assertEqual(nsp, '1:2:1') + # in use count + common = self.driver._login() + vluns = common.client.getVLUNs() + nsp = self.driver._get_least_used_nsp(common, vluns['members'], + ['0:2:1', '1:2:1']) + self.assertEqual(nsp, '1:2:1') - ports = [ - {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}, - {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, 'active': True}] + ports = [ + {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 1, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, + 'active': True}, + {'portPos': {'node': 0, 'slot': 2, 'cardPort': 1}, + 'active': True}] - mock_client.getVLUNs.return_value = {'members': ports} + mock_client.getVLUNs.return_value = {'members': ports} - # in use count - vluns = self.driver.common.client.getVLUNs() - nsp = self.driver._get_least_used_nsp(vluns['members'], - ['1:1:1', '1:2:1']) - self.assertEqual(nsp, '1:1:1') + # in use count + common = self.driver._login() + vluns = common.client.getVLUNs() + nsp = self.driver._get_least_used_nsp(common, vluns['members'], + ['1:1:1', '1:2:1']) + self.assertEqual(nsp, '1:1:1') def test_set_3par_chaps(self): # setup_mock_client drive with default configuration # and return the mock HTTP 3PAR client mock_client = self.setup_driver() - expected = [] - self.driver._set_3par_chaps( - 'test-host', 'test-vol', 'test-host', 'pass') - mock_client.assert_has_calls(expected) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + expected = [] + self.driver._set_3par_chaps( + common, 'test-host', 'test-vol', 'test-host', 'pass') + mock_client.assert_has_calls(expected) # setup_mock_client drive with CHAP enabled configuration # and return the mock HTTP 3PAR client @@ -3451,19 +3865,23 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): config.hp3par_iscsi_chap_enabled = True mock_client = self.setup_driver(config=config) - expected_mod_request = { - 'chapOperation': mock_client.HOST_EDIT_ADD, - 'chapOperationMode': mock_client.CHAP_INITIATOR, - 'chapName': 'test-host', - 'chapSecret': 'fake' - } + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + expected_mod_request = { + 'chapOperation': mock_client.HOST_EDIT_ADD, + 'chapOperationMode': mock_client.CHAP_INITIATOR, + 'chapName': 'test-host', + 'chapSecret': 'fake' + } - expected = [ - mock.call.modifyHost('test-host', expected_mod_request) - ] - self.driver._set_3par_chaps( - 'test-host', 'test-vol', 'test-host', 'fake') - mock_client.assert_has_calls(expected) + expected = [ + mock.call.modifyHost('test-host', expected_mod_request) + ] + self.driver._set_3par_chaps( + common, 'test-host', 'test-vol', 'test-host', 'fake') + mock_client.assert_has_calls(expected) @mock.patch('cinder.volume.utils.generate_password') def test_do_export(self, mock_utils): @@ -3490,12 +3908,17 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): expected = [] expected_model = {'provider_auth': None} - model = self.driver._do_export(volume) - mock_client.assert_has_calls(expected) - self.assertEqual(expected_model, model) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + model = self.driver._do_export(common, volume) - mock_client.reset_mock() + mock_client.assert_has_calls(expected) + self.assertEqual(expected_model, model) + + mock_client.reset_mock() # setup_mock_client drive with CHAP enabled configuration # and return the mock HTTP 3PAR client @@ -3532,9 +3955,13 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): ] expected_model = {'provider_auth': 'CHAP test-host random-pass'} - model = self.driver._do_export(volume) - mock_client.assert_has_calls(expected) - self.assertEqual(expected_model, model) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + model = self.driver._do_export(common, volume) + mock_client.assert_has_calls(expected) + self.assertEqual(expected_model, model) @mock.patch('cinder.volume.utils.generate_password') def test_do_export_host_not_found(self, mock_utils): @@ -3554,9 +3981,6 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): 'value': 'random-pass' } - config = self.setup_configuration() - config.hp3par_iscsi_chap_enabled = True - expected = [ mock.call.getHostVLUNs('test-host'), mock.call.setVolumeMetaData( @@ -3566,9 +3990,13 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): ] expected_model = {'provider_auth': 'CHAP test-host random-pass'} - model = self.driver._do_export(volume) - mock_client.assert_has_calls(expected) - self.assertEqual(expected_model, model) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + model = self.driver._do_export(common, volume) + mock_client.assert_has_calls(expected) + self.assertEqual(expected_model, model) @mock.patch('cinder.volume.utils.generate_password') def test_do_export_host_chap_disabled(self, mock_utils): @@ -3595,11 +4023,6 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): 'value': 'random-pass' } - mock_client.reset_mock() - - config = self.setup_configuration() - config.hp3par_iscsi_chap_enabled = True - expected = [ mock.call.getHostVLUNs('test-host'), mock.call.getHost('test-host'), @@ -3612,9 +4035,13 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): ] expected_model = {'provider_auth': 'CHAP test-host random-pass'} - model = self.driver._do_export(volume) - mock_client.assert_has_calls(expected) - self.assertEqual(expected_model, model) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + model = self.driver._do_export(common, volume) + mock_client.assert_has_calls(expected) + self.assertEqual(expected_model, model) @mock.patch('cinder.volume.utils.generate_password') def test_do_export_no_active_vluns(self, mock_utils): @@ -3651,9 +4078,13 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): ] expected_model = {'provider_auth': 'CHAP test-host random-pass'} - model = self.driver._do_export(volume) - mock_client.assert_has_calls(expected) - self.assertEqual(model, expected_model) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + model = self.driver._do_export(common, volume) + mock_client.assert_has_calls(expected) + self.assertEqual(model, expected_model) def test_ensure_export(self): # setup_mock_client drive with default configuration @@ -3668,51 +4099,56 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): 'members': [] } - model = self.driver.ensure_export(None, volume) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + model = self.driver.ensure_export(None, volume) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.getAllVolumeMetaData('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.logout() - ] - - expected_model = {'provider_auth': None} - - mock_client.assert_has_calls(expected) - self.assertEqual(model, expected_model) - - mock_client.getAllVolumeMetaData.return_value = { - 'total': 2, - 'members': [ - { - 'creationTimeSec': 1406074222, - 'value': 'fake-host', - 'key': CHAP_USER_KEY, - 'creationTime8601': '2014-07-22T17:10:22-07:00' - }, - { - 'creationTimeSec': 1406074222, - 'value': 'random-pass', - 'key': CHAP_PASS_KEY, - 'creationTime8601': '2014-07-22T17:10:22-07:00' - } + expected = [ + mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.getAllVolumeMetaData('osv-0DM4qZEVSKON-DXN-NwVpw') ] - } - model = self.driver.ensure_export(None, volume) + expected_model = {'provider_auth': None} - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.getAllVolumeMetaData('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.logout() - ] + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + self.assertEqual(model, expected_model) - expected_model = {'provider_auth': "CHAP fake-host random-pass"} + mock_client.getAllVolumeMetaData.return_value = { + 'total': 2, + 'members': [ + { + 'creationTimeSec': 1406074222, + 'value': 'fake-host', + 'key': CHAP_USER_KEY, + 'creationTime8601': '2014-07-22T17:10:22-07:00' + }, + { + 'creationTimeSec': 1406074222, + 'value': 'random-pass', + 'key': CHAP_PASS_KEY, + 'creationTime8601': '2014-07-22T17:10:22-07:00' + } + ] + } - mock_client.assert_has_calls(expected) - self.assertEqual(model, expected_model) + model = self.driver.ensure_export(None, volume) + + expected = [ + mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), + mock.call.getAllVolumeMetaData('osv-0DM4qZEVSKON-DXN-NwVpw') + ] + + expected_model = {'provider_auth': "CHAP fake-host random-pass"} + + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + self.assertEqual(model, expected_model) def test_ensure_export_missing_volume(self): # setup_mock_client drive with default configuration @@ -3725,18 +4161,20 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): mock_client.getVolume.side_effect = hpexceptions.HTTPNotFound( 'fake') - model = self.driver.ensure_export(None, volume) + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + model = self.driver.ensure_export(None, volume) - expected = [ - mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS), - mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'), - mock.call.logout() - ] + expected = [mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw')] - expected_model = None + expected_model = None - mock_client.assert_has_calls(expected) - self.assertEqual(model, expected_model) + mock_client.assert_has_calls( + self.standard_login + + expected + + self.standard_logout) + self.assertEqual(model, expected_model) @mock.patch.object(volume_types, 'get_volume_type') def test_get_volume_settings_default_pool(self, _mock_volume_types): @@ -3744,19 +4182,27 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase): 'name': 'gold', 'id': 'gold-id', 'extra_specs': {}} - self.setup_driver() - volume = {'host': 'test-host@3pariscsi#pool_foo', - 'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'} - pool = volume_utils.extract_host(volume['host'], 'pool') - model = self.driver.common.get_volume_settings_from_type_id('gold-id', - pool) - self.assertEqual(model['cpg'], 'pool_foo') + mock_client = self.setup_driver() + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + + volume = {'host': 'test-host@3pariscsi#pool_foo', + 'id': 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'} + pool = volume_utils.extract_host(volume['host'], 'pool') + model = common.get_volume_settings_from_type_id('gold-id', pool) + self.assertEqual(model['cpg'], 'pool_foo') def test_get_model_update(self): - self.setup_driver() - model_update = self.driver.common._get_model_update('xxx@yyy#zzz', - 'CPG') - self.assertEqual(model_update, {'host': 'xxx@yyy#CPG'}) + mock_client = self.setup_driver() + with mock.patch.object(hpcommon.HP3PARCommon, '_create_client')\ + as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + + model_update = common._get_model_update('xxx@yyy#zzz', 'CPG') + self.assertEqual(model_update, {'host': 'xxx@yyy#CPG'}) VLUNS5_RET = ({'members': [{'portPos': {'node': 0, 'slot': 8, 'cardPort': 2}, diff --git a/cinder/volume/drivers/san/hp/hp_3par_common.py b/cinder/volume/drivers/san/hp/hp_3par_common.py index e1e7bc01a04..0ea4876e577 100644 --- a/cinder/volume/drivers/san/hp/hp_3par_common.py +++ b/cinder/volume/drivers/san/hp/hp_3par_common.py @@ -158,10 +158,11 @@ class HP3PARCommon(object): 2.0.25 - Migrate without losing type settings bug #1356608 2.0.26 - Don't ignore extra-specs snap_cpg when missing cpg #1368972 2.0.27 - Fixing manage source-id error bug #1357075 + 2.0.28 - Removing locks bug #1381190 """ - VERSION = "2.0.27" + VERSION = "2.0.28" stats = {} @@ -199,6 +200,7 @@ class HP3PARCommon(object): self.config = config self.hosts_naming_dict = dict() self.client = None + self.uuid = uuid.uuid4() def get_version(self): return self.VERSION @@ -222,27 +224,6 @@ class HP3PARCommon(object): LOG.error(ex_msg) raise exception.InvalidInput(reason=ex_msg) - if client_version < MIN_CLIENT_SSH_ARGS_VERSION: - cl.setSSHOptions(self.config.san_ip, - self.config.san_login, - self.config.san_password, - port=self.config.san_ssh_port, - conn_timeout=self.config.ssh_conn_timeout, - privatekey=self.config.san_private_key) - else: - known_hosts_file = CONF.ssh_hosts_key_file - policy = "AutoAddPolicy" - if CONF.strict_ssh_host_key_policy: - policy = "RejectPolicy" - cl.setSSHOptions(self.config.san_ip, - self.config.san_login, - self.config.san_password, - port=self.config.san_ssh_port, - conn_timeout=self.config.ssh_conn_timeout, - privatekey=self.config.san_private_key, - missing_key_policy=policy, - known_hosts_file=known_hosts_file) - return cl def client_login(self): @@ -256,9 +237,35 @@ class HP3PARCommon(object): LOG.error(msg) raise exception.InvalidInput(reason=msg) + client_version = hp3parclient.version + + if client_version < MIN_CLIENT_SSH_ARGS_VERSION: + self.client.setSSHOptions( + self.config.san_ip, + self.config.san_login, + self.config.san_password, + port=self.config.san_ssh_port, + conn_timeout=self.config.ssh_conn_timeout, + privatekey=self.config.san_private_key) + else: + known_hosts_file = CONF.ssh_hosts_key_file + policy = "AutoAddPolicy" + if CONF.strict_ssh_host_key_policy: + policy = "RejectPolicy" + self.client.setSSHOptions( + self.config.san_ip, + self.config.san_login, + self.config.san_password, + port=self.config.san_ssh_port, + conn_timeout=self.config.ssh_conn_timeout, + privatekey=self.config.san_private_key, + missing_key_policy=policy, + known_hosts_file=known_hosts_file) + def client_logout(self): + LOG.info(_LI("Disconnect from 3PAR REST and SSH %s") % self.uuid) self.client.logout() - LOG.debug("Disconnect from 3PAR") + LOG.info(_LI("logout Done %s") % self.uuid) def do_setup(self, context): if hp3parclient is None: @@ -274,8 +281,8 @@ class HP3PARCommon(object): if self.config.hp3par_debug: self.client.debug_rest(True) + def check_for_setup_error(self): self.client_login() - try: cpg_names = self.config.hp3par_cpg for cpg_name in cpg_names: diff --git a/cinder/volume/drivers/san/hp/hp_3par_fc.py b/cinder/volume/drivers/san/hp/hp_3par_fc.py index 981dc42485a..66b80858828 100644 --- a/cinder/volume/drivers/san/hp/hp_3par_fc.py +++ b/cinder/volume/drivers/san/hp/hp_3par_fc.py @@ -34,10 +34,11 @@ try: except ImportError: hpexceptions = None +import pprint + from cinder import exception -from cinder.i18n import _, _LI +from cinder.i18n import _, _LI, _LW from cinder.openstack.common import log as logging -from cinder import utils import cinder.volume.driver from cinder.volume.drivers.san.hp import hp_3par_common as hpcommon from cinder.volume.drivers.san import san @@ -72,14 +73,14 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver): 2.0.8 - Fixing missing login/logout around attach/detach bug #1367429 2.0.9 - Add support for pools with model update 2.0.10 - Migrate without losing type settings bug #1356608 + 2.0.11 - Removing locks bug #1381190 """ - VERSION = "2.0.10" + VERSION = "2.0.11" def __init__(self, *args, **kwargs): super(HP3PARFCDriver, self).__init__(*args, **kwargs) - self.common = None self.configuration.append_config_values(hpcommon.hp3par_opts) self.configuration.append_config_values(san.san_opts) self.lookup_service = fczm_utils.create_lookup_service() @@ -87,18 +88,26 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver): def _init_common(self): return hpcommon.HP3PARCommon(self.configuration) - def _check_flags(self): + def _login(self): + common = self._init_common() + common.do_setup(None) + common.client_login() + return common + + def _logout(self, common): + common.client_logout() + + def _check_flags(self, common): """Sanity check to ensure we have required options set.""" required_flags = ['hp3par_api_url', 'hp3par_username', 'hp3par_password', 'san_ip', 'san_login', 'san_password'] - self.common.check_flags(self.configuration, required_flags) + common.check_flags(self.configuration, required_flags) - @utils.synchronized('3par', external=True) def get_volume_stats(self, refresh): - self.common.client_login() + common = self._login() try: - stats = self.common.get_volume_stats(refresh) + stats = common.get_volume_stats(refresh) stats['storage_protocol'] = 'FC' stats['driver_version'] = self.VERSION backend_name = self.configuration.safe_get('volume_backend_name') @@ -106,72 +115,65 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver): self.__class__.__name__) return stats finally: - self.common.client_logout() + self._logout(common) def do_setup(self, context): - self.common = self._init_common() - self._check_flags() - self.common.do_setup(context) + common = self._init_common() + common.do_setup(context) + self._check_flags(common) + common.check_for_setup_error() def check_for_setup_error(self): - """Returns an error if prerequisites aren't met.""" - self._check_flags() + """Setup errors are already checked for in do_setup so return pass.""" + pass - @utils.synchronized('3par', external=True) def create_volume(self, volume): - self.common.client_login() + common = self._login() try: - return self.common.create_volume(volume) + return common.create_volume(volume) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def create_cloned_volume(self, volume, src_vref): - self.common.client_login() + common = self._login() try: - return self.common.create_cloned_volume(volume, src_vref) + return common.create_cloned_volume(volume, src_vref) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def delete_volume(self, volume): - self.common.client_login() + common = self._login() try: - self.common.delete_volume(volume) + common.delete_volume(volume) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def create_volume_from_snapshot(self, volume, snapshot): """Create a volume from a snapshot. TODO: support using the size from the user. """ - self.common.client_login() + common = self._login() try: - return self.common.create_volume_from_snapshot(volume, - snapshot) + return common.create_volume_from_snapshot(volume, snapshot) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def create_snapshot(self, snapshot): - self.common.client_login() + common = self._login() try: - self.common.create_snapshot(snapshot) + common.create_snapshot(snapshot) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def delete_snapshot(self, snapshot): - self.common.client_login() + common = self._login() try: - self.common.delete_snapshot(snapshot) + common.delete_snapshot(snapshot) finally: - self.common.client_logout() + self._logout(common) @fczm_utils.AddFCZone - @utils.synchronized('3par', external=True) def initialize_connection(self, volume, connector): """Assigns the volume to a server. @@ -209,25 +211,25 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver): * Create a VLUN for that HOST with the volume we want to export. """ - self.common.client_login() + common = self._login() try: # we have to make sure we have a host - host = self._create_host(volume, connector) + host = self._create_host(common, volume, connector) target_wwns, init_targ_map, numPaths = \ - self._build_initiator_target_map(connector) + self._build_initiator_target_map(common, connector) # now that we have a host, create the VLUN if self.lookup_service is not None and numPaths == 1: nsp = None - active_fc_port_list = self.common.get_active_fc_target_ports() + active_fc_port_list = common.get_active_fc_target_ports() for port in active_fc_port_list: if port['portWWN'].lower() == target_wwns[0].lower(): nsp = port['nsp'] break - vlun = self.common.create_vlun(volume, host, nsp) + vlun = common.create_vlun(volume, host, nsp) else: - vlun = self.common.create_vlun(volume, host) + vlun = common.create_vlun(volume, host) info = {'driver_volume_type': 'fibre_channel', 'data': {'target_lun': vlun['lun'], @@ -236,42 +238,41 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver): 'initiator_target_map': init_targ_map}} return info finally: - self.common.client_logout() + self._logout(common) @fczm_utils.RemoveFCZone - @utils.synchronized('3par', external=True) def terminate_connection(self, volume, connector, **kwargs): """Driver entry point to unattach a volume from an instance.""" - self.common.client_login() + common = self._login() try: - hostname = self.common._safe_hostname(connector['host']) - self.common.terminate_connection(volume, hostname, - wwn=connector['wwpns']) + hostname = common._safe_hostname(connector['host']) + common.terminate_connection(volume, hostname, + wwn=connector['wwpns']) info = {'driver_volume_type': 'fibre_channel', 'data': {}} try: - self.common.client.getHostVLUNs(hostname) + common.client.getHostVLUNs(hostname) except hpexceptions.HTTPNotFound: # No more exports for this host. LOG.info(_LI("Need to remove FC Zone, building initiator " "target map")) target_wwns, init_targ_map, _numPaths = \ - self._build_initiator_target_map(connector) + self._build_initiator_target_map(common, connector) info['data'] = {'target_wwn': target_wwns, 'initiator_target_map': init_targ_map} return info finally: - self.common.client_logout() + self._logout(common) - def _build_initiator_target_map(self, connector): + def _build_initiator_target_map(self, common, connector): """Build the target_wwns and the initiator target map.""" - fc_ports = self.common.get_active_fc_target_ports() + fc_ports = common.get_active_fc_target_ports() all_target_wwns = [] target_wwns = [] init_targ_map = {} @@ -308,7 +309,8 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver): return target_wwns, init_targ_map, numPaths - def _create_3par_fibrechan_host(self, hostname, wwns, domain, persona_id): + def _create_3par_fibrechan_host(self, common, hostname, wwns, + domain, persona_id): """Create a 3PAR host. Create a 3PAR host, if there is already a host on the 3par using @@ -317,48 +319,50 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver): """ # first search for an existing host host_found = None - for wwn in wwns: - host_found = self.common.client.findHost(wwn=wwn) - if host_found is not None: - break + hosts = common.client.queryHost(wwns) + + LOG.warn(_LW("Found HOSTS %s") % pprint.pformat(hosts)) + if hosts and hosts['members']: + host_found = hosts['members'][0]['name'] if host_found is not None: - self.common.hosts_naming_dict[hostname] = host_found + common.hosts_naming_dict[hostname] = host_found return host_found else: persona_id = int(persona_id) - self.common.client.createHost(hostname, FCWwns=wwns, - optional={'domain': domain, - 'persona': persona_id}) + common.client.createHost(hostname, FCWwns=wwns, + optional={'domain': domain, + 'persona': persona_id}) return hostname - def _modify_3par_fibrechan_host(self, hostname, wwn): - mod_request = {'pathOperation': self.common.client.HOST_EDIT_ADD, + def _modify_3par_fibrechan_host(self, common, hostname, wwn): + mod_request = {'pathOperation': common.client.HOST_EDIT_ADD, 'FCWWNs': wwn} - self.common.client.modifyHost(hostname, mod_request) + common.client.modifyHost(hostname, mod_request) - def _create_host(self, volume, connector): + def _create_host(self, common, volume, connector): """Creates or modifies existing 3PAR host.""" host = None - hostname = self.common._safe_hostname(connector['host']) - cpg = self.common.get_cpg(volume, allowSnap=True) - domain = self.common.get_domain(cpg) + hostname = common._safe_hostname(connector['host']) + cpg = common.get_cpg(volume, allowSnap=True) + domain = common.get_domain(cpg) try: - host = self.common._get_3par_host(hostname) + host = common._get_3par_host(hostname) except hpexceptions.HTTPNotFound: # get persona from the volume type extra specs - persona_id = self.common.get_persona_type(volume) + persona_id = common.get_persona_type(volume) # host doesn't exist, we have to create it - hostname = self._create_3par_fibrechan_host(hostname, + hostname = self._create_3par_fibrechan_host(common, + hostname, connector['wwpns'], domain, persona_id) - host = self.common._get_3par_host(hostname) + host = common._get_3par_host(hostname) - return self._add_new_wwn_to_host(host, connector['wwpns']) + return self._add_new_wwn_to_host(common, host, connector['wwpns']) - def _add_new_wwn_to_host(self, host, wwns): + def _add_new_wwn_to_host(self, common, host, wwns): """Add wwns to a host if one or more don't exist. Identify if argument wwns contains any world wide names @@ -383,85 +387,71 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver): # if any wwns found that were not in host list, # add them to the host if (len(new_wwns) > 0): - self._modify_3par_fibrechan_host(host['name'], new_wwns) - host = self.common._get_3par_host(host['name']) + self._modify_3par_fibrechan_host(common, host['name'], new_wwns) + host = common._get_3par_host(host['name']) return host - @utils.synchronized('3par', external=True) def create_export(self, context, volume): pass - @utils.synchronized('3par', external=True) def ensure_export(self, context, volume): pass - @utils.synchronized('3par', external=True) def remove_export(self, context, volume): pass - @utils.synchronized('3par', external=True) def extend_volume(self, volume, new_size): - self.common.client_login() + common = self._login() try: - self.common.extend_volume(volume, new_size) + common.extend_volume(volume, new_size) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def manage_existing(self, volume, existing_ref): - self.common.client_login() + common = self._login() try: - return self.common.manage_existing(volume, existing_ref) + return common.manage_existing(volume, existing_ref) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def manage_existing_get_size(self, volume, existing_ref): - self.common.client_login() + common = self._login() try: - size = self.common.manage_existing_get_size(volume, existing_ref) + return common.manage_existing_get_size(volume, existing_ref) finally: - self.common.client_logout() + self._logout(common) - return size - - @utils.synchronized('3par', external=True) def unmanage(self, volume): - self.common.client_login() + common = self._login() try: - self.common.unmanage(volume) + common.unmanage(volume) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def attach_volume(self, context, volume, instance_uuid, host_name, mountpoint): - self.common.client_login() + common = self._login() try: - self.common.attach_volume(volume, instance_uuid) + common.attach_volume(volume, instance_uuid) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def detach_volume(self, context, volume): - self.common.client_login() + common = self._login() try: - self.common.detach_volume(volume) + common.detach_volume(volume) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def retype(self, context, volume, new_type, diff, host): """Convert the volume to be of the new type.""" - self.common.client_login() + common = self._login() try: - return self.common.retype(volume, new_type, diff, host) + return common.retype(volume, new_type, diff, host) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def migrate_volume(self, context, volume, host): - if volume['status'] == 'in-use': protocol = host['capabilities']['storage_protocol'] if protocol != 'FC': @@ -469,19 +459,19 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver): "to a host with storage_protocol=%s." % protocol) return False, None - self.common.client_login() + common = self._login() try: - return self.common.migrate_volume(volume, host) + return common.migrate_volume(volume, host) finally: - self.common.client_logout() + self._logout(common) def get_pool(self, volume): - self.common.client_login() + common = self._login() try: - return self.common.get_cpg(volume) + return common.get_cpg(volume) except hpexceptions.HTTPNotFound: reason = (_("Volume %s doesn't exist on array.") % volume) LOG.error(reason) raise exception.InvalidVolume(reason) finally: - self.common.client_logout() + self._logout(common) diff --git a/cinder/volume/drivers/san/hp/hp_3par_iscsi.py b/cinder/volume/drivers/san/hp/hp_3par_iscsi.py index 6f1d673bf5f..376749d1b5a 100644 --- a/cinder/volume/drivers/san/hp/hp_3par_iscsi.py +++ b/cinder/volume/drivers/san/hp/hp_3par_iscsi.py @@ -38,7 +38,6 @@ except ImportError: from cinder import exception from cinder.i18n import _, _LE from cinder.openstack.common import log as logging -from cinder import utils import cinder.volume.driver from cinder.volume.drivers.san.hp import hp_3par_common as hpcommon from cinder.volume.drivers.san import san @@ -76,32 +75,40 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): 2.0.6 - Fixing missing login/logout around attach/detach bug #1367429 2.0.7 - Add support for pools with model update 2.0.8 - Migrate without losing type settings bug #1356608 + 2.0.9 - Removing locks bug #1381190 """ - VERSION = "2.0.8" + VERSION = "2.0.9" def __init__(self, *args, **kwargs): super(HP3PARISCSIDriver, self).__init__(*args, **kwargs) - self.common = None self.configuration.append_config_values(hpcommon.hp3par_opts) self.configuration.append_config_values(san.san_opts) def _init_common(self): return hpcommon.HP3PARCommon(self.configuration) - def _check_flags(self): + def _login(self): + common = self._init_common() + common.do_setup(None) + common.client_login() + return common + + def _logout(self, common): + common.client_logout() + + def _check_flags(self, common): """Sanity check to ensure we have required options set.""" required_flags = ['hp3par_api_url', 'hp3par_username', 'hp3par_password', 'san_ip', 'san_login', 'san_password'] - self.common.check_flags(self.configuration, required_flags) + common.check_flags(self.configuration, required_flags) - @utils.synchronized('3par', external=True) def get_volume_stats(self, refresh): - self.common.client_login() + common = self._login() try: - stats = self.common.get_volume_stats(refresh) + stats = common.get_volume_stats(refresh) stats['storage_protocol'] = 'iSCSI' stats['driver_version'] = self.VERSION backend_name = self.configuration.safe_get('volume_backend_name') @@ -109,20 +116,21 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): self.__class__.__name__) return stats finally: - self.common.client_logout() + self._logout(common) def do_setup(self, context): - self.common = self._init_common() - self._check_flags() - self.common.do_setup(context) + common = self._init_common() + common.do_setup(context) + self._check_flags(common) + common.check_for_setup_error() - self.common.client_login() + common.client_login() try: - self.initialize_iscsi_ports() + self.initialize_iscsi_ports(common) finally: - self.common.client_logout() + self._logout(common) - def initialize_iscsi_ports(self): + def initialize_iscsi_ports(self, common): # map iscsi_ip-> ip_port # -> iqn # -> nsp @@ -153,7 +161,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): # get all the valid iSCSI ports from 3PAR # when found, add the valid iSCSI ip, ip port, iqn and nsp # to the iSCSI IP dictionary - iscsi_ports = self.common.get_active_iscsi_target_ports() + iscsi_ports = common.get_active_iscsi_target_ports() for port in iscsi_ports: ip = port['IPAddr'] @@ -184,64 +192,56 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): raise exception.InvalidInput(reason=(msg)) def check_for_setup_error(self): - """Returns an error if prerequisites aren't met.""" - self._check_flags() + """Setup errors are already checked for in do_setup so return pass.""" + pass - @utils.synchronized('3par', external=True) def create_volume(self, volume): - self.common.client_login() + common = self._login() try: - return self.common.create_volume(volume) + return common.create_volume(volume) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def create_cloned_volume(self, volume, src_vref): """Clone an existing volume.""" - self.common.client_login() + common = self._login() try: - return self.common.create_cloned_volume(volume, src_vref) + return common.create_cloned_volume(volume, src_vref) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def delete_volume(self, volume): - self.common.client_login() + common = self._login() try: - self.common.delete_volume(volume) + common.delete_volume(volume) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def create_volume_from_snapshot(self, volume, snapshot): """Creates a volume from a snapshot. TODO: support using the size from the user. """ - self.common.client_login() + common = self._login() try: - return self.common.create_volume_from_snapshot(volume, - snapshot) + return common.create_volume_from_snapshot(volume, snapshot) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def create_snapshot(self, snapshot): - self.common.client_login() + common = self._login() try: - self.common.create_snapshot(snapshot) + common.create_snapshot(snapshot) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def delete_snapshot(self, snapshot): - self.common.client_login() + common = self._login() try: - self.common.delete_snapshot(snapshot) + common.delete_snapshot(snapshot) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def initialize_connection(self, volume, connector): """Assigns the volume to a server. @@ -267,14 +267,19 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): * Create a host on the 3par * create vlun on the 3par """ - self.common.client_login() + common = self._login() try: # we have to make sure we have a host - host, username, password = self._create_host(volume, connector) - least_used_nsp = self._get_least_used_nsp_for_host(host['name']) + host, username, password = self._create_host( + common, + volume, + connector) + least_used_nsp = self._get_least_used_nsp_for_host( + common, + host['name']) # now that we have a host, create the VLUN - vlun = self.common.create_vlun(volume, host, least_used_nsp) + vlun = common.create_vlun(volume, host, least_used_nsp) if least_used_nsp is None: msg = _("Least busy iSCSI port not found, " @@ -302,42 +307,44 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): return info finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def terminate_connection(self, volume, connector, **kwargs): """Driver entry point to unattach a volume from an instance.""" - self.common.client_login() + common = self._login() try: - hostname = self.common._safe_hostname(connector['host']) - self.common.terminate_connection(volume, hostname, - iqn=connector['initiator']) - self._clear_chap_3par(volume) + hostname = common._safe_hostname(connector['host']) + common.terminate_connection( + volume, + hostname, + iqn=connector['initiator']) + self._clear_chap_3par(common, volume) finally: - self.common.client_logout() + self._logout(common) - def _clear_chap_3par(self, volume): + def _clear_chap_3par(self, common, volume): """Clears CHAP credentials on a 3par volume. Ignore exceptions caused by the keys not being present on a volume. """ - vol_name = self.common._get_3par_vol_name(volume['id']) + vol_name = common._get_3par_vol_name(volume['id']) try: - self.common.client.removeVolumeMetaData(vol_name, CHAP_USER_KEY) + common.client.removeVolumeMetaData(vol_name, CHAP_USER_KEY) except hpexceptions.HTTPNotFound: pass except Exception: raise try: - self.common.client.removeVolumeMetaData(vol_name, CHAP_PASS_KEY) + common.client.removeVolumeMetaData(vol_name, CHAP_PASS_KEY) except hpexceptions.HTTPNotFound: pass except Exception: raise - def _create_3par_iscsi_host(self, hostname, iscsi_iqn, domain, persona_id): + def _create_3par_iscsi_host(self, common, hostname, iscsi_iqn, domain, + persona_id): """Create a 3PAR host. Create a 3PAR host, if there is already a host on the 3par using @@ -345,9 +352,9 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): used by 3PAR. """ # first search for an existing host - host_found = self.common.client.findHost(iqn=iscsi_iqn) + host_found = common.client.findHost(iqn=iscsi_iqn) if host_found is not None: - self.common.hosts_naming_dict[hostname] = host_found + common.hosts_naming_dict[hostname] = host_found return host_found else: if isinstance(iscsi_iqn, str) or isinstance(iscsi_iqn, unicode): @@ -355,73 +362,86 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): else: iqn = iscsi_iqn persona_id = int(persona_id) - self.common.client.createHost(hostname, iscsiNames=iqn, - optional={'domain': domain, - 'persona': persona_id}) + common.client.createHost(hostname, iscsiNames=iqn, + optional={'domain': domain, + 'persona': persona_id}) return hostname - def _modify_3par_iscsi_host(self, hostname, iscsi_iqn): - mod_request = {'pathOperation': self.common.client.HOST_EDIT_ADD, + def _modify_3par_iscsi_host(self, common, hostname, iscsi_iqn): + mod_request = {'pathOperation': common.client.HOST_EDIT_ADD, 'iSCSINames': [iscsi_iqn]} - self.common.client.modifyHost(hostname, mod_request) + common.client.modifyHost(hostname, mod_request) - def _set_3par_chaps(self, hostname, volume, username, password): + def _set_3par_chaps(self, common, hostname, volume, username, password): """Sets a 3PAR host's CHAP credentials.""" if not self.configuration.hp3par_iscsi_chap_enabled: return - mod_request = {'chapOperation': self.common.client.HOST_EDIT_ADD, - 'chapOperationMode': self.common.client.CHAP_INITIATOR, + mod_request = {'chapOperation': common.client.HOST_EDIT_ADD, + 'chapOperationMode': common.client.CHAP_INITIATOR, 'chapName': username, 'chapSecret': password} - self.common.client.modifyHost(hostname, mod_request) + common.client.modifyHost(hostname, mod_request) - def _create_host(self, volume, connector): + def _create_host(self, common, volume, connector): """Creates or modifies existing 3PAR host.""" # make sure we don't have the host already host = None username = None password = None - hostname = self.common._safe_hostname(connector['host']) - cpg = self.common.get_cpg(volume, allowSnap=True) - domain = self.common.get_domain(cpg) + hostname = common._safe_hostname(connector['host']) + cpg = common.get_cpg(volume, allowSnap=True) + domain = common.get_domain(cpg) # Get the CHAP secret if CHAP is enabled if self.configuration.hp3par_iscsi_chap_enabled: - vol_name = self.common._get_3par_vol_name(volume['id']) - username = self.common.client.getVolumeMetaData( + vol_name = common._get_3par_vol_name(volume['id']) + username = common.client.getVolumeMetaData( vol_name, CHAP_USER_KEY)['value'] - password = self.common.client.getVolumeMetaData( + password = common.client.getVolumeMetaData( vol_name, CHAP_PASS_KEY)['value'] try: - host = self.common._get_3par_host(hostname) + host = common._get_3par_host(hostname) except hpexceptions.HTTPNotFound: # get persona from the volume type extra specs - persona_id = self.common.get_persona_type(volume) + persona_id = common.get_persona_type(volume) # host doesn't exist, we have to create it - hostname = self._create_3par_iscsi_host(hostname, + hostname = self._create_3par_iscsi_host(common, + hostname, connector['initiator'], domain, persona_id) - self._set_3par_chaps(hostname, volume, username, password) - host = self.common._get_3par_host(hostname) + self._set_3par_chaps(common, hostname, volume, username, password) + host = common._get_3par_host(hostname) else: if 'iSCSIPaths' not in host or len(host['iSCSIPaths']) < 1: - self._modify_3par_iscsi_host(hostname, connector['initiator']) - self._set_3par_chaps(hostname, volume, username, password) - host = self.common._get_3par_host(hostname) + self._modify_3par_iscsi_host( + common, hostname, + connector['initiator']) + self._set_3par_chaps( + common, + hostname, + volume, + username, + password) + host = common._get_3par_host(hostname) elif (not host['initiatorChapEnabled'] and self.configuration.hp3par_iscsi_chap_enabled): LOG.warn(_("Host exists without CHAP credentials set and has " "iSCSI attachments but CHAP is enabled. Updating " "host with new CHAP credentials.")) - self._set_3par_chaps(hostname, volume, username, password) + self._set_3par_chaps( + common, + hostname, + volume, + username, + password) return host, username, password - def _do_export(self, volume): + def _do_export(self, common, volume): """Gets the associated account, generates CHAP info and updates.""" model_update = {} @@ -435,10 +455,10 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): chap_password = None try: # Get all active VLUNs for the host - vluns = self.common.client.getHostVLUNs(chap_username) + vluns = common.client.getHostVLUNs(chap_username) # Host has active VLUNs... is CHAP enabled on host? - host_info = self.common.client.getHost(chap_username) + host_info = common.client.getHost(chap_username) if not host_info['initiatorChapEnabled']: LOG.warn(_("Host has no CHAP key, but CHAP is enabled.")) @@ -464,7 +484,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): if ('remoteName' in vlun and re.match('iqn.*', vlun['remoteName'])): try: - chap_password = self.common.client.getVolumeMetaData( + chap_password = common.client.getVolumeMetaData( vlun['volumeName'], CHAP_PASS_KEY)['value'] chap_exists = True break @@ -481,10 +501,10 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): "Generating new CHAP key.")) # Add CHAP credentials to the volume metadata - vol_name = self.common._get_3par_vol_name(volume['id']) - self.common.client.setVolumeMetaData( + vol_name = common._get_3par_vol_name(volume['id']) + common.client.setVolumeMetaData( vol_name, CHAP_USER_KEY, chap_username) - self.common.client.setVolumeMetaData( + common.client.setVolumeMetaData( vol_name, CHAP_PASS_KEY, chap_password) model_update['provider_auth'] = ('CHAP %s %s' % @@ -492,28 +512,26 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): return model_update - @utils.synchronized('3par', external=True) def create_export(self, context, volume): + common = self._login() try: - self.common.client_login() - return self._do_export(volume) + return self._do_export(common, volume) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def ensure_export(self, context, volume): """Ensure the volume still exists on the 3PAR. Also retrieves CHAP credentials, if present on the volume """ + common = self._login() try: - self.common.client_login() - vol_name = self.common._get_3par_vol_name(volume['id']) - self.common.client.getVolume(vol_name) + vol_name = common._get_3par_vol_name(volume['id']) + common.client.getVolume(vol_name) except hpexceptions.HTTPNotFound: LOG.error(_LE("Volume %s doesn't exist on array.") % vol_name) else: - metadata = self.common.client.getAllVolumeMetaData(vol_name) + metadata = common.client.getAllVolumeMetaData(vol_name) username = None password = None @@ -532,13 +550,12 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): return model_update finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def remove_export(self, context, volume): pass - def _get_least_used_nsp_for_host(self, hostname): + def _get_least_used_nsp_for_host(self, common, hostname): """Get the least used NSP for the current host. Steps to determine which NSP to use. @@ -553,17 +570,18 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): return iscsi_nsps[0] # Try to reuse an existing iscsi path to the host - vluns = self.common.client.getVLUNs() + vluns = common.client.getVLUNs() for vlun in vluns['members']: if vlun['active']: if vlun['hostname'] == hostname: - temp_nsp = self.common.build_nsp(vlun['portPos']) + temp_nsp = common.build_nsp(vlun['portPos']) if temp_nsp in iscsi_nsps: # this host already has an iscsi path, so use it return temp_nsp # Calculate the least used iscsi nsp - least_used_nsp = self._get_least_used_nsp(vluns['members'], + least_used_nsp = self._get_least_used_nsp(common, + vluns['members'], self._get_iscsi_nsps()) return least_used_nsp @@ -580,7 +598,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): if value['nsp'] == nsp: return key - def _get_least_used_nsp(self, vluns, nspss): + def _get_least_used_nsp(self, common, vluns, nspss): """"Return the nsp that has the fewest active vluns.""" # return only the nsp (node:server:port) # count the number of nsps @@ -593,7 +611,7 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): for vlun in vluns: if vlun['active']: - nsp = self.common.build_nsp(vlun['portPos']) + nsp = common.build_nsp(vlun['portPos']) if nsp in nsp_counts: nsp_counts[nsp] = nsp_counts[nsp] + 1 @@ -606,69 +624,58 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): return current_least_used_nsp - @utils.synchronized('3par', external=True) def extend_volume(self, volume, new_size): - self.common.client_login() + common = self._login() try: - self.common.extend_volume(volume, new_size) + common.extend_volume(volume, new_size) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def manage_existing(self, volume, existing_ref): - self.common.client_login() + common = self._login() try: - return self.common.manage_existing(volume, existing_ref) + return common.manage_existing(volume, existing_ref) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def manage_existing_get_size(self, volume, existing_ref): - self.common.client_login() + common = self._login() try: - size = self.common.manage_existing_get_size(volume, existing_ref) + return common.manage_existing_get_size(volume, existing_ref) finally: - self.common.client_logout() + self._logout(common) - return size - - @utils.synchronized('3par', external=True) def unmanage(self, volume): - self.common.client_login() + common = self._login() try: - self.common.unmanage(volume) + common.unmanage(volume) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def attach_volume(self, context, volume, instance_uuid, host_name, mountpoint): - self.common.client_login() + common = self._login() try: - self.common.attach_volume(volume, instance_uuid) + common.attach_volume(volume, instance_uuid) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def detach_volume(self, context, volume): - self.common.client_login() + common = self._login() try: - self.common.detach_volume(volume) + common.detach_volume(volume) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def retype(self, context, volume, new_type, diff, host): """Convert the volume to be of the new type.""" - self.common.client_login() + common = self._login() try: - return self.common.retype(volume, new_type, diff, host) + return common.retype(volume, new_type, diff, host) finally: - self.common.client_logout() + self._logout(common) - @utils.synchronized('3par', external=True) def migrate_volume(self, context, volume, host): - if volume['status'] == 'in-use': protocol = host['capabilities']['storage_protocol'] if protocol != 'iSCSI': @@ -676,19 +683,19 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver): "to a host with storage_protocol=%s." % protocol) return False, None - self.common.client_login() + common = self._login() try: - return self.common.migrate_volume(volume, host) + return common.migrate_volume(volume, host) finally: - self.common.client_logout() + self._logout(common) def get_pool(self, volume): - self.common.client_login() + common = self._login() try: - return self.common.get_cpg(volume) + return common.get_cpg(volume) except hpexceptions.HTTPNotFound: reason = (_("Volume %s doesn't exist on array.") % volume) LOG.error(reason) raise exception.InvalidVolume(reason) finally: - self.common.client_logout() + self._logout(common)