summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2021-05-12 11:08:19 +0000
committerGerrit Code Review <review@openstack.org>2021-05-12 11:08:19 +0000
commitd6e4fbd8272bbd5129863c03712a3324eb7e48f9 (patch)
tree15d8ac5f921258c238d426f29d9444c0075b9a88
parent719f20aaf5103e50a39d2c9d043b07699424ab13 (diff)
parentbe3882162e432b292af9e787661661a05fa7d11a (diff)
downloadironic-python-agent-d6e4fbd8272bbd5129863c03712a3324eb7e48f9.tar.gz
Merge "Remove the iscsi extension"
-rw-r--r--ironic_python_agent/agent.py1
-rw-r--r--ironic_python_agent/errors.py19
-rw-r--r--ironic_python_agent/extensions/image.py6
-rw-r--r--ironic_python_agent/extensions/iscsi.py221
-rw-r--r--ironic_python_agent/tests/unit/extensions/test_image.py71
-rw-r--r--ironic_python_agent/tests/unit/extensions/test_iscsi.py388
-rw-r--r--releasenotes/notes/no-iscsi-fd21808edbea5ac2.yaml5
-rw-r--r--requirements.txt1
-rw-r--r--setup.cfg1
9 files changed, 14 insertions, 699 deletions
diff --git a/ironic_python_agent/agent.py b/ironic_python_agent/agent.py
index 660619eb..8e36af2c 100644
--- a/ironic_python_agent/agent.py
+++ b/ironic_python_agent/agent.py
@@ -187,7 +187,6 @@ class IronicPythonAgent(base.ExecuteCommandMixin):
# in the event of long running ramdisks where the conductor
# got upgraded somewhere along the way.
self.agent_token_required = cfg.CONF.agent_token_required
- self.iscsi_started = False
self.generated_cert = None
def get_status(self):
diff --git a/ironic_python_agent/errors.py b/ironic_python_agent/errors.py
index 0f7a185c..1f97c0e2 100644
--- a/ironic_python_agent/errors.py
+++ b/ironic_python_agent/errors.py
@@ -312,31 +312,12 @@ class DeploymentError(RESTError):
super(DeploymentError, self).__init__(details)
-class ISCSIError(RESTError):
- """Error raised when an image cannot be written to a device."""
-
- message = 'Error starting iSCSI target'
-
- def __init__(self, error_msg):
- details = 'Error starting iSCSI target: {}'.format(error_msg)
- super(ISCSIError, self).__init__(details)
-
-
class IncompatibleNumaFormatError(RESTError):
"""Error raised when unexpected format data in NUMA node."""
message = 'Error in NUMA node data format'
-class ISCSICommandError(ISCSIError):
- """Error executing TGT command."""
-
- def __init__(self, error_msg, exit_code, stdout, stderr):
- details = ('{}. Failed with exit code {}. stdout: {}. stderr: {}')
- details = details.format(error_msg, exit_code, stdout, stderr)
- super(ISCSICommandError, self).__init__(details)
-
-
class DeviceNotFound(NotFound):
"""Error raised when the device to deploy the image onto is not found."""
diff --git a/ironic_python_agent/extensions/image.py b/ironic_python_agent/extensions/image.py
index 96cb9503..c7e4c9c4 100644
--- a/ironic_python_agent/extensions/image.py
+++ b/ironic_python_agent/extensions/image.py
@@ -27,7 +27,6 @@ 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 raid_utils
from ironic_python_agent import utils
@@ -289,8 +288,7 @@ def _manage_uefi(device, efi_system_part_uuid=None):
efi_mounted = False
try:
- # Force UEFI to rescan the device. Required if the deployment
- # was over iscsi.
+ # Force UEFI to rescan the device.
_rescan_device(device)
local_path = tempfile.mkdtemp()
@@ -1011,8 +1009,6 @@ class ImageExtension(base.BaseAgentExtension):
"""
device = hardware.dispatch_to_managers('get_os_install_device')
- if self.agent.iscsi_started:
- iscsi.clean_up(device)
# Always allow the API client to be the final word on if this is
# overridden or not.
diff --git a/ironic_python_agent/extensions/iscsi.py b/ironic_python_agent/extensions/iscsi.py
deleted file mode 100644
index c519ab32..00000000
--- a/ironic_python_agent/extensions/iscsi.py
+++ /dev/null
@@ -1,221 +0,0 @@
-# Copyright 2015 Red Hat, 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.
-
-
-from ironic_lib import disk_utils
-from oslo_concurrency import processutils
-from oslo_log import log
-from oslo_utils import uuidutils
-try:
- import rtslib_fb
-except ImportError:
- import rtslib as rtslib_fb
-
-from ironic_python_agent import errors
-from ironic_python_agent.extensions import base
-from ironic_python_agent import hardware
-from ironic_python_agent import netutils
-from ironic_python_agent import utils
-
-LOG = log.getLogger(__name__)
-DEFAULT_ISCSI_PORTAL_PORT = 3260
-
-
-def _execute(cmd, error_msg, **kwargs):
- try:
- stdout, stderr = utils.execute(*cmd, **kwargs)
- except processutils.ProcessExecutionError as e:
- LOG.error(error_msg)
- raise errors.ISCSICommandError(error_msg, e.exit_code,
- e.stdout, e.stderr)
- except OSError as e:
- LOG.error("Error: %(error)s: OS Error: %(os_error)s",
- {'error': error_msg, 'os_error': e})
- raise errors.ISCSICommandError(e, e.errno, None, None)
-
-
-def _wait_for_tgtd(attempts=10):
- """Wait for the ISCSI daemon to start."""
- # here, iscsi daemon is considered not running in case
- # tgtadm is not able to talk to tgtd to show iscsi targets
- cmd = ['tgtadm', '--lld', 'iscsi', '--mode', 'target', '--op', 'show']
- _execute(cmd, "ISCSI daemon didn't initialize", attempts=attempts)
-
-
-def _start_tgtd(iqn, portal_port, device):
- """Start a ISCSI target for the device."""
- # Start ISCSI Target daemon
- _execute(['tgtd'], "Unable to start the ISCSI daemon")
-
- _wait_for_tgtd()
-
- # tgt service will create default portal on default port 3260.
- # so no need to create again if input portal_port == 3260.
- if portal_port != DEFAULT_ISCSI_PORTAL_PORT:
- cmd = ['tgtadm', '--lld', 'iscsi', '--mode', 'portal', '--op',
- 'new', '--param', 'portal=0.0.0.0:' + str(portal_port)]
- _execute(cmd, "Error when adding a new portal with portal_port %d"
- % portal_port)
-
- cmd = ['tgtadm', '--lld', 'iscsi', '--mode', 'target', '--op',
- 'new', '--tid', '1', '--targetname', iqn]
- _execute(cmd, "Error when adding a new target for iqn %s" % iqn)
-
- cmd = ['tgtadm', '--lld', 'iscsi', '--mode', 'logicalunit', '--op',
- 'new', '--tid', '1', '--lun', '1', '--backing-store', device]
- _execute(cmd, "Error when adding a new logical unit for iqn %s" % iqn)
-
- cmd = ['tgtadm', '--lld', 'iscsi', '--mode', 'target', '--op',
- 'bind', '--tid', '1', '--initiator-address', 'ALL']
- _execute(cmd, "Error when enabling the target to accept the specific "
- "initiators for iqn %s" % iqn)
-
-
-def _start_lio(iqn, portal_port, device):
- try:
- storage = rtslib_fb.BlockStorageObject(name=iqn, dev=device)
- target = rtslib_fb.Target(rtslib_fb.FabricModule('iscsi'), iqn,
- mode='create')
- tpg = rtslib_fb.TPG(target, mode='create')
- # disable all authentication
- tpg.set_attribute('authentication', '0')
- tpg.set_attribute('demo_mode_write_protect', '0')
- tpg.set_attribute('generate_node_acls', '1')
- # lun=1 is hardcoded in ironic
- rtslib_fb.LUN(tpg, storage_object=storage, lun=1)
- tpg.enable = 1
- except rtslib_fb.utils.RTSLibError as exc:
- msg = 'Failed to create a target: {}'.format(exc)
- raise errors.ISCSIError(msg)
-
- try:
- # bind to the default port on all interfaces
- listen_ip = netutils.wrap_ipv6(netutils.get_wildcard_address())
- rtslib_fb.NetworkPortal(tpg, listen_ip, portal_port)
- except rtslib_fb.utils.RTSLibError as exc:
- msg = 'Failed to publish a target: {}'.format(exc)
- raise errors.ISCSIError(msg)
-
-
-def clean_up(device):
- """Clean up iSCSI for a given device."""
- try:
- rts_root = rtslib_fb.RTSRoot()
- except (OSError, EnvironmentError, rtslib_fb.RTSLibError) as exc:
- try:
- LOG.info('Linux-IO is not available, attemting to stop tgtd '
- 'mapping. Error: %s.', exc)
- cmd = ['tgtadm', '--lld', 'iscsi', '--mode', 'target', '--op',
- 'unbind', '--tid', '1', '--initiator-address', 'ALL']
- _execute(cmd, "Error when cleaning up iscsi binds.")
- except errors.ISCSICommandError:
- # This command may fail if the target was already torn down
- # and that is okay, we just want to ensure it has been torn
- # down so there should be no disk locks persisting.
- pass
- cmd = ['sync']
- _execute(cmd, "Error flushing buffers to disk.")
- try:
- cmd = ['tgtadm', '--lld', 'iscsi', '--mode', 'target', '--op',
- 'delete', '--tid', '1']
- _execute(cmd, "Error deleting the iscsi target configuration.")
- except errors.ISCSICommandError:
- # This command should remove the target from being offered.
- # It is just proper clean-up, and often previously the IPA
- # side, or "target" was never really torn down in many cases.
- pass
- 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, wipe_disk_metadata=False,
- portal_port=None):
- """Expose the disk as an ISCSI target.
-
- :param iqn: IQN for iSCSI target. If None, a new IQN is generated.
- :param wipe_disk_metadata: if the disk metadata should be wiped out
- before the disk is exposed.
- :param portal_port: customized port for iSCSI port, can be None.
- :returns: a dict that provides IQN of iSCSI target.
- """
- # If iqn is not given, generate one
- if iqn is None:
- iqn = 'iqn.2008-10.org.openstack:%s' % uuidutils.generate_uuid()
-
- device = hardware.dispatch_to_managers('get_os_install_device',
- permit_refresh=True)
-
- if wipe_disk_metadata:
- disk_utils.destroy_disk_metadata(
- device,
- self.agent.get_node_uuid())
-
- LOG.debug("Starting ISCSI target with iqn %(iqn)s on device "
- "%(device)s", {'iqn': iqn, 'device': device})
-
- try:
- rts_root = rtslib_fb.RTSRoot()
- except (EnvironmentError, rtslib_fb.RTSLibError) as exc:
- LOG.warning('Linux-IO is not available, falling back to TGT. '
- 'Error: %s.', exc)
- rts_root = None
-
- if portal_port is None:
- portal_port = DEFAULT_ISCSI_PORTAL_PORT
-
- if rts_root is None:
- _start_tgtd(iqn, portal_port, device)
- else:
- _start_lio(iqn, portal_port, device)
- LOG.debug('Linux-IO configuration: %s', rts_root.dump())
- # Mark iscsi as previously started
- self.agent.iscsi_started = True
- LOG.info('Created iSCSI target with iqn %(iqn)s, portal port %(port)d,'
- ' on device %(dev)s using %(method)s',
- {'iqn': iqn, 'port': portal_port, 'dev': device,
- 'method': 'tgtd' if rts_root is None else 'linux-io'})
-
- return {"iscsi_target_iqn": iqn}
diff --git a/ironic_python_agent/tests/unit/extensions/test_image.py b/ironic_python_agent/tests/unit/extensions/test_image.py
index 232e4647..d8471d2b 100644
--- a/ironic_python_agent/tests/unit/extensions/test_image.py
+++ b/ironic_python_agent/tests/unit/extensions/test_image.py
@@ -23,7 +23,6 @@ from oslo_concurrency import processutils
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.tests.unit import base
from ironic_python_agent import utils
@@ -46,12 +45,9 @@ class TestImageExtension(base.IronicAgentTest):
self.fake_efi_system_part_uuid = '45AB-2312'
self.fake_prep_boot_part_uuid = '76937797-3253-8843-999999999999'
self.fake_dir = '/tmp/fake-dir'
- self.agent_extension.agent = mock.Mock()
- self.agent_extension.agent.iscsi_started = True
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
@mock.patch.object(image, '_install_grub2', autospec=True)
- def test__install_bootloader_bios(self, mock_grub2, mock_iscsi_clean,
+ def test__install_bootloader_bios(self, mock_grub2,
mock_execute, mock_dispatch):
mock_dispatch.side_effect = [
self.fake_dev, hardware.BootInfo(current_boot_mode='bios')
@@ -66,13 +62,10 @@ class TestImageExtension(base.IronicAgentTest):
efi_system_part_uuid=None, prep_boot_part_uuid=None,
target_boot_mode='bios'
)
- mock_iscsi_clean.assert_called_once_with(self.fake_dev)
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
@mock.patch.object(image, '_manage_uefi', autospec=True)
@mock.patch.object(image, '_install_grub2', autospec=True)
def test__install_bootloader_uefi(self, mock_grub2, mock_uefi,
- mock_iscsi_clean,
mock_execute, mock_dispatch):
mock_dispatch.side_effect = [
self.fake_dev, hardware.BootInfo(current_boot_mode='uefi')
@@ -93,14 +86,11 @@ class TestImageExtension(base.IronicAgentTest):
prep_boot_part_uuid=None,
target_boot_mode='uefi'
)
- mock_iscsi_clean.assert_called_once_with(self.fake_dev)
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
@mock.patch.object(image, '_manage_uefi', autospec=True)
@mock.patch.object(image, '_install_grub2', autospec=True)
def test__install_bootloader_uefi_ignores_manage_failure(
self, mock_grub2, mock_uefi,
- mock_iscsi_clean,
mock_execute, mock_dispatch):
self.config(ignore_bootloader_failure=True)
mock_uefi.side_effect = OSError('meow')
@@ -123,14 +113,11 @@ class TestImageExtension(base.IronicAgentTest):
prep_boot_part_uuid=None,
target_boot_mode='uefi'
)
- mock_iscsi_clean.assert_called_once_with(self.fake_dev)
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
@mock.patch.object(image, '_manage_uefi', autospec=True)
@mock.patch.object(image, '_install_grub2', autospec=True)
def test__install_bootloader_uefi_ignores_grub_failure(
self, mock_grub2, mock_uefi,
- mock_iscsi_clean,
mock_execute, mock_dispatch):
self.config(ignore_bootloader_failure=True)
mock_grub2.side_effect = OSError('meow')
@@ -153,14 +140,11 @@ class TestImageExtension(base.IronicAgentTest):
prep_boot_part_uuid=None,
target_boot_mode='uefi'
)
- mock_iscsi_clean.assert_called_once_with(self.fake_dev)
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
@mock.patch.object(image, '_manage_uefi', autospec=True)
@mock.patch.object(image, '_install_grub2', autospec=True)
def test__install_bootloader_uefi_ignores_grub_failure_api_override(
self, mock_grub2, mock_uefi,
- mock_iscsi_clean,
mock_execute, mock_dispatch):
self.config(ignore_bootloader_failure=False)
mock_grub2.side_effect = OSError('meow')
@@ -183,14 +167,11 @@ class TestImageExtension(base.IronicAgentTest):
prep_boot_part_uuid=None,
target_boot_mode='uefi'
)
- mock_iscsi_clean.assert_called_once_with(self.fake_dev)
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
@mock.patch.object(image, '_manage_uefi', autospec=True)
@mock.patch.object(image, '_install_grub2', autospec=True)
def test__install_bootloader_uefi_grub_failure_api_override(
self, mock_grub2, mock_uefi,
- mock_iscsi_clean,
mock_execute, mock_dispatch):
self.config(ignore_bootloader_failure=True)
mock_grub2.side_effect = OSError('meow')
@@ -214,11 +195,9 @@ class TestImageExtension(base.IronicAgentTest):
prep_boot_part_uuid=None,
target_boot_mode='uefi'
)
- mock_iscsi_clean.assert_called_once_with(self.fake_dev)
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
@mock.patch.object(image, '_install_grub2', autospec=True)
- def test__install_bootloader_no_root(self, mock_grub2, mock_iscsi_clean,
+ def test__install_bootloader_no_root(self, mock_grub2,
mock_execute, mock_dispatch):
mock_dispatch.side_effect = [
self.fake_dev, hardware.BootInfo(current_boot_mode='bios')
@@ -229,18 +208,16 @@ class TestImageExtension(base.IronicAgentTest):
mock_dispatch.assert_any_call('get_boot_info')
self.assertEqual(2, mock_dispatch.call_count)
self.assertFalse(mock_grub2.called)
- mock_iscsi_clean.assert_called_once_with(self.fake_dev)
@mock.patch.object(hardware, 'is_md_device', lambda *_: False)
@mock.patch.object(os.path, 'exists', lambda *_: False)
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
@mock.patch.object(image, '_get_efi_bootloaders', autospec=True)
@mock.patch.object(image, '_get_partition', autospec=True)
@mock.patch.object(utils, 'get_efi_part_on_device', autospec=False)
@mock.patch.object(os, 'makedirs', autospec=True)
def test__uefi_bootloader_given_partition(
self, mkdir_mock, mock_utils_efi_part, mock_partition,
- mock_efi_bl, mock_iscsi_clean, mock_execute, mock_dispatch):
+ mock_efi_bl, mock_execute, mock_dispatch):
mock_dispatch.side_effect = [
self.fake_dev, hardware.BootInfo(current_boot_mode='uefi')
]
@@ -282,14 +259,13 @@ class TestImageExtension(base.IronicAgentTest):
@mock.patch.object(hardware, 'is_md_device', lambda *_: False)
@mock.patch.object(os.path, 'exists', lambda *_: False)
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
@mock.patch.object(image, '_get_efi_bootloaders', autospec=True)
@mock.patch.object(image, '_get_partition', autospec=True)
@mock.patch.object(utils, 'get_efi_part_on_device', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True)
def test__uefi_bootloader_find_partition(
self, mkdir_mock, mock_utils_efi_part, mock_partition,
- mock_efi_bl, mock_iscsi_clean, mock_execute, mock_dispatch):
+ mock_efi_bl, mock_execute, mock_dispatch):
mock_dispatch.side_effect = [
self.fake_dev, hardware.BootInfo(current_boot_mode='uefi')
]
@@ -330,14 +306,13 @@ class TestImageExtension(base.IronicAgentTest):
@mock.patch.object(hardware, 'is_md_device', lambda *_: False)
@mock.patch.object(os.path, 'exists', lambda *_: False)
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
@mock.patch.object(image, '_get_efi_bootloaders', autospec=True)
@mock.patch.object(image, '_get_partition', autospec=True)
@mock.patch.object(utils, 'get_efi_part_on_device', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True)
def test__uefi_bootloader_with_entry_removal(
self, mkdir_mock, mock_utils_efi_part, mock_partition,
- mock_efi_bl, mock_iscsi_clean, mock_execute, mock_dispatch):
+ mock_efi_bl, mock_execute, mock_dispatch):
mock_dispatch.side_effect = [
self.fake_dev, hardware.BootInfo(current_boot_mode='uefi')
]
@@ -385,14 +360,13 @@ efibootmgr: ** Warning ** : Boot0005 has same label ironic1\n
@mock.patch.object(hardware, 'is_md_device', lambda *_: False)
@mock.patch.object(os.path, 'exists', lambda *_: False)
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
@mock.patch.object(image, '_get_efi_bootloaders', autospec=True)
@mock.patch.object(image, '_get_partition', autospec=True)
@mock.patch.object(utils, 'get_efi_part_on_device', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True)
def test__add_multi_bootloaders(
self, mkdir_mock, mock_utils_efi_part, mock_partition,
- mock_efi_bl, mock_iscsi_clean, mock_execute, mock_dispatch):
+ mock_efi_bl, mock_execute, mock_dispatch):
mock_dispatch.side_effect = [
self.fake_dev, hardware.BootInfo(current_boot_mode='uefi')
]
@@ -438,9 +412,8 @@ efibootmgr: ** Warning ** : Boot0005 has same label ironic1\n
mock_utils_efi_part.assert_called_once_with(self.fake_dev)
self.assertEqual(9, mock_execute.call_count)
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
@mock.patch.object(image, '_install_grub2', autospec=True)
- def test__install_bootloader_prep(self, mock_grub2, mock_iscsi_clean,
+ def test__install_bootloader_prep(self, mock_grub2,
mock_execute, mock_dispatch):
mock_dispatch.side_effect = [
self.fake_dev, hardware.BootInfo(current_boot_mode='bios')
@@ -459,38 +432,10 @@ efibootmgr: ** Warning ** : Boot0005 has same label ironic1\n
prep_boot_part_uuid=self.fake_prep_boot_part_uuid,
target_boot_mode='bios'
)
- mock_iscsi_clean.assert_called_once_with(self.fake_dev)
-
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
- @mock.patch.object(image, '_install_grub2', autospec=True)
- def test__install_bootloader_prep_no_iscsi(
- self, mock_grub2, mock_iscsi_clean,
- mock_execute, mock_dispatch):
- self.agent_extension.agent.iscsi_started = False
- mock_dispatch.side_effect = [
- self.fake_dev, hardware.BootInfo(current_boot_mode='bios')
- ]
- self.agent_extension.install_bootloader(
- root_uuid=self.fake_root_uuid,
- efi_system_part_uuid=None,
- prep_boot_part_uuid=self.fake_prep_boot_part_uuid).join()
- mock_dispatch.assert_any_call('get_os_install_device')
- mock_dispatch.assert_any_call('get_boot_info')
- self.assertEqual(2, mock_dispatch.call_count)
- mock_grub2.assert_called_once_with(
- self.fake_dev,
- root_uuid=self.fake_root_uuid,
- efi_system_part_uuid=None,
- prep_boot_part_uuid=self.fake_prep_boot_part_uuid,
- target_boot_mode='bios'
- )
- mock_iscsi_clean.assert_not_called()
@mock.patch.object(hardware, 'is_md_device', lambda *_: False)
@mock.patch.object(os.path, 'exists', lambda *_: False)
- @mock.patch.object(iscsi, 'clean_up', autospec=True)
- def test_install_bootloader_failure(self, mock_iscsi_clean, mock_execute,
- mock_dispatch):
+ def test_install_bootloader_failure(self, mock_execute, mock_dispatch):
mock_dispatch.side_effect = [
self.fake_dev, hardware.BootInfo(current_boot_mode='uefi')
]
diff --git a/ironic_python_agent/tests/unit/extensions/test_iscsi.py b/ironic_python_agent/tests/unit/extensions/test_iscsi.py
deleted file mode 100644
index a4678a98..00000000
--- a/ironic_python_agent/tests/unit/extensions/test_iscsi.py
+++ /dev/null
@@ -1,388 +0,0 @@
-# Copyright 2015 Red Hat, 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.
-
-from unittest import mock
-
-from ironic_lib import disk_utils
-from oslo_concurrency import processutils
-
-from ironic_python_agent import errors
-from ironic_python_agent.extensions import iscsi
-from ironic_python_agent import hardware
-from ironic_python_agent.tests.unit import base
-from ironic_python_agent import utils
-
-
-class FakeAgent(object):
-
- iscsi_started = False
-
- def get_node_uuid(self):
- return 'my_node_uuid'
-
-
-@mock.patch.object(disk_utils, 'destroy_disk_metadata', autospec=True)
-@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
-@mock.patch.object(utils, 'execute', autospec=True)
-@mock.patch.object(iscsi.rtslib_fb, 'RTSRoot',
- mock.Mock(side_effect=iscsi.rtslib_fb.RTSLibError()))
-class TestISCSIExtensionTgt(base.IronicAgentTest):
-
- def setUp(self):
- super(TestISCSIExtensionTgt, self).setUp()
- self.agent_extension = iscsi.ISCSIExtension(FakeAgent())
- self.fake_dev = '/dev/fake'
- self.fake_iqn = 'iqn-fake'
-
- def test_start_iscsi_target(self, mock_execute,
- mock_dispatch,
- mock_destroy):
- mock_dispatch.return_value = self.fake_dev
- mock_execute.return_value = ('', '')
- self.assertFalse(self.agent_extension.agent.iscsi_started)
-
- result = self.agent_extension.start_iscsi_target(iqn=self.fake_iqn)
-
- self.assertTrue(self.agent_extension.agent.iscsi_started)
- expected = [mock.call('tgtd'),
- mock.call('tgtadm', '--lld', 'iscsi', '--mode',
- 'target', '--op', 'show', attempts=10),
- mock.call('tgtadm', '--lld', 'iscsi', '--mode',
- 'target', '--op', 'new', '--tid', '1',
- '--targetname', self.fake_iqn),
- mock.call('tgtadm', '--lld', 'iscsi', '--mode',
- 'logicalunit', '--op', 'new', '--tid', '1',
- '--lun', '1', '--backing-store', self.fake_dev),
- mock.call('tgtadm', '--lld', 'iscsi', '--mode', 'target',
- '--op', 'bind', '--tid', '1',
- '--initiator-address', 'ALL')]
- mock_execute.assert_has_calls(expected)
- mock_dispatch.assert_called_once_with('get_os_install_device',
- permit_refresh=True)
- self.assertEqual({'iscsi_target_iqn': self.fake_iqn},
- result.command_result)
- self.assertFalse(mock_destroy.called)
-
- def test_start_iscsi_target_with_special_port(self, mock_execute,
- mock_dispatch,
- mock_destroy):
- mock_dispatch.return_value = self.fake_dev
- mock_execute.return_value = ('', '')
- result = self.agent_extension.start_iscsi_target(iqn=self.fake_iqn,
- portal_port=3268)
-
- expected = [mock.call('tgtd'),
- mock.call('tgtadm', '--lld', 'iscsi', '--mode',
- 'target', '--op', 'show', attempts=10),
- mock.call('tgtadm', '--lld', 'iscsi', '--mode',
- 'portal', '--op', 'new', '--param',
- 'portal=0.0.0.0:3268'),
- mock.call('tgtadm', '--lld', 'iscsi', '--mode',
- 'target', '--op', 'new', '--tid', '1',
- '--targetname', self.fake_iqn),
- mock.call('tgtadm', '--lld', 'iscsi', '--mode',
- 'logicalunit', '--op', 'new', '--tid', '1',
- '--lun', '1', '--backing-store', self.fake_dev),
- mock.call('tgtadm', '--lld', 'iscsi', '--mode', 'target',
- '--op', 'bind', '--tid', '1',
- '--initiator-address', 'ALL')]
- mock_execute.assert_has_calls(expected)
- mock_dispatch.assert_called_once_with('get_os_install_device',
- permit_refresh=True)
- self.assertEqual({'iscsi_target_iqn': self.fake_iqn},
- result.command_result)
-
- def test_start_iscsi_target_fail_wait_daemon(self, mock_execute,
- mock_dispatch,
- mock_destroy):
- mock_dispatch.return_value = self.fake_dev
- # side effects here:
- # - execute tgtd: stdout=='', stderr==''
- # - induce tgtadm failure while in _wait_for_scsi_daemon
- mock_execute.side_effect = [('', ''),
- processutils.ProcessExecutionError('blah')]
- self.assertRaises(errors.ISCSIError,
- self.agent_extension.start_iscsi_target,
- iqn=self.fake_iqn)
- expected = [mock.call('tgtd'),
- mock.call('tgtadm', '--lld', 'iscsi', '--mode', 'target',
- '--op', 'show', attempts=10)]
-
- mock_execute.assert_has_calls(expected)
- mock_dispatch.assert_called_once_with('get_os_install_device',
- permit_refresh=True)
- self.assertFalse(mock_destroy.called)
-
- @mock.patch.object(iscsi, '_wait_for_tgtd', autospec=True)
- def test_start_iscsi_target_fail_command(self, mock_wait_iscsi,
- mock_execute, mock_dispatch,
- mock_destroy):
- mock_dispatch.return_value = self.fake_dev
- mock_execute.side_effect = [('', ''), ('', ''),
- processutils.ProcessExecutionError('blah')]
- self.assertRaises(errors.ISCSIError,
- self.agent_extension.start_iscsi_target,
- iqn=self.fake_iqn)
-
- expected = [mock.call('tgtd'),
- mock.call('tgtadm', '--lld', 'iscsi', '--mode',
- 'target', '--op', 'new', '--tid', '1',
- '--targetname', self.fake_iqn)]
- mock_execute.assert_has_calls(expected)
- mock_dispatch.assert_called_once_with('get_os_install_device',
- permit_refresh=True)
-
- def test_start_iscsi_target_fail_command_not_exist(self, mock_execute,
- mock_dispatch,
- mock_destroy):
- mock_dispatch.return_value = self.fake_dev
- mock_execute.side_effect = OSError('file not found')
- self.assertRaises(errors.ISCSIError,
- self.agent_extension.start_iscsi_target,
- iqn=self.fake_iqn)
-
-
-_ORIG_UTILS = iscsi.rtslib_fb.utils
-
-
-@mock.patch.object(disk_utils, 'destroy_disk_metadata', autospec=True)
-@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
-# Don't mock the utils module, as it contains exceptions
-@mock.patch.object(iscsi, 'rtslib_fb', utils=_ORIG_UTILS, autospec=True)
-class TestISCSIExtensionLIO(base.IronicAgentTest):
-
- def setUp(self):
- super(TestISCSIExtensionLIO, self).setUp()
- self.agent_extension = iscsi.ISCSIExtension(FakeAgent())
- self.fake_dev = '/dev/fake'
- self.fake_iqn = 'iqn-fake'
-
- @mock.patch('ironic_python_agent.netutils.get_wildcard_address',
- autospec=True)
- def test_start_iscsi_target(self, mock_get_wildcard_address,
- mock_rtslib, mock_dispatch,
- mock_destroy):
- mock_get_wildcard_address.return_value = '::'
- mock_dispatch.return_value = self.fake_dev
- result = self.agent_extension.start_iscsi_target(iqn=self.fake_iqn)
-
- self.assertEqual({'iscsi_target_iqn': self.fake_iqn},
- result.command_result)
- mock_rtslib.BlockStorageObject.assert_called_once_with(
- name=self.fake_iqn, dev=self.fake_dev)
- mock_rtslib.Target.assert_called_once_with(mock.ANY, self.fake_iqn,
- mode='create')
- mock_rtslib.TPG.assert_called_once_with(
- mock_rtslib.Target.return_value, mode='create')
- mock_rtslib.LUN.assert_called_once_with(
- mock_rtslib.TPG.return_value,
- storage_object=mock_rtslib.BlockStorageObject.return_value,
- lun=1)
- mock_rtslib.NetworkPortal.assert_called_once_with(
- mock_rtslib.TPG.return_value, '[::]', 3260)
- self.assertFalse(mock_destroy.called)
-
- @mock.patch('ironic_python_agent.netutils.get_wildcard_address',
- autospec=True)
- def test_start_iscsi_target_noipv6(self, mock_get_wildcard_address,
- mock_rtslib, mock_dispatch,
- mock_destroy):
- mock_get_wildcard_address.return_value = '0.0.0.0'
- mock_dispatch.return_value = self.fake_dev
- result = self.agent_extension.start_iscsi_target(iqn=self.fake_iqn)
-
- self.assertEqual({'iscsi_target_iqn': self.fake_iqn},
- result.command_result)
- mock_rtslib.BlockStorageObject.assert_called_once_with(
- name=self.fake_iqn, dev=self.fake_dev)
- mock_rtslib.Target.assert_called_once_with(mock.ANY, self.fake_iqn,
- mode='create')
- mock_rtslib.TPG.assert_called_once_with(
- mock_rtslib.Target.return_value, mode='create')
- mock_rtslib.LUN.assert_called_once_with(
- mock_rtslib.TPG.return_value,
- storage_object=mock_rtslib.BlockStorageObject.return_value,
- lun=1)
- mock_rtslib.NetworkPortal.assert_called_once_with(
- mock_rtslib.TPG.return_value, '0.0.0.0', 3260)
- self.assertFalse(mock_destroy.called)
-
- @mock.patch('ironic_python_agent.netutils.get_wildcard_address',
- autospec=True)
- def test_start_iscsi_target_with_special_port(self,
- mock_get_wildcard_address,
- mock_rtslib, mock_dispatch,
- mock_destroy):
- mock_get_wildcard_address.return_value = '::'
- mock_dispatch.return_value = self.fake_dev
- result = self.agent_extension.start_iscsi_target(iqn=self.fake_iqn,
- portal_port=3266)
-
- self.assertEqual({'iscsi_target_iqn': self.fake_iqn},
- result.command_result)
- mock_rtslib.BlockStorageObject.assert_called_once_with(
- name=self.fake_iqn, dev=self.fake_dev)
- mock_rtslib.Target.assert_called_once_with(mock.ANY, self.fake_iqn,
- mode='create')
- mock_rtslib.TPG.assert_called_once_with(
- mock_rtslib.Target.return_value, mode='create')
- mock_rtslib.LUN.assert_called_once_with(
- mock_rtslib.TPG.return_value,
- storage_object=mock_rtslib.BlockStorageObject.return_value,
- lun=1)
- mock_rtslib.NetworkPortal.assert_called_once_with(
- mock_rtslib.TPG.return_value, '[::]', 3266)
-
- def test_failed_to_start_iscsi(self, mock_rtslib, mock_dispatch,
- mock_destroy):
- mock_dispatch.return_value = self.fake_dev
- mock_rtslib.Target.side_effect = _ORIG_UTILS.RTSLibError()
- self.assertRaisesRegex(
- errors.ISCSIError, 'Failed to create a target',
- self.agent_extension.start_iscsi_target, iqn=self.fake_iqn)
-
- @mock.patch('ironic_python_agent.netutils.get_wildcard_address',
- autospec=True)
- def test_failed_to_bind_iscsi(self, mock_get_wildcard_address,
- mock_rtslib, mock_dispatch, mock_destroy):
- mock_get_wildcard_address.return_value = '::'
- mock_dispatch.return_value = self.fake_dev
- mock_rtslib.NetworkPortal.side_effect = _ORIG_UTILS.RTSLibError()
- self.assertRaisesRegex(
- errors.ISCSIError, 'Failed to publish a target',
- self.agent_extension.start_iscsi_target, iqn=self.fake_iqn,
- portal_port=None)
-
- mock_rtslib.BlockStorageObject.assert_called_once_with(
- name=self.fake_iqn, dev=self.fake_dev)
- mock_rtslib.Target.assert_called_once_with(mock.ANY, self.fake_iqn,
- mode='create')
- mock_rtslib.TPG.assert_called_once_with(
- mock_rtslib.Target.return_value, mode='create')
- mock_rtslib.LUN.assert_called_once_with(
- mock_rtslib.TPG.return_value,
- storage_object=mock_rtslib.BlockStorageObject.return_value,
- lun=1)
- mock_rtslib.NetworkPortal.assert_called_once_with(
- mock_rtslib.TPG.return_value, '[::]', 3260)
- self.assertFalse(mock_destroy.called)
-
- def test_failed_to_start_iscsi_wipe_disk_metadata(self, mock_rtslib,
- mock_dispatch,
- mock_destroy):
- mock_dispatch.return_value = self.fake_dev
- mock_rtslib.Target.side_effect = _ORIG_UTILS.RTSLibError()
- self.assertRaisesRegex(
- errors.ISCSIError, 'Failed to create a target',
- self.agent_extension.start_iscsi_target,
- iqn=self.fake_iqn,
- wipe_disk_metadata=True)
- mock_destroy.assert_called_once_with('/dev/fake', 'my_node_uuid')
-
-
-@mock.patch.object(iscsi.rtslib_fb, 'RTSRoot', autospec=True)
-@mock.patch.object(utils, 'execute', autospec=True)
-class TestISCSIExtensionCleanUpFallback(base.IronicAgentTest):
-
- def setUp(self):
- super(TestISCSIExtensionCleanUpFallback, self).setUp()
- self.agent_extension = iscsi.ISCSIExtension()
- self.fake_dev = '/dev/fake'
- self.fake_iqn = 'iqn-fake'
-
- def test_lio_not_available(self, mock_execute, mock_rtslib):
- mock_execute.return_value = ('', '')
- mock_rtslib.side_effect = EnvironmentError()
- expected = [mock.call('tgtadm', '--lld', 'iscsi', '--mode',
- 'target', '--op', 'unbind', '--tid', '1',
- '--initiator-address', 'ALL'),
- mock.call('sync'),
- mock.call('tgtadm', '--lld', 'iscsi', '--mode', 'target',
- '--op', 'delete', '--tid', '1')]
- iscsi.clean_up(self.fake_dev)
- mock_execute.assert_has_calls(expected)
-
- def test_commands_fail(self, mock_execute, mock_rtslib):
- mock_execute.side_effect = [processutils.ProcessExecutionError(),
- ('', ''),
- processutils.ProcessExecutionError()]
- mock_rtslib.side_effect = EnvironmentError()
- expected = [mock.call('tgtadm', '--lld', 'iscsi', '--mode',
- 'target', '--op', 'unbind', '--tid', '1',
- '--initiator-address', 'ALL'),
- mock.call('sync'),
- mock.call('tgtadm', '--lld', 'iscsi', '--mode', 'target',
- '--op', 'delete', '--tid', '1')]
- iscsi.clean_up(self.fake_dev)
- mock_execute.assert_has_calls(expected)
-
-
-@mock.patch.object(iscsi.rtslib_fb, 'RTSRoot', autospec=True)
-class TestISCSIExtensionCleanUp(base.IronicAgentTest):
-
- def setUp(self):
- super(TestISCSIExtensionCleanUp, self).setUp()
- self.agent_extension = iscsi.ISCSIExtension()
- self.fake_dev = '/dev/fake'
- self.fake_iqn = 'iqn-fake'
-
- 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)
diff --git a/releasenotes/notes/no-iscsi-fd21808edbea5ac2.yaml b/releasenotes/notes/no-iscsi-fd21808edbea5ac2.yaml
new file mode 100644
index 00000000..efebe63d
--- /dev/null
+++ b/releasenotes/notes/no-iscsi-fd21808edbea5ac2.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+ - |
+ Removes support for the ``iscsi`` deploy interface. Please use a ramdisk
+ from the Wallaby cycle if you need it.
diff --git a/requirements.txt b/requirements.txt
index eacef315..a08384d2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -15,7 +15,6 @@ Pint>=0.5 # BSD
psutil>=3.2.2 # BSD
pyudev>=0.18 # LGPLv2.1+
requests>=2.14.2 # Apache-2.0
-rtslib-fb>=2.1.65 # Apache-2.0
stevedore>=1.20.0 # Apache-2.0
tenacity>=6.2.0 # Apache-2.0
ironic-lib>=4.5.0 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index bea68f54..cdeb73a1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -39,7 +39,6 @@ ironic_python_agent.extensions =
clean = ironic_python_agent.extensions.clean:CleanExtension
deploy = ironic_python_agent.extensions.deploy:DeployExtension
flow = ironic_python_agent.extensions.flow:FlowExtension
- iscsi = ironic_python_agent.extensions.iscsi:ISCSIExtension
image = ironic_python_agent.extensions.image:ImageExtension
log = ironic_python_agent.extensions.log:LogExtension
rescue = ironic_python_agent.extensions.rescue:RescueExtension