4b092e8d9d
Since NVMET target driver will be implemented it would be good to use the same config options both for NVMEoF and iSCSI targets. That's why this path renames iscsi_ip_address, iscsi_port, iscsi_helper iscsi_target_prefix, iscsi_protocol to target_ip_address, target_port, target_helper, target_prefix, and target_protocol according to current deprecation policy. Change-Id: I5231f8fe3399deb9c57e6efb121d0d008dc9c7f4 Related-Blueprint: nvme-target-cli
355 lines
15 KiB
Python
355 lines
15 KiB
Python
# 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.tests.unit.targets import targets_fixture as tf
|
|
from cinder import utils
|
|
from cinder.volume.targets import lio
|
|
|
|
|
|
class TestLioAdmDriver(tf.TargetDriverFixture):
|
|
|
|
def setUp(self):
|
|
super(TestLioAdmDriver, self).setUp()
|
|
|
|
with mock.patch.object(lio.LioAdm, '_verify_rtstool'):
|
|
self.target = lio.LioAdm(root_helper=utils.get_root_helper(),
|
|
configuration=self.configuration)
|
|
|
|
@mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
|
|
@mock.patch.object(lio.LioAdm, '_persist_configuration')
|
|
@mock.patch('cinder.utils.execute')
|
|
def test_get_target(self, mexecute, mpersist_cfg, mlock_exec):
|
|
mexecute.return_value = (self.test_vol, None)
|
|
self.assertEqual(self.test_vol, self.target._get_target(self.test_vol))
|
|
self.assertFalse(mpersist_cfg.called)
|
|
expected_args = ('cinder-rtstool', 'get-targets')
|
|
mlock_exec.assert_called_once_with(*expected_args, run_as_root=True)
|
|
mexecute.assert_called_once_with(*expected_args, run_as_root=True)
|
|
|
|
def test_get_iscsi_target(self):
|
|
ctxt = context.get_admin_context()
|
|
expected = 0
|
|
self.assertEqual(expected,
|
|
self.target._get_iscsi_target(ctxt,
|
|
self.testvol['id']))
|
|
|
|
def test_get_target_and_lun(self):
|
|
lun = 0
|
|
iscsi_target = 0
|
|
ctxt = context.get_admin_context()
|
|
expected = (iscsi_target, lun)
|
|
self.assertEqual(expected,
|
|
self.target._get_target_and_lun(ctxt, self.testvol))
|
|
|
|
@mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
|
|
@mock.patch.object(lio.LioAdm, '_persist_configuration')
|
|
@mock.patch('cinder.utils.execute')
|
|
@mock.patch.object(lio.LioAdm, '_get_target')
|
|
def test_create_iscsi_target(self, mget_target, mexecute, mpersist_cfg,
|
|
mlock_exec):
|
|
|
|
mget_target.return_value = 1
|
|
# create_iscsi_target sends volume_name instead of volume_id on error
|
|
self.assertEqual(
|
|
1,
|
|
self.target.create_iscsi_target(
|
|
self.test_vol,
|
|
1,
|
|
0,
|
|
self.fake_volumes_dir))
|
|
mpersist_cfg.assert_called_once_with(self.VOLUME_NAME)
|
|
mexecute.assert_called_once_with(
|
|
'cinder-rtstool',
|
|
'create',
|
|
self.fake_volumes_dir,
|
|
self.test_vol,
|
|
'',
|
|
'',
|
|
self.target.iscsi_protocol == 'iser',
|
|
run_as_root=True)
|
|
|
|
@mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
|
|
@mock.patch.object(lio.LioAdm, '_persist_configuration')
|
|
@mock.patch.object(utils, 'execute')
|
|
@mock.patch.object(lio.LioAdm, '_get_target', return_value=1)
|
|
def test_create_iscsi_target_port_ip(self, mget_target, mexecute,
|
|
mpersist_cfg, mlock_exec):
|
|
ip = '10.0.0.15'
|
|
port = 3261
|
|
|
|
self.assertEqual(
|
|
1,
|
|
self.target.create_iscsi_target(
|
|
name=self.test_vol,
|
|
tid=1,
|
|
lun=0,
|
|
path=self.fake_volumes_dir,
|
|
**{'portals_port': port, 'portals_ips': [ip]}))
|
|
|
|
expected_args = (
|
|
'cinder-rtstool',
|
|
'create',
|
|
self.fake_volumes_dir,
|
|
self.test_vol,
|
|
'',
|
|
'',
|
|
self.target.iscsi_protocol == 'iser',
|
|
'-p%s' % port,
|
|
'-a' + ip)
|
|
|
|
mlock_exec.assert_any_call(*expected_args, run_as_root=True)
|
|
mexecute.assert_any_call(*expected_args, run_as_root=True)
|
|
mpersist_cfg.assert_called_once_with(self.VOLUME_NAME)
|
|
|
|
@mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
|
|
@mock.patch.object(lio.LioAdm, '_persist_configuration')
|
|
@mock.patch.object(utils, 'execute')
|
|
@mock.patch.object(lio.LioAdm, '_get_target', return_value=1)
|
|
def test_create_iscsi_target_port_ips(self, mget_target, mexecute,
|
|
mpersist_cfg, mlock_exec):
|
|
test_vol = 'iqn.2010-10.org.openstack:' + self.VOLUME_NAME
|
|
ips = ['10.0.0.15', '127.0.0.1']
|
|
port = 3261
|
|
|
|
self.assertEqual(
|
|
1,
|
|
self.target.create_iscsi_target(
|
|
name=test_vol,
|
|
tid=1,
|
|
lun=0,
|
|
path=self.fake_volumes_dir,
|
|
**{'portals_port': port, 'portals_ips': ips}))
|
|
|
|
expected_args = (
|
|
'cinder-rtstool',
|
|
'create',
|
|
self.fake_volumes_dir,
|
|
test_vol,
|
|
'',
|
|
'',
|
|
self.target.iscsi_protocol == 'iser',
|
|
'-p%s' % port,
|
|
'-a' + ','.join(ips))
|
|
|
|
mlock_exec.assert_any_call(*expected_args, run_as_root=True)
|
|
mexecute.assert_any_call(*expected_args, run_as_root=True)
|
|
mpersist_cfg.assert_called_once_with(self.VOLUME_NAME)
|
|
|
|
@mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
|
|
@mock.patch.object(lio.LioAdm, '_persist_configuration')
|
|
@mock.patch('cinder.utils.execute',
|
|
side_effect=putils.ProcessExecutionError)
|
|
@mock.patch.object(lio.LioAdm, '_get_target')
|
|
def test_create_iscsi_target_already_exists(self, mget_target, mexecute,
|
|
mpersist_cfg, mlock_exec):
|
|
chap_auth = ('foo', 'bar')
|
|
self.assertRaises(exception.ISCSITargetCreateFailed,
|
|
self.target.create_iscsi_target,
|
|
self.test_vol,
|
|
1,
|
|
0,
|
|
self.fake_volumes_dir,
|
|
chap_auth)
|
|
self.assertFalse(mpersist_cfg.called)
|
|
expected_args = ('cinder-rtstool', 'create', self.fake_volumes_dir,
|
|
self.test_vol, chap_auth[0], chap_auth[1], False)
|
|
mlock_exec.assert_called_once_with(*expected_args, run_as_root=True)
|
|
mexecute.assert_called_once_with(*expected_args, run_as_root=True)
|
|
|
|
@mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
|
|
@mock.patch.object(lio.LioAdm, '_persist_configuration')
|
|
@mock.patch('cinder.utils.execute')
|
|
def test_remove_iscsi_target(self, mexecute, mpersist_cfg, mlock_exec):
|
|
# Test the normal case
|
|
self.target.remove_iscsi_target(0,
|
|
0,
|
|
self.testvol['id'],
|
|
self.testvol['name'])
|
|
expected_args = ('cinder-rtstool', 'delete',
|
|
self.iscsi_target_prefix + self.testvol['name'])
|
|
|
|
mlock_exec.assert_called_once_with(*expected_args, run_as_root=True)
|
|
mexecute.assert_called_once_with(*expected_args, run_as_root=True)
|
|
mpersist_cfg.assert_called_once_with(self.fake_volume_id)
|
|
|
|
# Test the failure case: putils.ProcessExecutionError
|
|
mlock_exec.reset_mock()
|
|
mpersist_cfg.reset_mock()
|
|
mexecute.side_effect = putils.ProcessExecutionError
|
|
self.assertRaises(exception.ISCSITargetRemoveFailed,
|
|
self.target.remove_iscsi_target,
|
|
0,
|
|
0,
|
|
self.testvol['id'],
|
|
self.testvol['name'])
|
|
mlock_exec.assert_called_once_with(*expected_args, run_as_root=True)
|
|
|
|
# Ensure there have been no calls to persist configuration
|
|
self.assertFalse(mpersist_cfg.called)
|
|
|
|
@mock.patch.object(lio.LioAdm, '_get_targets')
|
|
@mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
|
|
@mock.patch('cinder.utils.execute')
|
|
def test_ensure_export(self, mock_exec, mock_execute, mock_get_targets):
|
|
|
|
ctxt = context.get_admin_context()
|
|
mock_get_targets.return_value = None
|
|
self.target.ensure_export(ctxt,
|
|
self.testvol,
|
|
self.fake_volumes_dir)
|
|
|
|
expected_args = ('cinder-rtstool', 'restore')
|
|
mock_exec.assert_called_once_with(*expected_args, run_as_root=True)
|
|
|
|
@mock.patch.object(lio.LioAdm, '_get_targets')
|
|
@mock.patch.object(lio.LioAdm, '_restore_configuration')
|
|
def test_ensure_export_target_exist(self, mock_restore, mock_get_targets):
|
|
|
|
ctxt = context.get_admin_context()
|
|
mock_get_targets.return_value = 'target'
|
|
self.target.ensure_export(ctxt,
|
|
self.testvol,
|
|
self.fake_volumes_dir)
|
|
self.assertFalse(mock_restore.called)
|
|
|
|
@mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
|
|
@mock.patch.object(lio.LioAdm, '_persist_configuration')
|
|
@mock.patch('cinder.utils.execute')
|
|
@mock.patch.object(lio.LioAdm, '_get_iscsi_properties')
|
|
def test_initialize_connection(self, mock_get_iscsi, mock_execute,
|
|
mpersist_cfg, mlock_exec):
|
|
target_id = self.iscsi_target_prefix + 'volume-' + self.fake_volume_id
|
|
connector = {'initiator': 'fake_init'}
|
|
|
|
# Test the normal case
|
|
mock_get_iscsi.return_value = 'foo bar'
|
|
expected_return = {'driver_volume_type': 'iscsi',
|
|
'data': 'foo bar'}
|
|
self.assertEqual(expected_return,
|
|
self.target.initialize_connection(self.testvol,
|
|
connector))
|
|
|
|
expected_args = ('cinder-rtstool', 'add-initiator', target_id,
|
|
self.expected_iscsi_properties['auth_username'],
|
|
'2FE0CQ8J196R', connector['initiator'])
|
|
|
|
mlock_exec.assert_called_once_with(*expected_args, run_as_root=True)
|
|
mock_execute.assert_called_once_with(*expected_args, run_as_root=True)
|
|
mpersist_cfg.assert_called_once_with(self.fake_volume_id)
|
|
|
|
# Test the failure case: putils.ProcessExecutionError
|
|
mlock_exec.reset_mock()
|
|
mpersist_cfg.reset_mock()
|
|
mock_execute.side_effect = putils.ProcessExecutionError
|
|
self.assertRaises(exception.ISCSITargetAttachFailed,
|
|
self.target.initialize_connection,
|
|
self.testvol,
|
|
connector)
|
|
|
|
mlock_exec.assert_called_once_with(*expected_args, run_as_root=True)
|
|
|
|
# Ensure there have been no calls to persist configuration
|
|
self.assertFalse(mpersist_cfg.called)
|
|
|
|
@mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
|
|
@mock.patch.object(lio.LioAdm, '_persist_configuration')
|
|
@mock.patch('cinder.utils.execute')
|
|
def test_terminate_connection(self, mock_execute, mpersist_cfg,
|
|
mlock_exec):
|
|
|
|
target_id = self.iscsi_target_prefix + 'volume-' + self.fake_volume_id
|
|
|
|
connector = {'initiator': 'fake_init'}
|
|
self.target.terminate_connection(self.testvol,
|
|
connector)
|
|
expected_args = ('cinder-rtstool', 'delete-initiator', target_id,
|
|
connector['initiator'])
|
|
|
|
mlock_exec.assert_called_once_with(*expected_args, run_as_root=True)
|
|
mock_execute.assert_called_once_with(*expected_args, run_as_root=True)
|
|
mpersist_cfg.assert_called_once_with(self.fake_volume_id)
|
|
|
|
@mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
|
|
@mock.patch.object(lio.LioAdm, '_persist_configuration')
|
|
@mock.patch('cinder.utils.execute')
|
|
def test_terminate_connection_no_prov_loc(self,
|
|
mock_execute,
|
|
mpersist_cfg,
|
|
mlock_exec):
|
|
"""terminate_connection does nothing if provider_location is None"""
|
|
|
|
connector = {'initiator': 'fake_init'}
|
|
self.target.terminate_connection(self.testvol_no_prov_loc,
|
|
connector)
|
|
|
|
mlock_exec.assert_not_called()
|
|
mock_execute.assert_not_called()
|
|
mpersist_cfg.assert_not_called()
|
|
|
|
@mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
|
|
@mock.patch.object(lio.LioAdm, '_persist_configuration')
|
|
@mock.patch('cinder.utils.execute')
|
|
def test_terminate_connection_fail(self, mock_execute, mpersist_cfg,
|
|
mlock_exec):
|
|
|
|
target_id = self.iscsi_target_prefix + 'volume-' + self.fake_volume_id
|
|
mock_execute.side_effect = putils.ProcessExecutionError
|
|
connector = {'initiator': 'fake_init'}
|
|
self.assertRaises(exception.ISCSITargetDetachFailed,
|
|
self.target.terminate_connection,
|
|
self.testvol,
|
|
connector)
|
|
mlock_exec.assert_called_once_with('cinder-rtstool',
|
|
'delete-initiator', target_id,
|
|
connector['initiator'],
|
|
run_as_root=True)
|
|
self.assertFalse(mpersist_cfg.called)
|
|
|
|
def test_iscsi_protocol(self):
|
|
self.assertEqual('iscsi', self.target.iscsi_protocol)
|
|
|
|
@mock.patch.object(lio.LioAdm, '_get_target_and_lun', return_value=(1, 2))
|
|
@mock.patch.object(lio.LioAdm, 'create_iscsi_target', return_value=3)
|
|
@mock.patch.object(lio.LioAdm, '_get_target_chap_auth',
|
|
return_value=(mock.sentinel.user, mock.sentinel.pwd))
|
|
def test_create_export(self, mock_chap, mock_create, mock_get_target):
|
|
ctxt = context.get_admin_context()
|
|
result = self.target.create_export(ctxt, self.testvol_2,
|
|
self.fake_volumes_dir)
|
|
|
|
loc = (u'%(ip)s:%(port)d,3 %(prefix)s%(name)s 2' %
|
|
{'ip': self.configuration.target_ip_address,
|
|
'port': self.configuration.target_port,
|
|
'prefix': self.iscsi_target_prefix,
|
|
'name': self.testvol_2['name']})
|
|
|
|
expected_result = {
|
|
'location': loc,
|
|
'auth': 'CHAP %s %s' % (mock.sentinel.user, mock.sentinel.pwd),
|
|
}
|
|
|
|
self.assertEqual(expected_result, result)
|
|
|
|
mock_create.assert_called_once_with(
|
|
self.iscsi_target_prefix + self.testvol_2['name'],
|
|
1,
|
|
2,
|
|
self.fake_volumes_dir,
|
|
(mock.sentinel.user, mock.sentinel.pwd),
|
|
portals_ips=[self.configuration.target_ip_address],
|
|
portals_port=self.configuration.target_port)
|