From f14794ca2e6115a0b025f673dce5bee0ac2aec90 Mon Sep 17 00:00:00 2001 From: Julia Kreger Date: Tue, 18 Jun 2024 16:08:58 -0700 Subject: [PATCH] Remove ibmc hardware type There has been no testing of this hardware type in quite some time, and the last we heard the vendor was moving towards redfish. Change-Id: Ib32db463981ec54430884ac760956b7c7b40b17f --- devstack/lib/ironic | 24 -- doc/source/admin/drivers.rst | 1 - doc/source/admin/drivers/ibmc.rst | 323 ------------------ driver-requirements.txt | 3 - ironic/common/exception.py | 8 - ironic/conf/__init__.py | 2 - ironic/conf/ibmc.py | 35 -- ironic/drivers/ibmc.py | 47 --- ironic/drivers/modules/ibmc/management.py | 241 ------------- ironic/drivers/modules/ibmc/mappings.py | 70 ---- ironic/drivers/modules/ibmc/power.py | 149 -------- ironic/drivers/modules/ibmc/raid.py | 203 ----------- ironic/drivers/modules/ibmc/utils.py | 172 ---------- ironic/drivers/modules/ibmc/vendor.py | 112 ------ ironic/tests/unit/db/utils.py | 9 - .../tests/unit/drivers/modules/ibmc/base.py | 43 --- .../drivers/modules/ibmc/test_management.py | 294 ---------------- .../unit/drivers/modules/ibmc/test_power.py | 288 ---------------- .../unit/drivers/modules/ibmc/test_raid.py | 167 --------- .../unit/drivers/modules/ibmc/test_utils.py | 173 ---------- .../unit/drivers/modules/ibmc/test_vendor.py | 79 ----- .../unit/drivers/modules/intel_ipmi/base.py | 2 +- ironic/tests/unit/drivers/test_ibmc.py | 52 --- .../drivers/third_party_driver_mock_specs.py | 7 - .../unit/drivers/third_party_driver_mocks.py | 42 --- ...precated-ibmc-driver-0c90817d8ad0ccc4.yaml | 4 + setup.cfg | 5 - 27 files changed, 5 insertions(+), 2550 deletions(-) delete mode 100644 doc/source/admin/drivers/ibmc.rst delete mode 100644 ironic/conf/ibmc.py delete mode 100644 ironic/drivers/ibmc.py delete mode 100644 ironic/drivers/modules/ibmc/management.py delete mode 100644 ironic/drivers/modules/ibmc/mappings.py delete mode 100644 ironic/drivers/modules/ibmc/power.py delete mode 100644 ironic/drivers/modules/ibmc/raid.py delete mode 100644 ironic/drivers/modules/ibmc/utils.py delete mode 100644 ironic/drivers/modules/ibmc/vendor.py delete mode 100644 ironic/tests/unit/drivers/modules/ibmc/base.py delete mode 100644 ironic/tests/unit/drivers/modules/ibmc/test_management.py delete mode 100644 ironic/tests/unit/drivers/modules/ibmc/test_power.py delete mode 100644 ironic/tests/unit/drivers/modules/ibmc/test_raid.py delete mode 100644 ironic/tests/unit/drivers/modules/ibmc/test_utils.py delete mode 100644 ironic/tests/unit/drivers/modules/ibmc/test_vendor.py delete mode 100644 ironic/tests/unit/drivers/test_ibmc.py create mode 100644 releasenotes/notes/remove-deprecated-ibmc-driver-0c90817d8ad0ccc4.yaml diff --git a/devstack/lib/ironic b/devstack/lib/ironic index cb1ef003e5..65ea02621d 100644 --- a/devstack/lib/ironic +++ b/devstack/lib/ironic @@ -842,26 +842,11 @@ function is_deployed_by_irmc { return 1 } -function is_deployed_by_ibmc { - [[ "$IRONIC_DEPLOY_DRIVER" == ibmc ]] && return 0 - return 1 -} - function is_drac_enabled { [[ -z "${IRONIC_ENABLED_HARDWARE_TYPES%%*idrac*}" ]] && return 0 return 1 } -function is_ibmc_enabled { - [[ -z "${IRONIC_ENABLED_HARDWARE_TYPES%%*ibmc*}" ]] && return 0 - return 1 -} - -function is_irmc_enabled { - [[ -z "${IRONIC_ENABLED_HARDWARE_TYPES%%*irmc*}" ]] && return 0 - return 1 -} - function is_ansible_deploy_enabled { [[ -z "${IRONIC_ENABLED_DEPLOY_INTERFACES%%*ansible*}" ]] && return 0 return 1 @@ -1183,10 +1168,6 @@ function install_ironic { pip_install python-dracclient fi - if is_ibmc_enabled; then - pip_install python-ibmcclient - fi - if is_irmc_enabled; then pip_install python-scciclient pysnmp fi @@ -2598,11 +2579,6 @@ function enroll_nodes { if [[ -n "$IRONIC_DEPLOY_ISO_ID" ]]; then node_options+=" --driver-info deploy_iso=$IRONIC_DEPLOY_ISO_ID" fi - elif is_deployed_by_ibmc; then - node_options+=" --driver-info ibmc_address=$bmc_address \ - --driver-info ibmc_username=$bmc_username \ - --driver-info ibmc_password=$bmc_passwd \ - --driver-info ibmc_verify_ca=False" fi interface_info="${mac_address}" diff --git a/doc/source/admin/drivers.rst b/doc/source/admin/drivers.rst index 988f1376a1..e2bae7d9da 100644 --- a/doc/source/admin/drivers.rst +++ b/doc/source/admin/drivers.rst @@ -17,7 +17,6 @@ Hardware Types .. toctree:: :maxdepth: 1 - drivers/ibmc drivers/idrac drivers/ilo drivers/intel-ipmi diff --git a/doc/source/admin/drivers/ibmc.rst b/doc/source/admin/drivers/ibmc.rst deleted file mode 100644 index 8a173a8ad8..0000000000 --- a/doc/source/admin/drivers/ibmc.rst +++ /dev/null @@ -1,323 +0,0 @@ -=============== -iBMC driver -=============== - -Overview -======== - -.. warning:: - The ``ibmc`` driver has been deprecated and is anticipated to be removed - from Ironic at some point during or after the 2024.2 development cycle. - The anticipated forward management path is to migrate to the ``redfish`` - hardware type. - -The ``ibmc`` driver is targeted for Huawei V5 series rack server such as -2288H V5, CH121 V5. The iBMC hardware type enables the user to take advantage -of features of `Huawei iBMC`_ to control Huawei server. - -The ``ibmc`` hardware type supports the following Ironic interfaces: - -* Management Interface: Boot device management -* Power Interface: Power management -* `RAID Interface`_: RAID controller and disk management -* `Vendor Interface`_: ibmc passthru interfaces - -Prerequisites -============= - -The `HUAWEI iBMC Client library`_ should be installed on the ironic conductor - node(s). - -For example, it can be installed with ``pip``:: - - sudo pip install python-ibmcclient - -Enabling the iBMC driver -============================ - -#. Add ``ibmc`` to the list of ``enabled_hardware_types``, - ``enabled_power_interfaces``, ``enabled_vendor_interfaces`` - and ``enabled_management_interfaces`` in ``/etc/ironic/ironic.conf``. For example:: - - [DEFAULT] - ... - enabled_hardware_types = ibmc - enabled_power_interfaces = ibmc - enabled_management_interfaces = ibmc - enabled_raid_interfaces = ibmc - enabled_vendor_interfaces = ibmc - -#. Restart the ironic conductor service:: - - sudo service ironic-conductor restart - - # Or, for RDO: - sudo systemctl restart openstack-ironic-conductor - -Registering a node with the iBMC driver -=========================================== - -Nodes configured to use the driver should have the ``driver`` property -set to ``ibmc``. - -The following properties are specified in the node's ``driver_info`` -field: - -- ``ibmc_address``: - - The URL address to the ibmc controller. It must - include the authority portion of the URL, and can - optionally include the scheme. If the scheme is - missing, https is assumed. - For example: https://ibmc.example.com. This is required. - -- ``ibmc_username``: - - User account with admin/server-profile access - privilege. This is required. - -- ``ibmc_password``: - - User account password. This is required. - -- ``ibmc_verify_ca``: - - If ibmc_address has the **https** scheme, the - driver will use a secure (TLS_) connection when - talking to the ibmc controller. By default - (if this is set to True), the driver will try to - verify the host certificates. This can be set to - the path of a certificate file or directory with - trusted certificates that the driver will use for - verification. To disable verifying TLS_, set this - to False. This is optional. - -The ``baremetal node create`` command can be used to enroll -a node with the ``ibmc`` driver. For example: - -.. code-block:: bash - - baremetal node create --driver ibmc - --driver-info ibmc_address=https://example.com \ - --driver-info ibmc_username=admin \ - --driver-info ibmc_password=password - -For more information about enrolling nodes see :ref:`enrollment` -in the install guide. - -RAID Interface -============== - -Currently, only RAID controller which supports OOB management can be managed. - -See :doc:`/admin/raid` for more information on Ironic RAID support. - -The following properties are supported by the iBMC raid interface -implementation, ``ibmc``: - -Mandatory properties --------------------- - -* ``size_gb``: Size in gigabytes (integer) for the logical disk. Use ``MAX`` as - ``size_gb`` if this logical disk is supposed to use the rest of the space - available. -* ``raid_level``: RAID level for the logical disk. Valid values are - ``JBOD``, ``0``, ``1``, ``5``, ``6``, ``1+0``, ``5+0`` and ``6+0``. And it - is possible that some RAID controllers can only support a subset RAID - levels. - -.. NOTE:: - RAID level ``2`` is not supported by ``iBMC`` driver. - -Optional properties -------------------- - -* ``is_root_volume``: Optional. Specifies whether this disk is a root volume. - By default, this is ``False``. -* ``volume_name``: Optional. Name of the volume to be created. If this is not - specified, it will be N/A. - -Backing physical disk hints ---------------------------- - -See :doc:`/admin/raid` for more information on backing disk hints. - -These are machine-independent properties. The hints are specified for each -logical disk to help Ironic find the desired disks for RAID configuration. - -* ``share_physical_disks`` -* ``disk_type`` -* ``interface_type`` -* ``number_of_physical_disks`` - -Backing physical disks ----------------------- - -These are HUAWEI RAID controller dependent properties: - -* ``controller``: Optional. Supported values are: RAID storage id, - RAID storage name or RAID controller name. If a bare metal server have more - than one controller, this is mandatory. Typical values would look like: - - * RAID Storage Id: ``RAIDStorage0`` - * RAID Storage Name: ``RAIDStorage0`` - * RAID Controller Name: ``RAID Card1 Controller``. - -* ``physical_disks``: Optional. Supported values are: disk-id, disk-name or - disk serial number. Typical values for hdd disk would look like: - - * Disk Id: ``HDDPlaneDisk0`` - * Disk Name: ``Disk0``. - * Disk SerialNumber: ``38DGK77LF77D`` - -Delete RAID configuration -------------------------- - -For ``delete_configuration`` step, ``ibmc`` will do: - -* delete all logical disks -* delete all hot-spare disks - -Logical disks creation priority -------------------------------- - -Logical Disks creation priority based on three properties: - -* ``share_physical_disks`` -* ``physical_disks`` -* ``size_gb`` - -The logical disks creation priority strictly follow the table below, if -multiple logical disks have the same priority, then they will be created with -the same order in ``logical_disks`` array. - -==================== ========================== ========= -Share physical disks Specified Physical Disks Size -==================== ========================== ========= -no yes int|max -no no int -yes yes int -yes yes max -yes no int -yes no max -no no max -==================== ========================== ========= - -Physical disks choice strategy ------------------------------- - -.. note:: - physical-disk-group: a group of physical disks which have been used by some - logical-disks with same RAID level. - - -* If no ``physical_disks`` are specified, the "waste least" strategy will be - used to choose the physical disks. - - * waste least disk capacity: when using disks with different capacity, it - will cause a waste of disk capacity. This is to avoid with highest - priority. - * using least total disk capacity: for example, we can create 400G RAID 5 - with both 5 100G-disks and 3 200G-disks. 5 100G disks is a better - strategy because it uses a 500G capacity totally. While 3 200G-disks - are 600G totally. - * using least disk count: finally, if waste capacity and total disk - capacity are both the same (it rarely happens?), we will choose the one - with the minimum number of disks. - -* when ``share_physical_disks`` option is present, ``ibmc`` driver will - create logical disk upon existing physical-disk-group list first. Only - when no existing physical-disk-group matches, then it chooses unused - physical disks with same strategy described above. When multiple exists - physical-disk-groups matches, it will use "waste least" strategy too, - the bigger capacity left the better. For example, to create a logical disk - shown below on a ``ibmc`` server which has two RAID5 logical disks already. - And the shareable capacity of this two logical-disks are 500G and 300G, - then ``ibmc`` driver will choose the second one. - - .. code-block:: json - - { - "logical_disks": [ - { - "controller": "RAID Card1 Controller", - "raid_level": "5", - "size_gb": 100, - "share_physical_disks": true - } - ] - } - - And the ``ibmc`` server has two RAID5 logical disks already. - -* When ``size_gb`` is set to ``MAX``, ``ibmc`` driver will auto work through - all possible cases and choose the "best" solution which has the biggest - capacity and use least capacity. For example: to create a RAID 5+0 logical - disk with MAX size in a server has 9 200G-disks, it will finally choose - "8 disks + span-number 2" but not "9 disks + span-number 3". Although they - both have 1200G capacity totally, but the former uses only 8 disks and the - latter uses 9 disks. If you want to choose the latter solution, you can - specified the disk count to use by adding ``number_of_physical_disks`` - option. - - .. code-block:: json - - { - "logical_disks": [ - { - "controller": "RAID Card1 Controller", - "raid_level": "5+0", - "size_gb": "MAX" - } - ] - } - - -Examples --------- - -In a typical scenario we may want to create: - * RAID 5, 500G, root OS volume with 3 disks - * RAID 5, rest available space, data volume with rest disks - -.. code-block:: json - - { - "logical_disks": [ - { - "volume_name": "os_volume", - "controller": "RAID Card1 Controller", - "is_root_volume": "True", - "physical_disks": [ - "Disk0", - "Disk1", - "Disk2" - ], - "raid_level": "5", - "size_gb": "500" - }, - { - "volume_name": "data_volume", - "controller": "RAID Card1 Controller", - "raid_level": "5", - "size_gb": "MAX" - } - ] - } - -Vendor Interface -========================================= - -The ``ibmc`` hardware type provides vendor passthru interfaces shown below: - - -======================== ============ ====================================== -Method Name HTTP Method Description -======================== ============ ====================================== -boot_up_seq GET Query boot up sequence -get_raid_controller_list GET Query RAID controller summary info -======================== ============ ====================================== - -.. _Huawei iBMC: https://e.huawei.com/en/products/computing/kunpeng/accessories/ibmc -.. _TLS: https://en.wikipedia.org/wiki/Transport_Layer_Security -.. _HUAWEI iBMC Client library: https://pypi.org/project/python-ibmcclient/ diff --git a/driver-requirements.txt b/driver-requirements.txt index f02e65f4ef..1b8cb28a22 100644 --- a/driver-requirements.txt +++ b/driver-requirements.txt @@ -14,8 +14,5 @@ python-dracclient>=5.1.0,<9.0.0 # Ansible-deploy interface ansible>=2.7 -# HUAWEI iBMC hardware type uses the python-ibmcclient library -python-ibmcclient>=0.2.2,<0.3.0 - # Dell EMC iDRAC sushy OEM extension sushy-oem-idrac>=5.0.0,<6.0.0 diff --git a/ironic/common/exception.py b/ironic/common/exception.py index 1b5f0f9ae4..b838b745c1 100644 --- a/ironic/common/exception.py +++ b/ironic/common/exception.py @@ -720,14 +720,6 @@ class InvalidKickstartFile(Invalid): _msg_fmt = _("The kickstart file is not valid.") -class IBMCError(DriverOperationError): - _msg_fmt = _("IBMC exception occurred on node %(node)s. Error: %(error)s") - - -class IBMCConnectionError(IBMCError): - _msg_fmt = _("IBMC connection failed for node %(node)s: %(error)s") - - class ClientSideError(RuntimeError): def __init__(self, msg=None, status_code=400, faultcode='Client'): self.msg = msg diff --git a/ironic/conf/__init__.py b/ironic/conf/__init__.py index 44a04f5024..91d298a365 100644 --- a/ironic/conf/__init__.py +++ b/ironic/conf/__init__.py @@ -32,7 +32,6 @@ from ironic.conf import drac from ironic.conf import fake from ironic.conf import glance from ironic.conf import healthcheck -from ironic.conf import ibmc from ironic.conf import ilo from ironic.conf import inspector from ironic.conf import inventory @@ -69,7 +68,6 @@ dnsmasq.register_opts(CONF) fake.register_opts(CONF) glance.register_opts(CONF) healthcheck.register_opts(CONF) -ibmc.register_opts(CONF) ilo.register_opts(CONF) inspector.register_opts(CONF) inventory.register_opts(CONF) diff --git a/ironic/conf/ibmc.py b/ironic/conf/ibmc.py deleted file mode 100644 index 82c5eaac75..0000000000 --- a/ironic/conf/ibmc.py +++ /dev/null @@ -1,35 +0,0 @@ -# -# 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. - -# Version 1.0.0 - -from oslo_config import cfg - -from ironic.common.i18n import _ - -opts = [ - cfg.IntOpt('connection_attempts', - min=1, - default=5, - help=_('Maximum number of attempts to try to connect ' - 'to iBMC')), - cfg.IntOpt('connection_retry_interval', - min=1, - default=4, - help=_('Number of seconds to wait between attempts to ' - 'connect to iBMC')) -] - - -def register_opts(conf): - conf.register_opts(opts, group='ibmc') diff --git a/ironic/drivers/ibmc.py b/ironic/drivers/ibmc.py deleted file mode 100644 index 5f37d520e5..0000000000 --- a/ironic/drivers/ibmc.py +++ /dev/null @@ -1,47 +0,0 @@ -# -# 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. -""" -iBMC Driver for managing HUAWEI V5 series rack servers such as 2288H V5, -CH121 V5. -""" - -from ironic.drivers import generic -from ironic.drivers.modules.ibmc import management as ibmc_mgmt -from ironic.drivers.modules.ibmc import power as ibmc_power -from ironic.drivers.modules.ibmc import raid as ibmc_raid -from ironic.drivers.modules.ibmc import vendor as ibmc_vendor -from ironic.drivers.modules import noop - - -class IBMCHardware(generic.GenericHardware): - """Huawei iBMC hardware type.""" - - @property - def supported_management_interfaces(self): - """List of supported management interfaces.""" - return [ibmc_mgmt.IBMCManagement] - - @property - def supported_power_interfaces(self): - """List of supported power interfaces.""" - return [ibmc_power.IBMCPower] - - @property - def supported_vendor_interfaces(self): - """List of supported vendor interfaces.""" - return [ibmc_vendor.IBMCVendor, noop.NoVendor] - - @property - def supported_raid_interfaces(self): - """List of supported raid interfaces.""" - return [ibmc_raid.IbmcRAID, noop.NoRAID] diff --git a/ironic/drivers/modules/ibmc/management.py b/ironic/drivers/modules/ibmc/management.py deleted file mode 100644 index b6420dc016..0000000000 --- a/ironic/drivers/modules/ibmc/management.py +++ /dev/null @@ -1,241 +0,0 @@ -# Copyright 2019 HUAWEI, Inc. All Rights Reserved. -# Copyright 2017 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. -""" -iBMC Management Interface -""" - -from oslo_log import log -from oslo_utils import importutils - -from ironic.common import exception -from ironic.common.i18n import _ -from ironic.conductor import task_manager -from ironic.drivers import base -from ironic.drivers.modules.ibmc import mappings -from ironic.drivers.modules.ibmc import utils - -constants = importutils.try_import('ibmc_client.constants') -ibmc_client = importutils.try_import('ibmc_client') - -LOG = log.getLogger(__name__) - - -class IBMCManagement(base.ManagementInterface): - - # NOTE(TheJulia): Deprecating November 2023 in favor of Redfish - # and due to a lack of active driver maintenance. - supported = False - - def __init__(self): - """Initialize the iBMC management interface - - :raises: DriverLoadError if the driver can't be loaded due to - missing dependencies - """ - super(IBMCManagement, self).__init__() - if not ibmc_client: - raise exception.DriverLoadError( - driver='ibmc', - reason=_('Unable to import the python-ibmcclient library')) - - def get_properties(self): - """Return the properties of the interface. - - :returns: dictionary of : entries. - """ - return utils.COMMON_PROPERTIES.copy() - - def validate(self, task): - """Validates the driver information needed by the iBMC driver. - - :param task: A TaskManager instance containing the node to act on. - :raises: InvalidParameterValue on malformed parameter(s) - :raises: MissingParameterValue on missing parameter(s) - """ - utils.parse_driver_info(task.node) - - @utils.handle_ibmc_exception('get iBMC supported boot devices') - def get_supported_boot_devices(self, task): - """Get a list of the supported boot devices. - - :param task: a task from TaskManager. - :raises: InvalidParameterValue on malformed parameter(s) - :raises: MissingParameterValue on missing parameter(s) - :raises: IBMCConnectionError when it fails to connect to iBMC - :raises: IBMCError when iBMC responses an error information - :returns: A list with the supported boot devices defined - in :mod:`ironic.common.boot_devices`. - """ - ibmc = utils.parse_driver_info(task.node) - with ibmc_client.connect(**ibmc) as conn: - system = conn.system.get() - boot_source_override = system.boot_source_override - return list(map(mappings.GET_BOOT_DEVICE_MAP.get, - boot_source_override.supported_boot_devices)) - - @task_manager.require_exclusive_lock - @utils.handle_ibmc_exception('set iBMC boot device') - def set_boot_device(self, task, device, persistent=False): - """Set the boot device for a node. - - :param task: A task from TaskManager. - :param device: The boot device, one of - :mod:`ironic.common.boot_device`. - :param persistent: Boolean value. True if the boot device will - persist to all future boots, False if not. - Default: False. - :raises: InvalidParameterValue on malformed parameter(s) - :raises: MissingParameterValue on missing parameter(s) - :raises: IBMCConnectionError when it fails to connect to iBMC - :raises: IBMCError when iBMC responses an error information - """ - ibmc = utils.parse_driver_info(task.node) - with ibmc_client.connect(**ibmc) as conn: - boot_device = mappings.SET_BOOT_DEVICE_MAP[device] - enabled = mappings.SET_BOOT_DEVICE_PERSISTENT_MAP[persistent] - conn.system.set_boot_source(boot_device, enabled=enabled) - - @utils.handle_ibmc_exception('get iBMC boot device') - def get_boot_device(self, task): - """Get the current boot device for a node. - - :param task: A task from TaskManager. - :raises: InvalidParameterValue on malformed parameter(s) - :raises: MissingParameterValue on missing parameter(s) - :raises: IBMCConnectionError when it fails to connect to iBMC - :raises: IBMCError when iBMC responses an error information - :returns: a dictionary containing: - - :boot_device: - the boot device, one of :mod:`ironic.common.boot_devices` or - None if it is unknown. - :persistent: - Boolean value or None, True if the boot device persists, - False otherwise. None if it's disabled. - - """ - ibmc = utils.parse_driver_info(task.node) - with ibmc_client.connect(**ibmc) as conn: - system = conn.system.get() - boot_source_override = system.boot_source_override - boot_device = boot_source_override.target - enabled = boot_source_override.enabled - return { - 'boot_device': mappings.GET_BOOT_DEVICE_MAP.get(boot_device), - 'persistent': - mappings.GET_BOOT_DEVICE_PERSISTENT_MAP.get(enabled) - } - - def get_supported_boot_modes(self, task): - """Get a list of the supported boot modes. - - :param task: A task from TaskManager. - :returns: A list with the supported boot modes defined - in :mod:`ironic.common.boot_modes`. If boot - mode support can't be determined, empty list - is returned. - """ - return list(mappings.SET_BOOT_MODE_MAP) - - @task_manager.require_exclusive_lock - @utils.handle_ibmc_exception('set iBMC boot mode') - def set_boot_mode(self, task, mode): - """Set the boot mode for a node. - - Set the boot mode to use on next reboot of the node. - - :param task: A task from TaskManager. - :param mode: The boot mode, one of - :mod:`ironic.common.boot_modes`. - :raises: InvalidParameterValue on malformed parameter(s) - :raises: MissingParameterValue on missing parameter(s) - :raises: IBMCConnectionError when it fails to connect to iBMC - :raises: IBMCError when iBMC responses an error information - """ - ibmc = utils.parse_driver_info(task.node) - with ibmc_client.connect(**ibmc) as conn: - system = conn.system.get() - boot_source_override = system.boot_source_override - boot_device = boot_source_override.target - boot_override = boot_source_override.enabled - - # Copied from redfish driver - # TODO(Qianbiao.NG) what if boot device is "NONE"? - if not boot_device: - error_msg = (_('Cannot change boot mode on node %(node)s ' - 'because its boot device is not set.') % - {'node': task.node.uuid}) - LOG.error(error_msg) - raise exception.IBMCError(error_msg) - - # TODO(Qianbiao.NG) what if boot override is "disabled"? - if not boot_override: - i18n = _('Cannot change boot mode on node %(node)s ' - 'because its boot source override is not set.') - error_msg = i18n % {'node': task.node.uuid} - LOG.error(error_msg) - raise exception.IBMCError(error_msg) - - boot_mode = mappings.SET_BOOT_MODE_MAP[mode] - conn.system.set_boot_source(boot_device, - enabled=boot_override, - mode=boot_mode) - - @utils.handle_ibmc_exception('get iBMC boot mode') - def get_boot_mode(self, task): - """Get the current boot mode for a node. - - Provides the current boot mode of the node. - - :param task: A task from TaskManager. - :raises: InvalidParameterValue on malformed parameter(s) - :raises: MissingParameterValue on missing parameter(s) - :raises: IBMCConnectionError when it fails to connect to iBMC - :raises: IBMCError when iBMC responses an error information - :returns: The boot mode, one of :mod:`ironic.common.boot_mode` or - None if it is unknown. - """ - ibmc = utils.parse_driver_info(task.node) - with ibmc_client.connect(**ibmc) as conn: - system = conn.system.get() - boot_source_override = system.boot_source_override - boot_mode = boot_source_override.mode - return mappings.GET_BOOT_MODE_MAP.get(boot_mode) - - def get_sensors_data(self, task): - """Get sensors data. - - Not implemented for this driver. - - :raises: NotImplementedError - """ - raise NotImplementedError() - - @task_manager.require_exclusive_lock - @utils.handle_ibmc_exception('inject iBMC NMI') - def inject_nmi(self, task): - """Inject NMI, Non Maskable Interrupt. - - Inject NMI (Non Maskable Interrupt) for a node immediately. - - :param task: A TaskManager instance containing the node to act on. - :raises: InvalidParameterValue on malformed parameter(s) - :raises: MissingParameterValue on missing parameter(s) - :raises: IBMCConnectionError when it fails to connect to iBMC - :raises: IBMCError when iBMC responses an error information - """ - ibmc = utils.parse_driver_info(task.node) - with ibmc_client.connect(**ibmc) as conn: - conn.system.reset(constants.RESET_NMI) diff --git a/ironic/drivers/modules/ibmc/mappings.py b/ironic/drivers/modules/ibmc/mappings.py deleted file mode 100644 index e303a72ea0..0000000000 --- a/ironic/drivers/modules/ibmc/mappings.py +++ /dev/null @@ -1,70 +0,0 @@ -# -# 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. -""" -iBMC and Ironic constants mapping -""" - -from oslo_utils import importutils - -from ironic.common import boot_devices -from ironic.common import boot_modes -from ironic.common import states -from ironic.drivers.modules.ibmc import utils - -constants = importutils.try_import('ibmc_client.constants') - -if constants: - # Set power state mapping - SET_POWER_STATE_MAP = { - states.POWER_ON: constants.RESET_ON, - states.POWER_OFF: constants.RESET_FORCE_OFF, - states.REBOOT: constants.RESET_FORCE_RESTART, - states.SOFT_REBOOT: constants.RESET_FORCE_POWER_CYCLE, - states.SOFT_POWER_OFF: constants.RESET_GRACEFUL_SHUTDOWN, - } - - # Get power state mapping - GET_POWER_STATE_MAP = { - constants.SYSTEM_POWER_STATE_ON: states.POWER_ON, - constants.SYSTEM_POWER_STATE_OFF: states.POWER_OFF, - } - - # Boot device mapping - GET_BOOT_DEVICE_MAP = { - constants.BOOT_SOURCE_TARGET_NONE: 'none', - constants.BOOT_SOURCE_TARGET_PXE: boot_devices.PXE, - constants.BOOT_SOURCE_TARGET_FLOPPY: 'floppy', - constants.BOOT_SOURCE_TARGET_CD: boot_devices.CDROM, - constants.BOOT_SOURCE_TARGET_HDD: boot_devices.DISK, - constants.BOOT_SOURCE_TARGET_BIOS_SETUP: boot_devices.BIOS, - } - - SET_BOOT_DEVICE_MAP = utils.revert_dictionary(GET_BOOT_DEVICE_MAP) - - # Boot mode mapping - GET_BOOT_MODE_MAP = { - constants.BOOT_SOURCE_MODE_BIOS: boot_modes.LEGACY_BIOS, - constants.BOOT_SOURCE_MODE_UEFI: boot_modes.UEFI, - } - - SET_BOOT_MODE_MAP = utils.revert_dictionary(GET_BOOT_MODE_MAP) - - # Boot device persistent mapping - GET_BOOT_DEVICE_PERSISTENT_MAP = { - constants.BOOT_SOURCE_ENABLED_ONCE: False, - constants.BOOT_SOURCE_ENABLED_CONTINUOUS: True, - constants.BOOT_SOURCE_ENABLED_DISABLED: None, - } - - SET_BOOT_DEVICE_PERSISTENT_MAP = utils.revert_dictionary( - GET_BOOT_DEVICE_PERSISTENT_MAP) diff --git a/ironic/drivers/modules/ibmc/power.py b/ironic/drivers/modules/ibmc/power.py deleted file mode 100644 index 9c2e2b4acd..0000000000 --- a/ironic/drivers/modules/ibmc/power.py +++ /dev/null @@ -1,149 +0,0 @@ -# -# 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. -""" -iBMC Power Interface -""" - -from oslo_log import log -from oslo_utils import importutils - -from ironic.common import exception -from ironic.common.i18n import _ -from ironic.common import states -from ironic.conductor import task_manager -from ironic.conductor import utils as cond_utils -from ironic.drivers import base -from ironic.drivers.modules.ibmc import mappings -from ironic.drivers.modules.ibmc import utils - -constants = importutils.try_import('ibmc_client.constants') -ibmc_client = importutils.try_import('ibmc_client') - -LOG = log.getLogger(__name__) - -EXPECT_POWER_STATE_MAP = { - states.REBOOT: states.POWER_ON, - states.SOFT_REBOOT: states.POWER_ON, - states.SOFT_POWER_OFF: states.POWER_OFF, -} - - -class IBMCPower(base.PowerInterface): - - # NOTE(TheJulia): Deprecating November 2023 in favor of Redfish - # and due to a lack of active driver maintenance. - supported = False - - def __init__(self): - """Initialize the iBMC power interface. - - :raises: DriverLoadError if the driver can't be loaded due to - missing dependencies - """ - super(IBMCPower, self).__init__() - if not ibmc_client: - raise exception.DriverLoadError( - driver='ibmc', - reason=_('Unable to import the python-ibmcclient library')) - - def get_properties(self): - """Return the properties of the interface. - - :returns: dictionary of : entries. - """ - return utils.COMMON_PROPERTIES.copy() - - def validate(self, task): - """Validates the driver information needed by the iBMC driver. - - :param task: A TaskManager instance containing the node to act on. - :raises: InvalidParameterValue on malformed parameter(s) - :raises: MissingParameterValue on missing parameter(s) - """ - utils.parse_driver_info(task.node) - - @utils.handle_ibmc_exception('get iBMC power state') - def get_power_state(self, task): - """Get the current power state of the task's node. - - :param task: A TaskManager instance containing the node to act on. - :returns: A power state. One of :mod:`ironic.common.states`. - :raises: InvalidParameterValue on malformed parameter(s) - :raises: MissingParameterValue on missing parameter(s) - :raises: IBMCConnectionError when it fails to connect to iBMC - :raises: IBMCError when iBMC responses an error information - """ - ibmc = utils.parse_driver_info(task.node) - with ibmc_client.connect(**ibmc) as conn: - system = conn.system.get() - return mappings.GET_POWER_STATE_MAP.get(system.power_state) - - @task_manager.require_exclusive_lock - @utils.handle_ibmc_exception('set iBMC power state') - def set_power_state(self, task, power_state, timeout=None): - """Set the power state of the task's node. - - :param task: A TaskManager instance containing the node to act on. - :param power_state: Any power state from :mod:`ironic.common.states`. - :param timeout: Time to wait for the node to reach the requested state. - :raises: InvalidParameterValue on malformed parameter(s) - :raises: MissingParameterValue if a required parameter is missing. - :raises: IBMCConnectionError when it fails to connect to iBMC - :raises: IBMCError when iBMC responses an error information - """ - ibmc = utils.parse_driver_info(task.node) - with ibmc_client.connect(**ibmc) as conn: - reset_type = mappings.SET_POWER_STATE_MAP.get(power_state) - conn.system.reset(reset_type) - - target_state = EXPECT_POWER_STATE_MAP.get(power_state, power_state) - cond_utils.node_wait_for_power_state(task, target_state, - timeout=timeout) - - @task_manager.require_exclusive_lock - @utils.handle_ibmc_exception('reboot iBMC') - def reboot(self, task, timeout=None): - """Perform a hard reboot of the task's node. - - :param task: A TaskManager instance containing the node to act on. - :param timeout: Time to wait for the node to become powered on. - :raises: InvalidParameterValue on malformed parameter(s) - :raises: MissingParameterValue if a required parameter is missing. - :raises: IBMCConnectionError when it fails to connect to iBMC - :raises: IBMCError when iBMC responses an error information - """ - ibmc = utils.parse_driver_info(task.node) - with ibmc_client.connect(**ibmc) as conn: - system = conn.system.get() - current_power_state = ( - mappings.GET_POWER_STATE_MAP.get(system.power_state) - ) - if current_power_state == states.POWER_ON: - conn.system.reset( - mappings.SET_POWER_STATE_MAP.get(states.REBOOT)) - else: - conn.system.reset( - mappings.SET_POWER_STATE_MAP.get(states.POWER_ON)) - - cond_utils.node_wait_for_power_state(task, states.POWER_ON, - timeout=timeout) - - def get_supported_power_states(self, task): - """Get a list of the supported power states. - - :param task: A TaskManager instance containing the node to act on. - Not used by this driver at the moment. - :returns: A list with the supported power states defined - in :mod:`ironic.common.states`. - """ - return list(mappings.SET_POWER_STATE_MAP) diff --git a/ironic/drivers/modules/ibmc/raid.py b/ironic/drivers/modules/ibmc/raid.py deleted file mode 100644 index bc0ee1623f..0000000000 --- a/ironic/drivers/modules/ibmc/raid.py +++ /dev/null @@ -1,203 +0,0 @@ -# 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. - -""" -iBMC RAID configuration specific methods -""" - -from ironic_lib import metrics_utils -from oslo_log import log as logging -from oslo_utils import importutils - -from ironic.common.i18n import _ -from ironic.common import raid -from ironic import conf -from ironic.drivers import base -from ironic.drivers.modules.ibmc import utils - -constants = importutils.try_import('ibmc_client.constants') -ibmc_client = importutils.try_import('ibmc_client') -ibmc_error = importutils.try_import('ibmc_client.exceptions') - -CONF = conf.CONF -LOG = logging.getLogger(__name__) -METRICS = metrics_utils.get_metrics_logger(__name__) - - -class IbmcRAID(base.RAIDInterface): - """Implementation of RAIDInterface for iBMC.""" - - # NOTE(TheJulia): Deprecating November 2023 in favor of Redfish - # and due to a lack of active driver maintenance. - supported = False - - RAID_APPLY_CONFIGURATION_ARGSINFO = { - "raid_config": { - "description": "The RAID configuration to apply.", - "required": True, - }, - "create_root_volume": { - "description": ( - "Setting this to 'False' indicates not to create root " - "volume that is specified in 'raid_config'. Default " - "value is 'True'." - ), - "required": False, - }, - "create_nonroot_volumes": { - "description": ( - "Setting this to 'False' indicates not to create " - "non-root volumes (all except the root volume) in " - "'raid_config'. Default value is 'True'." - ), - "required": False, - }, - "delete_existing": { - "description": ( - "Setting this to 'True' indicates to delete existing RAID " - "configuration prior to creating the new configuration. " - "Default value is 'True'." - ), - "required": False, - } - } - - def get_properties(self): - """Return the properties of the interface. - - :returns: dictionary of : entries. - """ - return utils.COMMON_PROPERTIES.copy() - - @utils.handle_ibmc_exception('delete iBMC RAID configuration') - def _delete_raid_configuration(self, task): - """Delete the RAID configuration through `python-ibmcclient` lib. - - :param task: a TaskManager instance containing the node to act on. - """ - ibmc = utils.parse_driver_info(task.node) - with ibmc_client.connect(**ibmc) as conn: - # NOTE(qianbiao.ng): To reduce review workload, we should keep all - # delete logic in python-ibmcclient. And delete raid configuration - # logic should be synchronized. if async required, do it in - # python-ibmcclient. - conn.system.storage.delete_all_raid_configuration() - - @utils.handle_ibmc_exception('create iBMC RAID configuration') - def _create_raid_configuration(self, task, logical_disks): - """Create the RAID configuration through `python-ibmcclient` lib. - - :param task: a TaskManager instance containing the node to act on. - :param logical_disks: a list of JSON dictionaries which represents - the logical disks to be created. The JSON dictionary should match - the (ironic.drivers.raid_config_schema.json) scheme. - """ - ibmc = utils.parse_driver_info(task.node) - with ibmc_client.connect(**ibmc) as conn: - # NOTE(qianbiao.ng): To reduce review workload, we should keep all - # apply logic in python-ibmcclient. And apply raid configuration - # logic should be synchronized. if async required, do it in - # python-ibmcclient. - conn.system.storage.apply_raid_configuration(logical_disks) - - @base.deploy_step(priority=0, - argsinfo=RAID_APPLY_CONFIGURATION_ARGSINFO) - def apply_configuration(self, task, raid_config, create_root_volume=True, - create_nonroot_volumes=False): - return super(IbmcRAID, self).apply_configuration( - task, raid_config, create_root_volume=create_root_volume, - create_nonroot_volumes=create_nonroot_volumes) - - @METRICS.timer('IbmcRAID.create_configuration') - @base.clean_step(priority=0, abortable=False, argsinfo={ - 'create_root_volume': { - 'description': ('This specifies whether to create the root ' - 'volume. Defaults to `True`.'), - 'required': False - }, - 'create_nonroot_volumes': { - 'description': ('This specifies whether to create the non-root ' - 'volumes. Defaults to `True`.'), - 'required': False - }, - "delete_existing": { - "description": ("Setting this to 'True' indicates to delete " - "existing RAID configuration prior to creating " - "the new configuration. " - "Default value is 'False'."), - "required": False, - } - }) - def create_configuration(self, task, create_root_volume=True, - create_nonroot_volumes=True, - delete_existing=False): - """Create a RAID configuration. - - This method creates a RAID configuration on the given node. - - :param task: a TaskManager instance. - :param create_root_volume: If True, a root volume is created - during RAID configuration. Otherwise, no root volume is - created. Default is True. - :param create_nonroot_volumes: If True, non-root volumes are - created. If False, no non-root volumes are created. Default - is True. - :param delete_existing: Setting this to True indicates to delete RAID - configuration prior to creating the new configuration. Default is - False. - :raises: MissingParameterValue, if node.target_raid_config is missing - or empty after skipping root volume and/or non-root volumes. - :raises: IBMCError, on failure to execute step. - """ - node = task.node - raid_config = raid.filter_target_raid_config( - node, create_root_volume=create_root_volume, - create_nonroot_volumes=create_nonroot_volumes) - LOG.info(_("Invoke RAID create_configuration step for node %s(uuid). " - "Current provision state is: %(status)s. " - "Target RAID configuration is: %(config)s."), - {'uuid': node.uuid, 'status': node.provision_state, - 'target': raid_config}) - - # cache current raid config to node's driver_internal_info - node.driver_internal_info['raid_config'] = raid_config - node.save() - - # delete exist volumes if necessary - if delete_existing: - self._delete_raid_configuration(task) - - # create raid configuration - logical_disks = raid_config.get('logical_disks', []) - self._create_raid_configuration(task, logical_disks) - LOG.info(_("Succeed to create raid configuration on node %s."), - task.node.uuid) - - @METRICS.timer('IbmcRAID.delete_configuration') - @base.clean_step(priority=0, abortable=False) - @base.deploy_step(priority=0) - def delete_configuration(self, task): - """Delete the RAID configuration. - - :param task: a TaskManager instance containing the node to act on. - :returns: states.CLEANWAIT if cleaning operation in progress - asynchronously or states.DEPLOYWAIT if deploy operation in - progress synchronously or None if it is completed. - :raises: IBMCError, on failure to execute step. - """ - node = task.node - LOG.info("Invoke RAID delete_configuration step for node %s(uuid). " - "Current provision state is: %(status)s. ", - {'uuid': node.uuid, 'status': node.provision_state}) - self._delete_raid_configuration(task) - LOG.info(_("Succeed to delete raid configuration on node %s."), - task.node.uuid) diff --git a/ironic/drivers/modules/ibmc/utils.py b/ironic/drivers/modules/ibmc/utils.py deleted file mode 100644 index 44113c0fe4..0000000000 --- a/ironic/drivers/modules/ibmc/utils.py +++ /dev/null @@ -1,172 +0,0 @@ -# -# 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. -""" -iBMC Driver common utils -""" - -import functools -import os - -from oslo_log import log -from oslo_utils import importutils -from oslo_utils import netutils -from oslo_utils import strutils -import tenacity - -from ironic.common import exception -from ironic.common.i18n import _ -from ironic.conductor import task_manager -from ironic.conf import CONF - -ibmc_client = importutils.try_import('ibmcclient') -ibmc_error = importutils.try_import('ibmc_client.exceptions') - -LOG = log.getLogger(__name__) - -REQUIRED_PROPERTIES = { - 'ibmc_address': _('The URL address to the iBMC controller. It must ' - 'include the authority portion of the URL. ' - 'If the scheme is missing, https is assumed. ' - 'For example: https://mgmt.vendor.com. Required.'), - 'ibmc_username': _('User account with admin/server-profile access ' - 'privilege. Required.'), - 'ibmc_password': _('User account password. Required.'), -} - -OPTIONAL_PROPERTIES = { - 'ibmc_verify_ca': _('Either a Boolean value, a path to a CA_BUNDLE ' - 'file or directory with certificates of trusted ' - 'CAs. If set to True the driver will verify the ' - 'host certificates; if False the driver will ' - 'ignore verifying the SSL certificate. If it\'s ' - 'a path the driver will use the specified ' - 'certificate or one of the certificates in the ' - 'directory. Defaults to True. Optional.'), -} - -COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy() -COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES) - - -def parse_driver_info(node): - """Parse the information required for Ironic to connect to iBMC. - - :param node: an Ironic node object - :returns: dictionary of parameters - :raises: InvalidParameterValue on malformed parameter(s) - :raises: MissingParameterValue on missing parameter(s) - """ - driver_info = node.driver_info or {} - missing_info = [key for key in REQUIRED_PROPERTIES - if not driver_info.get(key)] - if missing_info: - raise exception.MissingParameterValue(_( - 'Missing the following iBMC properties in node ' - '%(node)s driver_info: %(info)s') % {'node': node.uuid, - 'info': missing_info}) - - # Validate the iBMC address - address = driver_info['ibmc_address'] - if '://' not in address: - address = 'https://%s' % address - - parsed = netutils.urlsplit(address) - if not parsed.netloc: - raise exception.InvalidParameterValue( - _('Invalid iBMC address %(address)s set in ' - 'driver_info/ibmc_address on node %(node)s') % - {'address': address, 'node': node.uuid}) - - # Check if verify_ca is a Boolean or a file/directory in the file-system - verify_ca = driver_info.get('ibmc_verify_ca', True) - if isinstance(verify_ca, str): - if not os.path.exists(verify_ca): - try: - verify_ca = strutils.bool_from_string(verify_ca, strict=True) - except ValueError: - raise exception.InvalidParameterValue( - _('Invalid value type set in driver_info/' - 'ibmc_verify_ca on node %(node)s. ' - 'The value should be a Boolean or the path ' - 'to a file/directory, not "%(value)s"' - ) % {'value': verify_ca, 'node': node.uuid}) - elif not isinstance(verify_ca, bool): - raise exception.InvalidParameterValue( - _('Invalid value type set in driver_info/ibmc_verify_ca ' - 'on node %(node)s. The value should be a Boolean or the path ' - 'to a file/directory, not "%(value)s"') % {'value': verify_ca, - 'node': node.uuid}) - return {'address': address, - 'username': driver_info.get('ibmc_username'), - 'password': driver_info.get('ibmc_password'), - 'verify_ca': verify_ca} - - -def revert_dictionary(d): - return {v: k for k, v in d.items()} - - -def handle_ibmc_exception(action): - """Decorator to handle iBMC client exception. - - Decorated functions must take a :class:`TaskManager` as the first - parameter. - """ - - def decorator(f): - - def should_retry(e): - connect_error = isinstance(e, exception.IBMCConnectionError) - if connect_error: - LOG.info(_('Failed to connect to iBMC, will retry now. ' - 'Max retry times is %(retry_times)d.'), - {'retry_times': CONF.ibmc.connection_attempts}) - return connect_error - - @tenacity.retry( - retry=tenacity.retry_if_exception(should_retry), - stop=tenacity.stop_after_attempt(CONF.ibmc.connection_attempts), - wait=tenacity.wait_fixed(CONF.ibmc.connection_retry_interval), - reraise=True) - @functools.wraps(f) - def wrapper(*args, **kwargs): - # NOTE(dtantsur): this code could be written simpler, but then unit - # testing decorated functions is pretty hard, as we usually pass a - # Mock object instead of TaskManager there. - if len(args) > 1: - is_task_mgr = isinstance(args[1], task_manager.TaskManager) - task = args[1] if is_task_mgr else args[0] - else: - task = args[0] - - node = task.node - - try: - return f(*args, **kwargs) - except ibmc_error.IBMCConnectionError as e: - error = (_('Failed to connect to iBMC for node %(node)s, ' - 'Error: %(error)s') - % {'node': node.uuid, 'error': e}) - LOG.error(error) - raise exception.IBMCConnectionError(node=node.uuid, - error=error) - except ibmc_error.IBMCClientError as e: - error = (_('Failed to %(action)s for node %(node)s, ' - 'Error %(error)s') - % {'node': node.uuid, 'action': action, 'error': e}) - LOG.error(error) - raise exception.IBMCError(node=node.uuid, error=error) - - return wrapper - - return decorator diff --git a/ironic/drivers/modules/ibmc/vendor.py b/ironic/drivers/modules/ibmc/vendor.py deleted file mode 100644 index dcdb8f8394..0000000000 --- a/ironic/drivers/modules/ibmc/vendor.py +++ /dev/null @@ -1,112 +0,0 @@ -# -# 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. -""" -iBMC Vendor Interface -""" - -from oslo_log import log -from oslo_utils import importutils - -from ironic.common import exception -from ironic.common.i18n import _ -from ironic.drivers import base -from ironic.drivers.modules.ibmc import utils - -ibmc_client = importutils.try_import('ibmc_client') - -LOG = log.getLogger(__name__) - - -class IBMCVendor(base.VendorInterface): - - # NOTE(TheJulia): Deprecating November 2023 in favor of Redfish - # and due to a lack of active driver maintenance. - supported = False - - def __init__(self): - """Initialize the iBMC vendor interface. - - :raises: DriverLoadError if the driver can't be loaded due to - missing dependencies - """ - super(IBMCVendor, self).__init__() - if not ibmc_client: - raise exception.DriverLoadError( - driver='ibmc', - reason=_('Unable to import the python-ibmcclient library')) - - def validate(self, task, method=None, **kwargs): - """Validate vendor-specific actions. - - If invalid, raises an exception; otherwise returns None. - - :param task: A task from TaskManager. - :param method: Method to be validated - :param kwargs: Info for action. - :raises: UnsupportedDriverExtension if 'method' can not be mapped to - the supported interfaces. - :raises: InvalidParameterValue if kwargs does not contain 'method'. - :raises: MissingParameterValue - """ - utils.parse_driver_info(task.node) - - def get_properties(self): - """Return the properties of the interface. - - :returns: dictionary of : entries. - """ - return utils.COMMON_PROPERTIES.copy() - - @base.passthru(['GET'], async_call=False, - description=_('Returns a dictionary, ' - 'containing node boot up sequence, ' - 'in ascending order')) - @utils.handle_ibmc_exception('get iBMC boot up sequence') - def boot_up_seq(self, task, **kwargs): - """List boot type order of the node. - - :param task: A TaskManager instance containing the node to act on. - :param kwargs: Not used. - :raises: InvalidParameterValue if kwargs does not contain 'method'. - :raises: MissingParameterValue - :raises: IBMCConnectionError when it fails to connect to iBMC - :raises: IBMCError when iBMC responses an error information - :returns: A dictionary, containing node boot up sequence, - in ascending order. - """ - driver_info = utils.parse_driver_info(task.node) - with ibmc_client.connect(**driver_info) as conn: - system = conn.system.get() - boot_sequence = system.boot_sequence - return {'boot_up_sequence': boot_sequence} - - @base.passthru(['GET'], async_call=False, - description=_('Returns a list of dictionary, every ' - 'dictionary represents a RAID controller ' - 'summary info')) - @utils.handle_ibmc_exception('get iBMC RAID controller summary') - def get_raid_controller_list(self, task, **kwargs): - """List RAID controllers summary info of the node. - - :param task: A TaskManager instance containing the node to act on. - :param kwargs: Not used. - :raises: IBMCConnectionError when it fails to connect to iBMC - :raises: IBMCError when iBMC responses an error information - :returns: A list of dictionaries, every dictionary represents a RAID - controller summary of node. - """ - driver_info = utils.parse_driver_info(task.node) - with ibmc_client.connect(**driver_info) as conn: - controllers = conn.system.storage.list() - summaries = [ctrl.summary() for ctrl in controllers] - return summaries diff --git a/ironic/tests/unit/db/utils.py b/ironic/tests/unit/db/utils.py index e9f287c92c..33e9601609 100644 --- a/ironic/tests/unit/db/utils.py +++ b/ironic/tests/unit/db/utils.py @@ -679,15 +679,6 @@ def create_test_deploy_template(**kw): return dbapi.create_deploy_template(template) -def get_test_ibmc_info(): - return { - "ibmc_address": "https://example.com", - "ibmc_username": "username", - "ibmc_password": "password", - "verify_ca": False, - } - - def get_test_history(**kw): return { 'id': kw.get('id', 345), diff --git a/ironic/tests/unit/drivers/modules/ibmc/base.py b/ironic/tests/unit/drivers/modules/ibmc/base.py deleted file mode 100644 index 9a282b1cb2..0000000000 --- a/ironic/tests/unit/drivers/modules/ibmc/base.py +++ /dev/null @@ -1,43 +0,0 @@ -# -# 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. -"""Test base class for iBMC Driver.""" - -from unittest import mock - -from ironic.drivers.modules.ibmc import utils -from ironic.tests.unit.db import base as db_base -from ironic.tests.unit.db import utils as db_utils -from ironic.tests.unit.objects import utils as obj_utils - - -class IBMCTestCase(db_base.DbTestCase): - - def setUp(self): - super(IBMCTestCase, self).setUp() - self.driver_info = db_utils.get_test_ibmc_info() - self.config(enabled_hardware_types=['ibmc'], - enabled_power_interfaces=['ibmc'], - enabled_management_interfaces=['ibmc'], - enabled_vendor_interfaces=['ibmc'], - enabled_raid_interfaces=['ibmc']) - self.node = obj_utils.create_test_node( - self.context, driver='ibmc', driver_info=self.driver_info) - self.ibmc = utils.parse_driver_info(self.node) - - @staticmethod - def mock_ibmc_conn(ibmc_client_connect): - conn = mock.Mock(system=mock.PropertyMock()) - conn.__enter__ = mock.Mock(return_value=conn) - conn.__exit__ = mock.Mock(return_value=None) - ibmc_client_connect.return_value = conn - return conn diff --git a/ironic/tests/unit/drivers/modules/ibmc/test_management.py b/ironic/tests/unit/drivers/modules/ibmc/test_management.py deleted file mode 100644 index db6b0f7e64..0000000000 --- a/ironic/tests/unit/drivers/modules/ibmc/test_management.py +++ /dev/null @@ -1,294 +0,0 @@ -# -# 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. -"""Test class for iBMC Management interface.""" - -import itertools -from unittest import mock - -from oslo_utils import importutils - -from ironic.common import boot_devices -from ironic.common import boot_modes -from ironic.common import exception -from ironic.conductor import task_manager -from ironic.drivers.modules.ibmc import mappings -from ironic.drivers.modules.ibmc import utils -from ironic.tests.unit.drivers.modules.ibmc import base - -constants = importutils.try_import('ibmc_client.constants') -ibmc_client = importutils.try_import('ibmc_client') -ibmc_error = importutils.try_import('ibmc_client.exceptions') - - -class IBMCManagementTestCase(base.IBMCTestCase): - - def test_get_properties(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - properties = task.driver.get_properties() - for prop in utils.COMMON_PROPERTIES: - self.assertIn(prop, properties) - - @mock.patch.object(utils, 'parse_driver_info', autospec=True) - def test_validate(self, mock_parse_driver_info): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - task.driver.management.validate(task) - mock_parse_driver_info.assert_called_once_with(task.node) - - def test_get_supported_boot_devices(self): - if not mock._is_instance_mock(ibmc_client): - mock.patch.object(ibmc_client, 'connect', autospec=True).start() - connect_ibmc = ibmc_client.connect - conn = self.mock_ibmc_conn(connect_ibmc) - # mock return value - _supported_boot_devices = list(mappings.GET_BOOT_DEVICE_MAP) - conn.system.get.return_value = mock.Mock( - boot_source_override=mock.Mock( - supported_boot_devices=_supported_boot_devices - ) - ) - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - supported_boot_devices = ( - task.driver.management.get_supported_boot_devices(task)) - connect_ibmc.assert_called_with(**self.ibmc) - expect = sorted(list(mappings.GET_BOOT_DEVICE_MAP.values())) - self.assertEqual(expect, sorted(supported_boot_devices)) - - def test_set_boot_device(self): - if not mock._is_instance_mock(ibmc_client): - mock.patch.object(ibmc_client, 'connect', autospec=True).start() - connect_ibmc = ibmc_client.connect - conn = self.mock_ibmc_conn(connect_ibmc) - # mock return value - conn.system.set_boot_source.return_value = None - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - device_mapping = [ - (boot_devices.PXE, constants.BOOT_SOURCE_TARGET_PXE), - (boot_devices.DISK, constants.BOOT_SOURCE_TARGET_HDD), - (boot_devices.CDROM, constants.BOOT_SOURCE_TARGET_CD), - (boot_devices.BIOS, - constants.BOOT_SOURCE_TARGET_BIOS_SETUP), - ('floppy', constants.BOOT_SOURCE_TARGET_FLOPPY), - ] - - persistent_mapping = [ - (True, constants.BOOT_SOURCE_ENABLED_CONTINUOUS), - (False, constants.BOOT_SOURCE_ENABLED_ONCE) - ] - - data_source = list(itertools.product(device_mapping, - persistent_mapping)) - for (device, persistent) in data_source: - task.driver.management.set_boot_device( - task, device[0], persistent=persistent[0]) - connect_ibmc.assert_called_with(**self.ibmc) - conn.system.set_boot_source.assert_called_once_with( - device[1], - enabled=persistent[1]) - # Reset mocks - connect_ibmc.reset_mock() - conn.system.set_boot_source.reset_mock() - - def test_set_boot_device_fail(self): - if not mock._is_instance_mock(ibmc_client): - mock.patch.object(ibmc_client, 'connect', autospec=True).start() - connect_ibmc = ibmc_client.connect - conn = self.mock_ibmc_conn(connect_ibmc) - # mock return value - conn.system.set_boot_source.side_effect = ( - ibmc_error.IBMCClientError - ) - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - self.assertRaisesRegex( - exception.IBMCError, 'set iBMC boot device', - task.driver.management.set_boot_device, task, - boot_devices.PXE) - connect_ibmc.assert_called_with(**self.ibmc) - conn.system.set_boot_source.assert_called_once_with( - constants.BOOT_SOURCE_TARGET_PXE, - enabled=constants.BOOT_SOURCE_ENABLED_ONCE) - - def test_get_boot_device(self): - if not mock._is_instance_mock(ibmc_client): - mock.patch.object(ibmc_client, 'connect', autospec=True).start() - connect_ibmc = ibmc_client.connect - conn = self.mock_ibmc_conn(connect_ibmc) - # mock return value - conn.system.get.return_value = mock.Mock( - boot_source_override=mock.Mock( - target=constants.BOOT_SOURCE_TARGET_PXE, - enabled=constants.BOOT_SOURCE_ENABLED_CONTINUOUS - ) - ) - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - result_boot_device = task.driver.management.get_boot_device(task) - conn.system.get.assert_called_once() - connect_ibmc.assert_called_once_with(**self.ibmc) - expected = {'boot_device': boot_devices.PXE, - 'persistent': True} - self.assertEqual(expected, result_boot_device) - - def test_get_supported_boot_modes(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - supported_boot_modes = ( - task.driver.management.get_supported_boot_modes(task)) - self.assertEqual(list(mappings.SET_BOOT_MODE_MAP), - supported_boot_modes) - - def test_set_boot_mode(self): - if not mock._is_instance_mock(ibmc_client): - mock.patch.object(ibmc_client, 'connect', autospec=True).start() - connect_ibmc = ibmc_client.connect - conn = self.mock_ibmc_conn(connect_ibmc) - # mock system boot source override return value - conn.system.get.return_value = mock.Mock( - boot_source_override=mock.Mock( - target=constants.BOOT_SOURCE_TARGET_PXE, - enabled=constants.BOOT_SOURCE_ENABLED_CONTINUOUS - ) - ) - conn.system.set_boot_source.return_value = None - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - expected_values = [ - (boot_modes.LEGACY_BIOS, constants.BOOT_SOURCE_MODE_BIOS), - (boot_modes.UEFI, constants.BOOT_SOURCE_MODE_UEFI) - ] - - for ironic_boot_mode, ibmc_boot_mode in expected_values: - task.driver.management.set_boot_mode(task, - mode=ironic_boot_mode) - - conn.system.get.assert_called_once() - connect_ibmc.assert_called_with(**self.ibmc) - - conn.system.set_boot_source.assert_called_once_with( - constants.BOOT_SOURCE_TARGET_PXE, - enabled=constants.BOOT_SOURCE_ENABLED_CONTINUOUS, - mode=ibmc_boot_mode) - - # Reset - connect_ibmc.reset_mock() - conn.system.set_boot_source.reset_mock() - conn.system.get.reset_mock() - - def test_set_boot_mode_fail(self): - if not mock._is_instance_mock(ibmc_client): - mock.patch.object(ibmc_client, 'connect', autospec=True).start() - connect_ibmc = ibmc_client.connect - conn = self.mock_ibmc_conn(connect_ibmc) - # mock system boot source override return value - conn.system.get.return_value = mock.Mock( - boot_source_override=mock.Mock( - target=constants.BOOT_SOURCE_TARGET_PXE, - enabled=constants.BOOT_SOURCE_ENABLED_CONTINUOUS - ) - ) - conn.system.set_boot_source.side_effect = ( - ibmc_error.IBMCClientError - ) - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - expected_values = [ - (boot_modes.LEGACY_BIOS, constants.BOOT_SOURCE_MODE_BIOS), - (boot_modes.UEFI, constants.BOOT_SOURCE_MODE_UEFI) - ] - - for ironic_boot_mode, ibmc_boot_mode in expected_values: - self.assertRaisesRegex( - exception.IBMCError, 'set iBMC boot mode', - task.driver.management.set_boot_mode, task, - ironic_boot_mode) - - conn.system.set_boot_source.assert_called_once_with( - constants.BOOT_SOURCE_TARGET_PXE, - enabled=constants.BOOT_SOURCE_ENABLED_CONTINUOUS, - mode=ibmc_boot_mode) - - conn.system.get.assert_called_once() - connect_ibmc.assert_called_with(**self.ibmc) - - # Reset - connect_ibmc.reset_mock() - conn.system.set_boot_source.reset_mock() - conn.system.get.reset_mock() - - def test_get_boot_mode(self): - if not mock._is_instance_mock(ibmc_client): - mock.patch.object(ibmc_client, 'connect', autospec=True).start() - connect_ibmc = ibmc_client.connect - conn = self.mock_ibmc_conn(connect_ibmc) - # mock system boot source override return value - conn.system.get.return_value = mock.Mock( - boot_source_override=mock.Mock( - target=constants.BOOT_SOURCE_TARGET_PXE, - enabled=constants.BOOT_SOURCE_ENABLED_CONTINUOUS, - mode=constants.BOOT_SOURCE_MODE_BIOS, - ) - ) - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - response = task.driver.management.get_boot_mode(task) - - conn.system.get.assert_called_once() - connect_ibmc.assert_called_with(**self.ibmc) - - expected = boot_modes.LEGACY_BIOS - self.assertEqual(expected, response) - - def test_get_sensors_data(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - self.assertRaises(NotImplementedError, - task.driver.management.get_sensors_data, task) - - def test_inject_nmi(self): - if not mock._is_instance_mock(ibmc_client): - mock.patch.object(ibmc_client, 'connect', autospec=True).start() - connect_ibmc = ibmc_client.connect - conn = self.mock_ibmc_conn(connect_ibmc) - # mock system boot source override return value - conn.system.reset.return_value = None - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.management.inject_nmi(task) - - connect_ibmc.assert_called_with(**self.ibmc) - conn.system.reset.assert_called_once_with(constants.RESET_NMI) - - def test_inject_nmi_fail(self): - if not mock._is_instance_mock(ibmc_client): - mock.patch.object(ibmc_client, 'connect', autospec=True).start() - connect_ibmc = ibmc_client.connect - conn = self.mock_ibmc_conn(connect_ibmc) - # mock system boot source override return value - conn.system.reset.side_effect = ( - ibmc_error.IBMCClientError - ) - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - self.assertRaisesRegex( - exception.IBMCError, 'inject iBMC NMI', - task.driver.management.inject_nmi, task) - - connect_ibmc.assert_called_with(**self.ibmc) - conn.system.reset.assert_called_once_with(constants.RESET_NMI) diff --git a/ironic/tests/unit/drivers/modules/ibmc/test_power.py b/ironic/tests/unit/drivers/modules/ibmc/test_power.py deleted file mode 100644 index 58e6a738d6..0000000000 --- a/ironic/tests/unit/drivers/modules/ibmc/test_power.py +++ /dev/null @@ -1,288 +0,0 @@ -# -# 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. -"""Test class for iBMC Power interface.""" - -from unittest import mock - -from oslo_utils import importutils - -from ironic.common import exception -from ironic.common import states -from ironic.conductor import task_manager -from ironic.drivers.modules.ibmc import mappings -from ironic.drivers.modules.ibmc import utils -from ironic.tests.unit.drivers.modules.ibmc import base - -constants = importutils.try_import('ibmc_client.constants') -ibmc_client = importutils.try_import('ibmc_client') -ibmc_error = importutils.try_import('ibmc_client.exceptions') - - -@mock.patch('oslo_utils.eventletutils.EventletEvent.wait', - lambda *args, **kwargs: None) -class IBMCPowerTestCase(base.IBMCTestCase): - - def test_get_properties(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - properties = task.driver.get_properties() - for prop in utils.COMMON_PROPERTIES: - self.assertIn(prop, properties) - - @mock.patch.object(utils, 'parse_driver_info', autospec=True) - def test_validate(self, mock_parse_driver_info): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - task.driver.power.validate(task) - mock_parse_driver_info.assert_called_once_with(task.node) - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_get_power_state(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - expected_values = mappings.GET_POWER_STATE_MAP - for current, expected in expected_values.items(): - # Mock - conn.system.get.return_value = mock.Mock( - power_state=current - ) - - # Asserts - self.assertEqual(expected, - task.driver.power.get_power_state(task)) - - conn.system.get.assert_called_once() - connect_ibmc.assert_called_once_with(**self.ibmc) - - # Reset Mock - conn.system.get.reset_mock() - connect_ibmc.reset_mock() - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_set_power_state(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - state_mapping = mappings.SET_POWER_STATE_MAP - for (expect_state, reset_type) in state_mapping.items(): - if expect_state in (states.POWER_OFF, states.SOFT_POWER_OFF): - final = constants.SYSTEM_POWER_STATE_OFF - transient = constants.SYSTEM_POWER_STATE_ON - else: - final = constants.SYSTEM_POWER_STATE_ON - transient = constants.SYSTEM_POWER_STATE_OFF - - # Mocks - mock_system_get_results = ( - [mock.Mock(power_state=transient)] * 3 - + [mock.Mock(power_state=final)]) - conn.system.get.side_effect = mock_system_get_results - - task.driver.power.set_power_state(task, expect_state) - - # Asserts - connect_ibmc.assert_called_with(**self.ibmc) - conn.system.reset.assert_called_once_with(reset_type) - self.assertEqual(4, conn.system.get.call_count) - - # Reset Mocks - # TODO(Qianbiao.NG) why reset_mock does not reset call_count - connect_ibmc.reset_mock() - conn.system.get.reset_mock() - conn.system.reset.reset_mock() - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_set_power_state_not_reached(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - self.config(power_state_change_timeout=2, group='conductor') - - state_mapping = mappings.SET_POWER_STATE_MAP - for (expect_state, reset_type) in state_mapping.items(): - if expect_state in (states.POWER_OFF, states.SOFT_POWER_OFF): - final = constants.SYSTEM_POWER_STATE_OFF - transient = constants.SYSTEM_POWER_STATE_ON - else: - final = constants.SYSTEM_POWER_STATE_ON - transient = constants.SYSTEM_POWER_STATE_OFF - - # Mocks - mock_system_get_results = ( - [mock.Mock(power_state=transient)] * 5 - + [mock.Mock(power_state=final)]) - conn.system.get.side_effect = mock_system_get_results - - self.assertRaises(exception.PowerStateFailure, - task.driver.power.set_power_state, - task, expect_state) - - # Asserts - connect_ibmc.assert_called_with(**self.ibmc) - conn.system.reset.assert_called_once_with(reset_type) - - # Reset Mocks - connect_ibmc.reset_mock() - conn.system.get.reset_mock() - conn.system.reset.reset_mock() - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_set_power_state_fail(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - - # Mocks - conn.system.reset.side_effect = ( - ibmc_error.IBMCClientError - ) - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - # Asserts - self.assertRaisesRegex( - exception.IBMCError, 'set iBMC power state', - task.driver.power.set_power_state, task, states.POWER_ON) - connect_ibmc.assert_called_with(**self.ibmc) - conn.system.reset.assert_called_once_with(constants.RESET_ON) - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_set_power_state_timeout(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - self.config(power_state_change_timeout=2, group='conductor') - - # Mocks - conn.system.get.side_effect = ( - [mock.Mock(power_state=constants.SYSTEM_POWER_STATE_OFF)] * 3 - ) - - # Asserts - self.assertRaisesRegex( - exception.PowerStateFailure, - 'Failed to set node power state to power on', - task.driver.power.set_power_state, task, states.POWER_ON) - - connect_ibmc.assert_called_with(**self.ibmc) - conn.system.reset.assert_called_once_with(constants.RESET_ON) - - def test_get_supported_power_states(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - supported_power_states = ( - task.driver.power.get_supported_power_states(task)) - self.assertEqual(sorted(list(mappings.SET_POWER_STATE_MAP)), - sorted(supported_power_states)) - - -@mock.patch('oslo_utils.eventletutils.EventletEvent.wait', - lambda *args, **kwargs: None) -class IBMCPowerRebootTestCase(base.IBMCTestCase): - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_reboot(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - expected_values = [ - (constants.SYSTEM_POWER_STATE_OFF, constants.RESET_ON), - (constants.SYSTEM_POWER_STATE_ON, - constants.RESET_FORCE_RESTART) - ] - - # for (expect_state, reset_type) in state_mapping.items(): - for current, reset_type in expected_values: - mock_system_get_results = [ - # Initial state - mock.Mock(power_state=current), - # Transient state - powering off - mock.Mock(power_state=constants.SYSTEM_POWER_STATE_OFF), - # Final state - down powering off - mock.Mock(power_state=constants.SYSTEM_POWER_STATE_ON) - ] - conn.system.get.side_effect = mock_system_get_results - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.power.reboot(task) - - # Asserts - connect_ibmc.assert_called_with(**self.ibmc) - conn.system.reset.assert_called_once_with(reset_type) - - # Reset Mocks - connect_ibmc.reset_mock() - conn.system.get.reset_mock() - conn.system.reset.reset_mock() - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_reboot_not_reached(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - - # Mocks - conn.system.get.return_value = mock.Mock( - power_state=constants.SYSTEM_POWER_STATE_OFF) - self.assertRaisesRegex( - exception.PowerStateFailure, - 'Failed to set node power state to power on', - task.driver.power.reboot, task) - - # Asserts - connect_ibmc.assert_called_with(**self.ibmc) - conn.system.reset.assert_called_once_with(constants.RESET_ON) - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_reboot_fail(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - - # Mocks - conn.system.reset.side_effect = ( - ibmc_error.IBMCClientError - ) - conn.system.get.return_value = mock.Mock( - power_state=constants.SYSTEM_POWER_STATE_ON - ) - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - # Asserts - self.assertRaisesRegex( - exception.IBMCError, 'reboot iBMC', - task.driver.power.reboot, task) - connect_ibmc.assert_called_with(**self.ibmc) - conn.system.get.assert_called_once() - conn.system.reset.assert_called_once_with( - constants.RESET_FORCE_RESTART) - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_reboot_timeout(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - - # Mocks - conn.system.get.side_effect = [mock.Mock( - power_state=constants.SYSTEM_POWER_STATE_OFF - )] * 5 - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - self.config(power_state_change_timeout=2, group='conductor') - - # Asserts - self.assertRaisesRegex( - exception.PowerStateFailure, - 'Failed to set node power state to power on', - task.driver.power.reboot, task) - - # Asserts - connect_ibmc.assert_called_with(**self.ibmc) - conn.system.reset.assert_called_once_with( - constants.RESET_ON) diff --git a/ironic/tests/unit/drivers/modules/ibmc/test_raid.py b/ironic/tests/unit/drivers/modules/ibmc/test_raid.py deleted file mode 100644 index 5b62a54e2e..0000000000 --- a/ironic/tests/unit/drivers/modules/ibmc/test_raid.py +++ /dev/null @@ -1,167 +0,0 @@ -# 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. - -"""Test class for iBMC RAID interface.""" - -from unittest import mock - -from oslo_utils import importutils - -from ironic.common import exception -from ironic.conductor import task_manager -from ironic.drivers.modules.ilo import raid as ilo_raid -from ironic.tests.unit.db import utils as db_utils -from ironic.tests.unit.drivers.modules.ibmc import base - -constants = importutils.try_import('ibmc_client.constants') -ibmc_client = importutils.try_import('ibmc_client') -ibmc_error = importutils.try_import('ibmc_client.exceptions') - -INFO_DICT = db_utils.get_test_ilo_info() - - -class IbmcRAIDTestCase(base.IBMCTestCase): - - def setUp(self): - super(IbmcRAIDTestCase, self).setUp() - self.driver = mock.Mock(raid=ilo_raid.Ilo5RAID()) - self.target_raid_config = { - "logical_disks": [ - { - 'size_gb': 200, - 'raid_level': 0, - 'is_root_volume': True - }, - { - 'size_gb': 'MAX', - 'raid_level': 5 - } - ] - } - self.node.target_raid_config = self.target_raid_config - self.node.save() - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_sync_create_configuration_without_delete(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - conn.system.storage.apply_raid_configuration.return_value = None - - with task_manager.acquire(self.context, self.node.uuid) as task: - result = task.driver.raid.create_configuration( - task, create_root_volume=True, create_nonroot_volumes=True, - delete_existing=False) - self.assertIsNone(result, "synchronous create raid configuration " - "should return None") - - conn.system.storage.apply_raid_configuration.assert_called_once_with( - self.node.target_raid_config.get('logical_disks') - ) - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_sync_create_configuration_with_delete(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - conn.system.storage.delete_all_raid_configuration.return_value = None - conn.system.storage.apply_raid_configuration.return_value = None - - with task_manager.acquire(self.context, self.node.uuid) as task: - result = task.driver.raid.create_configuration( - task, create_root_volume=True, create_nonroot_volumes=True, - delete_existing=True) - self.assertIsNone(result, "synchronous create raid configuration " - "should return None") - - conn.system.storage.delete_all_raid_configuration.assert_called_once() - conn.system.storage.apply_raid_configuration.assert_called_once_with( - self.node.target_raid_config.get('logical_disks') - ) - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_sync_create_configuration_without_nonroot(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - conn.system.storage.delete_all_raid_configuration.return_value = None - conn.system.storage.apply_raid_configuration.return_value = None - - with task_manager.acquire(self.context, self.node.uuid) as task: - result = task.driver.raid.create_configuration( - task, create_root_volume=True, create_nonroot_volumes=False, - delete_existing=True) - self.assertIsNone(result, "synchronous create raid configuration " - "should return None") - - conn.system.storage.delete_all_raid_configuration.assert_called_once() - conn.system.storage.apply_raid_configuration.assert_called_once_with( - [{'size_gb': 200, 'raid_level': 0, 'is_root_volume': True}] - ) - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_sync_create_configuration_without_root(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - conn.system.storage.delete_all_raid_configuration.return_value = None - conn.system.storage.apply_raid_configuration.return_value = None - - with task_manager.acquire(self.context, self.node.uuid) as task: - result = task.driver.raid.create_configuration( - task, create_root_volume=False, create_nonroot_volumes=True, - delete_existing=True) - self.assertIsNone(result, "synchronous create raid configuration " - "should return None") - - conn.system.storage.delete_all_raid_configuration.assert_called_once() - conn.system.storage.apply_raid_configuration.assert_called_once_with( - [{'size_gb': 'MAX', 'raid_level': 5}] - ) - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_sync_create_configuration_failed(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - conn.system.storage.delete_all_raid_configuration.return_value = None - conn.system.storage.apply_raid_configuration.side_effect = ( - ibmc_error.IBMCClientError - ) - - with task_manager.acquire(self.context, self.node.uuid) as task: - self.assertRaisesRegex( - exception.IBMCError, 'create iBMC RAID configuration', - task.driver.raid.create_configuration, task, - create_root_volume=True, create_nonroot_volumes=True, - delete_existing=True) - - conn.system.storage.delete_all_raid_configuration.assert_called_once() - conn.system.storage.apply_raid_configuration.assert_called_once_with( - self.node.target_raid_config.get('logical_disks') - ) - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_sync_delete_configuration_success(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - conn.system.storage.delete_all_raid_configuration.return_value = None - - with task_manager.acquire(self.context, self.node.uuid) as task: - result = task.driver.raid.delete_configuration(task) - self.assertIsNone(result, "synchronous delete raid configuration " - "should return None") - - conn.system.storage.delete_all_raid_configuration.assert_called_once() - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_sync_delete_configuration_failed(self, connect_ibmc): - conn = self.mock_ibmc_conn(connect_ibmc) - conn.system.storage.delete_all_raid_configuration.side_effect = ( - ibmc_error.IBMCClientError - ) - - with task_manager.acquire(self.context, self.node.uuid) as task: - self.assertRaisesRegex( - exception.IBMCError, 'delete iBMC RAID configuration', - task.driver.raid.delete_configuration, task) - - conn.system.storage.delete_all_raid_configuration.assert_called_once() diff --git a/ironic/tests/unit/drivers/modules/ibmc/test_utils.py b/ironic/tests/unit/drivers/modules/ibmc/test_utils.py deleted file mode 100644 index 6b5a44c231..0000000000 --- a/ironic/tests/unit/drivers/modules/ibmc/test_utils.py +++ /dev/null @@ -1,173 +0,0 @@ -# -# 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. -"""Test class for iBMC Driver common utils.""" - -import copy -import os -from unittest import mock - -from oslo_utils import importutils - -from ironic.common import exception -from ironic.conductor import task_manager -from ironic.drivers.modules.ibmc import utils -from ironic.tests.unit.drivers.modules.ibmc import base - -constants = importutils.try_import('ibmc_client.constants') -ibmc_client = importutils.try_import('ibmc_client') -ibmc_error = importutils.try_import('ibmc_client.exceptions') - - -class IBMCUtilsTestCase(base.IBMCTestCase): - - def setUp(self): - super(IBMCUtilsTestCase, self).setUp() - # Redfish specific configurations - self.config(connection_attempts=2, group='ibmc') - self.parsed_driver_info = { - 'address': 'https://example.com', - 'username': 'username', - 'password': 'password', - 'verify_ca': True, - } - - def test_parse_driver_info(self): - response = utils.parse_driver_info(self.node) - self.assertEqual(self.parsed_driver_info, response) - - def test_parse_driver_info_default_scheme(self): - self.node.driver_info['ibmc_address'] = 'example.com' - response = utils.parse_driver_info(self.node) - self.assertEqual(self.parsed_driver_info, response) - - def test_parse_driver_info_default_scheme_with_port(self): - self.node.driver_info['ibmc_address'] = 'example.com:42' - self.parsed_driver_info['address'] = 'https://example.com:42' - response = utils.parse_driver_info(self.node) - self.assertEqual(self.parsed_driver_info, response) - - def test_parse_driver_info_missing_info(self): - for prop in utils.REQUIRED_PROPERTIES: - self.node.driver_info = self.driver_info.copy() - self.node.driver_info.pop(prop) - self.assertRaises(exception.MissingParameterValue, - utils.parse_driver_info, self.node) - - def test_parse_driver_info_invalid_address(self): - for value in ['/banana!', '#location', '?search=hello']: - self.node.driver_info['ibmc_address'] = value - self.assertRaisesRegex(exception.InvalidParameterValue, - 'Invalid iBMC address', - utils.parse_driver_info, self.node) - - @mock.patch.object(os.path, 'exists', autospec=True) - def test_parse_driver_info_path_verify_ca(self, - mock_isdir): - mock_isdir.return_value = True - fake_path = '/path/to/a/valid/CA' - self.node.driver_info['ibmc_verify_ca'] = fake_path - self.parsed_driver_info['verify_ca'] = fake_path - - response = utils.parse_driver_info(self.node) - self.assertEqual(self.parsed_driver_info, response) - mock_isdir.assert_called_once_with(fake_path) - - @mock.patch.object(os.path, 'exists', autospec=True) - def test_parse_driver_info_valid_capath(self, mock_isfile): - mock_isfile.return_value = True - fake_path = '/path/to/a/valid/CA.pem' - self.node.driver_info['ibmc_verify_ca'] = fake_path - self.parsed_driver_info['verify_ca'] = fake_path - - response = utils.parse_driver_info(self.node) - self.assertEqual(self.parsed_driver_info, response) - mock_isfile.assert_called_once_with(fake_path) - - def test_parse_driver_info_invalid_value_verify_ca(self): - # Integers are not supported - self.node.driver_info['ibmc_verify_ca'] = 123456 - self.assertRaisesRegex(exception.InvalidParameterValue, - 'Invalid value type', - utils.parse_driver_info, self.node) - - def test_parse_driver_info_valid_string_value_verify_ca(self): - for value in ('0', 'f', 'false', 'off', 'n', 'no'): - self.node.driver_info['ibmc_verify_ca'] = value - response = utils.parse_driver_info(self.node) - parsed_driver_info = copy.deepcopy(self.parsed_driver_info) - parsed_driver_info['verify_ca'] = False - self.assertEqual(parsed_driver_info, response) - - for value in ('1', 't', 'true', 'on', 'y', 'yes'): - self.node.driver_info['ibmc_verify_ca'] = value - response = utils.parse_driver_info(self.node) - self.assertEqual(self.parsed_driver_info, response) - - def test_parse_driver_info_invalid_string_value_verify_ca(self): - for value in ('xyz', '*', '!123', '123'): - self.node.driver_info['ibmc_verify_ca'] = value - self.assertRaisesRegex(exception.InvalidParameterValue, - 'The value should be a Boolean', - utils.parse_driver_info, self.node) - - def test_revert_dictionary(self): - data = { - "key1": "value1", - "key2": "value2" - } - - revert = utils.revert_dictionary(data) - self.assertEqual({ - "value1": "key1", - "value2": "key2" - }, revert) - - @mock.patch('time.sleep', autospec=True) - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_handle_ibmc_exception_retry(self, connect_ibmc, mock_sleep): - - @utils.handle_ibmc_exception('get IBMC system') - def get_ibmc_system(_task): - driver_info = utils.parse_driver_info(_task.node) - with ibmc_client.connect(**driver_info) as _conn: - return _conn.system.get() - - conn = self.mock_ibmc_conn(connect_ibmc) - # Mocks - conn.system.get.side_effect = [ - ibmc_error.IBMCConnectionError(url=self.ibmc['address'], - error='Failed to connect to host'), - mock.PropertyMock( - boot_source_override=mock.PropertyMock( - target=constants.BOOT_SOURCE_TARGET_PXE, - enabled=constants.BOOT_SOURCE_ENABLED_CONTINUOUS - ) - ) - ] - - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - system = get_ibmc_system(task) - - # Asserts - self.assertEqual(constants.BOOT_SOURCE_TARGET_PXE, - system.boot_source_override.target) - self.assertEqual(constants.BOOT_SOURCE_ENABLED_CONTINUOUS, - system.boot_source_override.enabled) - - # 1 failed, 1 succeed - connect_ibmc.assert_called_with(**self.ibmc) - self.assertEqual(2, connect_ibmc.call_count) - - # 1 failed, 1 succeed - self.assertEqual(2, conn.system.get.call_count) diff --git a/ironic/tests/unit/drivers/modules/ibmc/test_vendor.py b/ironic/tests/unit/drivers/modules/ibmc/test_vendor.py deleted file mode 100644 index 85fd39121b..0000000000 --- a/ironic/tests/unit/drivers/modules/ibmc/test_vendor.py +++ /dev/null @@ -1,79 +0,0 @@ -# -# 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. -"""Test class for iBMC vendor interface.""" - -from unittest import mock - -from oslo_utils import importutils - -from ironic.conductor import task_manager -from ironic.drivers.modules.ibmc import utils -from ironic.tests.unit.drivers.modules.ibmc import base - -ibmc_client = importutils.try_import('ibmc_client') - - -@mock.patch('oslo_utils.eventletutils.EventletEvent.wait', - lambda *args, **kwargs: None) -class IBMCVendorTestCase(base.IBMCTestCase): - - def setUp(self): - super(IBMCVendorTestCase, self).setUp() - - def test_get_properties(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - properties = task.driver.get_properties() - for prop in utils.COMMON_PROPERTIES: - self.assertIn(prop, properties) - - @mock.patch.object(utils, 'parse_driver_info', autospec=True) - def test_validate(self, mock_parse_driver_info): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - task.driver.power.validate(task) - mock_parse_driver_info.assert_called_once_with(task.node) - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_list_boot_type_order(self, connect_ibmc): - # Mocks - conn = self.mock_ibmc_conn(connect_ibmc) - boot_up_seq = ['Pxe', 'Hdd', 'Others', 'Cd'] - conn.system.get.return_value = mock.Mock( - boot_sequence=['Pxe', 'Hdd', 'Others', 'Cd'] - ) - - expected = {'boot_up_sequence': boot_up_seq} - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - seq = task.driver.vendor.boot_up_seq(task) - conn.system.get.assert_called_once_with() - connect_ibmc.assert_called_once_with(**self.ibmc) - self.assertEqual(expected, seq) - - @mock.patch.object(ibmc_client, 'connect', spec=object) - def test_list_raid_controller(self, connect_ibmc): - # Mocks - conn = self.mock_ibmc_conn(connect_ibmc) - - ctrl = mock.Mock() - summary = ctrl.summary.return_value - conn.system.storage.list.return_value = [ctrl] - - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - summries = task.driver.vendor.get_raid_controller_list(task) - ctrl.summary.assert_called_once_with() - conn.system.storage.list.assert_called_once_with() - connect_ibmc.assert_called_once_with(**self.ibmc) - self.assertEqual([summary], summries) diff --git a/ironic/tests/unit/drivers/modules/intel_ipmi/base.py b/ironic/tests/unit/drivers/modules/intel_ipmi/base.py index 0e9fbe0161..39375ec418 100644 --- a/ironic/tests/unit/drivers/modules/intel_ipmi/base.py +++ b/ironic/tests/unit/drivers/modules/intel_ipmi/base.py @@ -10,7 +10,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -"""Test base class for iBMC Driver.""" +"""Test base class for intel ipmi Driver.""" from ironic.tests.unit.db import base as db_base from ironic.tests.unit.db import utils as db_utils diff --git a/ironic/tests/unit/drivers/test_ibmc.py b/ironic/tests/unit/drivers/test_ibmc.py deleted file mode 100644 index 019b50be6f..0000000000 --- a/ironic/tests/unit/drivers/test_ibmc.py +++ /dev/null @@ -1,52 +0,0 @@ -# -# 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. - -# Version 1.0.0 - -from ironic.conductor import task_manager -from ironic.drivers.modules import agent -from ironic.drivers.modules.ibmc import management as ibmc_mgmt -from ironic.drivers.modules.ibmc import power as ibmc_power -from ironic.drivers.modules.ibmc import raid as ibmc_raid -from ironic.drivers.modules.ibmc import vendor as ibmc_vendor -from ironic.drivers.modules import inspector -from ironic.drivers.modules import noop -from ironic.drivers.modules import pxe -from ironic.tests.unit.db import base as db_base -from ironic.tests.unit.objects import utils as obj_utils - - -class IBMCHardwareTestCase(db_base.DbTestCase): - - def setUp(self): - super(IBMCHardwareTestCase, self).setUp() - self.config(enabled_hardware_types=['ibmc'], - enabled_power_interfaces=['ibmc'], - enabled_management_interfaces=['ibmc'], - enabled_vendor_interfaces=['ibmc'], - enabled_raid_interfaces=['ibmc'], - enabled_inspect_interfaces=['inspector', 'no-inspect']) - - def test_default_interfaces(self): - node = obj_utils.create_test_node(self.context, driver='ibmc') - with task_manager.acquire(self.context, node.id) as task: - self.assertIsInstance(task.driver.management, - ibmc_mgmt.IBMCManagement) - self.assertIsInstance(task.driver.power, - ibmc_power.IBMCPower) - self.assertIsInstance(task.driver.boot, pxe.PXEBoot) - self.assertIsInstance(task.driver.console, noop.NoConsole) - self.assertIsInstance(task.driver.deploy, agent.AgentDeploy) - self.assertIsInstance(task.driver.raid, ibmc_raid.IbmcRAID) - self.assertIsInstance(task.driver.vendor, ibmc_vendor.IBMCVendor) - self.assertIsInstance(task.driver.inspect, inspector.Inspector) diff --git a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py index 8dedc2fe10..e6c429b95c 100644 --- a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py +++ b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py @@ -121,10 +121,3 @@ SCCICLIENT_VIOM_CONF_SPEC = ( REDFISH_SPEC = ( 'redfish', ) - -# python-ibmcclient -IBMCCLIENT_SPEC = ( - 'connect', - 'exceptions', - 'constants', -) diff --git a/ironic/tests/unit/drivers/third_party_driver_mocks.py b/ironic/tests/unit/drivers/third_party_driver_mocks.py index 1d05f08e90..ddaf240160 100644 --- a/ironic/tests/unit/drivers/third_party_driver_mocks.py +++ b/ironic/tests/unit/drivers/third_party_driver_mocks.py @@ -27,7 +27,6 @@ Current list of mocked libraries: - pysnmp - scciclient - python-dracclient -- python-ibmcclient - sushy_oem_idrac """ @@ -194,44 +193,3 @@ class MockKwargsException(Exception): def __init__(self, *args, **kwargs): super(MockKwargsException, self).__init__(*args) self.kwargs = kwargs - - -# python-ibmcclient mocks for HUAWEI rack server driver -ibmc_client = importutils.try_import('ibmc_client') -if not ibmc_client: - ibmc_client = mock.MagicMock(spec_set=mock_specs.IBMCCLIENT_SPEC) - sys.modules['ibmc_client'] = ibmc_client - - # Mock iBMC client exceptions - exceptions = mock.MagicMock() - exceptions.IBMCConnectionError = ( - type('IBMCConnectionError', (MockKwargsException,), {})) - exceptions.IBMCClientError = ( - type('IBMCClientError', (MockKwargsException,), {})) - sys.modules['ibmc_client.exceptions'] = exceptions - - # Mock iIBMC client constants - constants = mock.MagicMock( - SYSTEM_POWER_STATE_ON='On', - SYSTEM_POWER_STATE_OFF='Off', - BOOT_SOURCE_TARGET_NONE='None', - BOOT_SOURCE_TARGET_PXE='Pxe', - BOOT_SOURCE_TARGET_FLOPPY='Floppy', - BOOT_SOURCE_TARGET_CD='Cd', - BOOT_SOURCE_TARGET_HDD='Hdd', - BOOT_SOURCE_TARGET_BIOS_SETUP='BiosSetup', - BOOT_SOURCE_MODE_BIOS='Legacy', - BOOT_SOURCE_MODE_UEFI='UEFI', - BOOT_SOURCE_ENABLED_ONCE='Once', - BOOT_SOURCE_ENABLED_CONTINUOUS='Continuous', - BOOT_SOURCE_ENABLED_DISABLED='Disabled', - RESET_NMI='Nmi', - RESET_ON='On', - RESET_FORCE_OFF='ForceOff', - RESET_GRACEFUL_SHUTDOWN='GracefulShutdown', - RESET_FORCE_RESTART='ForceRestart', - RESET_FORCE_POWER_CYCLE='ForcePowerCycle') - sys.modules['ibmc_client.constants'] = constants - - if 'ironic.drivers.modules.ibmc' in sys.modules: - importlib.reload(sys.modules['ironic.drivers.modules.ibmc']) diff --git a/releasenotes/notes/remove-deprecated-ibmc-driver-0c90817d8ad0ccc4.yaml b/releasenotes/notes/remove-deprecated-ibmc-driver-0c90817d8ad0ccc4.yaml new file mode 100644 index 0000000000..6c9b690ece --- /dev/null +++ b/releasenotes/notes/remove-deprecated-ibmc-driver-0c90817d8ad0ccc4.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + The deprecated ``ibmc`` hardware type has been removed from Ironic. diff --git a/setup.cfg b/setup.cfg index 90cb8fb213..a2369e7e8e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -119,7 +119,6 @@ ironic.hardware.interfaces.inspect = ironic.hardware.interfaces.management = fake = ironic.drivers.modules.fake:FakeManagement - ibmc = ironic.drivers.modules.ibmc.management:IBMCManagement idrac = ironic.drivers.modules.drac.management:DracManagement idrac-redfish = ironic.drivers.modules.drac.management:DracRedfishManagement idrac-wsman = ironic.drivers.modules.drac.management:DracWSManManagement @@ -139,7 +138,6 @@ ironic.hardware.interfaces.network = ironic.hardware.interfaces.power = agent = ironic.drivers.modules.agent_power:AgentPower fake = ironic.drivers.modules.fake:FakePower - ibmc = ironic.drivers.modules.ibmc.power:IBMCPower idrac = ironic.drivers.modules.drac.power:DracPower idrac-redfish = ironic.drivers.modules.drac.power:DracRedfishPower idrac-wsman = ironic.drivers.modules.drac.power:DracWSManPower @@ -152,7 +150,6 @@ ironic.hardware.interfaces.power = ironic.hardware.interfaces.raid = agent = ironic.drivers.modules.agent:AgentRAID fake = ironic.drivers.modules.fake:FakeRAID - ibmc = ironic.drivers.modules.ibmc.raid:IbmcRAID idrac = ironic.drivers.modules.drac.raid:DracRAID idrac-redfish = ironic.drivers.modules.drac.raid:DracRedfishRAID idrac-wsman = ironic.drivers.modules.drac.raid:DracWSManRAID @@ -174,7 +171,6 @@ ironic.hardware.interfaces.storage = ironic.hardware.interfaces.vendor = fake = ironic.drivers.modules.fake:FakeVendorB - ibmc = ironic.drivers.modules.ibmc.vendor:IBMCVendor idrac = ironic.drivers.modules.drac.vendor_passthru:DracVendorPassthru idrac-wsman = ironic.drivers.modules.drac.vendor_passthru:DracWSManVendorPassthru idrac-redfish = ironic.drivers.modules.drac.vendor_passthru:DracRedfishVendorPassthru @@ -186,7 +182,6 @@ ironic.hardware.interfaces.vendor = ironic.hardware.types = fake-hardware = ironic.drivers.fake_hardware:FakeHardware - ibmc = ironic.drivers.ibmc:IBMCHardware idrac = ironic.drivers.drac:IDRACHardware ilo = ironic.drivers.ilo:IloHardware ilo5 = ironic.drivers.ilo:Ilo5Hardware