cinder/cinder/tests/unit/volume/drivers/hitachi/test_hitachi_hnas_iscsi.py

620 lines
24 KiB
Python

# 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',
mock.Mock(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',
mock.Mock(return_value='1'))
self.mock_object(HNASSSHBackend, 'check_lu',
mock.Mock(return_value=lu_info))
self.mock_object(HNASSSHBackend, 'check_target',
mock.Mock(return_value=tgt))
self.mock_object(HNASSSHBackend, 'get_target_secret',
mock.Mock(return_value=''))
self.mock_object(HNASSSHBackend, 'set_target_secret')
self.mock_object(HNASSSHBackend, 'get_target_iqn',
mock.Mock(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',
mock.Mock(return_value='1'))
self.mock_object(HNASSSHBackend, 'check_lu',
mock.Mock(return_value=lu_info))
self.mock_object(HNASSSHBackend, 'check_target',
mock.Mock(return_value=tgt))
self.mock_object(HNASSSHBackend, 'get_target_iqn',
mock.Mock(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',
mock.Mock(return_value='1'))
self.mock_object(HNASSSHBackend, 'check_lu',
mock.Mock(return_value=lu_info))
self.mock_object(HNASSSHBackend, 'check_target',
mock.Mock(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',
mock.Mock(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',
mock.Mock(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',
mock.Mock(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',
mock.Mock(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',
mock.Mock(return_value=True))
self.mock_object(HNASSSHBackend, 'get_evs_info',
mock.Mock(return_value=evs_info))
self.mock_object(HNASSSHBackend, 'get_version',
mock.Mock(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',
mock.Mock(return_value=True))
self.mock_object(HNASSSHBackend, 'get_evs_info',
mock.Mock(return_value=evs_info))
self.mock_object(HNASSSHBackend, 'get_version',
mock.Mock(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',
mock.Mock(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',
mock.Mock(return_value=2))
self.mock_object(HNASSSHBackend, 'check_lu',
mock.Mock(return_value=lu_info))
self.mock_object(HNASSSHBackend, 'add_iscsi_conn',
mock.Mock(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',
mock.Mock(return_value=2))
self.mock_object(HNASSSHBackend, 'check_lu',
mock.Mock(return_value=lu_info))
self.mock_object(HNASSSHBackend, 'add_iscsi_conn',
mock.Mock(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',
mock.Mock(return_value=2))
self.mock_object(HNASSSHBackend, 'check_lu',
mock.Mock(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',
mock.Mock(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',
mock.Mock(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',
mock.Mock(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',
mock.Mock(return_value=lu_info))
self.mock_object(volume_types, 'get_volume_type',
mock.Mock(return_value=self.volume_type))
self.mock_object(HNASSSHBackend, 'create_cloned_lu')
self.mock_object(HNASSSHBackend, 'get_version',
mock.Mock(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',
mock.Mock(return_value=self.volume_type))
self.mock_object(HNASSSHBackend, 'get_existing_lu_info',
mock.Mock(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',
mock.Mock(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',
mock.Mock(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',
mock.Mock(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',
mock.Mock(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',
mock.Mock(return_value=metadata))
self.mock_object(HNASSSHBackend, 'get_version',
mock.Mock(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)