Remove the iscsi extension
Change-Id: I2f0e581575112d6c7ba0d211661cab3e0b6caca6
This commit is contained in:
parent
6fb4cec7aa
commit
be3882162e
@ -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):
|
||||
|
@ -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."""
|
||||
|
||||
|
@ -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()
|
||||
@ -1009,8 +1007,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.
|
||||
|
@ -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}
|
@ -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')
|
||||
]
|
||||
|
@ -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)
|
5
releasenotes/notes/no-iscsi-fd21808edbea5ac2.yaml
Normal file
5
releasenotes/notes/no-iscsi-fd21808edbea5ac2.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Removes support for the ``iscsi`` deploy interface. Please use a ramdisk
|
||||
from the Wallaby cycle if you need it.
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user