From: Ryan Lucio Date: Thu, 12 Mar 2015 23:12:10 +0000 (-0700) Subject: VMEM v6000: Fix export verify routines X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=ad5eb92dcd5afcbce99c5c4812415fdcb6d8b339;p=openstack-build%2Fcinder-build.git VMEM v6000: Fix export verify routines New 'state' nodes in vmos6 are reflecting the kernel/system state indicating lun accessibility on the data plane. The nodes used previously were only reflecting success on the mgmt plane, meaning that the routines were sometimes returning before the target/export was actually available for client use. Also, switch to using one global iscsi target group (backend container that groups target IPs) to avoid a backend export bug exposed by the changes mentioned above. Change-Id: I1cd296d6d02562e1eb7374df736b2649fe2692a2 Closes-Bug: #1430014 --- diff --git a/cinder/tests/unit/test_v6000_common.py b/cinder/tests/unit/test_v6000_common.py index e622dd9be..f37a1a931 100644 --- a/cinder/tests/unit/test_v6000_common.py +++ b/cinder/tests/unit/test_v6000_common.py @@ -497,12 +497,11 @@ class V6000CommonTestCase(test.TestCase): self.assertEqual(CONNECTOR['host'], self.driver._get_igroup(VOLUME, CONNECTOR)) - def test_wait_for_export_config(self): - """Queries to cluster nodes verify export config.""" - bn = "/vshare/config/export/container/myContainer/lun/%s" \ + def test_wait_for_export_state(self): + """Queries to cluster nodes verify export state.""" + bn = "/vshare/state/local/container/myContainer/lun/%s/usn_id" \ % VOLUME['id'] - response = {'/vshare/config/export/container/myContainer/lun/vol-01': - VOLUME['id']} + response = {bn: '012345'} conf = { 'basic.get_node_values.return_value': response, @@ -510,15 +509,17 @@ class V6000CommonTestCase(test.TestCase): self.driver.mga = self.setup_mock_vshare(m_conf=conf) self.driver.mgb = self.setup_mock_vshare(m_conf=conf) - result = self.driver._wait_for_export_config(VOLUME['id'], state=True) + result = self.driver._wait_for_export_state(VOLUME['id'], state=True) self.driver.mga.basic.get_node_values.assert_called_with(bn) self.driver.mgb.basic.get_node_values.assert_called_with(bn) self.assertTrue(result) - def test_wait_for_export_config_with_no_config(self): - """Queries to cluster nodes verify *no* export config.""" - response = {} + def test_wait_for_export_state_with_no_state(self): + """Queries to cluster nodes verify *no* export state.""" + bn = "/vshare/state/local/container/myContainer/lun/%s/usn_id" \ + % VOLUME['id'] + response = {bn: '(not exported)'} conf = { 'basic.get_node_values.return_value': response, @@ -526,7 +527,7 @@ class V6000CommonTestCase(test.TestCase): self.driver.mga = self.setup_mock_vshare(m_conf=conf) self.driver.mgb = self.setup_mock_vshare(m_conf=conf) - self.assertTrue(self.driver._wait_for_export_config( + self.assertTrue(self.driver._wait_for_export_state( VOLUME['id'], state=False)) def test_is_supported_vmos_version(self): diff --git a/cinder/tests/unit/test_v6000_fcp.py b/cinder/tests/unit/test_v6000_fcp.py index 9b11320d9..280df3d93 100644 --- a/cinder/tests/unit/test_v6000_fcp.py +++ b/cinder/tests/unit/test_v6000_fcp.py @@ -319,9 +319,9 @@ class V6000FCPDriverTestCase(test.TestCase): self.driver.common._send_cmd_and_verify.assert_called_with( self.driver.common.vip.lun.export_lun, - self.driver.common._wait_for_export_config, '', + self.driver.common._wait_for_export_state, '', [self.driver.common.container, VOLUME['id'], 'all', - igroup, 'auto'], [VOLUME['id'], 'state=True']) + igroup, 'auto'], [VOLUME['id'], None, True]) self.driver.common._get_lun_id.assert_called_with(VOLUME['id']) self.assertEqual(lun_id, result) @@ -350,9 +350,9 @@ class V6000FCPDriverTestCase(test.TestCase): self.driver.common._send_cmd_and_verify.assert_called_with( self.driver.common.vip.lun.unexport_lun, - self.driver.common._wait_for_export_config, '', + self.driver.common._wait_for_export_state, '', [self.driver.common.container, VOLUME['id'], 'all', 'all', 'auto'], - [VOLUME['id'], 'state=False']) + [VOLUME['id'], None, False]) self.assertTrue(result is None) def test_unexport_lun_fails_with_exception(self): @@ -372,7 +372,7 @@ class V6000FCPDriverTestCase(test.TestCase): self.driver.common.vip = self.setup_mock_vshare() self.driver.common._send_cmd = mock.Mock(return_value=response) - self.driver.common._wait_for_export_config = mock.Mock() + self.driver.common._wait_for_export_state = mock.Mock() self.driver.common._get_snapshot_id = mock.Mock(return_value=lun_id) result = self.driver._export_snapshot(SNAPSHOT, CONNECTOR, igroup) @@ -381,7 +381,7 @@ class V6000FCPDriverTestCase(test.TestCase): self.driver.common.vip.snapshot.export_lun_snapshot, '', self.driver.common.container, SNAPSHOT['volume_id'], SNAPSHOT['id'], igroup, 'all', 'auto') - self.driver.common._wait_for_export_config.assert_called_with( + self.driver.common._wait_for_export_state.assert_called_with( SNAPSHOT['volume_id'], SNAPSHOT['id'], state=True) self.driver.common._get_snapshot_id.assert_called_once_with( SNAPSHOT['volume_id'], SNAPSHOT['id']) @@ -392,7 +392,7 @@ class V6000FCPDriverTestCase(test.TestCase): self.driver.common.vip = self.setup_mock_vshare() self.driver.common._send_cmd = mock.Mock(return_value=response) - self.driver.common._wait_for_export_config = mock.Mock() + self.driver.common._wait_for_export_state = mock.Mock() result = self.driver._unexport_snapshot(SNAPSHOT) @@ -400,7 +400,7 @@ class V6000FCPDriverTestCase(test.TestCase): self.driver.common.vip.snapshot.unexport_lun_snapshot, '', self.driver.common.container, SNAPSHOT['volume_id'], SNAPSHOT['id'], 'all', 'all', 'auto', False) - self.driver.common._wait_for_export_config.assert_called_with( + self.driver.common._wait_for_export_state.assert_called_with( SNAPSHOT['volume_id'], SNAPSHOT['id'], state=False) self.assertTrue(result is None) diff --git a/cinder/tests/unit/test_v6000_iscsi.py b/cinder/tests/unit/test_v6000_iscsi.py index b884ba3ff..ae5841392 100644 --- a/cinder/tests/unit/test_v6000_iscsi.py +++ b/cinder/tests/unit/test_v6000_iscsi.py @@ -240,9 +240,10 @@ class V6000ISCSIDriverTestCase(test.TestCase): def test_initialize_connection(self): lun_id = 1 igroup = None + target_name = self.driver.TARGET_GROUP_NAME tgt = self.driver.array_info[0] iqn = "%s%s:%s" % (self.conf.iscsi_target_prefix, - tgt['node'], VOLUME['id']) + tgt['node'], target_name) volume = mock.MagicMock(spec=models.Volume) def getitem(name): @@ -251,15 +252,14 @@ class V6000ISCSIDriverTestCase(test.TestCase): volume.__getitem__.side_effect = getitem self.driver.common.vip = self.setup_mock_vshare() - self.driver._get_short_name = mock.Mock(return_value=VOLUME['id']) - self.driver._create_iscsi_target = mock.Mock(return_value=tgt) + self.driver._get_iscsi_target = mock.Mock(return_value=tgt) self.driver._export_lun = mock.Mock(return_value=lun_id) props = self.driver.initialize_connection(volume, CONNECTOR) - self.driver._get_short_name.assert_called_with(volume['id']) - self.driver._create_iscsi_target.assert_called_with(volume) - self.driver._export_lun.assert_called_with(volume, CONNECTOR, igroup) + self.driver._get_iscsi_target.assert_called_once_with() + self.driver._export_lun.assert_called_once_with( + volume, CONNECTOR, igroup) self.driver.common.vip.basic.save_config.assert_called_with() self.assertEqual("1.2.3.4:3260", props['data']['target_portal']) self.assertEqual(iqn, props['data']['target_iqn']) @@ -269,9 +269,10 @@ class V6000ISCSIDriverTestCase(test.TestCase): def test_initialize_connection_with_snapshot_object(self): lun_id = 1 igroup = None + target_name = self.driver.TARGET_GROUP_NAME tgt = self.driver.array_info[0] iqn = "%s%s:%s" % (self.conf.iscsi_target_prefix, - tgt['node'], SNAPSHOT['id']) + tgt['node'], target_name) snapshot = mock.MagicMock(spec=models.Snapshot) def getitem(name): @@ -280,16 +281,14 @@ class V6000ISCSIDriverTestCase(test.TestCase): snapshot.__getitem__.side_effect = getitem self.driver.common.vip = self.setup_mock_vshare() - self.driver._get_short_name = mock.Mock(return_value=SNAPSHOT['id']) - self.driver._create_iscsi_target = mock.Mock(return_value=tgt) + self.driver._get_iscsi_target = mock.Mock(return_value=tgt) self.driver._export_snapshot = mock.Mock(return_value=lun_id) props = self.driver.initialize_connection(snapshot, CONNECTOR) - self.driver._get_short_name.assert_called_with(SNAPSHOT['id']) - self.driver._create_iscsi_target.assert_called_with(snapshot) - self.driver._export_snapshot.assert_called_with(snapshot, CONNECTOR, - igroup) + self.driver._get_iscsi_target.assert_called_once_with() + self.driver._export_snapshot.assert_called_once_with( + snapshot, CONNECTOR, igroup) self.driver.common.vip.basic.save_config.assert_called_with() self.assertEqual("1.2.3.4:3260", props['data']['target_portal']) self.assertEqual(iqn, props['data']['target_iqn']) @@ -300,9 +299,10 @@ class V6000ISCSIDriverTestCase(test.TestCase): self.conf.use_igroups = True lun_id = 1 igroup = 'test-igroup-1' + target_name = self.driver.TARGET_GROUP_NAME tgt = self.driver.array_info[0] iqn = "%s%s:%s" % (self.conf.iscsi_target_prefix, - tgt['node'], VOLUME['id']) + tgt['node'], target_name) volume = mock.MagicMock(spec=models.Volume) def getitem(name): @@ -313,18 +313,19 @@ class V6000ISCSIDriverTestCase(test.TestCase): self.driver.common.vip = self.setup_mock_vshare() self.driver.common._get_igroup = mock.Mock(return_value=igroup) self.driver._add_igroup_member = mock.Mock() - self.driver._get_short_name = mock.Mock(return_value=VOLUME['id']) - self.driver._create_iscsi_target = mock.Mock(return_value=tgt) + self.driver._get_iscsi_target = mock.Mock(return_value=tgt) self.driver._export_lun = mock.Mock(return_value=lun_id) props = self.driver.initialize_connection(volume, CONNECTOR) - self.driver.common._get_igroup.assert_called_with(volume, CONNECTOR) - self.driver._add_igroup_member.assert_called_with(CONNECTOR, igroup) - self.driver._get_short_name.assert_called_with(volume['id']) - self.driver._create_iscsi_target.assert_called_with(volume) - self.driver._export_lun.assert_called_with(volume, CONNECTOR, igroup) - self.driver.common.vip.basic.save_config.assert_called_with() + self.driver.common._get_igroup.assert_called_once_with( + volume, CONNECTOR) + self.driver._add_igroup_member.assert_called_once_with( + CONNECTOR, igroup) + self.driver._get_iscsi_target.assert_called_once_with() + self.driver._export_lun.assert_called_once_with( + volume, CONNECTOR, igroup) + self.driver.common.vip.basic.save_config.assert_called_once_with() self.assertEqual("1.2.3.4:3260", props['data']['target_portal']) self.assertEqual(iqn, props['data']['target_iqn']) self.assertEqual(lun_id, props['data']['target_lun']) @@ -335,12 +336,10 @@ class V6000ISCSIDriverTestCase(test.TestCase): self.driver.common.vip = self.setup_mock_vshare() self.driver._unexport_lun = mock.Mock() - self.driver._delete_iscsi_target = mock.Mock() result = self.driver.terminate_connection(volume, CONNECTOR) - self.driver._unexport_lun.assert_called_with(volume) - self.driver._delete_iscsi_target.assert_called_with(volume) + self.driver._unexport_lun.assert_called_once_with(volume) self.driver.common.vip.basic.save_config.assert_called_with() self.assertTrue(result is None) @@ -349,12 +348,10 @@ class V6000ISCSIDriverTestCase(test.TestCase): self.driver.common.vip = self.setup_mock_vshare() self.driver._unexport_snapshot = mock.Mock() - self.driver._delete_iscsi_target = mock.Mock() result = self.driver.terminate_connection(snapshot, CONNECTOR) - self.driver._unexport_snapshot.assert_called_with(snapshot) - self.driver._delete_iscsi_target.assert_called_with(snapshot) + self.driver._unexport_snapshot.assert_called_once_with(snapshot) self.driver.common.vip.basic.save_config.assert_called_with() self.assertTrue(result is None) @@ -367,82 +364,59 @@ class V6000ISCSIDriverTestCase(test.TestCase): self.driver._update_stats.assert_called_with() self.assertEqual(self.driver.stats, result) - def test_create_iscsi_target(self): - target_name = VOLUME['id'] - response = {'code': 0, 'message': 'success'} + def test_create_iscsi_target_group(self): + target_name = self.driver.TARGET_GROUP_NAME + bn = "/vshare/config/iscsi/target/%s" % target_name + response1 = {} + response2 = {'code': 0, 'message': 'success'} - m_vshare = self.setup_mock_vshare() + conf = { + 'basic.get_node_values.return_value': response1, + } + m_vshare = self.setup_mock_vshare(conf) self.driver.common.vip = m_vshare self.driver.common.mga = m_vshare self.driver.common.mgb = m_vshare - self.driver._get_short_name = mock.Mock(return_value=VOLUME['id']) self.driver.common._send_cmd_and_verify = mock.Mock( - return_value=response) - self.driver.common._send_cmd = mock.Mock(return_value=response) + return_value=response2) + self.driver.common._send_cmd = mock.Mock(return_value=response2) calls = [mock.call(self.driver.common.mga.iscsi.bind_ip_to_target, '', - VOLUME['id'], + target_name, self.driver.gateway_iscsi_ip_addresses_mga), mock.call(self.driver.common.mgb.iscsi.bind_ip_to_target, '', - VOLUME['id'], + target_name, self.driver.gateway_iscsi_ip_addresses_mgb)] - result = self.driver._create_iscsi_target(VOLUME) + result = self.driver._create_iscsi_target_group() - self.driver._get_short_name.assert_called_with(VOLUME['id']) + self.driver.common.vip.basic.get_node_values.assert_called_with(bn) self.driver.common._send_cmd_and_verify.assert_called_with( self.driver.common.vip.iscsi.create_iscsi_target, - self.driver._wait_for_targetstate, '', + self.driver._wait_for_target_state, '', [target_name], [target_name]) self.driver.common._send_cmd.assert_has_calls(calls) - self.assertTrue(result in self.driver.array_info) - - def test_delete_iscsi_target(self): - response = {'code': 0, 'message': 'success'} - - self.driver.common.vip = self.setup_mock_vshare() - self.driver._get_short_name = mock.Mock(return_value=VOLUME['id']) - self.driver.common._send_cmd = mock.Mock(return_value=response) - - result = self.driver._delete_iscsi_target(VOLUME) - - self.driver._get_short_name.assert_called_with(VOLUME['id']) - self.driver.common._send_cmd( - self.driver.common.vip.iscsi.delete_iscsi_target, - '', VOLUME['id']) self.assertTrue(result is None) - def test_delete_iscsi_target_fails_with_exception(self): - response = {'code': 14000, 'message': 'Generic error'} - failure = exception.ViolinBackendErr - - self.driver.common.vip = self.setup_mock_vshare() - self.driver._get_short_name = mock.Mock(return_value=VOLUME['id']) - self.driver.common._send_cmd = mock.Mock( - side_effect=failure(response['message'])) - - self.assertRaises(failure, self.driver._delete_iscsi_target, VOLUME) - def test_export_lun(self): + target_name = self.driver.TARGET_GROUP_NAME igroup = 'test-igroup-1' lun_id = '1' response = {'code': 0, 'message': ''} self.driver.common.vip = self.setup_mock_vshare() - self.driver._get_short_name = mock.Mock(return_value=VOLUME['id']) self.driver.common._send_cmd_and_verify = mock.Mock( return_value=response) self.driver.common._get_lun_id = mock.Mock(return_value=lun_id) result = self.driver._export_lun(VOLUME, CONNECTOR, igroup) - self.driver._get_short_name.assert_called_with(VOLUME['id']) self.driver.common._send_cmd_and_verify.assert_called_with( self.driver.common.vip.lun.export_lun, - self.driver.common._wait_for_export_config, '', - [self.driver.common.container, VOLUME['id'], VOLUME['id'], - igroup, 'auto'], [VOLUME['id'], 'state=True']) + self.driver.common._wait_for_export_state, '', + [self.driver.common.container, VOLUME['id'], target_name, + igroup, 'auto'], [VOLUME['id'], None, True]) self.driver.common._get_lun_id.assert_called_with(VOLUME['id']) self.assertEqual(lun_id, result) @@ -453,7 +427,6 @@ class V6000ISCSIDriverTestCase(test.TestCase): failure = exception.ViolinBackendErr self.driver.common.vip = self.setup_mock_vshare() - self.driver._get_short_name = mock.Mock(return_value=VOLUME['id']) self.driver.common._send_cmd_and_verify = mock.Mock( side_effect=failure(response['message'])) self.driver._get_lun_id = mock.Mock(return_value=lun_id) @@ -472,9 +445,9 @@ class V6000ISCSIDriverTestCase(test.TestCase): self.driver.common._send_cmd_and_verify.assert_called_with( self.driver.common.vip.lun.unexport_lun, - self.driver.common._wait_for_export_config, '', + self.driver.common._wait_for_export_state, '', [self.driver.common.container, VOLUME['id'], 'all', 'all', 'auto'], - [VOLUME['id'], 'state=False']) + [VOLUME['id'], None, False]) self.assertTrue(result is None) def test_unexport_lun_fails_with_exception(self): @@ -489,23 +462,22 @@ class V6000ISCSIDriverTestCase(test.TestCase): def test_export_snapshot(self): lun_id = '1' + target_name = self.driver.TARGET_GROUP_NAME igroup = 'test-igroup-1' response = {'code': 0, 'message': ''} self.driver.common.vip = self.setup_mock_vshare() - self.driver._get_short_name = mock.Mock(return_value=SNAPSHOT['id']) self.driver.common._send_cmd = mock.Mock(return_value=response) - self.driver.common._wait_for_export_config = mock.Mock() + self.driver.common._wait_for_export_state = mock.Mock() self.driver.common._get_snapshot_id = mock.Mock(return_value=lun_id) result = self.driver._export_snapshot(SNAPSHOT, CONNECTOR, igroup) - self.driver._get_short_name.assert_called_with(SNAPSHOT['id']) self.driver.common._send_cmd.assert_called_with( self.driver.common.vip.snapshot.export_lun_snapshot, '', self.driver.common.container, SNAPSHOT['volume_id'], - SNAPSHOT['id'], igroup, SNAPSHOT['id'], 'auto') - self.driver.common._wait_for_export_config.assert_called_with( + SNAPSHOT['id'], igroup, target_name, 'auto') + self.driver.common._wait_for_export_state.assert_called_with( SNAPSHOT['volume_id'], SNAPSHOT['id'], state=True) self.driver.common._get_snapshot_id.assert_called_once_with( SNAPSHOT['volume_id'], SNAPSHOT['id']) @@ -517,7 +489,7 @@ class V6000ISCSIDriverTestCase(test.TestCase): self.driver.common.vip = self.setup_mock_vshare() self.driver.common._send_cmd = mock.Mock(return_value=response) - self.driver.common._wait_for_export_config = mock.Mock() + self.driver.common._wait_for_export_state = mock.Mock() result = self.driver._unexport_snapshot(SNAPSHOT) @@ -525,7 +497,7 @@ class V6000ISCSIDriverTestCase(test.TestCase): self.driver.common.vip.snapshot.unexport_lun_snapshot, '', self.driver.common.container, SNAPSHOT['volume_id'], SNAPSHOT['id'], 'all', 'all', 'auto', False) - self.driver.common._wait_for_export_config.assert_called_with( + self.driver.common._wait_for_export_state.assert_called_with( SNAPSHOT['volume_id'], SNAPSHOT['id'], state=False) self.assertTrue(result is None) @@ -723,9 +695,9 @@ class V6000ISCSIDriverTestCase(test.TestCase): self.assertEqual(self.conf.san_ip, self.driver._get_hostname()) - def test_wait_for_targetstate(self): + def test_wait_for_target_state(self): target = 'mytarget' - bn = "/vshare/config/iscsi/target/%s" % target + bn = "/vshare/state/local/target/iscsi/%s" % target response = {bn: target} conf = { @@ -734,7 +706,7 @@ class V6000ISCSIDriverTestCase(test.TestCase): self.driver.common.mga = self.setup_mock_vshare(m_conf=conf) self.driver.common.mgb = self.setup_mock_vshare(m_conf=conf) - result = self.driver._wait_for_targetstate(target) + result = self.driver._wait_for_target_state(target) self.driver.common.mga.basic.get_node_values.assert_called_with(bn) self.driver.common.mgb.basic.get_node_values.assert_called_with(bn) diff --git a/cinder/volume/drivers/violin/v6000_common.py b/cinder/volume/drivers/violin/v6000_common.py index 6d877fd45..a777d7d4f 100644 --- a/cinder/volume/drivers/violin/v6000_common.py +++ b/cinder/volume/drivers/violin/v6000_common.py @@ -48,7 +48,6 @@ if vmemclient: LOG.info(_LI("Running with vmemclient version: %s."), vmemclient.__version__) -# version vmos versions V6.3.0.4 or newer VMOS_SUPPORTED_VERSION_PATTERNS = ['V6.3.0.[4-9]', 'V6.3.[1-9].?[0-9]?'] violin_opts = [ @@ -75,9 +74,10 @@ class V6000Common(object): Version history: 1.0 - Initial driver + 1.0.1 - Fixes polling for export completion """ - VERSION = '1.0' + VERSION = '1.0.1' def __init__(self, config): self.vip = None @@ -500,9 +500,9 @@ class V6000Common(object): return igroup_name - def _wait_for_export_config(self, volume_name, snapshot_name=None, - state=False): - """Polls backend to verify volume's export configuration. + def _wait_for_export_state(self, volume_name, snapshot_name=None, + state=False): + """Polls backend to verify volume's export state. XG sets/queries following a request to create or delete a lun export may fail on the backend if vshared is still processing @@ -524,27 +524,41 @@ class V6000Common(object): (depending on 'state' param) """ if not snapshot_name: - bn = "/vshare/config/export/container/%s/lun/%s" \ + bn = "/vshare/state/local/container/%s/lun/%s/usn_id" \ % (self.container, volume_name) else: - bn = "/vshare/config/export/snapshot/container/%s/lun/%s/snap/%s" \ + bn = "/vshare/state/snapshot/container/%s/lun/%s/snap/%s/usn_id" \ % (self.container, volume_name, snapshot_name) def _loop_func(state): status = [False, False] mg_conns = [self.mga, self.mgb] - LOG.debug("Entering _wait_for_export_config loop: state=%s.", + LOG.debug("Entering _wait_for_export_state loop: state=%s.", state) + # TODO(rlucio): May need to handle situations where export + # fails, i.e., HBAs go offline and the array is in + # degraded mode. + # for node_id in range(2): resp = mg_conns[node_id].basic.get_node_values(bn) - if state and len(resp.keys()): - status[node_id] = True - elif (not state) and (not len(resp.keys())): - status[node_id] = True + + if state: + # Verify export was added. Validates when the usn_id is + # altered to a non-default binding string. + # + if resp[bn] != "(not exported)": + status[node_id] = True + else: + # Verify export was removed. Validates when the usn_id is + # reset to the default binding string. + # + if resp[bn] == "(not exported)": + status[node_id] = True if status[0] and status[1]: + LOG.debug("_wait_for_export_state loopingcall complete.") raise loopingcall.LoopingCallDone(retvalue=True) timer = loopingcall.FixedIntervalLoopingCall(_loop_func, state) diff --git a/cinder/volume/drivers/violin/v6000_fcp.py b/cinder/volume/drivers/violin/v6000_fcp.py index 1d9ecb1c0..b9a3b10ca 100644 --- a/cinder/volume/drivers/violin/v6000_fcp.py +++ b/cinder/volume/drivers/violin/v6000_fcp.py @@ -58,9 +58,10 @@ class V6000FCDriver(driver.FibreChannelDriver): Version history: 1.0 - Initial driver + 1.0.1 - Fixes polling for export completion """ - VERSION = '1.0' + VERSION = '1.0.1' def __init__(self, *args, **kwargs): super(V6000FCDriver, self).__init__(*args, **kwargs) @@ -236,9 +237,9 @@ class V6000FCDriver(driver.FibreChannelDriver): try: self.common._send_cmd_and_verify( - v.lun.export_lun, self.common._wait_for_export_config, '', + v.lun.export_lun, self.common._wait_for_export_state, '', [self.common.container, volume['id'], 'all', export_to, - 'auto'], [volume['id'], 'state=True']) + 'auto'], [volume['id'], None, True]) except Exception: LOG.exception(_LE("LUN export for %s failed!"), volume['id']) @@ -264,9 +265,9 @@ class V6000FCDriver(driver.FibreChannelDriver): try: self.common._send_cmd_and_verify( - v.lun.unexport_lun, self.common._wait_for_export_config, '', + v.lun.unexport_lun, self.common._wait_for_export_state, '', [self.common.container, volume['id'], 'all', 'all', 'auto'], - [volume['id'], 'state=False']) + [volume['id'], None, False]) except exception.ViolinBackendErrNotFound: LOG.debug("Lun %s already unexported, continuing.", volume['id']) @@ -315,8 +316,8 @@ class V6000FCDriver(driver.FibreChannelDriver): raise else: - self.common._wait_for_export_config(snapshot['volume_id'], - snapshot['id'], state=True) + self.common._wait_for_export_state(snapshot['volume_id'], + snapshot['id'], state=True) lun_id = self.common._get_snapshot_id(snapshot['volume_id'], snapshot['id']) @@ -347,8 +348,8 @@ class V6000FCDriver(driver.FibreChannelDriver): raise else: - self.common._wait_for_export_config(snapshot['volume_id'], - snapshot['id'], state=False) + self.common._wait_for_export_state(snapshot['volume_id'], + snapshot['id'], state=False) def _add_igroup_member(self, connector, igroup): """Add an initiator to the openstack igroup so it can see exports. diff --git a/cinder/volume/drivers/violin/v6000_iscsi.py b/cinder/volume/drivers/violin/v6000_iscsi.py index 509e6e536..a6534b98d 100644 --- a/cinder/volume/drivers/violin/v6000_iscsi.py +++ b/cinder/volume/drivers/violin/v6000_iscsi.py @@ -57,9 +57,11 @@ class V6000ISCSIDriver(driver.ISCSIDriver): Version history: 1.0 - Initial driver + 1.0.1 - Fixes polling for export completion """ - VERSION = '1.0' + VERSION = '1.0.1' + TARGET_GROUP_NAME = 'openstack' def __init__(self, *args, **kwargs): super(V6000ISCSIDriver, self).__init__(*args, **kwargs) @@ -92,6 +94,9 @@ class V6000ISCSIDriver(driver.ISCSIDriver): "addr": ip, "conn": self.common.mgb}) + # setup global target group for exports to use + self._create_iscsi_target_group() + def check_for_setup_error(self): """Returns an error if prerequisites aren't met.""" self.common.check_for_setup_error() @@ -175,15 +180,16 @@ class V6000ISCSIDriver(driver.ISCSIDriver): igroup = self.common._get_igroup(volume, connector) self._add_igroup_member(connector, igroup) - vol = self._get_short_name(volume['id']) - tgt = self._create_iscsi_target(volume) + tgt = self._get_iscsi_target() + target_name = self.TARGET_GROUP_NAME + if isinstance(volume, models.Volume): lun = self._export_lun(volume, connector, igroup) else: lun = self._export_snapshot(volume, connector, igroup) iqn = "%s%s:%s" % (self.configuration.iscsi_target_prefix, - tgt['node'], vol) + tgt['node'], target_name) self.common.vip.basic.save_config() properties = {} @@ -205,7 +211,6 @@ class V6000ISCSIDriver(driver.ISCSIDriver): self._unexport_lun(volume) else: self._unexport_snapshot(volume) - self._delete_iscsi_target(volume) self.common.vip.basic.save_config() def get_volume_stats(self, refresh=False): @@ -214,32 +219,31 @@ class V6000ISCSIDriver(driver.ISCSIDriver): self._update_stats() return self.stats - @utils.synchronized('vmem-export') - def _create_iscsi_target(self, volume): + def _create_iscsi_target_group(self): """Creates a new target for use in exporting a lun. - Openstack does not yet support multipathing. We still create - HA targets but we pick a single random target for the - Openstack infrastructure to use. This at least allows us to - evenly distribute LUN connections across the storage cluster. + Create an HA target on the backend that will be used for all + lun exports made via this driver. + The equivalent CLI commands are "iscsi target create " and "iscsi target bind to ". - - Arguments: - volume -- volume object provided by the Manager - - Returns: - reference to randomly selected target object """ v = self.common.vip - target_name = self._get_short_name(volume['id']) + target_name = self.TARGET_GROUP_NAME + + bn = "/vshare/config/iscsi/target/%s" % target_name + resp = self.common.vip.basic.get_node_values(bn) + + if resp: + LOG.debug("iscsi target group %s already exists.", target_name) + return LOG.debug("Creating iscsi target %s.", target_name) try: self.common._send_cmd_and_verify(v.iscsi.create_iscsi_target, - self._wait_for_targetstate, + self._wait_for_target_state, '', [target_name], [target_name]) except Exception: @@ -253,33 +257,20 @@ class V6000ISCSIDriver(driver.ISCSIDriver): self.common._send_cmd(self.common.mgb.iscsi.bind_ip_to_target, '', target_name, self.gateway_iscsi_ip_addresses_mgb) + except Exception: LOG.exception(_LE("Failed to bind iSCSI targets!")) raise - return self.array_info[random.randint(0, len(self.array_info) - 1)] - - @utils.synchronized('vmem-export') - def _delete_iscsi_target(self, volume): - """Deletes the iscsi target for a lun. - - The CLI equivalent is "no iscsi target create ". + def _get_iscsi_target(self): + """Get a random target IP for OpenStack to connect to. - Arguments: - volume -- volume object provided by the Manager + For the non-multipath case we pick a single random target for + the Openstack infrastructure to use. This at least allows us + to evenly distribute LUN connections across the storage + cluster. """ - v = self.common.vip - success_msgs = ['', 'Invalid target'] - target_name = self._get_short_name(volume['id']) - - LOG.debug("Deleting iscsi target for %s.", target_name) - - try: - self.common._send_cmd(v.iscsi.delete_iscsi_target, - success_msgs, target_name) - except Exception: - LOG.exception(_LE("Failed to delete iSCSI target!")) - raise + return self.array_info[random.randint(0, len(self.array_info) - 1)] @utils.synchronized('vmem-export') def _export_lun(self, volume, connector=None, igroup=None): @@ -307,15 +298,15 @@ class V6000ISCSIDriver(driver.ISCSIDriver): else: raise exception.Error(_("No initiators found, cannot proceed")) - target_name = self._get_short_name(volume['id']) + target_name = self.TARGET_GROUP_NAME LOG.debug("Exporting lun %s.", volume['id']) try: self.common._send_cmd_and_verify( - v.lun.export_lun, self.common._wait_for_export_config, '', + v.lun.export_lun, self.common._wait_for_export_state, '', [self.common.container, volume['id'], target_name, - export_to, 'auto'], [volume['id'], 'state=True']) + export_to, 'auto'], [volume['id'], None, True]) except Exception: LOG.exception(_LE("LUN export for %s failed!"), volume['id']) @@ -341,9 +332,9 @@ class V6000ISCSIDriver(driver.ISCSIDriver): try: self.common._send_cmd_and_verify( - v.lun.unexport_lun, self.common._wait_for_export_config, '', + v.lun.unexport_lun, self.common._wait_for_export_state, '', [self.common.container, volume['id'], 'all', 'all', 'auto'], - [volume['id'], 'state=False']) + [volume['id'], None, False]) except exception.ViolinBackendErrNotFound: LOG.debug("Lun %s already unexported, continuing.", volume['id']) @@ -371,7 +362,7 @@ class V6000ISCSIDriver(driver.ISCSIDriver): export_to = '' v = self.common.vip - target_name = self._get_short_name(snapshot['id']) + target_name = self.TARGET_GROUP_NAME LOG.debug("Exporting snapshot %s.", snapshot['id']) @@ -394,8 +385,8 @@ class V6000ISCSIDriver(driver.ISCSIDriver): raise else: - self.common._wait_for_export_config(snapshot['volume_id'], - snapshot['id'], state=True) + self.common._wait_for_export_state(snapshot['volume_id'], + snapshot['id'], state=True) lun_id = self.common._get_snapshot_id(snapshot['volume_id'], snapshot['id']) @@ -426,8 +417,8 @@ class V6000ISCSIDriver(driver.ISCSIDriver): raise else: - self.common._wait_for_export_config(snapshot['volume_id'], - snapshot['id'], state=False) + self.common._wait_for_export_state(snapshot['volume_id'], + snapshot['id'], state=False) def _add_igroup_member(self, connector, igroup): """Add an initiator to an igroup so it can see exports. @@ -567,7 +558,7 @@ class V6000ISCSIDriver(driver.ISCSIDriver): return hostname - def _wait_for_targetstate(self, target_name): + def _wait_for_target_state(self, target_name): """Polls backend to verify an iscsi target configuration. This function will try to verify the creation of an iscsi @@ -577,15 +568,15 @@ class V6000ISCSIDriver(driver.ISCSIDriver): target_name -- name of iscsi target to be polled Returns: - True if the export state was correctly added + True if the target state was correctly added """ - bn = "/vshare/config/iscsi/target/%s" % (target_name) + bn = "/vshare/state/local/target/iscsi/%s" % (target_name) def _loop_func(): status = [False, False] mg_conns = [self.common.mga, self.common.mgb] - LOG.debug("Entering _wait_for_targetstate loop: target=%s.", + LOG.debug("Entering _wait_for_target_state loop: target=%s.", target_name) for node_id in range(2):