From 6c603df9ca240299b706a9b6c19bbeb347539ce3 Mon Sep 17 00:00:00 2001 From: Adriano Rosso Date: Fri, 3 Mar 2017 17:31:02 -0300 Subject: [PATCH] Remove HNAS iSCSI driver Hitachi NAS Platform iSCSI driver is deprecated since the last release and this patch is removing its code from OpenStack. DocImpact Change-Id: I9c5b0443d52ccd1c6fd18b5cfaab8514be17a293 --- cinder/opts.py | 3 - .../hitachi/test_hitachi_hnas_backend.py | 386 +--------- .../hitachi/test_hitachi_hnas_iscsi.py | 590 -------------- .../hitachi/test_hitachi_hnas_utils.py | 32 +- cinder/volume/drivers/hitachi/hnas_backend.py | 471 +----------- cinder/volume/drivers/hitachi/hnas_iscsi.py | 724 ------------------ cinder/volume/drivers/hitachi/hnas_nfs.py | 2 +- cinder/volume/drivers/hitachi/hnas_utils.py | 28 +- cinder/volume/manager.py | 4 - ...-remove-iscsi-driver-419e9c08133f9f0a.yaml | 4 + 10 files changed, 29 insertions(+), 2215 deletions(-) delete mode 100644 cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hnas_iscsi.py delete mode 100644 cinder/volume/drivers/hitachi/hnas_iscsi.py create mode 100644 releasenotes/notes/hnas-remove-iscsi-driver-419e9c08133f9f0a.yaml diff --git a/cinder/opts.py b/cinder/opts.py index 0a9a770d5ec..6d957a60298 100644 --- a/cinder/opts.py +++ b/cinder/opts.py @@ -107,8 +107,6 @@ from cinder.volume.drivers.hitachi import hbsd_horcm as \ cinder_volume_drivers_hitachi_hbsdhorcm from cinder.volume.drivers.hitachi import hbsd_iscsi as \ cinder_volume_drivers_hitachi_hbsdiscsi -from cinder.volume.drivers.hitachi import hnas_iscsi as \ - cinder_volume_drivers_hitachi_hnasiscsi from cinder.volume.drivers.hitachi import hnas_nfs as \ cinder_volume_drivers_hitachi_hnasnfs from cinder.volume.drivers.hitachi import hnas_utils as \ @@ -286,7 +284,6 @@ def list_opts(): cinder_volume_drivers_hitachi_hbsdfc.volume_opts, cinder_volume_drivers_hitachi_hbsdhorcm.volume_opts, cinder_volume_drivers_hitachi_hbsdiscsi.volume_opts, - cinder_volume_drivers_hitachi_hnasiscsi.iSCSI_OPTS, cinder_volume_drivers_hitachi_hnasnfs.NFS_OPTS, cinder_volume_drivers_hitachi_hnasutils.drivers_common_opts, cinder_volume_drivers_hitachi_vspcommon.common_opts, diff --git a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hnas_backend.py b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hnas_backend.py index a0ad7be4382..3285c2c8d67 100644 --- a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hnas_backend.py +++ b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hnas_backend.py @@ -103,22 +103,6 @@ Export configuration: \n\ 127.0.0.1 \n\ \n" -iscsi_one_target = "\n\ -Alias : cinder-default \n\ -Globally unique name: iqn.2014-12.10.10.10.10:evstest1.cinder-default \n\ -Comment : \n\ -Secret : pxr6U37LZZJBoMc \n\ -Authentication : Enabled \n\ -Logical units : No logical units. \n\ -\n\ - LUN Logical Unit \n\ - ---- -------------------------------- \n\ - 0 cinder-lu \n\ - 1 volume-99da7ae7-1e7f-4d57-8bf... \n\ -\n\ -Access configuration: \n\ -" - df_f_single_evs = "\n\ ID Label Size Used Snapshots Deduped Avail \ Thin ThinSize ThinAvail FS Type\n\ @@ -156,51 +140,27 @@ Node EVS ID Type Label Enabled Status IP Address Port \n\ 1 3 Service EVS-Test Yes Online 192.168.100.100 ag2 \n\ \n" -iscsilu_list = "Name : cinder-lu \n\ +lu_list = "Name : cinder-lu \n\ Comment: \n\ -Path : /.cinder/cinder-lu.iscsi \n\ +Path : /.cinder/cinder-lu \n\ Size : 2 GB \n\ File System : fs-cinder \n\ File System Mounted : YES \n\ Logical Unit Mounted: No" -iscsilu_list_tb = "Name : test-lu \n\ +lu_list_tb = "Name : test-lu \n\ Comment: \n\ -Path : /.cinder/test-lu.iscsi \n\ +Path : /.cinder/test-lu \n\ Size : 2 TB \n\ File System : fs-cinder \n\ File System Mounted : YES \n\ Logical Unit Mounted: No" -hnas_fs_list = "%(l1)s\n\n%(l2)s\n\n " % {'l1': iscsilu_list, - 'l2': iscsilu_list_tb} +hnas_fs_list = "%(l1)s\n\n%(l2)s\n\n " % {'l1': lu_list, + 'l2': lu_list_tb} add_targetsecret = "Target created successfully." -iscsi_target_list = "\n\ -Alias : cinder-GoldIsh\n\ -Globally unique name: iqn.2015-06.10.10.10.10:evstest1.cinder-goldish\n\ -Comment :\n\ -Secret : None\n\ -Authentication : Enabled\n\ -Logical units : No logical units.\n\ -Access configuration :\n\ -\n\ -Alias : cinder-default\n\ -Globally unique name: iqn.2014-12.10.10.10.10:evstest1.cinder-default\n\ -Comment :\n\ -Secret : pxr6U37LZZJBoMc\n\ -Authentication : Enabled\n\ -Logical units : Logical units :\n\ -\n\ - LUN Logical Unit\n\ - ---- --------------------------------\n\ - 0 cinder-lu\n\ - 1 volume-99da7ae7-1e7f-4d57-8bf...\n\ -\n\ -Access configuration :\n\ -" - backend_opts = {'mgmt_ip0': '0.0.0.0', 'cluster_admin_ip0': None, 'ssh_port': '22', @@ -320,51 +280,6 @@ class HDSHNASBackendTest(test.TestCase): self.hnas_backend._run_cmd, 'ssh', '0.0.0.0', 'supervisor', 'supervisor', 'df', '-a') - def test_get_targets_empty_list(self): - self.mock_object(self.hnas_backend, '_run_cmd', - return_value=('No targets', '')) - - out = self.hnas_backend._get_targets('2') - self.assertEqual([], out) - - def test_get_targets_not_found(self): - self.mock_object(self.hnas_backend, '_run_cmd', - return_value=(iscsi_target_list, '')) - - out = self.hnas_backend._get_targets('2', 'fake-volume') - self.assertEqual([], out) - - def test__get_unused_luid_number_0(self): - tgt_info = { - 'alias': 'cinder-default', - 'secret': 'pxr6U37LZZJBoMc', - 'iqn': 'iqn.2014-12.10.10.10.10:evstest1.cinder-default', - 'lus': [ - {'id': '1', - 'name': 'cinder-lu2'}, - {'id': '2', - 'name': 'volume-test2'} - ], - 'auth': 'Enabled' - } - - out = self.hnas_backend._get_unused_luid(tgt_info) - - self.assertEqual(0, out) - - def test__get_unused_no_luns(self): - tgt_info = { - 'alias': 'cinder-default', - 'secret': 'pxr6U37LZZJBoMc', - 'iqn': 'iqn.2014-12.10.10.10.10:evstest1.cinder-default', - 'lus': [], - 'auth': 'Enabled' - } - - out = self.hnas_backend._get_unused_luid(tgt_info) - - self.assertEqual(0, out) - def test_get_version(self): expected_out = { 'hardware': 'NAS Platform (M2SEKW1339109)', @@ -479,7 +394,7 @@ class HDSHNASBackendTest(test.TestCase): self.assertEqual('fs-cinder', out['label']) self.assertEqual('228', out['available_size']) self.assertEqual('250', out['total_size']) - self.assertEqual(2050.0, out['provisioned_capacity']) + self.assertEqual(0, out['provisioned_capacity']) def test_get_fs_empty_return(self): self.mock_object(self.hnas_backend, '_run_cmd', @@ -498,7 +413,7 @@ class HDSHNASBackendTest(test.TestCase): self.assertEqual('fs-cinder', out['label']) self.assertEqual('228', out['available_size']) self.assertEqual('250', out['total_size']) - self.assertEqual(2050.0, out['provisioned_capacity']) + self.assertEqual(0, out['provisioned_capacity']) def test_get_fs_tb(self): available_size = float(228 * 1024 ** 2) @@ -513,7 +428,7 @@ class HDSHNASBackendTest(test.TestCase): self.assertEqual('fs-cinder', out['label']) self.assertEqual(str(available_size), out['available_size']) self.assertEqual(str(total_size), out['total_size']) - self.assertEqual(2050.0, out['provisioned_capacity']) + self.assertEqual(0, out['provisioned_capacity']) def test_get_fs_single_evs_tb(self): available_size = float(228 * 1024 ** 2) @@ -528,288 +443,7 @@ class HDSHNASBackendTest(test.TestCase): self.assertEqual('fs-cinder', out['label']) self.assertEqual(str(available_size), out['available_size']) self.assertEqual(str(total_size), out['total_size']) - self.assertEqual(2050.0, out['provisioned_capacity']) - - def test_create_lu(self): - self.mock_object(self.hnas_backend, '_run_cmd', - return_value=(evsfs_list, '')) - - self.hnas_backend.create_lu('fs-cinder', '128', 'cinder-lu') - - calls = [mock.call('evsfs', 'list'), mock.call('console-context', - '--evs', '2', - 'iscsi-lu', 'add', - '-e', 'cinder-lu', - 'fs-cinder', - '/.cinder/cinder-lu.' - 'iscsi', '128G')] - self.hnas_backend._run_cmd.assert_has_calls(calls, any_order=False) - - def test_delete_lu(self): - self.mock_object(self.hnas_backend, '_run_cmd', - return_value=(evsfs_list, '')) - - self.hnas_backend.delete_lu('fs-cinder', 'cinder-lu') - - calls = [mock.call('evsfs', 'list'), mock.call('console-context', - '--evs', '2', - 'iscsi-lu', 'del', '-d', - '-f', 'cinder-lu')] - self.hnas_backend._run_cmd.assert_has_calls(calls, any_order=False) - - def test_extend_lu(self): - self.mock_object(self.hnas_backend, '_run_cmd', - return_value=(evsfs_list, '')) - - self.hnas_backend.extend_lu('fs-cinder', '128', 'cinder-lu') - - calls = [mock.call('evsfs', 'list'), mock.call('console-context', - '--evs', '2', - 'iscsi-lu', 'expand', - 'cinder-lu', '128G')] - self.hnas_backend._run_cmd.assert_has_calls(calls, any_order=False) - - def test_cloned_lu(self): - self.mock_object(self.hnas_backend, '_run_cmd', - return_value=(evsfs_list, '')) - - self.hnas_backend.create_cloned_lu('cinder-lu', 'fs-cinder', 'snap') - - calls = [mock.call('evsfs', 'list'), mock.call('console-context', - '--evs', '2', - 'iscsi-lu', 'clone', - '-e', 'cinder-lu', - 'snap', - '/.cinder/snap.iscsi')] - self.hnas_backend._run_cmd.assert_has_calls(calls, any_order=False) - - def test_get_existing_lu_info(self): - self.mock_object(self.hnas_backend, '_run_cmd', - side_effect=[(evsfs_list, ''), - (iscsilu_list, '')]) - - out = self.hnas_backend.get_existing_lu_info('cinder-lu', None, None) - - self.assertEqual('cinder-lu', out['name']) - self.assertEqual('fs-cinder', out['filesystem']) - self.assertEqual(2.0, out['size']) - - def test_get_existing_lu_info_tb(self): - self.mock_object(self.hnas_backend, '_run_cmd', - side_effect=[(evsfs_list, ''), - (iscsilu_list_tb, '')]) - - out = self.hnas_backend.get_existing_lu_info('test-lu', None, None) - - self.assertEqual('test-lu', out['name']) - self.assertEqual('fs-cinder', out['filesystem']) - self.assertEqual(2048.0, out['size']) - - def test_rename_existing_lu(self): - self.mock_object(self.hnas_backend, '_run_cmd', - return_value=(evsfs_list, '')) - self.hnas_backend.rename_existing_lu('fs-cinder', 'cinder-lu', - 'new-lu-name') - - calls = [mock.call('evsfs', 'list'), mock.call('console-context', - '--evs', '2', - 'iscsi-lu', 'mod', '-n', - "'new-lu-name'", - 'cinder-lu')] - self.hnas_backend._run_cmd.assert_has_calls(calls, any_order=False) - - def test_check_lu(self): - self.mock_object(self.hnas_backend, '_run_cmd', - side_effect=[(evsfs_list, ''), - (iscsi_target_list, '')]) - - out = self.hnas_backend.check_lu('cinder-lu', 'fs-cinder') - - self.assertEqual('cinder-lu', out['tgt']['lus'][0]['name']) - self.assertEqual('pxr6U37LZZJBoMc', out['tgt']['secret']) - self.assertTrue(out['mapped']) - calls = [mock.call('evsfs', 'list'), mock.call('console-context', - '--evs', '2', - 'iscsi-target', 'list')] - self.hnas_backend._run_cmd.assert_has_calls(calls, any_order=False) - - def test_check_lu_not_found(self): - self.mock_object(self.hnas_backend, '_run_cmd', - side_effect=[(evsfs_list, ''), - (iscsi_target_list, '')]) - - # passing a volume fake-volume not mapped - out = self.hnas_backend.check_lu('fake-volume', 'fs-cinder') - self.assertFalse(out['mapped']) - self.assertEqual(0, out['id']) - self.assertIsNone(out['tgt']) - - def test_add_iscsi_conn(self): - self.mock_object(self.hnas_backend, '_run_cmd', - side_effect=[(evsfs_list, ''), - (iscsi_target_list, ''), - (evsfs_list, '')]) - - out = self.hnas_backend.add_iscsi_conn('cinder-lu', 'fs-cinder', 3260, - 'cinder-default', 'initiator') - - self.assertEqual('cinder-lu', out['lu_name']) - self.assertEqual('fs-cinder', out['fs']) - self.assertEqual('0', out['lu_id']) - self.assertEqual(3260, out['port']) - calls = [mock.call('evsfs', 'list'), - mock.call('console-context', '--evs', '2', 'iscsi-target', - 'list')] - self.hnas_backend._run_cmd.assert_has_calls(calls, any_order=False) - - def test_add_iscsi_conn_not_mapped_volume(self): - not_mapped = {'mapped': False, - 'id': 0, - 'tgt': None} - - self.mock_object(self.hnas_backend, 'check_lu', - return_value=not_mapped) - self.mock_object(self.hnas_backend, '_run_cmd', - side_effect=[(evsfs_list, ''), - (iscsi_target_list, ''), - ('', '')]) - - out = self.hnas_backend.add_iscsi_conn('cinder-lu', 'fs-cinder', 3260, - 'cinder-default', 'initiator') - - self.assertEqual('cinder-lu', out['lu_name']) - self.assertEqual('fs-cinder', out['fs']) - self.assertEqual(2, out['lu_id']) - self.assertEqual(3260, out['port']) - calls = [mock.call('evsfs', 'list'), - mock.call('console-context', '--evs', '2', 'iscsi-target', - 'list')] - self.hnas_backend._run_cmd.assert_has_calls(calls, any_order=False) - - def test_del_iscsi_conn(self): - iqn = 'iqn.2014-12.10.10.10.10:evstest1.cinder-default' - - self.mock_object(self.hnas_backend, '_run_cmd', - return_value=(iscsi_one_target, '')) - - self.hnas_backend.del_iscsi_conn('2', iqn, '0') - - calls = [mock.call('console-context', '--evs', '2', 'iscsi-target', - 'list', iqn), - mock.call('console-context', '--evs', '2', 'iscsi-target', - 'dellu', '-f', iqn, '0')] - self.hnas_backend._run_cmd.assert_has_calls(calls, any_order=False) - - def test_del_iscsi_conn_volume_not_found(self): - iqn = 'iqn.2014-12.10.10.10.10:evstest1.cinder-fake' - - self.mock_object(self.hnas_backend, '_run_cmd', - return_value=(iscsi_one_target, '')) - - self.hnas_backend.del_iscsi_conn('2', iqn, '10') - - self.hnas_backend._run_cmd.assert_called_with('console-context', - '--evs', '2', - 'iscsi-target', 'list', - iqn) - - def test_check_target(self): - self.mock_object(self.hnas_backend, '_run_cmd', - side_effect=[(evsfs_list, ''), - (iscsi_target_list, '')]) - - out = self.hnas_backend.check_target('fs-cinder', 'cinder-default') - - self.assertTrue(out['found']) - self.assertEqual('cinder-lu', out['tgt']['lus'][0]['name']) - self.assertEqual('cinder-default', out['tgt']['alias']) - self.assertEqual('pxr6U37LZZJBoMc', out['tgt']['secret']) - - def test_check_target_not_found(self): - self.mock_object(self.hnas_backend, '_run_cmd', - side_effect=[(evsfs_list, ''), - (iscsi_target_list, '')]) - - out = self.hnas_backend.check_target('fs-cinder', 'cinder-fake') - - self.assertFalse(out['found']) - self.assertIsNone(out['tgt']) - - def test_set_target_secret(self): - targetalias = 'cinder-default' - secret = 'pxr6U37LZZJBoMc' - self.mock_object(self.hnas_backend, '_run_cmd', - return_value=(evsfs_list, '')) - - self.hnas_backend.set_target_secret(targetalias, 'fs-cinder', secret) - - calls = [mock.call('evsfs', 'list'), - mock.call('console-context', '--evs', '2', 'iscsi-target', - 'mod', '-s', 'pxr6U37LZZJBoMc', '-a', 'enable', - 'cinder-default')] - self.hnas_backend._run_cmd.assert_has_calls(calls, any_order=False) - - def test_set_target_secret_empty_target_list(self): - targetalias = 'cinder-default' - secret = 'pxr6U37LZZJBoMc' - - self.mock_object(self.hnas_backend, '_run_cmd', - side_effect=[(evsfs_list, ''), - ('does not exist', ''), - ('', '')]) - - self.hnas_backend.set_target_secret(targetalias, 'fs-cinder', secret) - - calls = [mock.call('console-context', '--evs', '2', 'iscsi-target', - 'mod', '-s', 'pxr6U37LZZJBoMc', '-a', 'enable', - 'cinder-default')] - self.hnas_backend._run_cmd.assert_has_calls(calls, any_order=False) - - def test_get_target_secret(self): - self.mock_object(self.hnas_backend, '_run_cmd', - side_effect=[(evsfs_list, ''), - (iscsi_one_target, '')]) - out = self.hnas_backend.get_target_secret('cinder-default', - 'fs-cinder') - - self.assertEqual('pxr6U37LZZJBoMc', out) - - self.hnas_backend._run_cmd.assert_called_with('console-context', - '--evs', '2', - 'iscsi-target', 'list', - 'cinder-default') - - def test_get_target_secret_chap_disabled(self): - self.mock_object(self.hnas_backend, '_run_cmd', - side_effect=[(evsfs_list, ''), - (target_chap_disable, '')]) - out = self.hnas_backend.get_target_secret('cinder-default', - 'fs-cinder') - - self.assertEqual('', out) - - self.hnas_backend._run_cmd.assert_called_with('console-context', - '--evs', '2', - 'iscsi-target', 'list', - 'cinder-default') - - def test_get_target_iqn(self): - self.mock_object(self.hnas_backend, '_run_cmd', - side_effect=[(evsfs_list, ''), - (iscsi_one_target, ''), - (add_targetsecret, '')]) - - out = self.hnas_backend.get_target_iqn('cinder-default', 'fs-cinder') - - self.assertEqual('iqn.2014-12.10.10.10.10:evstest1.cinder-default', - out) - - def test_create_target(self): - self.mock_object(self.hnas_backend, '_run_cmd', - return_value=(evsfs_list, '')) - - self.hnas_backend.create_target('cinder-default', 'fs-cinder', - 'pxr6U37LZZJBoMc') + self.assertEqual(0, out['provisioned_capacity']) def test_get_cloned_file_relatives(self): self.mock_object(self.hnas_backend, '_run_cmd', diff --git a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hnas_iscsi.py b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hnas_iscsi.py deleted file mode 100644 index 59e8eb3d5fe..00000000000 --- a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hnas_iscsi.py +++ /dev/null @@ -1,590 +0,0 @@ -# Copyright (c) 2014 Hitachi Data Systems, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -import mock - -from oslo_concurrency import processutils as putils - -from cinder import context -from cinder import exception -from cinder import test -from cinder.tests.unit import fake_constants as fake -from cinder.tests.unit import fake_snapshot -from cinder.tests.unit import fake_volume -from cinder.volume import configuration as conf -from cinder.volume.drivers.hitachi.hnas_backend import HNASSSHBackend -from cinder.volume.drivers.hitachi import hnas_iscsi as iscsi -from cinder.volume.drivers.hitachi import hnas_utils -from cinder.volume import volume_types - - -# The following information is passed on to tests, when creating a volume -_VOLUME = {'name': 'volume-cinder', - 'id': fake.VOLUME_ID, - 'size': 128, - 'host': 'host1@hnas-iscsi-backend#default', - 'provider_location': '83-68-96-AA-DA-5D.volume-2dfe280e-470a-' - '4182-afb8-1755025c35b8'} - -_VOLUME2 = {'name': 'volume-clone', - 'id': fake.VOLUME2_ID, - 'size': 150, - 'host': 'host1@hnas-iscsi-backend#default', - 'provider_location': '83-68-96-AA-DA-5D.volume-8fe1802a-316b-' - '5237-1c57-c35b81755025'} - -_SNAPSHOT = { - 'name': 'snapshot-51dd4-8d8a-4aa9-9176-086c9d89e7fc', - 'id': fake.SNAPSHOT_ID, - 'size': 128, - 'volume_type': None, - 'provider_location': None, - 'volume_size': 128, - 'volume': _VOLUME, - 'volume_name': _VOLUME['name'], - 'host': 'host1@hnas-iscsi-backend#silver', - 'volume_type_id': fake.VOLUME_TYPE_ID, -} - - -class HNASiSCSIDriverTest(test.TestCase): - """Test HNAS iSCSI volume driver.""" - def setUp(self): - super(HNASiSCSIDriverTest, self).setUp() - self.context = context.get_admin_context() - self.volume = fake_volume.fake_volume_obj( - self.context, **_VOLUME) - self.volume_clone = fake_volume.fake_volume_obj( - self.context, **_VOLUME2) - self.snapshot = self.instantiate_snapshot(_SNAPSHOT) - - self.volume_type = fake_volume.fake_volume_type_obj( - None, - **{'name': 'silver', - 'id': fake.VOLUME_TYPE_ID} - ) - - self.parsed_xml = { - 'username': 'supervisor', - 'password': 'supervisor', - 'hnas_cmd': 'ssc', - 'fs': {'fs2': 'fs2'}, - 'ssh_port': '22', - 'port': '3260', - 'services': { - 'default': { - 'hdp': 'fs2', - 'iscsi_ip': '172.17.39.132', - 'iscsi_port': '3260', - 'port': '22', - 'pool_name': 'default', - 'label': 'svc_0', - 'evs': '1', - 'tgt': { - 'alias': 'test', - 'secret': 'itEpgB5gPefGhW2' - } - }, - 'silver': { - 'hdp': 'fs3', - 'iscsi_ip': '172.17.39.133', - 'iscsi_port': '3260', - 'port': '22', - 'pool_name': 'silver', - 'label': 'svc_1', - 'evs': '2', - 'tgt': { - 'alias': 'iscsi-test', - 'secret': 'itEpgB5gPefGhW2' - } - } - }, - 'cluster_admin_ip0': None, - 'ssh_private_key': None, - 'chap_enabled': True, - 'mgmt_ip0': '172.17.44.15', - 'ssh_enabled': None - } - - self.configuration = mock.Mock(spec=conf.Configuration) - self.configuration.hds_hnas_iscsi_config_file = 'fake.xml' - - self.mock_object(hnas_utils, 'read_cinder_conf', - return_value=self.parsed_xml) - - self.driver = iscsi.HNASISCSIDriver(configuration=self.configuration) - - @staticmethod - def instantiate_snapshot(snap): - snap = snap.copy() - snap['volume'] = fake_volume.fake_volume_obj( - None, **snap['volume']) - snapshot = fake_snapshot.fake_snapshot_obj( - None, expected_attrs=['volume'], **snap) - return snapshot - - def test_get_service_target_chap_enabled(self): - lu_info = {'mapped': False, - 'id': 1, - 'tgt': {'alias': 'iscsi-test', - 'secret': 'itEpgB5gPefGhW2'}} - tgt = {'found': True, - 'tgt': { - 'alias': 'cinder-default', - 'secret': 'pxr6U37LZZJBoMc', - 'iqn': 'iqn.2014-12.10.10.10.10:evstest1.cinder-default', - 'lus': [ - {'id': '0', - 'name': 'cinder-lu'}, - {'id': '1', - 'name': 'volume-99da7ae7-1e7f-4d57-8bf...'} - ], - 'auth': 'Enabled'}} - iqn = 'iqn.2014-12.10.10.10.10:evstest1.cinder-default' - - self.mock_object(HNASSSHBackend, 'get_evs', return_value='1') - self.mock_object(HNASSSHBackend, 'check_lu', return_value=lu_info) - self.mock_object(HNASSSHBackend, 'check_target', return_value=tgt) - self.mock_object(HNASSSHBackend, 'get_target_secret', return_value='') - self.mock_object(HNASSSHBackend, 'set_target_secret') - self.mock_object(HNASSSHBackend, 'get_target_iqn', return_value=iqn) - - self.driver._get_service_target(self.volume) - - def test_get_service_target_chap_disabled(self): - lu_info = {'mapped': False, - 'id': 1, - 'tgt': {'alias': 'iscsi-test', - 'secret': 'itEpgB5gPefGhW2'}} - tgt = {'found': False, - 'tgt': { - 'alias': 'cinder-default', - 'secret': 'pxr6U37LZZJBoMc', - 'iqn': 'iqn.2014-12.10.10.10.10:evstest1.cinder-default', - 'lus': [ - {'id': '0', - 'name': 'cinder-lu'}, - {'id': '1', - 'name': 'volume-99da7ae7-1e7f-4d57-8bf...'} - ], - 'auth': 'Enabled'}} - iqn = 'iqn.2014-12.10.10.10.10:evstest1.cinder-default' - - self.driver.config['chap_enabled'] = False - - self.mock_object(HNASSSHBackend, 'get_evs', return_value='1') - self.mock_object(HNASSSHBackend, 'check_lu', return_value=lu_info) - self.mock_object(HNASSSHBackend, 'check_target', return_value=tgt) - self.mock_object(HNASSSHBackend, 'get_target_iqn', return_value=iqn) - self.mock_object(HNASSSHBackend, 'create_target') - - self.driver._get_service_target(self.volume) - - def test_get_service_target_no_more_targets_exception(self): - iscsi.MAX_HNAS_LUS_PER_TARGET = 4 - lu_info = {'mapped': False, 'id': 1, - 'tgt': {'alias': 'iscsi-test', 'secret': 'itEpgB5gPefGhW2'}} - tgt = {'found': True, - 'tgt': { - 'alias': 'cinder-default', 'secret': 'pxr6U37LZZJBoMc', - 'iqn': 'iqn.2014-12.10.10.10.10:evstest1.cinder-default', - 'lus': [ - {'id': '0', 'name': 'volume-0'}, - {'id': '1', 'name': 'volume-1'}, - {'id': '2', 'name': 'volume-2'}, - {'id': '3', 'name': 'volume-3'}, ], - 'auth': 'Enabled'}} - - self.mock_object(HNASSSHBackend, 'get_evs', return_value='1') - self.mock_object(HNASSSHBackend, 'check_lu', return_value=lu_info) - self.mock_object(HNASSSHBackend, 'check_target', return_value=tgt) - - self.assertRaises(exception.NoMoreTargets, - self.driver._get_service_target, self.volume) - - def test_check_pool_and_fs(self): - self.mock_object(hnas_utils, 'get_pool', return_value='default') - self.driver._check_pool_and_fs(self.volume, 'fs2') - - def test_check_pool_and_fs_no_default_configured(self): - self.volume.volume_type = self.volume_type - - self.mock_object(hnas_utils, 'get_pool', return_value='default') - - self.driver.config['services'] = { - 'silver': { - 'hdp': 'fs3', - 'iscsi_ip': '172.17.39.133', - 'iscsi_port': '3260', - 'port': '22', - 'volume_type': 'silver', - 'label': 'svc_1', - 'evs': '2', - 'tgt': { - 'alias': 'iscsi-test', - 'secret': 'itEpgB5gPefGhW2' - } - } - } - - self.assertRaises(exception.ManageExistingVolumeTypeMismatch, - self.driver._check_pool_and_fs, self.volume, - 'fs-cinder') - - def test_check_pool_and_fs_mismatch(self): - self.mock_object(hnas_utils, 'get_pool', return_value='default') - - self.assertRaises(exception.ManageExistingVolumeTypeMismatch, - self.driver._check_pool_and_fs, self.volume, - 'fs-cinder') - - def test_check_pool_and_fs_host_mismatch(self): - self.mock_object(hnas_utils, 'get_pool', return_value='silver') - - self.assertRaises(exception.ManageExistingVolumeTypeMismatch, - self.driver._check_pool_and_fs, self.volume, - 'fs3') - - def test_do_setup(self): - evs_info = {'172.17.39.132': {'evs_number': 1}, - '172.17.39.133': {'evs_number': 2}, - '172.17.39.134': {'evs_number': 3}} - - version_info = { - 'mac': '83-68-96-AA-DA-5D', - 'model': 'HNAS 4040', - 'version': '12.4.3924.11', - 'hardware': 'NAS Platform', - 'serial': 'B1339109', - } - - self.mock_object(HNASSSHBackend, 'get_fs_info', return_value=True) - self.mock_object(HNASSSHBackend, 'get_evs_info', return_value=evs_info) - self.mock_object(HNASSSHBackend, 'get_version', - return_value=version_info) - - self.driver.do_setup(None) - - HNASSSHBackend.get_fs_info.assert_called_with('fs2') - self.assertTrue(HNASSSHBackend.get_evs_info.called) - - def test_do_setup_portal_not_found(self): - evs_info = {'172.17.48.132': {'evs_number': 1}, - '172.17.39.133': {'evs_number': 2}, - '172.17.39.134': {'evs_number': 3}} - - version_info = { - 'mac': '83-68-96-AA-DA-5D', - 'model': 'HNAS 4040', - 'version': '12.4.3924.11', - 'hardware': 'NAS Platform', - 'serial': 'B1339109', - } - - self.mock_object(HNASSSHBackend, 'get_fs_info', return_value=True) - self.mock_object(HNASSSHBackend, 'get_evs_info', return_value=evs_info) - self.mock_object(HNASSSHBackend, 'get_version', - return_value=version_info) - - self.assertRaises(exception.InvalidParameterValue, - self.driver.do_setup, None) - - def test_do_setup_umounted_filesystem(self): - self.mock_object(HNASSSHBackend, 'get_fs_info', return_value=False) - - self.assertRaises(exception.ParameterNotFound, self.driver.do_setup, - None) - - def test_initialize_connection(self): - lu_info = {'mapped': True, - 'id': 1, - 'tgt': {'alias': 'iscsi-test', - 'secret': 'itEpgB5gPefGhW2'}} - - conn = {'lun_name': 'cinder-lu', - 'initiator': 'initiator', - 'hdp': 'fs-cinder', - 'lu_id': '0', - 'iqn': 'iqn.2014-12.10.10.10.10:evstest1.cinder-default', - 'port': 3260} - - connector = {'initiator': 'fake_initiator'} - - self.mock_object(HNASSSHBackend, 'get_evs', return_value=2) - self.mock_object(HNASSSHBackend, 'check_lu', return_value=lu_info) - self.mock_object(HNASSSHBackend, 'add_iscsi_conn', return_value=conn) - - self.driver.initialize_connection(self.volume, connector) - - HNASSSHBackend.add_iscsi_conn.assert_called_with(self.volume.name, - 'fs2', '22', - 'iscsi-test', - connector[ - 'initiator']) - - def test_initialize_connection_command_error(self): - lu_info = {'mapped': True, - 'id': 1, - 'tgt': {'alias': 'iscsi-test', - 'secret': 'itEpgB5gPefGhW2'}} - - connector = {'initiator': 'fake_initiator'} - - self.mock_object(HNASSSHBackend, 'get_evs', return_value=2) - self.mock_object(HNASSSHBackend, 'check_lu', return_value=lu_info) - self.mock_object(HNASSSHBackend, 'add_iscsi_conn', - side_effect=putils.ProcessExecutionError) - - self.assertRaises(exception.ISCSITargetAttachFailed, - self.driver.initialize_connection, self.volume, - connector) - - def test_terminate_connection(self): - connector = {} - lu_info = {'mapped': True, - 'id': 1, - 'tgt': {'alias': 'iscsi-test', - 'secret': 'itEpgB5gPefGhW2'}} - - self.mock_object(HNASSSHBackend, 'get_evs', return_value=2) - self.mock_object(HNASSSHBackend, 'check_lu', return_value=lu_info) - self.mock_object(HNASSSHBackend, 'del_iscsi_conn') - - self.driver.terminate_connection(self.volume, connector) - - HNASSSHBackend.del_iscsi_conn.assert_called_with('1', - 'iscsi-test', - lu_info['id']) - - def test_get_volume_stats(self): - self.driver.pools = [{'pool_name': 'default', - 'service_label': 'svc_0', - 'fs': '172.17.39.132:/fs2'}, - {'pool_name': 'silver', - 'service_label': 'svc_1', - 'fs': '172.17.39.133:/fs3'}] - - fs_cinder = { - 'evs_id': '2', - 'total_size': '250', - 'label': 'fs-cinder', - 'available_size': '228', - 'used_size': '21.4', - 'id': '1025', - 'provisioned_capacity': 0.0 - } - - self.mock_object(HNASSSHBackend, 'get_fs_info', return_value=fs_cinder) - - stats = self.driver.get_volume_stats(refresh=True) - - self.assertEqual('5.0.0', stats['driver_version']) - self.assertEqual('Hitachi', stats['vendor_name']) - self.assertEqual('iSCSI', stats['storage_protocol']) - - def test_create_volume(self): - version_info = {'mac': '83-68-96-AA-DA-5D'} - expected_out = { - 'provider_location': version_info['mac'] + '.' + self.volume.name - } - - self.mock_object(HNASSSHBackend, 'create_lu') - self.mock_object(HNASSSHBackend, 'get_version', - return_value=version_info) - out = self.driver.create_volume(self.volume) - - self.assertEqual(expected_out, out) - HNASSSHBackend.create_lu.assert_called_with('fs2', u'128', - self.volume.name) - - def test_create_volume_missing_fs(self): - self.volume.host = 'host1@hnas-iscsi-backend#missing' - - self.assertRaises(exception.ParameterNotFound, - self.driver.create_volume, self.volume) - - def test_delete_volume(self): - self.mock_object(HNASSSHBackend, 'delete_lu') - - self.driver.delete_volume(self.volume) - - HNASSSHBackend.delete_lu.assert_called_once_with( - self.parsed_xml['fs']['fs2'], self.volume.name) - - def test_extend_volume(self): - new_size = 200 - self.mock_object(HNASSSHBackend, 'extend_lu') - - self.driver.extend_volume(self.volume, new_size) - - HNASSSHBackend.extend_lu.assert_called_once_with( - self.parsed_xml['fs']['fs2'], new_size, - self.volume.name) - - def test_create_cloned_volume(self): - clone_name = self.volume_clone.name - version_info = {'mac': '83-68-96-AA-DA-5D'} - expected_out = { - 'provider_location': - version_info['mac'] + '.' + self.volume_clone.name - } - - self.mock_object(HNASSSHBackend, 'create_cloned_lu') - self.mock_object(HNASSSHBackend, 'get_version', - return_value=version_info) - self.mock_object(HNASSSHBackend, 'extend_lu') - - out = self.driver.create_cloned_volume(self.volume_clone, self.volume) - self.assertEqual(expected_out, out) - HNASSSHBackend.create_cloned_lu.assert_called_with(self.volume.name, - 'fs2', - clone_name) - - def test_functions_with_pass(self): - self.driver.check_for_setup_error() - self.driver.ensure_export(None, self.volume) - self.driver.create_export(None, self.volume, 'connector') - self.driver.remove_export(None, self.volume) - - def test_create_snapshot(self): - lu_info = {'lu_mounted': 'No', - 'name': 'cinder-lu', - 'fs_mounted': 'YES', - 'filesystem': 'FS-Cinder', - 'path': '/.cinder/cinder-lu.iscsi', - 'size': 2.0} - version_info = {'mac': '83-68-96-AA-DA-5D'} - expected_out = { - 'provider_location': version_info['mac'] + '.' + self.snapshot.name - } - - self.mock_object(HNASSSHBackend, 'get_existing_lu_info', - return_value=lu_info) - self.mock_object(volume_types, 'get_volume_type', - return_value=self.volume_type) - self.mock_object(HNASSSHBackend, 'create_cloned_lu') - self.mock_object(HNASSSHBackend, 'get_version', - return_value=version_info) - - out = self.driver.create_snapshot(self.snapshot) - self.assertEqual(expected_out, out) - - def test_delete_snapshot(self): - lu_info = {'filesystem': 'FS-Cinder'} - - self.mock_object(volume_types, 'get_volume_type', - return_value=self.volume_type) - self.mock_object(HNASSSHBackend, 'get_existing_lu_info', - return_value=lu_info) - self.mock_object(HNASSSHBackend, 'delete_lu') - - self.driver.delete_snapshot(self.snapshot) - - def test_create_volume_from_snapshot(self): - version_info = {'mac': '83-68-96-AA-DA-5D'} - expected_out = { - 'provider_location': version_info['mac'] + '.' + self.snapshot.name - } - - self.mock_object(HNASSSHBackend, 'create_cloned_lu') - self.mock_object(HNASSSHBackend, 'get_version', - return_value=version_info) - - out = self.driver.create_volume_from_snapshot(self.volume, - self.snapshot) - self.assertEqual(expected_out, out) - HNASSSHBackend.create_cloned_lu.assert_called_with(self.snapshot.name, - 'fs2', - self.volume.name) - - def test_manage_existing_get_size(self): - existing_vol_ref = {'source-name': 'fs-cinder/volume-cinder'} - lu_info = { - 'name': 'volume-cinder', - 'comment': None, - 'path': ' /.cinder/volume-cinder', - 'size': 128, - 'filesystem': 'fs-cinder', - 'fs_mounted': 'Yes', - 'lu_mounted': 'Yes' - } - - self.mock_object(HNASSSHBackend, 'get_existing_lu_info', - return_value=lu_info) - - out = self.driver.manage_existing_get_size(self.volume, - existing_vol_ref) - - self.assertEqual(lu_info['size'], out) - HNASSSHBackend.get_existing_lu_info.assert_called_with( - 'volume-cinder', lu_info['filesystem']) - - def test_manage_existing_get_size_no_source_name(self): - existing_vol_ref = {} - - self.assertRaises(exception.ManageExistingInvalidReference, - self.driver.manage_existing_get_size, self.volume, - existing_vol_ref) - - def test_manage_existing_get_size_wrong_source_name(self): - existing_vol_ref = {'source-name': 'fs-cinder/volume/cinder'} - - self.mock_object(HNASSSHBackend, 'get_existing_lu_info', - return_value={}) - - self.assertRaises(exception.ManageExistingInvalidReference, - self.driver.manage_existing_get_size, self.volume, - existing_vol_ref) - - def test_manage_existing_get_size_volume_not_found(self): - existing_vol_ref = {'source-name': 'fs-cinder/volume-cinder'} - - self.mock_object(HNASSSHBackend, 'get_existing_lu_info', - return_value={}) - - self.assertRaises(exception.ManageExistingInvalidReference, - self.driver.manage_existing_get_size, self.volume, - existing_vol_ref) - - def test_manage_existing(self): - self.volume.volume_type = self.volume_type - existing_vol_ref = {'source-name': 'fs2/volume-cinder'} - metadata = {'service_label': 'default'} - version_info = {'mac': '83-68-96-AA-DA-5D'} - expected_out = { - 'provider_location': version_info['mac'] + '.' + self.volume.name - } - self.mock_object(HNASSSHBackend, 'rename_existing_lu') - self.mock_object(volume_types, 'get_volume_type_extra_specs', - return_value=metadata) - self.mock_object(HNASSSHBackend, 'get_version', - return_value=version_info) - - out = self.driver.manage_existing(self.volume, existing_vol_ref) - - self.assertEqual(expected_out, out) - HNASSSHBackend.rename_existing_lu.assert_called_with('fs2', - 'volume-cinder', - self.volume.name) - - def test_unmanage(self): - self.mock_object(HNASSSHBackend, 'rename_existing_lu') - - self.driver.unmanage(self.volume) - - HNASSSHBackend.rename_existing_lu.assert_called_with( - self.parsed_xml['fs']['fs2'], - self.volume.name, 'unmanage-' + self.volume.name) diff --git a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hnas_utils.py b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hnas_utils.py index 7bf68b3fdeb..1ad11a4fc1d 100644 --- a/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hnas_utils.py +++ b/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hnas_utils.py @@ -14,7 +14,6 @@ # under the License. # -import copy import ddt import os @@ -26,7 +25,6 @@ from cinder import test from cinder.tests.unit import fake_constants from cinder.tests.unit import fake_volume from cinder.volume import configuration as conf -from cinder.volume.drivers.hitachi import hnas_iscsi from cinder.volume.drivers.hitachi import hnas_utils from cinder.volume import volume_types @@ -38,14 +36,13 @@ _VOLUME = {'name': 'cinder-volume', 'provider_location': 'hnas'} service_parameters = ['volume_type', 'hdp'] -optional_parameters = ['ssc_cmd', 'cluster_admin_ip0', 'iscsi_ip'] +optional_parameters = ['ssc_cmd', 'cluster_admin_ip0'] config_from_cinder_conf = { 'username': 'supervisor', 'fs': {'easy-stack': 'easy-stack', 'silver': 'silver'}, 'ssh_port': 22, - 'chap_enabled': True, 'cluster_admin_ip0': None, 'ssh_private_key': None, 'mgmt_ip0': '172.24.44.15', @@ -70,12 +67,10 @@ valid_XML_str = ''' /home/ubuntu/.ssh/id_rsa default - 172.24.49.21 easy-stack silver - 172.24.49.32 FS-CinderDev1 @@ -98,7 +93,6 @@ XML_empty_authentication_param = ''' default - 172.24.49.21 easy-stack @@ -112,7 +106,6 @@ XML_without_mandatory_params = ''' False default - 172.24.49.21 easy-stack @@ -130,7 +123,7 @@ XML_no_services_configured = ''' ''' parsed_xml = {'username': 'supervisor', 'password': 'supervisor', - 'ssc_cmd': 'ssc', 'iscsi_ip': None, 'ssh_port': 22, + 'ssc_cmd': 'ssc', 'ssh_port': 22, 'fs': {'easy-stack': 'easy-stack', 'FS-CinderDev1': 'FS-CinderDev1'}, 'cluster_admin_ip0': None, @@ -159,17 +152,14 @@ class HNASUtilsTest(test.TestCase): super(HNASUtilsTest, self).setUp() self.fake_conf = conf.Configuration(hnas_utils.CONF) - self.fake_conf.append_config_values(hnas_iscsi.iSCSI_OPTS) self.override_config('hnas_username', 'supervisor') self.override_config('hnas_password', 'supervisor') self.override_config('hnas_mgmt_ip0', '172.24.44.15') self.override_config('hnas_svc0_pool_name', 'default') self.override_config('hnas_svc0_hdp', 'easy-stack') - self.override_config('hnas_svc0_iscsi_ip', '172.24.49.21') self.override_config('hnas_svc1_pool_name', 'FS-CinderDev1') self.override_config('hnas_svc1_hdp', 'silver') - self.override_config('hnas_svc1_iscsi_ip', '172.24.49.32') self.context = context.get_admin_context() self.volume = fake_volume.fake_volume_obj(self.context, **_VOLUME) @@ -286,32 +276,22 @@ class HNASUtilsTest(test.TestCase): self.assertEqual('default', out) def test_read_cinder_conf_nfs(self): - out = hnas_utils.read_cinder_conf(self.fake_conf, 'nfs') + out = hnas_utils.read_cinder_conf(self.fake_conf) self.assertEqual(config_from_cinder_conf, out) - def test_read_cinder_conf_iscsi(self): - local_config = copy.deepcopy(config_from_cinder_conf) - - local_config['services']['FS-CinderDev1']['iscsi_ip'] = '172.24.49.32' - local_config['services']['default']['iscsi_ip'] = '172.24.49.21' - - out = hnas_utils.read_cinder_conf(self.fake_conf, 'iscsi') - - self.assertEqual(local_config, out) - def test_read_cinder_conf_break(self): self.override_config('hnas_username', None) self.override_config('hnas_password', None) self.override_config('hnas_mgmt_ip0', None) - out = hnas_utils.read_cinder_conf(self.fake_conf, 'nfs') + out = hnas_utils.read_cinder_conf(self.fake_conf) self.assertIsNone(out) @ddt.data('hnas_username', 'hnas_password', - 'hnas_mgmt_ip0', 'hnas_svc0_iscsi_ip', 'hnas_svc0_pool_name', + 'hnas_mgmt_ip0', 'hnas_svc0_pool_name', 'hnas_svc0_hdp', ) def test_init_invalid_conf_parameters(self, attr_name): self.override_config(attr_name, None) self.assertRaises(exception.InvalidParameterValue, - hnas_utils.read_cinder_conf, self.fake_conf, 'iscsi') + hnas_utils.read_cinder_conf, self.fake_conf) diff --git a/cinder/volume/drivers/hitachi/hnas_backend.py b/cinder/volume/drivers/hitachi/hnas_backend.py index ebe742a61ab..bae111bf11a 100644 --- a/cinder/volume/drivers/hitachi/hnas_backend.py +++ b/cinder/volume/drivers/hitachi/hnas_backend.py @@ -220,19 +220,7 @@ class HNASSSHBackend(object): fs_info['available_size'] = _convert_size( fs_info['available_size']) - # Get the iSCSI LUs in the FS - evs_id = self.get_evs(fs_label) - out, err = self._run_cmd('console-context', '--evs', evs_id, - 'iscsi-lu', 'list') - all_lus = [self._parse_lu_info(lu_raw) - for lu_raw in out.split('\n\n')[:-1]] - - provisioned_cap = 0 - for lu in all_lus: - if lu['filesystem'] == fs_label: - provisioned_cap += lu['size'] - - fs_info['provisioned_capacity'] = provisioned_cap + fs_info['provisioned_capacity'] = 0 LOG.debug("File system info of %(fs)s (sizes in GB): %(info)s.", {'fs': fs_label, 'info': fs_info}) @@ -256,119 +244,6 @@ class HNASSSHBackend(object): return self.fslist[key]['evsid'] LOG.debug("Can't find EVS ID for fs %(fs)s.", {'fs': fs_label}) - def _get_targets(self, evs_id, tgt_alias=None, refresh=False): - """Gets the target list of an EVS. - - Gets the target list of an EVS. Optionally can return the information - of a specific target. - :returns: Target list or Target info (EVS ID) or empty list - """ - LOG.debug("Getting target list for evs %(evs)s, tgtalias: %(tgt)s.", - {'evs': evs_id, 'tgt': tgt_alias}) - - if (refresh or - evs_id not in self.tgt_list.keys() or - tgt_alias is not None): - self.tgt_list[evs_id] = [] - out, err = self._run_cmd("console-context", "--evs", evs_id, - 'iscsi-target', 'list') - - if 'No targets' in out: - LOG.debug("No targets found in EVS %(evsid)s.", - {'evsid': evs_id}) - return self.tgt_list[evs_id] - - tgt_raw_list = out.split('Alias')[1:] - for tgt_raw_info in tgt_raw_list: - tgt = {} - tgt['alias'] = tgt_raw_info.split('\n')[0].split(' ').pop() - tgt['iqn'] = tgt_raw_info.split('\n')[1].split(' ').pop() - tgt['secret'] = tgt_raw_info.split('\n')[3].split(' ').pop() - tgt['auth'] = tgt_raw_info.split('\n')[4].split(' ').pop() - lus = [] - tgt_raw_info = tgt_raw_info.split('\n\n')[1] - tgt_raw_list = tgt_raw_info.split('\n')[2:] - - for lu_raw_line in tgt_raw_list: - lu_raw_line = lu_raw_line.strip() - lu_raw_line = lu_raw_line.split(' ') - lu = {} - lu['id'] = lu_raw_line[0] - lu['name'] = lu_raw_line.pop() - lus.append(lu) - - tgt['lus'] = lus - - if tgt_alias == tgt['alias']: - return tgt - - self.tgt_list[evs_id].append(tgt) - - if tgt_alias is not None: - # We tried to find 'tgtalias' but didn't find. Return a empty - # list. - LOG.debug("There's no target %(alias)s in EVS %(evsid)s.", - {'alias': tgt_alias, 'evsid': evs_id}) - return [] - - LOG.debug("Targets in EVS %(evs)s: %(tgtl)s.", - {'evs': evs_id, 'tgtl': self.tgt_list[evs_id]}) - - return self.tgt_list[evs_id] - - def _get_unused_luid(self, tgt_info): - """Gets a free logical unit id number to be used. - - :param tgt_info: dictionary with the target information - :returns: a free logical unit id number - """ - if len(tgt_info['lus']) == 0: - return 0 - - free_lu = 0 - for lu in tgt_info['lus']: - if int(lu['id']) == free_lu: - free_lu += 1 - - if int(lu['id']) > free_lu: - # Found a free LU number - break - - LOG.debug("Found the free LU ID: %(lu)s.", {'lu': free_lu}) - - return free_lu - - def create_lu(self, fs_label, size, lu_name): - """Creates a new Logical Unit. - - If the operation can not be performed for some reason, utils.execute() - throws an error and aborts the operation. Used for iSCSI only - - :param fs_label: data pool the Logical Unit will be created - :param size: Size (GB) of the new Logical Unit - :param lu_name: name of the Logical Unit - """ - evs_id = self.get_evs(fs_label) - - self._run_cmd("console-context", "--evs", evs_id, 'iscsi-lu', 'add', - "-e", lu_name, fs_label, '/.cinder/' + lu_name + - '.iscsi', size + 'G') - - LOG.debug('Created %(size)s GB LU: %(name)s FS: %(fs)s.', - {'size': size, 'name': lu_name, 'fs': fs_label}) - - def delete_lu(self, fs_label, lu_name): - """Deletes a Logical Unit. - - :param fs_label: data pool of the Logical Unit - :param lu_name: id of the Logical Unit being deleted - """ - evs_id = self.get_evs(fs_label) - self._run_cmd("console-context", "--evs", evs_id, 'iscsi-lu', 'del', - '-d', '-f', lu_name) - - LOG.debug('LU %(lu)s deleted.', {'lu': lu_name}) - def file_clone(self, fs_label, src, name): """Clones NFS files to a new one named 'name'. @@ -391,319 +266,6 @@ class HNASSSHBackend(object): LOG.debug('file_clone: fs:%(fs_label)s %(src)s/src: -> %(name)s/dst', {'fs_label': fs_label, 'src': src, 'name': name}) - def extend_lu(self, fs_label, new_size, lu_name): - """Extends an iSCSI volume. - - :param fs_label: data pool of the Logical Unit - :param new_size: new size of the Logical Unit - :param lu_name: name of the Logical Unit - """ - evs_id = self.get_evs(fs_label) - size = six.text_type(new_size) - self._run_cmd("console-context", "--evs", evs_id, 'iscsi-lu', 'expand', - lu_name, size + 'G') - - LOG.debug('LU %(lu)s extended.', {'lu': lu_name}) - - @utils.retry(putils.ProcessExecutionError, retries=HNAS_SSC_RETRIES, - wait_random=True) - def add_iscsi_conn(self, lu_name, fs_label, port, tgt_alias, initiator): - """Sets up the Logical Unit on the specified target port. - - :param lu_name: id of the Logical Unit being extended - :param fs_label: data pool of the Logical Unit - :param port: iSCSI port - :param tgt_alias: iSCSI qualified name - :param initiator: initiator address - :returns: dictionary (conn_info) with the connection information - conn_info={ - 'lu': Logical Unit ID, - 'iqn': iSCSI qualified name, - 'lu_name': Logical Unit name, - 'initiator': iSCSI initiator, - 'fs_label': File system to connect, - 'port': Port to make the iSCSI connection - } - """ - conn_info = {} - lu_info = self.check_lu(lu_name, fs_label) - _evs_id = self.get_evs(fs_label) - - if not lu_info['mapped']: - tgt = self._get_targets(_evs_id, tgt_alias) - lu_id = self._get_unused_luid(tgt) - conn_info['lu_id'] = lu_id - conn_info['iqn'] = tgt['iqn'] - - # In busy situations where 2 or more instances of the driver are - # trying to map an LU, 2 hosts can retrieve the same 'lu_id', - # and try to map the LU in the same LUN. To handle that we - # capture the ProcessExecutionError exception, backoff for some - # seconds and retry it. - self._run_cmd("console-context", "--evs", _evs_id, 'iscsi-target', - 'addlu', tgt_alias, lu_name, six.text_type(lu_id)) - else: - conn_info['lu_id'] = lu_info['id'] - conn_info['iqn'] = lu_info['tgt']['iqn'] - - conn_info['lu_name'] = lu_name - conn_info['initiator'] = initiator - conn_info['fs'] = fs_label - conn_info['port'] = port - - LOG.debug('add_iscsi_conn: LU %(lu)s added to %(tgt)s.', - {'lu': lu_name, 'tgt': tgt_alias}) - LOG.debug('conn_info: %(conn_info)s', {'conn_info': conn_info}) - - return conn_info - - def del_iscsi_conn(self, evs_id, iqn, lu_id): - """Removes the Logical Unit on the specified target port. - - :param evs_id: EVSID for the file system - :param iqn: iSCSI qualified name - :param lu_id: Logical Unit id - """ - found = False - out, err = self._run_cmd("console-context", "--evs", evs_id, - 'iscsi-target', 'list', iqn) - - # see if LU is already detached - lines = out.split('\n') - for line in lines: - if line.startswith(' '): - lu_line = line.split()[0] - if lu_line[0].isdigit() and lu_line == lu_id: - found = True - break - - # LU wasn't found - if not found: - LOG.debug("del_iscsi_conn: LU already deleted from " - "target %(iqn)s", {'lu': lu_id, 'iqn': iqn}) - return - - # remove the LU from the target - self._run_cmd("console-context", "--evs", evs_id, 'iscsi-target', - 'dellu', '-f', iqn, lu_id) - - LOG.debug("del_iscsi_conn: LU: %(lu)s successfully deleted from " - "target %(iqn)s", {'lu': lu_id, 'iqn': iqn}) - - def get_target_iqn(self, tgt_alias, fs_label): - """Obtains the target full iqn - - Returns the target's full iqn rather than its alias. - - :param tgt_alias: alias of the target - :param fs_label: data pool of the Logical Unit - :returns: string with full IQN - """ - _evs_id = self.get_evs(fs_label) - out, err = self._run_cmd("console-context", "--evs", _evs_id, - 'iscsi-target', 'list', tgt_alias) - - lines = out.split('\n') - # returns the first iqn - for line in lines: - if 'Globally unique name' in line: - full_iqn = line.split()[3] - LOG.debug('get_target_iqn: %(iqn)s', {'iqn': full_iqn}) - return full_iqn - LOG.debug("Could not find iqn for alias %(alias)s on fs %(fs_label)s", - {'alias': tgt_alias, 'fs_label': fs_label}) - - def set_target_secret(self, targetalias, fs_label, secret): - """Sets the chap secret for the specified target. - - :param targetalias: alias of the target - :param fs_label: data pool of the Logical Unit - :param secret: CHAP secret of the target - """ - _evs_id = self.get_evs(fs_label) - self._run_cmd("console-context", "--evs", _evs_id, 'iscsi-target', - 'mod', '-s', secret, '-a', 'enable', targetalias) - - LOG.debug("set_target_secret: Secret set on target %(tgt)s.", - {'tgt': targetalias}) - - def get_target_secret(self, targetalias, fs_label): - """Gets the chap secret for the specified target. - - :param targetalias: alias of the target - :param fs_label: data pool of the Logical Unit - :returns: CHAP secret of the target - """ - _evs_id = self.get_evs(fs_label) - out, err = self._run_cmd("console-context", "--evs", _evs_id, - 'iscsi-target', 'list', targetalias) - - enabled = "" - secret = "" - lines = out.split('\n') - for line in lines: - if 'Secret' in line: - if len(line.split()) > 2: - secret = line.split()[2] - if 'Authentication' in line: - enabled = line.split()[2] - - if enabled == 'Enabled': - return secret - else: - return "" - - def check_target(self, fs_label, target_alias): - """Checks if a given target exists and gets its info. - - :param fs_label: pool name used - :param target_alias: alias of the target - :returns: dictionary (tgt_info) - tgt_info={ - 'alias': The alias of the target, - 'found': boolean to inform if the target was found or not, - 'tgt': dictionary with the target information - } - """ - tgt_info = {} - _evs_id = self.get_evs(fs_label) - _tgt_list = self._get_targets(_evs_id) - - for tgt in _tgt_list: - if tgt['alias'] == target_alias: - attached_lus = len(tgt['lus']) - tgt_info['found'] = True - tgt_info['tgt'] = tgt - LOG.debug("Target %(tgt)s has %(lu)s volumes.", - {'tgt': target_alias, 'lu': attached_lus}) - return tgt_info - - tgt_info['found'] = False - tgt_info['tgt'] = None - - LOG.debug("check_target: Target %(tgt)s does not exist.", - {'tgt': target_alias}) - - return tgt_info - - def check_lu(self, vol_name, fs_label): - """Checks if a given LU is already mapped - - :param vol_name: name of the LU - :param fs_label: storage pool of the LU - :returns: dictionary (lu_info) with LU information - lu_info={ - 'mapped': LU state (mapped or not), - 'id': ID of the LU, - 'tgt': the iSCSI target alias - } - """ - lu_info = {} - evs_id = self.get_evs(fs_label) - tgt_list = self._get_targets(evs_id, refresh=True) - - for tgt in tgt_list: - if len(tgt['lus']) == 0: - continue - - for lu in tgt['lus']: - lu_id = lu['id'] - lu_name = lu['name'] - if lu_name[:29] == vol_name[:29]: - lu_info['mapped'] = True - lu_info['id'] = lu_id - lu_info['tgt'] = tgt - LOG.debug("LU %(lu)s attached on %(luid)s, " - "target: %(tgt)s.", - {'lu': vol_name, 'luid': lu_id, 'tgt': tgt}) - return lu_info - - lu_info['mapped'] = False - lu_info['id'] = 0 - lu_info['tgt'] = None - - LOG.debug("LU %(lu)s not attached. lu_info: %(lu_info)s", - {'lu': vol_name, 'lu_info': lu_info}) - - return lu_info - - def _parse_lu_info(self, output): - lu_info = {} - if 'does not exist.' not in output: - aux = output.split('\n') - lu_info['name'] = aux[0].split(':')[1].strip() - lu_info['comment'] = aux[1].split(':')[1].strip() - lu_info['path'] = aux[2].split(':')[1].strip() - lu_info['size'] = aux[3].split(':')[1].strip() - lu_info['filesystem'] = aux[4].split(':')[1].strip() - lu_info['fs_mounted'] = aux[5].split(':')[1].strip() - lu_info['lu_mounted'] = aux[6].split(':')[1].strip() - - if 'TB' in lu_info['size']: - sz_convert = float(lu_info['size'].split()[0]) * units.Ki - lu_info['size'] = sz_convert - elif 'MB' in lu_info['size']: - sz_convert = float(lu_info['size'].split()[0]) / units.Ki - lu_info['size'] = sz_convert - else: - lu_info['size'] = float(lu_info['size'].split()[0]) - - return lu_info - - def get_existing_lu_info(self, lu_name, fs_label=None, evs_id=None): - """Gets the information for the specified Logical Unit. - - Returns the information of an existing Logical Unit on HNAS, according - to the name provided. - - :param lu_name: label of the Logical Unit - :param fs_label: label of the file system - :param evs_id: ID of the EVS where the LU is located - :returns: dictionary (lu_info) with LU information - lu_info={ - 'name': A Logical Unit name, - 'comment': A comment about the LU, not used for Cinder, - 'path': Path to LU inside filesystem, - 'size': Logical Unit size returned always in GB (volume size), - 'filesystem': File system where the Logical Unit was created, - 'fs_mounted': Information about the state of file system - (mounted or not), - 'lu_mounted': Information about the state of Logical Unit - (mounted or not) - } - """ - - if evs_id is None: - evs_id = self.get_evs(fs_label) - - lu_name = "'{}'".format(lu_name) - out, err = self._run_cmd("console-context", "--evs", evs_id, - 'iscsi-lu', 'list', lu_name) - lu_info = self._parse_lu_info(out) - LOG.debug('get_existing_lu_info: LU info: %(lu)s', {'lu': lu_info}) - - return lu_info - - def rename_existing_lu(self, fs_label, vol_name, new_name): - """Renames the specified Logical Unit. - - Renames an existing Logical Unit on HNAS according to the new name - provided. - - :param fs_label: label of the file system - :param vol_name: current name of the existing volume - :param new_name: new name to the existing volume - """ - - new_name = "'{}'".format(new_name) - evs_id = self.get_evs(fs_label) - self._run_cmd("console-context", "--evs", evs_id, "iscsi-lu", "mod", - "-n", new_name, vol_name) - - LOG.debug('rename_existing_lu_info:' - 'LU %(old)s was renamed to %(new)s', - {'old': vol_name, 'new': new_name}) - def _get_fs_list(self): """Gets a list of file systems configured on the backend. @@ -804,37 +366,6 @@ class HNASSSHBackend(object): LOG.debug("get_export_list: %(exp_list)s", {'exp_list': export_list}) return export_list - def create_cloned_lu(self, src_lu, fs_label, clone_name): - """Clones a Logical Unit - - Clone primitive used to support all iSCSI snapshot/cloning functions. - - :param src_lu: id of the Logical Unit being deleted - :param fs_label: data pool of the Logical Unit - :param clone_name: name of the snapshot - """ - evs_id = self.get_evs(fs_label) - self._run_cmd("console-context", "--evs", evs_id, 'iscsi-lu', 'clone', - '-e', src_lu, clone_name, - '/.cinder/' + clone_name + '.iscsi') - - LOG.debug('LU %(lu)s cloned.', {'lu': clone_name}) - - def create_target(self, tgt_alias, fs_label, secret): - """Creates a new iSCSI target - - :param tgt_alias: the alias with which the target will be created - :param fs_label: the label of the file system to create the target - :param secret: the secret for authentication of the target - """ - _evs_id = self.get_evs(fs_label) - self._run_cmd("console-context", "--evs", _evs_id, - 'iscsi-target', 'add', tgt_alias, secret) - - self._get_targets(_evs_id, refresh=True) - LOG.debug("create_target: alias: %(alias)s fs_label: %(fs_label)s", - {'alias': tgt_alias, 'fs_label': fs_label}) - def _get_file_handler(self, volume_path, _evs_id, fs_label, raise_except): diff --git a/cinder/volume/drivers/hitachi/hnas_iscsi.py b/cinder/volume/drivers/hitachi/hnas_iscsi.py deleted file mode 100644 index 2dcb5abe3e6..00000000000 --- a/cinder/volume/drivers/hitachi/hnas_iscsi.py +++ /dev/null @@ -1,724 +0,0 @@ -# Copyright (c) 2014 Hitachi Data Systems, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -""" -iSCSI Cinder Volume driver for Hitachi Unified Storage (HUS-HNAS) platform. -""" - -from oslo_concurrency import processutils -from oslo_config import cfg -from oslo_log import log as logging -from oslo_log import versionutils -import six - -from cinder import exception -from cinder.i18n import _, _LE, _LI -from cinder import interface - -from cinder import utils as cinder_utils -from cinder.volume import driver -from cinder.volume.drivers.hitachi import hnas_backend -from cinder.volume.drivers.hitachi import hnas_utils -from cinder.volume import utils - - -HNAS_ISCSI_VERSION = '5.0.0' - -LOG = logging.getLogger(__name__) - -iSCSI_OPTS = [ - cfg.StrOpt('hds_hnas_iscsi_config_file', - default='/opt/hds/hnas/cinder_iscsi_conf.xml', - help='Legacy configuration file for HNAS iSCSI Cinder ' - 'plugin. This is not needed if you fill all ' - 'configuration on cinder.conf', - deprecated_for_removal=True), - cfg.BoolOpt('hnas_chap_enabled', - default=True, - help='Whether the chap authentication is enabled in the ' - 'iSCSI target or not.'), - cfg.IPOpt('hnas_svc0_iscsi_ip', - help='Service 0 iSCSI IP'), - cfg.IPOpt('hnas_svc1_iscsi_ip', - help='Service 1 iSCSI IP'), - cfg.IPOpt('hnas_svc2_iscsi_ip', - help='Service 2 iSCSI IP'), - cfg.IPOpt('hnas_svc3_iscsi_ip', - help='Service 3 iSCSI IP') -] - - -CONF = cfg.CONF -CONF.register_opts(iSCSI_OPTS) - -HNAS_DEFAULT_CONFIG = {'ssc_cmd': 'ssc', - 'chap_enabled': True, - 'ssh_port': 22} -MAX_HNAS_ISCSI_TARGETS = 32 -MAX_HNAS_LUS_PER_TARGET = 32 - - -@interface.volumedriver -class HNASISCSIDriver(driver.ISCSIDriver): - """HNAS iSCSI volume driver. - - Version history: - - code-block:: none - - Version 1.0.0: Initial driver version - Version 2.2.0: Added support to SSH authentication - Version 3.2.0: Added pool aware scheduling - Fixed concurrency errors - Version 3.3.0: Fixed iSCSI target limitation error - Version 4.0.0: Added manage/unmanage features - Version 4.1.0: Fixed XML parser checks on blank options - Version 4.2.0: Fixed SSH and cluster_admin_ip0 verification - Version 4.3.0: Fixed attachment with os-brick 1.0.0 - Version 5.0.0: Code cleaning up - New communication interface between the driver and HNAS - Removed the option to use local SSC (ssh_enabled=False) - Updated to use versioned objects - Changed the class name to HNASISCSIDriver - Deprecated XML config file - Fixed driver stats reporting -""" - - # ThirdPartySystems wiki page - CI_WIKI_NAME = "Hitachi_HNAS_CI" - VERSION = HNAS_ISCSI_VERSION - - SUPPORTED = False - - def __init__(self, *args, **kwargs): - """Initializes and reads different config parameters.""" - super(HNASISCSIDriver, self).__init__(*args, **kwargs) - msg = _("The Hitachi NAS iSCSI driver is deprecated and will be " - "removed in a future release.") - versionutils.report_deprecated_feature(LOG, msg) - self.configuration = kwargs.get('configuration', None) - self.context = {} - self.config = {} - - service_parameters = ['volume_type', 'hdp', 'iscsi_ip'] - optional_parameters = ['ssc_cmd', 'cluster_admin_ip0', - 'chap_enabled'] - - if self.configuration: - self.configuration.append_config_values( - hnas_utils.drivers_common_opts) - self.configuration.append_config_values(iSCSI_OPTS) - - # Trying to get HNAS configuration from cinder.conf - self.config = hnas_utils.read_cinder_conf( - self.configuration, 'iscsi') - - # If HNAS configuration are not set on cinder.conf, tries to use - # the deprecated XML configuration file - if not self.config: - self.config = hnas_utils.read_xml_config( - self.configuration.hds_hnas_iscsi_config_file, - service_parameters, - optional_parameters) - - self.reserved_percentage = ( - self.configuration.safe_get('reserved_percentage')) - self.max_osr = ( - self.configuration.safe_get('max_over_subscription_ratio')) - - self.backend = hnas_backend.HNASSSHBackend(self.config) - - def _get_service(self, volume): - """Gets the available service parameters. - - Get the available service parameters for a given volume using its - type. - - :param volume: dictionary volume reference - :returns: HDP (file system) related to the service or error if no - configuration is found. - :raises: ParameterNotFound - """ - LOG.debug("Available services: %(svc)s.", - {'svc': self.config['services'].keys()}) - label = utils.extract_host(volume.host, level='pool') - - if label in self.config['services'].keys(): - svc = self.config['services'][label] - LOG.info(_LI("Using service label: %(lbl)s."), {'lbl': label}) - return svc['hdp'] - else: - LOG.error(_LE("No configuration found for service: %(lbl)s."), - {'lbl': label}) - raise exception.ParameterNotFound(param=label) - - def _get_service_target(self, volume): - """Gets the available service parameters - - Gets the available service parameters for a given volume using its - type. - :param volume: dictionary volume reference - :returns: service target information or raises error - :raises: NoMoreTargets - """ - fs_label = self._get_service(volume) - evs_id = self.backend.get_evs(fs_label) - - svc_label = utils.extract_host(volume.host, level='pool') - svc = self.config['services'][svc_label] - - lu_info = self.backend.check_lu(volume.name, fs_label) - - # The volume is already mapped to a LU, so no need to create any - # targets - if lu_info['mapped']: - service = ( - svc['iscsi_ip'], svc['iscsi_port'], svc['evs'], svc['port'], - fs_label, lu_info['tgt']['alias'], lu_info['tgt']['secret']) - LOG.info(_LI("Volume %(vol_name)s already mapped on target " - "%(tgt)s to LUN %(lunid)s."), - {'vol_name': volume.name, 'tgt': lu_info['tgt']['alias'], - 'lunid': lu_info['id']}) - return service - - # Each EVS can have up to 32 targets. Each target can have up to 32 - # LUs attached and have the name format 'evs-tgt<0-N>'. We run - # from the first 'evs1-tgt0' until we find a target that is not already - # created in the BE or is created but have slots to place new LUs. - tgt_alias = '' - for i in range(0, MAX_HNAS_ISCSI_TARGETS): - tgt_alias = 'evs' + evs_id + '-tgt' + six.text_type(i) - tgt = self.backend.check_target(fs_label, tgt_alias) - - if (tgt['found'] and - len(tgt['tgt']['lus']) < MAX_HNAS_LUS_PER_TARGET or - not tgt['found']): - # Target exists and has free space or, target does not exist - # yet. Proceed and use the target or create a target using this - # name. - break - else: - # If we've got here, we run out of targets, raise and go away. - LOG.error(_LE("No more targets available.")) - raise exception.NoMoreTargets(param=tgt_alias) - - LOG.info(_LI("Using target label: %(tgt)s."), {'tgt': tgt_alias}) - - # Check if we have a secret stored for this target so we don't have to - # go to BE on every query - if 'targets' not in self.config.keys(): - self.config['targets'] = {} - - if tgt_alias not in self.config['targets'].keys(): - self.config['targets'][tgt_alias] = {} - - tgt_info = self.config['targets'][tgt_alias] - - # HNAS - one time lookup - # see if the client supports CHAP authentication and if - # iscsi_secret has already been set, retrieve the secret if - # available, otherwise generate and store - if self.config['chap_enabled']: - # CHAP support is enabled. Tries to get the target secret. - if 'iscsi_secret' not in tgt_info.keys(): - LOG.info(_LI("Retrieving secret for service: %(tgt)s."), - {'tgt': tgt_alias}) - out = self.backend.get_target_secret(tgt_alias, fs_label) - tgt_info['iscsi_secret'] = out - - # CHAP supported and the target has no secret yet. So, the - # secret is created for the target - if tgt_info['iscsi_secret'] == "": - random_secret = utils.generate_password()[0:15] - tgt_info['iscsi_secret'] = random_secret - - LOG.info(_LI("Set tgt CHAP secret for service: %(tgt)s."), - {'tgt': tgt_alias}) - else: - # We set blank password when the client does not - # support CHAP. Later on, if the client tries to create a new - # target that does not exist in the backend, we check for this - # value and use a temporary dummy password. - if 'iscsi_secret' not in tgt_info.keys(): - # Warns in the first time - LOG.info(_LI("CHAP authentication disabled.")) - - tgt_info['iscsi_secret'] = "''" - - # If the target does not exist, it should be created - if not tgt['found']: - self.backend.create_target(tgt_alias, fs_label, - tgt_info['iscsi_secret']) - elif (tgt['tgt']['secret'] == "" and - self.config['chap_enabled']): - # The target exists, has no secret and chap is enabled - self.backend.set_target_secret(tgt_alias, fs_label, - tgt_info['iscsi_secret']) - - if 'tgt_iqn' not in tgt_info: - LOG.info(_LI("Retrieving IQN for service: %(tgt)s."), - {'tgt': tgt_alias}) - - out = self.backend.get_target_iqn(tgt_alias, fs_label) - tgt_info['tgt_iqn'] = out - - self.config['targets'][tgt_alias] = tgt_info - - service = (svc['iscsi_ip'], svc['iscsi_port'], svc['evs'], svc['port'], - fs_label, tgt_alias, tgt_info['iscsi_secret']) - - return service - - def _get_stats(self): - """Get FS stats from HNAS. - - :returns: dictionary with the stats from HNAS - _stats['pools'] = { - 'total_capacity_gb': total size of the pool, - 'free_capacity_gb': the available size, - 'QoS_support': bool to indicate if QoS is supported, - 'reserved_percentage': percentage of size reserved, - 'max_over_subscription_ratio': oversubscription rate, - 'thin_provisioning_support': thin support (True), - 'reserved_percentage': reserved percentage - } - """ - hnas_stat = {} - be_name = self.configuration.safe_get('volume_backend_name') - hnas_stat["volume_backend_name"] = be_name or 'HNASISCSIDriver' - hnas_stat["vendor_name"] = 'Hitachi' - hnas_stat["driver_version"] = HNAS_ISCSI_VERSION - hnas_stat["storage_protocol"] = 'iSCSI' - - for pool in self.pools: - fs_info = self.backend.get_fs_info(pool['fs']) - pool['provisioned_capacity_gb'] = fs_info['provisioned_capacity'] - pool['total_capacity_gb'] = (float(fs_info['total_size'])) - pool['free_capacity_gb'] = ( - float(fs_info['total_size']) - float(fs_info['used_size'])) - pool['QoS_support'] = 'False' - pool['reserved_percentage'] = self.reserved_percentage - pool['max_over_subscription_ratio'] = self.max_osr - pool['thin_provisioning_support'] = True - - hnas_stat['pools'] = self.pools - - LOG.debug("stats: %(stat)s.", {'stat': hnas_stat}) - return hnas_stat - - def _check_fs_list(self): - """Verifies the FSs list in HNAS. - - Verify that all FSs specified in the configuration files actually - exists on the storage. - """ - fs_list = self.config['fs'].keys() - - for fs in fs_list: - if not self.backend.get_fs_info(fs): - msg = (_("File system not found or not mounted: %(fs)s") % - {'fs': fs}) - LOG.error(msg) - raise exception.ParameterNotFound(param=msg) - - def _check_pool_and_fs(self, volume, fs_label): - """Validates pool and file system of a volume being managed. - - Checks if the file system for the volume-type chosen matches the - one passed in the volume reference. Also, checks if the pool - for the volume type matches the pool for the host passed. - - :param volume: Reference to the volume. - :param fs_label: Label of the file system. - :raises: ManageExistingVolumeTypeMismatch - """ - pool_from_vol_type = hnas_utils.get_pool(self.config, volume) - - if (pool_from_vol_type == 'default' and - 'default' not in self.config['services']): - msg = (_("Failed to manage existing volume %(volume)s because the " - "chosen volume type %(vol_type)s does not have a " - "service_label configured in its extra-specs and there " - "is no pool configured with hnas_svcX_volume_type as " - "'default' in cinder.conf.") % - {'volume': volume.id, - 'vol_type': getattr(volume.volume_type, 'id', None)}) - LOG.error(msg) - raise exception.ManageExistingVolumeTypeMismatch(reason=msg) - - pool = self.config['services'][pool_from_vol_type]['hdp'] - if pool != fs_label: - msg = (_("Failed to manage existing volume because the " - "pool %(pool)s of the volume type chosen does not " - "match the file system %(fs_label)s passed in the " - "volume reference.") - % {'pool': pool, 'fs_label': fs_label}) - LOG.error(msg) - raise exception.ManageExistingVolumeTypeMismatch(reason=msg) - - pool_from_host = utils.extract_host(volume.host, level='pool') - - if pool_from_host != pool_from_vol_type: - msg = (_("Failed to manage existing volume because the pool " - "%(pool)s of the volume type chosen does not match the " - "pool %(pool_host)s of the host.") % - {'pool': pool_from_vol_type, 'pool_host': pool_from_host}) - LOG.error(msg) - raise exception.ManageExistingVolumeTypeMismatch(reason=msg) - - def _get_info_from_vol_ref(self, vol_ref): - """Gets information from the volume reference. - - Returns the information (File system and volume name) taken from - the volume reference. - - :param vol_ref: existing volume to take under management - :returns: the file system label and the volume name or raises error - :raises: ManageExistingInvalidReference - """ - vol_info = vol_ref.strip().split('/') - - if len(vol_info) == 2 and '' not in vol_info: - fs_label = vol_info[0] - vol_name = vol_info[1] - - return fs_label, vol_name - else: - msg = _("The reference to the volume in the backend should have " - "the format file_system/volume_name (volume_name cannot " - "contain '/')") - LOG.error(msg) - raise exception.ManageExistingInvalidReference( - existing_ref=vol_ref, reason=msg) - - def check_for_setup_error(self): - pass - - def do_setup(self, context): - """Sets up and verify Hitachi HNAS storage connection.""" - self.context = context - self._check_fs_list() - - version_info = self.backend.get_version() - LOG.info(_LI("HNAS iSCSI driver.")) - LOG.info(_LI("HNAS model: %(mdl)s"), {'mdl': version_info['model']}) - LOG.info(_LI("HNAS version: %(version)s"), - {'version': version_info['version']}) - LOG.info(_LI("HNAS hardware: %(hw)s"), - {'hw': version_info['hardware']}) - LOG.info(_LI("HNAS S/N: %(sn)s"), {'sn': version_info['serial']}) - service_list = self.config['services'].keys() - for svc in service_list: - svc = self.config['services'][svc] - pool = {} - pool['pool_name'] = svc['pool_name'] - pool['service_label'] = svc['pool_name'] - pool['fs'] = svc['hdp'] - - self.pools.append(pool) - - LOG.debug("Configured pools: %(pool)s", {'pool': self.pools}) - - evs_info = self.backend.get_evs_info() - LOG.info(_LI("Configured EVSs: %(evs)s"), {'evs': evs_info}) - - for svc in self.config['services'].keys(): - svc_ip = self.config['services'][svc]['iscsi_ip'] - if svc_ip in evs_info.keys(): - LOG.info(_LI("iSCSI portal found for service: %(svc_ip)s"), - {'svc_ip': svc_ip}) - self.config['services'][svc]['evs'] = ( - evs_info[svc_ip]['evs_number']) - self.config['services'][svc]['iscsi_port'] = '3260' - self.config['services'][svc]['port'] = '0' - else: - LOG.error(_LE("iSCSI portal not found " - "for service: %(svc)s"), {'svc': svc_ip}) - raise exception.InvalidParameterValue(err=svc_ip) - LOG.info(_LI("HNAS iSCSI Driver loaded successfully.")) - - def ensure_export(self, context, volume): - pass - - def create_export(self, context, volume, connector): - pass - - def remove_export(self, context, volume): - pass - - @cinder_utils.trace - def create_volume(self, volume): - """Creates a LU on HNAS. - - :param volume: dictionary volume reference - :returns: the volume provider location - """ - fs = self._get_service(volume) - size = six.text_type(volume.size) - - self.backend.create_lu(fs, size, volume.name) - - return {'provider_location': self._get_provider_location(volume)} - - @cinder_utils.trace - def create_cloned_volume(self, dst, src): - """Creates a clone of a volume. - - :param dst: dictionary destination volume reference - :param src: dictionary source volume reference - :returns: the provider location of the extended volume - """ - fs_label = self._get_service(dst) - - self.backend.create_cloned_lu(src.name, fs_label, dst.name) - - if src.size < dst.size: - LOG.debug("Increasing dest size from %(old_size)s to " - "%(new_size)s", - {'old_size': src.size, 'new_size': dst.size}) - self.extend_volume(dst, dst.size) - - return {'provider_location': self._get_provider_location(dst)} - - @cinder_utils.trace - def extend_volume(self, volume, new_size): - """Extends an existing volume. - - :param volume: dictionary volume reference - :param new_size: int size in GB to extend - """ - fs = self._get_service(volume) - self.backend.extend_lu(fs, new_size, volume.name) - - @cinder_utils.trace - def delete_volume(self, volume): - """Deletes the volume on HNAS. - - :param volume: dictionary volume reference - """ - fs = self._get_service(volume) - self.backend.delete_lu(fs, volume.name) - - @cinder_utils.synchronized('volume_mapping') - @cinder_utils.trace - def initialize_connection(self, volume, connector): - """Maps the created volume to connector['initiator']. - - :param volume: dictionary volume reference - :param connector: dictionary connector reference - :returns: The connection information - :raises: ISCSITargetAttachFailed - """ - service_info = self._get_service_target(volume) - (ip, ipp, evs, port, _fs, tgtalias, secret) = service_info - - try: - conn = self.backend.add_iscsi_conn(volume.name, _fs, port, - tgtalias, - connector['initiator']) - - except processutils.ProcessExecutionError: - msg = (_("Error attaching volume %(vol)s. " - "Target limit might be reached!") % {'vol': volume.id}) - LOG.error(msg) - raise exception.ISCSITargetAttachFailed(volume_id=volume.id) - - hnas_portal = ip + ':' + ipp - lu_id = six.text_type(conn['lu_id']) - fulliqn = conn['iqn'] - tgt = (hnas_portal + ',' + tgtalias + ',' + - volume.provider_location + ',' + evs + ',' + - port + ',' + lu_id) - - LOG.info(_LI("initiate: connection %(tgt)s"), {'tgt': tgt}) - - properties = {} - properties['provider_location'] = tgt - properties['target_discovered'] = False - properties['target_portal'] = hnas_portal - properties['target_iqn'] = fulliqn - properties['target_lu'] = int(lu_id) - properties['volume_id'] = volume.id - properties['auth_username'] = connector['initiator'] - - if self.config['chap_enabled']: - properties['auth_method'] = 'CHAP' - properties['auth_password'] = secret - - conn_info = {'driver_volume_type': 'iscsi', 'data': properties} - - return conn_info - - @cinder_utils.synchronized('volume_mapping') - @cinder_utils.trace - def terminate_connection(self, volume, connector, **kwargs): - """Terminate a connection to a volume. - - :param volume: dictionary volume reference - :param connector: dictionary connector reference - """ - service_info = self._get_service_target(volume) - (ip, ipp, evs, port, fs, tgtalias, secret) = service_info - lu_info = self.backend.check_lu(volume.name, fs) - - self.backend.del_iscsi_conn(evs, tgtalias, lu_info['id']) - - @cinder_utils.trace - def create_volume_from_snapshot(self, volume, snapshot): - """Creates a volume from a snapshot. - - :param volume: dictionary volume reference - :param snapshot: dictionary snapshot reference - :returns: the provider location of the snapshot - """ - fs = self._get_service(volume) - - self.backend.create_cloned_lu(snapshot.name, fs, volume.name) - - return {'provider_location': self._get_provider_location(snapshot)} - - @cinder_utils.trace - def create_snapshot(self, snapshot): - """Creates a snapshot. - - :param snapshot: dictionary snapshot reference - :returns: the provider location of the snapshot - """ - fs = self._get_service(snapshot.volume) - - self.backend.create_cloned_lu(snapshot.volume_name, fs, snapshot.name) - - return {'provider_location': self._get_provider_location(snapshot)} - - @cinder_utils.trace - def delete_snapshot(self, snapshot): - """Deletes a snapshot. - - :param snapshot: dictionary snapshot reference - """ - fs = self._get_service(snapshot.volume) - self.backend.delete_lu(fs, snapshot.name) - - def get_volume_stats(self, refresh=False): - """Gets the volume driver stats. - - :param refresh: if refresh is True, the driver_stats is updated - :returns: the driver stats - """ - if refresh: - self.driver_stats = self._get_stats() - - return self.driver_stats - - @cinder_utils.trace - def manage_existing_get_size(self, volume, existing_vol_ref): - """Gets the size to manage_existing. - - Returns the size of volume to be managed by manage_existing. - - :param volume: cinder volume to manage - :param existing_vol_ref: existing volume to take under management - :returns: the size of the volume to be managed or raises error - :raises: ManageExistingInvalidReference - """ - # Check if the reference is valid. - if 'source-name' not in existing_vol_ref: - reason = _('Reference must contain source-name element.') - raise exception.ManageExistingInvalidReference( - existing_ref=existing_vol_ref, reason=reason) - - fs_label, vol_name = ( - self._get_info_from_vol_ref(existing_vol_ref['source-name'])) - - LOG.debug("File System: %(fs_label)s " - "Volume name: %(vol_name)s.", - {'fs_label': fs_label, 'vol_name': vol_name}) - - if utils.check_already_managed_volume(vol_name): - raise exception.ManageExistingAlreadyManaged(volume_ref=vol_name) - - lu_info = self.backend.get_existing_lu_info(vol_name, fs_label) - - if lu_info != {}: - return lu_info['size'] - else: - raise exception.ManageExistingInvalidReference( - existing_ref=existing_vol_ref, - reason=_('Volume not found on configured storage backend. ' - 'If your volume name contains "/", please rename it ' - 'and try to manage again.')) - - @cinder_utils.trace - def manage_existing(self, volume, existing_vol_ref): - """Manages an existing volume. - - The specified Cinder volume is to be taken into Cinder management. - The driver will verify its existence and then rename it to the - new Cinder volume name. It is expected that the existing volume - reference is a File System and some volume_name; - e.g., openstack/vol_to_manage - - :param volume: cinder volume to manage - :param existing_vol_ref: driver specific information used to identify a - volume - :returns: the provider location of the volume managed - """ - LOG.info(_LI("Asked to manage ISCSI volume %(vol)s, with vol " - "ref %(ref)s."), {'vol': volume.id, - 'ref': existing_vol_ref['source-name']}) - - fs_label, vol_name = ( - self._get_info_from_vol_ref(existing_vol_ref['source-name'])) - - if volume.volume_type is not None: - self._check_pool_and_fs(volume, fs_label) - - self.backend.rename_existing_lu(fs_label, vol_name, volume.name) - - LOG.info(_LI("Set newly managed Cinder volume name to %(name)s."), - {'name': volume.name}) - - return {'provider_location': self._get_provider_location(volume)} - - @cinder_utils.trace - def unmanage(self, volume): - """Unmanages a volume from cinder. - - Removes the specified volume from Cinder management. - Does not delete the underlying backend storage object. A log entry - will be made to notify the admin that the volume is no longer being - managed. - - :param volume: cinder volume to unmanage - """ - fslabel = self._get_service(volume) - new_name = 'unmanage-' + volume.name - vol_path = fslabel + '/' + volume.name - - self.backend.rename_existing_lu(fslabel, volume.name, new_name) - - LOG.info(_LI("The volume with path %(old)s is no longer being managed " - "by Cinder. However, it was not deleted and can be found " - "with the new name %(cr)s on backend."), - {'old': vol_path, 'cr': new_name}) - - def _get_provider_location(self, volume): - """Gets the provider location of a given volume - - :param volume: dictionary volume reference - :returns: the provider_location related to the volume - """ - return self.backend.get_version()['mac'] + '.' + volume.name diff --git a/cinder/volume/drivers/hitachi/hnas_nfs.py b/cinder/volume/drivers/hitachi/hnas_nfs.py index 13186defc5f..9a58c6ab712 100644 --- a/cinder/volume/drivers/hitachi/hnas_nfs.py +++ b/cinder/volume/drivers/hitachi/hnas_nfs.py @@ -105,7 +105,7 @@ class HNASNFSDriver(nfs.NfsDriver): # Trying to get HNAS configuration from cinder.conf self.config = hnas_utils.read_cinder_conf( - self.configuration, 'nfs') + self.configuration) # If HNAS configuration are not set on cinder.conf, tries to use # the deprecated XML configuration file diff --git a/cinder/volume/drivers/hitachi/hnas_utils.py b/cinder/volume/drivers/hitachi/hnas_utils.py index 0778478a728..72d051e41b7 100644 --- a/cinder/volume/drivers/hitachi/hnas_utils.py +++ b/cinder/volume/drivers/hitachi/hnas_utils.py @@ -86,7 +86,7 @@ CONF = cfg.CONF CONF.register_opts(drivers_common_opts) -def _check_conf_params(config, pool_name, dv_type, idx): +def _check_conf_params(config, pool_name, idx): """Validates if the configuration on cinder.conf is complete. :param config: Dictionary with the driver configurations @@ -134,15 +134,6 @@ def _check_conf_params(config, pool_name, dv_type, idx): LOG.error(msg) raise exception.InvalidParameterValue(err=msg) - if (dv_type == 'iscsi' and - config['services'][pool_name]['iscsi_ip'] is None): - msg = (_("The config parameter " - "hnas_svc%(idx)s_iscsi_ip is not set " - "in the cinder.conf. Note that you need to " - "have at least one pool configured.") % {'idx': idx}) - LOG.error(msg) - raise exception.InvalidParameterValue(err=msg) - def _xml_read(root, element, check=None): """Read an xml element. @@ -183,7 +174,7 @@ def read_xml_config(xml_config_file, svc_params, optional_params): :param xml_config_file: string filename containing XML configuration :param svc_params: parameters to configure the services - ['volume_type', 'hdp', 'iscsi_ip'] + ['volume_type', 'hdp'] :param optional_params: parameters to configure that are not mandatory ['ssc_cmd', 'cluster_admin_ip0', 'chap_enabled'] """ @@ -208,13 +199,13 @@ def read_xml_config(xml_config_file, svc_params, optional_params): LOG.error(msg) raise exception.ConfigNotFound(message=msg) - # mandatory parameters for NFS and iSCSI + # mandatory parameters for NFS config = {} arg_prereqs = ['mgmt_ip0', 'username'] for req in arg_prereqs: config[req] = _xml_read(root, req, 'check') - # optional parameters for NFS and iSCSI + # optional parameters for NFS for req in optional_params: config[req] = _xml_read(root, req) if config[req] is None and HNAS_DEFAULT_CONFIG.get(req) is not None: @@ -279,7 +270,7 @@ def get_pool(config, volume): return 'default' -def read_cinder_conf(config_opts, dv_type): +def read_cinder_conf(config_opts): """Reads cinder.conf Gets the driver specific information set on cinder.conf configuration @@ -295,7 +286,7 @@ def read_cinder_conf(config_opts, dv_type): config['services'] = {} config['fs'] = {} mandatory_parameters = ['username', 'password', 'mgmt_ip0'] - optional_parameters = ['ssc_cmd', 'chap_enabled', + optional_parameters = ['ssc_cmd', 'ssh_port', 'cluster_admin_ip0', 'ssh_private_key'] @@ -334,14 +325,9 @@ def read_cinder_conf(config_opts, dv_type): config['services'][svc_pool_name]['hdp'] = svc_hdp config['services'][svc_pool_name]['pool_name'] = svc_pool_name - if dv_type == 'iscsi': - svc_ip = (config_opts.safe_get( - 'hnas_svc%(idx)s_iscsi_ip' % {'idx': idx})) - config['services'][svc_pool_name]['iscsi_ip'] = svc_ip - config['services'][svc_pool_name]['label'] = ( 'svc_%(idx)s' % {'idx': idx}) # Checking to ensure that the pools configurations are complete - _check_conf_params(config, svc_pool_name, dv_type, idx) + _check_conf_params(config, svc_pool_name, idx) return config diff --git a/cinder/volume/manager.py b/cinder/volume/manager.py index b89a8c5f0f1..d2316db7809 100644 --- a/cinder/volume/manager.py +++ b/cinder/volume/manager.py @@ -147,12 +147,8 @@ CONF.register_opts(volume_manager_opts) MAPPING = { 'cinder.volume.drivers.hds.nfs.HDSNFSDriver': 'cinder.volume.drivers.hitachi.hnas_nfs.HNASNFSDriver', - 'cinder.volume.drivers.hds.iscsi.HDSISCSIDriver': - 'cinder.volume.drivers.hitachi.hnas_iscsi.HNASISCSIDriver', 'cinder.volume.drivers.hitachi.hnas_nfs.HDSNFSDriver': 'cinder.volume.drivers.hitachi.hnas_nfs.HNASNFSDriver', - 'cinder.volume.drivers.hitachi.hnas_iscsi.HDSISCSIDriver': - 'cinder.volume.drivers.hitachi.hnas_iscsi.HNASISCSIDriver', 'cinder.volume.drivers.ibm.xiv_ds8k': 'cinder.volume.drivers.ibm.ibm_storage', 'cinder.volume.drivers.emc.scaleio': diff --git a/releasenotes/notes/hnas-remove-iscsi-driver-419e9c08133f9f0a.yaml b/releasenotes/notes/hnas-remove-iscsi-driver-419e9c08133f9f0a.yaml new file mode 100644 index 00000000000..9d01d0d1f9a --- /dev/null +++ b/releasenotes/notes/hnas-remove-iscsi-driver-419e9c08133f9f0a.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - The Hitachi NAS Platform iSCSI driver was marked as not supported in the + Ocata realease and has now been removed.