Delete the Linux-IO target before setting up local boot

Otherwise it fails with "device already mounted".

Change-Id: If959ba5a10e97f84be2f7498bed7589d13fa30c2
Closes-Bug: #1526304
This commit is contained in:
Dmitry Tantsur 2015-12-15 13:47:40 +01:00
parent 15e4454e68
commit 4cd64e28aa
4 changed files with 113 additions and 4 deletions

View File

@ -25,6 +25,7 @@ from oslo_log import log
from ironic_python_agent import errors
from ironic_python_agent.extensions import base
from ironic_python_agent.extensions import iscsi
from ironic_python_agent import hardware
from ironic_python_agent import utils
@ -194,6 +195,7 @@ class ImageExtension(base.BaseAgentExtension):
"""
device = hardware.dispatch_to_managers('get_os_install_device')
iscsi.clean_up(device)
_install_grub2(device,
root_uuid=root_uuid,
efi_system_part_uuid=efi_system_part_uuid)

View File

@ -95,6 +95,45 @@ def _start_lio(iqn, device):
raise errors.ISCSIError(msg)
def clean_up(device):
"""Clean up iSCSI for a given device."""
try:
rts_root = rtslib_fb.RTSRoot()
except (EnvironmentError, rtslib_fb.RTSLibError) as exc:
LOG.info('Linux-IO is not available, not cleaning up. Error: %s.', exc)
return
storage = None
for x in rts_root.storage_objects:
if x.udev_path == device:
storage = x
break
if storage is None:
LOG.info('Device %(dev)s not found in the current iSCSI mounts '
'%(mounts)s.',
{'dev': device,
'mounts': [x.udev_path for x in rts_root.storage_objects]})
return
else:
LOG.info('Deleting iSCSI target %(target)s for device %(dev)s.',
{'target': storage.name, 'dev': device})
try:
for x in rts_root.targets:
if x.wwn == storage.name:
x.delete()
break
storage.delete()
except rtslib_fb.utils.RTSLibError as exc:
msg = ('Failed to delete iSCSI target %(target)s for device %(dev)s: '
'%(error)s') % {'target': storage.name,
'dev': device,
'error': exc}
raise errors.ISCSIError(msg)
class ISCSIExtension(base.BaseAgentExtension):
@base.sync_command('start_iscsi_target')
def start_iscsi_target(self, iqn=None):

View File

@ -25,6 +25,7 @@ from oslotest import base as test_base
from ironic_python_agent import errors
from ironic_python_agent.extensions import image
from ironic_python_agent.extensions import iscsi
from ironic_python_agent import hardware
from ironic_python_agent import utils
@ -45,19 +46,22 @@ class TestImageExtension(test_base.BaseTestCase):
self.fake_efi_system_part_uuid = '45AB-2312'
self.fake_dir = '/tmp/fake-dir'
@mock.patch.object(iscsi, 'clean_up')
@mock.patch.object(image, '_install_grub2')
def test_install_bootloader_bios(self, mock_grub2, mock_execute,
mock_dispatch):
def test_install_bootloader_bios(self, mock_grub2, mock_iscsi_clean,
mock_execute, mock_dispatch):
mock_dispatch.return_value = self.fake_dev
self.agent_extension.install_bootloader(root_uuid=self.fake_root_uuid)
mock_dispatch.assert_called_once_with('get_os_install_device')
mock_grub2.assert_called_once_with(
self.fake_dev, root_uuid=self.fake_root_uuid,
efi_system_part_uuid=None)
mock_iscsi_clean.assert_called_once_with(self.fake_dev)
@mock.patch.object(iscsi, 'clean_up')
@mock.patch.object(image, '_install_grub2')
def test_install_bootloader_uefi(self, mock_grub2, mock_execute,
mock_dispatch):
def test_install_bootloader_uefi(self, mock_grub2, mock_iscsi_clean,
mock_execute, mock_dispatch):
mock_dispatch.return_value = self.fake_dev
self.agent_extension.install_bootloader(
root_uuid=self.fake_root_uuid,
@ -67,6 +71,7 @@ class TestImageExtension(test_base.BaseTestCase):
self.fake_dev,
root_uuid=self.fake_root_uuid,
efi_system_part_uuid=self.fake_efi_system_part_uuid)
mock_iscsi_clean.assert_called_once_with(self.fake_dev)
@mock.patch.object(os, 'environ')
@mock.patch.object(image, '_get_partition')

View File

@ -155,3 +155,66 @@ class TestISCSIExtensionLIO(test_base.BaseTestCase):
lun=1)
mock_rtslib.NetworkPortal.assert_called_once_with(
mock_rtslib.TPG.return_value, '0.0.0.0')
@mock.patch.object(iscsi.rtslib_fb, 'RTSRoot')
class TestISCSIExtensionCleanUp(test_base.BaseTestCase):
def setUp(self):
super(TestISCSIExtensionCleanUp, self).setUp()
self.agent_extension = iscsi.ISCSIExtension()
self.fake_dev = '/dev/fake'
self.fake_iqn = 'iqn-fake'
def test_lio_not_available(self, mock_rtslib):
mock_rtslib.side_effect = IOError()
iscsi.clean_up(self.fake_dev)
def test_device_not_found(self, mock_rtslib):
mock_rtslib.return_value.storage_objects = []
iscsi.clean_up(self.fake_dev)
def test_ok(self, mock_rtslib):
mock_rtslib.return_value.storage_objects = [
mock.Mock(udev_path='wrong path'),
mock.Mock(udev_path=self.fake_dev),
mock.Mock(udev_path='wrong path'),
]
# mocks don't play well with name attribute
for i, fake_storage in enumerate(
mock_rtslib.return_value.storage_objects):
fake_storage.name = 'iqn%d' % i
mock_rtslib.return_value.targets = [
mock.Mock(wwn='iqn0'),
mock.Mock(wwn='iqn1'),
]
iscsi.clean_up(self.fake_dev)
for fake_storage in mock_rtslib.return_value.storage_objects:
self.assertEqual(fake_storage.udev_path == self.fake_dev,
fake_storage.delete.called)
for fake_target in mock_rtslib.return_value.targets:
self.assertEqual(fake_target.wwn == 'iqn1',
fake_target.delete.called)
def test_delete_fails(self, mock_rtslib):
mock_rtslib.return_value.storage_objects = [
mock.Mock(udev_path='wrong path'),
mock.Mock(udev_path=self.fake_dev),
mock.Mock(udev_path='wrong path'),
]
# mocks don't play well with name attribute
for i, fake_storage in enumerate(
mock_rtslib.return_value.storage_objects):
fake_storage.name = 'iqn%d' % i
mock_rtslib.return_value.targets = [
mock.Mock(wwn='iqn0'),
mock.Mock(wwn='iqn1'),
]
mock_rtslib.return_value.targets[1].delete.side_effect = (
_ORIG_UTILS.RTSLibError())
self.assertRaises(errors.ISCSIError, iscsi.clean_up, self.fake_dev)