cinder/cinder/tests/unit/targets/test_lio_driver.py

356 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.
from unittest 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)