[LVM] Restore target config during ensure_export
If server crash or reboot happened, LIO target configuration will be initialized after boot up a server. Currently, Cinder has a functionality to save LIO target configuration to save file at several checkpoint. If LIO target service is configured properly, LIO target configuration is restored by this service during boot up a server, but if not, existing in-use volumes would become inconsistent status after c-vol service starts. If there is no iSCSI target configuration during ensure_export, LIO dirver should restore the saved configuration file to avoid the problem. Closes-Bug: #1536248 Change-Id: I74d300ba26a08b6f423f5ed3e13495b73cfbbd52
This commit is contained in:
parent
61985202dd
commit
5cec4056eb
@ -225,6 +225,20 @@ def save_to_file(destination_file):
|
|||||||
{'file_path': destination_file, 'exc': exc})
|
{'file_path': destination_file, 'exc': exc})
|
||||||
|
|
||||||
|
|
||||||
|
def restore_from_file(configration_file):
|
||||||
|
rtsroot = rtslib_fb.root.RTSRoot()
|
||||||
|
# If configuration file is None, use rtslib default save file.
|
||||||
|
if not configration_file:
|
||||||
|
configration_file = rtslib_fb.root.default_save_file
|
||||||
|
|
||||||
|
try:
|
||||||
|
rtsroot.restore_from_file(configration_file)
|
||||||
|
except (OSError, IOError) as exc:
|
||||||
|
raise RtstoolError(_('Could not restore configuration file '
|
||||||
|
'%(file_path)s: %(exc)s'),
|
||||||
|
{'file_path': configration_file, 'exc': exc})
|
||||||
|
|
||||||
|
|
||||||
def parse_optional_create(argv):
|
def parse_optional_create(argv):
|
||||||
optional_args = {}
|
optional_args = {}
|
||||||
|
|
||||||
@ -316,6 +330,14 @@ def main(argv=None):
|
|||||||
save_to_file(destination_file)
|
save_to_file(destination_file)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
elif argv[1] == 'restore':
|
||||||
|
if len(argv) > 3:
|
||||||
|
usage()
|
||||||
|
|
||||||
|
configuration_file = argv[2] if len(argv) > 2 else None
|
||||||
|
restore_from_file(configuration_file)
|
||||||
|
return 0
|
||||||
|
|
||||||
else:
|
else:
|
||||||
usage()
|
usage()
|
||||||
|
|
||||||
|
@ -201,23 +201,30 @@ class TestLioAdmDriver(tf.TargetDriverFixture):
|
|||||||
# Ensure there have been no calls to persist configuration
|
# Ensure there have been no calls to persist configuration
|
||||||
self.assertFalse(mpersist_cfg.called)
|
self.assertFalse(mpersist_cfg.called)
|
||||||
|
|
||||||
@mock.patch.object(lio.LioAdm, '_get_target_chap_auth')
|
@mock.patch.object(lio.LioAdm, '_get_targets')
|
||||||
@mock.patch.object(lio.LioAdm, 'create_iscsi_target')
|
@mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
|
||||||
def test_ensure_export(self, _mock_create, mock_get_chap):
|
@mock.patch('cinder.utils.execute')
|
||||||
|
def test_ensure_export(self, mock_exec, mock_execute, mock_get_targets):
|
||||||
|
|
||||||
ctxt = context.get_admin_context()
|
ctxt = context.get_admin_context()
|
||||||
mock_get_chap.return_value = ('foo', 'bar')
|
mock_get_targets.return_value = None
|
||||||
self.target.ensure_export(ctxt,
|
self.target.ensure_export(ctxt,
|
||||||
self.testvol,
|
self.testvol,
|
||||||
self.fake_volumes_dir)
|
self.fake_volumes_dir)
|
||||||
|
|
||||||
_mock_create.assert_called_once_with(
|
expected_args = ('cinder-rtstool', 'restore')
|
||||||
self.iscsi_target_prefix + 'testvol',
|
mock_exec.assert_called_once_with(*expected_args, run_as_root=True)
|
||||||
0, 0, self.fake_volumes_dir, ('foo', 'bar'),
|
|
||||||
check_exit_code=False,
|
@mock.patch.object(lio.LioAdm, '_get_targets')
|
||||||
old_name=None,
|
@mock.patch.object(lio.LioAdm, '_restore_configuration')
|
||||||
portals_ips=[self.configuration.iscsi_ip_address],
|
def test_ensure_export_target_exist(self, mock_restore, mock_get_targets):
|
||||||
portals_port=self.configuration.iscsi_port)
|
|
||||||
|
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, '_execute', side_effect=lio.LioAdm._execute)
|
||||||
@mock.patch.object(lio.LioAdm, '_persist_configuration')
|
@mock.patch.object(lio.LioAdm, '_persist_configuration')
|
||||||
|
@ -1218,6 +1218,32 @@ class TestCinderRtstoolCmd(test.TestCase):
|
|||||||
self.assertRaisesRegexp(cinder_rtstool.RtstoolError, regexp,
|
self.assertRaisesRegexp(cinder_rtstool.RtstoolError, regexp,
|
||||||
cinder_rtstool.save_to_file, 'myfile')
|
cinder_rtstool.save_to_file, 'myfile')
|
||||||
|
|
||||||
|
@mock.patch.object(cinder_rtstool, 'rtslib_fb',
|
||||||
|
**{'root.default_save_file': mock.sentinel.filename})
|
||||||
|
def test_restore(self, mock_rtslib):
|
||||||
|
"""Test that we restore target configuration with default file."""
|
||||||
|
cinder_rtstool.restore_from_file(None)
|
||||||
|
rtsroot = mock_rtslib.root.RTSRoot
|
||||||
|
rtsroot.assert_called_once_with()
|
||||||
|
rtsroot.return_value.restore_from_file.assert_called_once_with(
|
||||||
|
mock.sentinel.filename)
|
||||||
|
|
||||||
|
@mock.patch.object(cinder_rtstool, 'rtslib_fb')
|
||||||
|
def test_restore_with_file(self, mock_rtslib):
|
||||||
|
"""Test that we restore target configuration with specified file."""
|
||||||
|
cinder_rtstool.restore_from_file('saved_file')
|
||||||
|
rtsroot = mock_rtslib.root.RTSRoot
|
||||||
|
rtsroot.return_value.restore_from_file.assert_called_once_with(
|
||||||
|
'saved_file')
|
||||||
|
|
||||||
|
@mock.patch('cinder.cmd.rtstool.restore_from_file')
|
||||||
|
def test_restore_error(self, restore_from_file):
|
||||||
|
"""Test that we fail to restore target configuration."""
|
||||||
|
restore_from_file.side_effect = OSError
|
||||||
|
self.assertRaises(OSError,
|
||||||
|
cinder_rtstool.restore_from_file,
|
||||||
|
mock.sentinel.filename)
|
||||||
|
|
||||||
def test_usage(self):
|
def test_usage(self):
|
||||||
with mock.patch('sys.stdout', new=six.StringIO()):
|
with mock.patch('sys.stdout', new=six.StringIO()):
|
||||||
exit = self.assertRaises(SystemExit, cinder_rtstool.usage)
|
exit = self.assertRaises(SystemExit, cinder_rtstool.usage)
|
||||||
|
@ -62,6 +62,12 @@ class LioAdm(iscsi.ISCSITarget):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _get_targets(self):
|
||||||
|
(out, err) = self._execute('cinder-rtstool',
|
||||||
|
'get-targets',
|
||||||
|
run_as_root=True)
|
||||||
|
return out
|
||||||
|
|
||||||
def _get_iscsi_target(self, context, vol_id):
|
def _get_iscsi_target(self, context, vol_id):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@ -81,6 +87,15 @@ class LioAdm(iscsi.ISCSITarget):
|
|||||||
"modifying volume id: %(vol_id)s."),
|
"modifying volume id: %(vol_id)s."),
|
||||||
{'vol_id': vol_id})
|
{'vol_id': vol_id})
|
||||||
|
|
||||||
|
def _restore_configuration(self):
|
||||||
|
try:
|
||||||
|
self._execute('cinder-rtstool', 'restore', run_as_root=True)
|
||||||
|
|
||||||
|
# On persistence failure we don't raise an exception, as target has
|
||||||
|
# been successfully created.
|
||||||
|
except putils.ProcessExecutionError:
|
||||||
|
LOG.warning(_LW("Failed to restore iscsi LIO configuration."))
|
||||||
|
|
||||||
def create_iscsi_target(self, name, tid, lun, path,
|
def create_iscsi_target(self, name, tid, lun, path,
|
||||||
chap_auth=None, **kwargs):
|
chap_auth=None, **kwargs):
|
||||||
# tid and lun are not used
|
# tid and lun are not used
|
||||||
@ -188,3 +203,14 @@ class LioAdm(iscsi.ISCSITarget):
|
|||||||
|
|
||||||
# We make changes persistent
|
# We make changes persistent
|
||||||
self._persist_configuration(volume['id'])
|
self._persist_configuration(volume['id'])
|
||||||
|
|
||||||
|
def ensure_export(self, context, volume, volume_path):
|
||||||
|
"""Recreate exports for logical volumes."""
|
||||||
|
|
||||||
|
# Restore saved configuration file if no target exists.
|
||||||
|
if not self._get_targets():
|
||||||
|
LOG.info(_LI('Restoring iSCSI target from configuration file'))
|
||||||
|
self._restore_configuration()
|
||||||
|
return
|
||||||
|
|
||||||
|
LOG.info(_LI("Skipping ensure_export. Found existing iSCSI target."))
|
||||||
|
Loading…
Reference in New Issue
Block a user