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
This commit is contained in:
parent
c02ad76fef
commit
f14794ca2e
@ -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}"
|
||||
|
@ -17,7 +17,6 @@ Hardware Types
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
drivers/ibmc
|
||||
drivers/idrac
|
||||
drivers/ilo
|
||||
drivers/intel-ipmi
|
||||
|
@ -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/
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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')
|
@ -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]
|
@ -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 <property name>:<property description> 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)
|
@ -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)
|
@ -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 <property name>:<property description> 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)
|
@ -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 <property name>:<property description> 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)
|
@ -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
|
@ -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 <property name>:<property description> 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
|
@ -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),
|
||||
|
@ -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
|
@ -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)
|
@ -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)
|
@ -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()
|
@ -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)
|
@ -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)
|
@ -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
|
||||
|
@ -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)
|
@ -121,10 +121,3 @@ SCCICLIENT_VIOM_CONF_SPEC = (
|
||||
REDFISH_SPEC = (
|
||||
'redfish',
|
||||
)
|
||||
|
||||
# python-ibmcclient
|
||||
IBMCCLIENT_SPEC = (
|
||||
'connect',
|
||||
'exceptions',
|
||||
'constants',
|
||||
)
|
||||
|
@ -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'])
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
The deprecated ``ibmc`` hardware type has been removed from Ironic.
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user