[iRMC] Handle IPMI incompatibility in iRMC S6 2.x

Since iRMC S6 2.00, iRMC firmware disables IPMI over LAN
with default iRMC firmware configuration.

To deal with this firmware incompatibility, this commit
modifies driver's methods which use IPMI to first try
IPMI and, if IPMI fails, try to use Redfish API.

Story: 2010396
Task: 46746
Change-Id: I1730279d2225f1248ecf7fe403a5e503b6c3ff87
This commit is contained in:
Vanou Ishii 2022-11-09 00:02:01 -05:00
parent eae33a0acb
commit d23f72ee50
9 changed files with 790 additions and 86 deletions

View File

@ -124,6 +124,29 @@ Configuration via ``driver_info``
- ``driver_info/irmc_password`` property to be ``password`` for
irmc_username.
.. note::
Fujitsu server equipped with iRMC S6 2.00 or later version of firmware
disables IPMI over LAN by default. However user may be able to enable IPMI
via BMC settings.
To handle this change, ``irmc`` hardware type first tries IPMI and,
if IPMI operation fails, ``irmc`` hardware type uses Redfish API of Fujitsu
server to provide Ironic functionalities.
So if user deploys Fujitsu server with iRMC S6 2.00 or later, user needs
to set Redfish related parameters in ``driver_info``.
- ``driver_info/redifsh_address`` property to be ``IP address`` or
``hostname`` of the iRMC. You can prefix it with protocol (e.g.
``https://``). If you don't provide protocol, Ironic assumes HTTPS
(i.e. add ``https://`` prefix).
iRMC with S6 2.00 or later only support HTTPS connection to Redfish API.
- ``driver_info/redfish_username`` to be user name of iRMC with administrative
privileges
- ``driver_info/redfish_password`` to be password of ``redfish_username``
- ``driver_info/redfish_verify_ca`` accepts values those accepted in
``driver_info/irmc_verify_ca``
- ``driver_info/redfish_auth_type`` to be one of ``basic``, ``session`` or
``auto``
* If ``port`` in ``[irmc]`` section of ``/etc/ironic/ironic.conf`` or
``driver_info/irmc_port`` is set to 443, ``driver_info/irmc_verify_ca``
will take effect:

View File

@ -41,6 +41,13 @@ IRMC_OS_NAME_NUM_R = re.compile(r'\d+$')
IRMC_FW_VER_R = re.compile(r'\d(\.\d+)*\w*')
IRMC_FW_VER_NUM_R = re.compile(r'\d(\.\d+)*')
IPMI_ENABLED_BY_DEFAULT_RANGES = {
# iRMC S4 enables IPMI over LAN by default
'4': None,
# iRMC S5 enables IPMI over LAN by default
'5': None,
# iRMC S6 disables IPMI over LAN by default from version 2.00
'6': {'upper': '2.00'}}
ELCM_STATUS_PATH = '/rest/v1/Oem/eLCM/eLCMStatus'

View File

@ -32,7 +32,7 @@ from ironic.drivers.modules.irmc import common as irmc_common
from ironic.drivers.modules import snmp
from ironic import objects
scci = importutils.try_import('scciclient.irmc.scci')
irmc = importutils.try_import('scciclient.irmc')
LOG = logging.getLogger(__name__)
@ -122,6 +122,39 @@ def _get_mac_addresses(node):
if c == NODE_CLASS_OID_VALUE['primary']]
def _get_capabilities_properties_without_ipmi(d_info, cap_props,
current_cap, props):
capabilities = {}
snmp_client = snmp.SNMPClient(
address=d_info['irmc_address'],
port=d_info['irmc_snmp_port'],
version=d_info['irmc_snmp_version'],
read_community=d_info['irmc_snmp_community'],
user=d_info.get('irmc_snmp_user'),
auth_proto=d_info.get('irmc_snmp_auth_proto'),
auth_key=d_info.get('irmc_snmp_auth_password'),
priv_proto=d_info.get('irmc_snmp_priv_proto'),
priv_key=d_info.get('irmc_snmp_priv_password'))
if 'rom_firmware_version' in cap_props:
capabilities['rom_firmware_version'] = \
irmc.snmp.get_bios_firmware_version(snmp_client)
if 'irmc_firmware_version' in cap_props:
capabilities['irmc_firmware_version'] = \
irmc.snmp.get_irmc_firmware_version(snmp_client)
if 'server_model' in cap_props:
capabilities['server_model'] = irmc.snmp.get_server_model(
snmp_client)
capabilities = utils.get_updated_capabilities(current_cap, capabilities)
if capabilities:
props['capabilities'] = capabilities
return props
def _inspect_hardware(node, existing_traits=None, **kwargs):
"""Inspect the node and get hardware information.
@ -161,35 +194,41 @@ def _inspect_hardware(node, existing_traits=None, **kwargs):
try:
report = irmc_common.get_irmc_report(node)
props = scci.get_essential_properties(
props = irmc.scci.get_essential_properties(
report, IRMCInspect.ESSENTIAL_PROPERTIES)
d_info = irmc_common.parse_driver_info(node)
capabilities = scci.get_capabilities_properties(
d_info,
capabilities_props,
gpu_ids,
fpga_ids=fpga_ids,
**kwargs)
if capabilities:
if capabilities.get('pci_gpu_devices') == 0:
capabilities.pop('pci_gpu_devices')
cpu_fpga = capabilities.pop('cpu_fpga', 0)
if cpu_fpga == 0 and 'CUSTOM_CPU_FPGA' in new_traits:
new_traits.remove('CUSTOM_CPU_FPGA')
elif cpu_fpga != 0 and 'CUSTOM_CPU_FPGA' not in new_traits:
new_traits.append('CUSTOM_CPU_FPGA')
# Ironic no longer supports trusted boot
capabilities.pop('trusted_boot', None)
capabilities = utils.get_updated_capabilities(
node.properties.get('capabilities'), capabilities)
if node.driver_internal_info.get('irmc_ipmi_succeed'):
capabilities = irmc.scci.get_capabilities_properties(
d_info,
capabilities_props,
gpu_ids,
fpga_ids=fpga_ids,
**kwargs)
if capabilities:
props['capabilities'] = capabilities
if capabilities.get('pci_gpu_devices') == 0:
capabilities.pop('pci_gpu_devices')
cpu_fpga = capabilities.pop('cpu_fpga', 0)
if cpu_fpga == 0 and 'CUSTOM_CPU_FPGA' in new_traits:
new_traits.remove('CUSTOM_CPU_FPGA')
elif cpu_fpga != 0 and 'CUSTOM_CPU_FPGA' not in new_traits:
new_traits.append('CUSTOM_CPU_FPGA')
# Ironic no longer supports trusted boot
capabilities.pop('trusted_boot', None)
capabilities = utils.get_updated_capabilities(
node.properties.get('capabilities', ''), capabilities)
if capabilities:
props['capabilities'] = capabilities
else:
props = _get_capabilities_properties_without_ipmi(
d_info, capabilities_props,
node.properties.get('capabilities', ''), props)
macs = _get_mac_addresses(node)
except (scci.SCCIInvalidInputError,
scci.SCCIClientError,
except (irmc.scci.SCCIInvalidInputError,
irmc.scci.SCCIClientError,
exception.SNMPFailure) as e:
advice = ""
if ("SNMP operation" in str(e)):

View File

@ -30,6 +30,7 @@ from ironic.drivers import base
from ironic.drivers.modules import boot_mode_utils
from ironic.drivers.modules import ipmitool
from ironic.drivers.modules.irmc import common as irmc_common
from ironic.drivers.modules.redfish import management as redfish_management
irmc = importutils.try_import('scciclient.irmc')
@ -204,7 +205,8 @@ def _restore_bios_config(task):
manager_utils.node_power_action(task, states.POWER_ON)
class IRMCManagement(ipmitool.IPMIManagement):
class IRMCManagement(ipmitool.IPMIManagement,
redfish_management.RedfishManagement):
def get_properties(self):
"""Return the properties of the interface.
@ -224,9 +226,30 @@ class IRMCManagement(ipmitool.IPMIManagement):
:raises: InvalidParameterValue if required parameters are invalid.
:raises: MissingParameterValue if a required parameter is missing.
"""
irmc_common.parse_driver_info(task.node)
irmc_common.update_ipmi_properties(task)
super(IRMCManagement, self).validate(task)
if task.node.driver_internal_info.get('irmc_ipmi_succeed'):
irmc_common.parse_driver_info(task.node)
irmc_common.update_ipmi_properties(task)
super(IRMCManagement, self).validate(task)
else:
irmc_common.parse_driver_info(task.node)
super(ipmitool.IPMIManagement, self).validate(task)
def get_supported_boot_devices(self, task):
"""Get list of supported boot devices
Actual code is delegated to IPMIManagement or RedfishManagement
based on iRMC firmware version.
:param task: A TaskManager instance
:returns: A list with the supported boot devices defined
in :mod:`ironic.common.boot_devices`.
"""
if task.node.driver_internal_info.get('irmc_ipmi_succeed'):
return super(IRMCManagement, self).get_supported_boot_devices(task)
else:
return super(ipmitool.IPMIManagement,
self).get_supported_boot_devices(task)
@METRICS.timer('IRMCManagement.set_boot_device')
@task_manager.require_exclusive_lock
@ -245,39 +268,112 @@ class IRMCManagement(ipmitool.IPMIManagement):
specified.
:raises: MissingParameterValue if a required parameter is missing.
:raises: IPMIFailure on an error from ipmitool.
:raises: RedfishConnectionError on Redfish operation failure.
:raises: RedfishError on Redfish operation failure.
"""
if device not in self.get_supported_boot_devices(task):
raise exception.InvalidParameterValue(_(
"Invalid boot device %s specified.") % device)
if task.node.driver_internal_info.get('irmc_ipmi_succeed'):
if device not in self.get_supported_boot_devices(task):
raise exception.InvalidParameterValue(_(
"Invalid boot device %s specified.") % device)
uefi_mode = (
boot_mode_utils.get_boot_mode(task.node) == 'uefi')
uefi_mode = (
boot_mode_utils.get_boot_mode(task.node) == 'uefi')
# disable 60 secs timer
timeout_disable = "0x00 0x08 0x03 0x08"
ipmitool.send_raw(task, timeout_disable)
# disable 60 secs timer
timeout_disable = "0x00 0x08 0x03 0x08"
ipmitool.send_raw(task, timeout_disable)
# note(naohirot):
# Set System Boot Options : ipmi cmd '0x08', bootparam '0x05'
#
# $ ipmitool raw 0x00 0x08 0x05 data1 data2 0x00 0x00 0x00
#
# data1 : '0xe0' persistent + uefi
# '0xc0' persistent + bios
# '0xa0' next only + uefi
# '0x80' next only + bios
# data2 : boot device defined in the dict _BOOTPARAM5_DATA2
# note(naohirot):
# Set System Boot Options : ipmi cmd '0x08', bootparam '0x05'
#
# $ ipmitool raw 0x00 0x08 0x05 data1 data2 0x00 0x00 0x00
#
# data1 : '0xe0' persistent + uefi
# '0xc0' persistent + bios
# '0xa0' next only + uefi
# '0x80' next only + bios
# data2 : boot device defined in the dict _BOOTPARAM5_DATA2
bootparam5 = '0x00 0x08 0x05 %s %s 0x00 0x00 0x00'
if persistent:
data1 = '0xe0' if uefi_mode else '0xc0'
bootparam5 = '0x00 0x08 0x05 %s %s 0x00 0x00 0x00'
if persistent:
data1 = '0xe0' if uefi_mode else '0xc0'
else:
data1 = '0xa0' if uefi_mode else '0x80'
data2 = _BOOTPARAM5_DATA2[device]
cmd8 = bootparam5 % (data1, data2)
ipmitool.send_raw(task, cmd8)
else:
data1 = '0xa0' if uefi_mode else '0x80'
data2 = _BOOTPARAM5_DATA2[device]
if device not in self.get_supported_boot_devices(task):
raise exception.InvalidParameterValue(_(
"Invalid boot device %s specified. "
"Current iRMC firmware condition doesn't support IPMI "
"but Redfish.") % device)
super(ipmitool.IPMIManagement, self).set_boot_device(
task, device, persistent)
cmd8 = bootparam5 % (data1, data2)
ipmitool.send_raw(task, cmd8)
def get_boot_device(self, task):
"""Get the current boot device for the task's node.
Returns the current boot device of the node.
:param task: a task from TaskManager.
:raises: InvalidParameterValue if an invalid boot device is
specified.
:raises: MissingParameterValue if a required parameter is missing.
:raises: IPMIFailure on an error from ipmitool.
:raises: RedfishConnectionError on Redfish operation failure.
:raises: RedfishError on Redfish operation failure.
:returns: a dictionary containing:
:boot_device: the boot device, one of
:mod:`ironic.common.boot_devices` or None if it is unknown.
:persistent: Whether the boot device will persist to all
future boots or not, None if it is unknown.
"""
if task.node.driver_internal_info.get('irmc_ipmi_succeed'):
return super(IRMCManagement, self).get_boot_device(task)
else:
return super(
ipmitool.IPMIManagement, self).get_boot_device(task)
def get_supported_boot_modes(self, task):
"""Get a list of the supported boot modes.
IRMCManagement class doesn't support this method
:param task: a task from TaskManager.
:raises: UnsupportedDriverExtension if requested operation is
not supported by the driver
"""
raise exception.UnsupportedDriverExtension(
driver=task.node.driver, extension='get_supported_boot_modes')
def set_boot_mode(self, task, mode):
"""Set the boot mode for a node.
IRMCManagement class doesn't support this method
:param task: a task from TaskManager.
:param mode: The boot mode, one of
:mod:`ironic.common.boot_modes`.
:raises: UnsupportedDriverExtension if requested operation is
not supported by the driver
"""
raise exception.UnsupportedDriverExtension(
driver=task.node.driver, extension='set_boot_mode')
def get_boot_mode(self, task):
"""Get the current boot mode for a node.
IRMCManagement class doesn't support this method
:param task: a task from TaskManager.
:raises: UnsupportedDriverExtension if requested operation is
not supported by the driver
"""
raise exception.UnsupportedDriverExtension(
driver=task.node.driver, extension='get_boot_mode')
@METRICS.timer('IRMCManagement.get_sensors_data')
def get_sensors_data(self, task):
@ -330,7 +426,13 @@ class IRMCManagement(ipmitool.IPMIManagement):
if sensor_method == 'scci':
return _get_sensors_data(task)
elif sensor_method == 'ipmitool':
return super(IRMCManagement, self).get_sensors_data(task)
if task.node.driver_internal_info.get('irmc_ipmi_succeed'):
return super(IRMCManagement, self).get_sensors_data(task)
else:
raise exception.InvalidParameterValue(_(
"Invalid sensor method %s specified. "
"IPMI operation doesn't work on current iRMC "
"condition.") % sensor_method)
@METRICS.timer('IRMCManagement.inject_nmi')
@task_manager.require_exclusive_lock
@ -402,6 +504,85 @@ class IRMCManagement(ipmitool.IPMIManagement):
"""
return irmc_common.set_secure_boot_mode(task.node, state)
def get_supported_indicators(self, task, component=None):
"""Get a map of the supported indicators (e.g. LEDs).
IRMCManagement class doesn't support this method
:param task: a task from TaskManager.
:param component: If not `None`, return indicator information
for just this component, otherwise return indicators for
all existing components.
:raises: UnsupportedDriverExtension if requested operation is
not supported by the driver
"""
raise exception.UnsupportedDriverExtension(
driver=task.node.driver, extension='get_supported_indicators')
def set_indicator_state(self, task, component, indicator, state):
"""Set indicator on the hardware component to the desired state.
IRMCManagement class doesn't support this method
:param task: A task from TaskManager.
:param component: The hardware component, one of
:mod:`ironic.common.components`.
:param indicator: Indicator ID (as reported by
`get_supported_indicators`).
:state: Desired state of the indicator, one of
:mod:`ironic.common.indicator_states`.
:raises: UnsupportedDriverExtension if requested operation is
not supported by the driver
"""
raise exception.UnsupportedDriverExtension(
driver=task.node.driver, extension='set_indicator_state')
def get_indicator_state(self, task, component, indicator):
"""Get current state of the indicator of the hardware component.
IRMCManagement class doesn't support this method
:param task: A task from TaskManager.
:param component: The hardware component, one of
:mod:`ironic.common.components`.
:param indicator: Indicator ID (as reported by
`get_supported_indicators`).
:raises: UnsupportedDriverExtension if requested operation is
not supported by the driver
"""
raise exception.UnsupportedDriverExtension(
driver=task.node.driver, extension='get_indicator_state')
def detect_vendor(self, task):
"""Detects and returns the hardware vendor.
:param task: A task from TaskManager.
:raises: InvalidParameterValue if a required parameter is missing
:raises: MissingParameterValue if a required parameter is missing
:raises: RedfishError on Redfish operation error.
:raises: PasswordFileFailedToCreate from creating or writing to the
temporary file during IPMI operation.
:raises: processutils.ProcessExecutionError from executing ipmi command
:returns: String representing the BMC reported Vendor or
Manufacturer, otherwise returns None.
"""
if task.node.driver_internal_info.get('irmc_ipmi_succeed'):
return super(IRMCManagement, self).detect_vendor(task)
else:
return super(ipmitool.IPMIManagement, self).detect_vendor(task)
def get_mac_addresses(self, task):
"""Get MAC address information for the node.
IRMCManagement class doesn't support this method
:param task: A TaskManager instance containing the node to act on.
:raises: UnsupportedDriverExtension
"""
raise exception.UnsupportedDriverExtension(
driver=task.node.driver, extension='get_mac_addresses')
@base.verify_step(priority=10)
def verify_http_https_connection_and_fw_version(self, task):
"""Check http(s) connection to iRMC and save fw version

View File

@ -29,6 +29,7 @@ from ironic.drivers import base
from ironic.drivers.modules import ipmitool
from ironic.drivers.modules.irmc import boot as irmc_boot
from ironic.drivers.modules.irmc import common as irmc_common
from ironic.drivers.modules.redfish import power as redfish_power
from ironic.drivers.modules import snmp
scci = importutils.try_import('scciclient.irmc.scci')
@ -213,7 +214,7 @@ def _set_power_state(task, target_state, timeout=None):
error=snmp_exception)
class IRMCPower(base.PowerInterface):
class IRMCPower(redfish_power.RedfishPower, base.PowerInterface):
"""Interface for power-related actions."""
def get_properties(self):
@ -236,7 +237,19 @@ class IRMCPower(base.PowerInterface):
is missing or invalid on the node.
:raises: MissingParameterValue if a required parameter is missing.
"""
irmc_common.parse_driver_info(task.node)
# validate method of power interface is called at very first point
# in verifying.
# We take try-fallback approach against iRMC S6 2.00 and later
# incompatibility in which iRMC firmware disables IPMI by default.
# get_power_state method first try IPMI and if fails try Redfish
# along with setting irmc_ipmi_succeed flag to indicate if IPMI works.
if (task.node.driver_internal_info.get('irmc_ipmi_succeed')
or (task.node.driver_internal_info.get('irmc_ipmi_succeed')
is None)):
irmc_common.parse_driver_info(task.node)
else:
irmc_common.parse_driver_info(task.node)
super(IRMCPower, self).validate(task)
@METRICS.timer('IRMCPower.get_power_state')
def get_power_state(self, task):
@ -244,14 +257,40 @@ class IRMCPower(base.PowerInterface):
:param task: a TaskManager instance containing the node to act on.
:returns: a power state. One of :mod:`ironic.common.states`.
:raises: InvalidParameterValue if required ipmi parameters are missing.
:raises: MissingParameterValue if a required parameter is missing.
:raises: IPMIFailure on an error from ipmitool (from _power_status
call).
:raises: InvalidParameterValue if required parameters are incorrect.
:raises: MissingParameterValue if required parameters are missing.
:raises: IRMCOperationError If IPMI or Redfish operation fails
"""
irmc_common.update_ipmi_properties(task)
ipmi_power = ipmitool.IPMIPower()
return ipmi_power.get_power_state(task)
# If IPMI operation failed, iRMC may not enable/support IPMI,
# so fallback to Redfish.
# get_power_state is called at verifying and is called periodically
# so this method is good choice to determine IPMI enablement.
try:
irmc_common.update_ipmi_properties(task)
ipmi_power = ipmitool.IPMIPower()
pw_state = ipmi_power.get_power_state(task)
if (task.node.driver_internal_info.get('irmc_ipmi_succeed')
is not True):
task.upgrade_lock(purpose='update irmc_ipmi_succeed flag',
retry=True)
task.node.set_driver_internal_info('irmc_ipmi_succeed', True)
task.node.save()
task.downgrade_lock()
return pw_state
except exception.IPMIFailure:
if (task.node.driver_internal_info.get('irmc_ipmi_succeed')
is not False):
task.upgrade_lock(purpose='update irmc_ipmi_succeed flag',
retry=True)
task.node.set_driver_internal_info('irmc_ipmi_succeed', False)
task.node.save()
task.downgrade_lock()
try:
return super(IRMCPower, self).get_power_state(task)
except (exception.RedfishConnectionError,
exception.RedfishError):
raise exception.IRMCOperationError(
operation='IPMI try and Redfish fallback operation')
@METRICS.timer('IRMCPower.set_power_state')
@task_manager.require_exclusive_lock

View File

@ -64,13 +64,16 @@ class IRMCInspectInternalMethodsTestCase(test_common.BaseIRMCTest):
@mock.patch.object(irmc_inspect, '_get_mac_addresses', spec_set=True,
autospec=True)
@mock.patch.object(irmc_inspect, 'scci',
@mock.patch.object(irmc_inspect.irmc, 'scci',
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
@mock.patch.object(irmc_common, 'get_irmc_report', spec_set=True,
autospec=True)
def test__inspect_hardware(
def test__inspect_hardware_ipmi(
self, get_irmc_report_mock, scci_mock, _get_mac_addresses_mock):
# Set config flags
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
gpu_ids = ['0x1000/0x0079', '0x2100/0x0080']
cpu_fpgas = ['0x1000/0x0179', '0x2100/0x0180']
self.config(gpu_ids=gpu_ids, group='irmc')
@ -117,9 +120,68 @@ class IRMCInspectInternalMethodsTestCase(test_common.BaseIRMCTest):
self.assertEqual((expected_props, inspected_macs, new_traits),
result)
@mock.patch.object(
irmc_inspect, '_get_capabilities_properties_without_ipmi',
autospec=True)
@mock.patch.object(irmc_inspect, '_get_mac_addresses', spec_set=True,
autospec=True)
@mock.patch.object(irmc_inspect, 'scci',
@mock.patch.object(irmc_inspect.irmc, 'scci',
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
@mock.patch.object(irmc_common, 'get_irmc_report', spec_set=True,
autospec=True)
def test__inspect_hardware_redfish(
self, get_irmc_report_mock, scci_mock, _get_mac_addresses_mock,
_get_cap_prop_without_ipmi_mock):
# Set config flags
self.node.set_driver_internal_info('irmc_ipmi_succeed', False)
self.node.save()
kwargs = {'sleep_flag': False}
parsed_info = irmc_common.parse_driver_info(self.node)
inspected_props = {
'memory_mb': '1024',
'local_gb': 10,
'cpus': 2,
'cpu_arch': 'x86_64'}
inspected_capabilities = {
'irmc_firmware_version': 'iRMC S6-2.00S',
'server_model': 'TX2540M1F5',
'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x'}
formatted_caps = utils.get_updated_capabilities(
'', inspected_capabilities)
existing_traits = ['EXISTING_TRAIT']
passed_cap_prop = {'irmc_firmware_version',
'rom_firmware_version', 'server_model'}
inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb']
report = 'fake_report'
get_irmc_report_mock.return_value = report
scci_mock.get_essential_properties.return_value = inspected_props
_get_cap_prop_without_ipmi_mock.return_value = {
'capabilities': formatted_caps,
**inspected_props}
_get_mac_addresses_mock.return_value = inspected_macs
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
result = irmc_inspect._inspect_hardware(task.node,
existing_traits,
**kwargs)
get_irmc_report_mock.assert_called_once_with(task.node)
scci_mock.get_essential_properties.assert_called_once_with(
report, irmc_inspect.IRMCInspect.ESSENTIAL_PROPERTIES)
_get_cap_prop_without_ipmi_mock.assert_called_once_with(
parsed_info, passed_cap_prop, '', inspected_props)
expected_props = dict(inspected_props)
inspected_capabilities = utils.get_updated_capabilities(
'', inspected_capabilities)
expected_props['capabilities'] = inspected_capabilities
self.assertEqual((expected_props, inspected_macs, existing_traits),
result)
@mock.patch.object(irmc_inspect, '_get_mac_addresses', spec_set=True,
autospec=True)
@mock.patch.object(irmc_inspect.irmc, 'scci',
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
@mock.patch.object(irmc_common, 'get_irmc_report', spec_set=True,
autospec=True)
@ -130,8 +192,8 @@ class IRMCInspectInternalMethodsTestCase(test_common.BaseIRMCTest):
get_irmc_report_mock.return_value = report
side_effect = exception.SNMPFailure("fake exception")
scci_mock.get_essential_properties.side_effect = side_effect
irmc_inspect.scci.SCCIInvalidInputError = Exception
irmc_inspect.scci.SCCIClientError = Exception
irmc_inspect.irmc.scci.SCCIInvalidInputError = Exception
irmc_inspect.irmc.scci.SCCIClientError = Exception
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
@ -192,6 +254,9 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest):
autospec=True)
def test_inspect_hardware(self, power_state_mock, _inspect_hardware_mock,
port_mock, info_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
inspected_props = {
'memory_mb': '1024',
'local_gb': 10,
@ -247,6 +312,9 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest):
port_mock, info_mock,
set_boot_device_mock,
power_action_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
inspected_props = {
'memory_mb': '1024',
'local_gb': 10,
@ -296,6 +364,9 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest):
autospec=True)
def test_inspect_hardware_inspect_exception(
self, power_state_mock, _inspect_hardware_mock, port_mock):
self.node.set_driver_internal_info('irmc_fw_version', 'iRMC S4/7.82F')
self.node.save()
side_effect = exception.HardwareInspectionFailure("fake exception")
_inspect_hardware_mock.side_effect = side_effect
power_state_mock.return_value = states.POWER_ON
@ -320,6 +391,9 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest):
def test_inspect_hardware_mac_already_exist(
self, power_state_mock, _inspect_hardware_mock,
port_mock, warn_mock, trait_mock):
self.node.set_driver_internal_info('irmc_fw_version', 'iRMC S4/7.82F')
self.node.save()
inspected_props = {
'memory_mb': '1024',
'local_gb': 10,
@ -352,7 +426,7 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest):
spec_set=True, autospec=True)
@mock.patch.object(irmc_inspect, '_get_mac_addresses', spec_set=True,
autospec=True)
@mock.patch.object(irmc_inspect, 'scci',
@mock.patch.object(irmc_inspect.irmc, 'scci',
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
@mock.patch.object(irmc_common, 'get_irmc_report', spec_set=True,
autospec=True)
@ -420,6 +494,9 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest):
self.assertEqual(expected_traits, result[2])
def test_inspect_hardware_existing_cap_in_props(self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
# Set config flags
gpu_ids = ['0x1000/0x0079', '0x2100/0x0080']
cpu_fpgas = ['0x1000/0x0179', '0x2100/0x0180']
@ -454,6 +531,9 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest):
expected_traits)
def test_inspect_hardware_props_empty_gpu_ids_fpga_ids(self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
# Set config flags
gpu_ids = []
cpu_fpgas = []
@ -478,6 +558,9 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest):
expected_traits)
def test_inspect_hardware_props_pci_gpu_devices_return_zero(self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
# Set config flags
gpu_ids = ['0x1000/0x0079', '0x2100/0x0080']
cpu_fpgas = ['0x1000/0x0179', '0x2100/0x0180']
@ -507,6 +590,9 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest):
def test_inspect_hardware_props_empty_gpu_ids_fpga_id_sand_existing_cap(
self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
# Set config flags
gpu_ids = []
cpu_fpgas = []
@ -537,6 +623,9 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest):
def test_inspect_hardware_props_gpu_cpu_fpgas_zero_and_existing_cap(
self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
# Set config flags
gpu_ids = ['0x1000/0x0079', '0x2100/0x0080']
cpu_fpgas = ['0x1000/0x0179', '0x2100/0x0180']
@ -568,6 +657,9 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest):
expected_traits)
def test_inspect_hardware_props_trusted_boot_removed(self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
# Set config flags
gpu_ids = ['0x1000/0x0079', '0x2100/0x0080']
cpu_fpgas = ['0x1000/0x0179', '0x2100/0x0180']
@ -598,6 +690,9 @@ class IRMCInspectTestCase(test_common.BaseIRMCTest):
def test_inspect_hardware_props_gpu_and_cpu_fpgas_results_are_different(
self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
# Set config flags
gpu_ids = ['0x1000/0x0079', '0x2100/0x0080']
cpu_fpgas = ['0x1000/0x0179', '0x2100/0x0180']

View File

@ -30,6 +30,8 @@ from ironic.drivers.modules import ipmitool
from ironic.drivers.modules.irmc import common as irmc_common
from ironic.drivers.modules.irmc import management as irmc_management
from ironic.drivers.modules.irmc import power as irmc_power
from ironic.drivers.modules.redfish import management as redfish_management
from ironic.drivers.modules.redfish import utils as redfish_util
from ironic.drivers import utils as driver_utils
from ironic.tests.unit.drivers.modules.irmc import test_common
from ironic.tests.unit.drivers import third_party_driver_mock_specs \
@ -155,26 +157,66 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
task.driver.deploy = fake.FakeDeploy()
self.assertEqual(expected, task.driver.get_properties())
@mock.patch.object(redfish_util, 'parse_driver_info', autospec=True)
@mock.patch.object(irmc_common, 'parse_driver_info', spec_set=True,
autospec=True)
def test_validate(self, mock_drvinfo):
def test_validate_ipmi_success(self, mock_drvinfo, redfish_parsedr_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.driver.management.validate(task)
mock_drvinfo.assert_called_once_with(task.node)
redfish_parsedr_mock.assert_not_called()
@mock.patch.object(redfish_util, 'parse_driver_info', autospec=True)
@mock.patch.object(irmc_common, 'parse_driver_info', spec_set=True,
autospec=True)
def test_validate_fail(self, mock_drvinfo):
def test_validate_ipmi_fail(self, mock_drvinfo, redfish_parsedr_mock):
side_effect = exception.InvalidParameterValue("Invalid Input")
mock_drvinfo.side_effect = side_effect
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
self.assertRaises(exception.InvalidParameterValue,
task.driver.management.validate,
task)
mock_drvinfo.assert_called_once_with(task.node)
redfish_parsedr_mock.assert_not_called()
def test_management_interface_get_supported_boot_devices(self):
@mock.patch.object(redfish_util, 'parse_driver_info', autospec=True)
@mock.patch.object(irmc_common, 'parse_driver_info', spec_set=True,
autospec=True)
def test_validate_redfish_success(
self, mock_drvinfo, redfish_parsedr_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', False)
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.driver.management.validate(task)
redfish_parsedr_mock.assert_called_once_with(task.node)
mock_drvinfo.assert_called_once_with(task.node)
@mock.patch.object(redfish_util, 'parse_driver_info', autospec=True)
@mock.patch.object(irmc_common, 'parse_driver_info', spec_set=True,
autospec=True)
def test_validate_redfish_fail(self, mock_drvinfo, redfish_parsedr_mock):
side_effect = exception.InvalidParameterValue("Invalid Input")
redfish_parsedr_mock.side_effect = side_effect
self.node.set_driver_internal_info('irmc_ipmi_succeed', False)
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mock_drvinfo.assert_not_called()
self.assertRaises(exception.InvalidParameterValue,
task.driver.management.validate,
task)
redfish_parsedr_mock.assert_called_once_with(task.node)
def test_management_interface_get_supported_boot_devices_ipmi(self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
expected = [boot_devices.PXE, boot_devices.DISK,
boot_devices.CDROM, boot_devices.BIOS,
@ -182,10 +224,20 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
self.assertEqual(sorted(expected), sorted(task.driver.management.
get_supported_boot_devices(task)))
def test_management_interface_get_supported_boot_devices_redfish(self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', False)
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
expected = list(redfish_management.BOOT_DEVICE_MAP_REV)
self.assertEqual(sorted(expected), sorted(task.driver.management.
get_supported_boot_devices(task)))
@mock.patch.object(irmc_management.ipmitool, "send_raw", spec_set=True,
autospec=True)
def _test_management_interface_set_boot_device_ok(
self, boot_mode, params, expected_raw_code, send_raw_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
send_raw_mock.return_value = [None, None]
with task_manager.acquire(self.context, self.node.uuid) as task:
@ -197,7 +249,10 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
mock.call(task, "0x00 0x08 0x03 0x08"),
mock.call(task, expected_raw_code)])
def test_management_interface_set_boot_device_ok_pxe(self):
def test_management_interface_set_boot_device_ok_pxe_ipmi(self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
params = {'device': boot_devices.PXE, 'persistent': False}
self._test_management_interface_set_boot_device_ok(
None,
@ -226,7 +281,10 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
params,
"0x00 0x08 0x05 0xe0 0x04 0x00 0x00 0x00")
def test_management_interface_set_boot_device_ok_disk(self):
def test_management_interface_set_boot_device_ok_disk_ipmi(self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
params = {'device': boot_devices.DISK, 'persistent': False}
self._test_management_interface_set_boot_device_ok(
None,
@ -255,7 +313,10 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
params,
"0x00 0x08 0x05 0xe0 0x08 0x00 0x00 0x00")
def test_management_interface_set_boot_device_ok_cdrom(self):
def test_management_interface_set_boot_device_ok_cdrom_ipmi(self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
params = {'device': boot_devices.CDROM, 'persistent': False}
self._test_management_interface_set_boot_device_ok(
None,
@ -284,7 +345,10 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
params,
"0x00 0x08 0x05 0xe0 0x20 0x00 0x00 0x00")
def test_management_interface_set_boot_device_ok_bios(self):
def test_management_interface_set_boot_device_ok_bios_ipmi(self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
params = {'device': boot_devices.BIOS, 'persistent': False}
self._test_management_interface_set_boot_device_ok(
None,
@ -313,7 +377,10 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
params,
"0x00 0x08 0x05 0xe0 0x18 0x00 0x00 0x00")
def test_management_interface_set_boot_device_ok_safe(self):
def test_management_interface_set_boot_device_ok_safe_ipmi(self):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
params = {'device': boot_devices.SAFE, 'persistent': False}
self._test_management_interface_set_boot_device_ok(
None,
@ -344,7 +411,10 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
@mock.patch.object(irmc_management.ipmitool, "send_raw", spec_set=True,
autospec=True)
def test_management_interface_set_boot_device_ng(self, send_raw_mock):
def test_management_interface_set_boot_device_ng_ipmi(self, send_raw_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
"""uefi mode, next boot only, unknown device."""
send_raw_mock.return_value = [None, None]
@ -355,11 +425,39 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
task,
"unknown")
@mock.patch.object(irmc_management.ipmitool, 'send_raw', autospec=True)
@mock.patch.object(redfish_management.RedfishManagement, 'set_boot_device',
autospec=True)
def test_management_interfase_set_boot_device_success_redfish(
self, redfish_set_boot_dev_mock, ipmi_raw_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', False)
self.node.save()
ipmi_raw_mock.side_effect = exception.IPMIFailure
management_inst = irmc_management.IRMCManagement()
with task_manager.acquire(self.context, self.node.uuid) as task:
params = ['pxe', True]
management_inst.set_boot_device(task, *params)
redfish_set_boot_dev_mock.assert_called_once_with(
management_inst, task, *params)
@mock.patch.object(redfish_management.RedfishManagement, 'set_boot_device',
autospec=True)
def test_management_interfase_set_boot_device_fail_redfish(
self, redfish_set_boot_dev_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', False)
self.node.save()
management_inst = irmc_management.IRMCManagement()
with task_manager.acquire(self.context, self.node.uuid) as task:
params = [task, 'safe', True]
self.assertRaises(exception.InvalidParameterValue,
management_inst.set_boot_device, *params)
redfish_set_boot_dev_mock.assert_not_called()
@mock.patch.object(irmc_management.irmc, 'scci',
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
@mock.patch.object(irmc_common, 'get_irmc_report', spec_set=True,
autospec=True)
def test_management_interface_get_sensors_data_scci_ok(
def test_management_interface_get_sensors_data_scci_ok_ipmi(
self, mock_get_irmc_report, mock_scci):
"""'irmc_sensor_method' = 'scci' specified and OK data."""
with open(os.path.join(os.path.dirname(__file__),
@ -371,6 +469,8 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
mock_scci.get_sensor_data.return_value = fake_xml.find(
"./System/SensorDataRecords")
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.driver_info['irmc_sensor_method'] = 'scci'
sensor_dict = irmc_management.IRMCManagement().get_sensors_data(
@ -408,7 +508,58 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
@mock.patch.object(irmc_common, 'get_irmc_report', spec_set=True,
autospec=True)
def test_management_interface_get_sensors_data_scci_ng(
def test_management_interface_get_sensors_data_scci_ok_redfish(
self, mock_get_irmc_report, mock_scci):
"""'irmc_sensor_method' = 'scci' specified and OK data."""
with open(os.path.join(os.path.dirname(__file__),
'fake_sensors_data_ok.xml'), "r") as report:
fake_txt = report.read()
fake_xml = ET.fromstring(fake_txt)
mock_get_irmc_report.return_value = fake_xml
mock_scci.get_sensor_data.return_value = fake_xml.find(
"./System/SensorDataRecords")
self.node.set_driver_internal_info('irmc_ipmi_succeed', False)
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.driver_info['irmc_sensor_method'] = 'scci'
sensor_dict = irmc_management.IRMCManagement().get_sensors_data(
task)
expected = {
'Fan (4)': {
'FAN1 SYS (29)': {
'Units': 'RPM',
'Sensor ID': 'FAN1 SYS (29)',
'Sensor Reading': '600 RPM'
},
'FAN2 SYS (29)': {
'Units': 'None',
'Sensor ID': 'FAN2 SYS (29)',
'Sensor Reading': 'None None'
}
},
'Temperature (1)': {
'Systemboard 1 (7)': {
'Units': 'degree C',
'Sensor ID': 'Systemboard 1 (7)',
'Sensor Reading': '80 degree C'
},
'Ambient (55)': {
'Units': 'degree C',
'Sensor ID': 'Ambient (55)',
'Sensor Reading': '42 degree C'
}
}
}
self.assertEqual(expected, sensor_dict)
@mock.patch.object(irmc_management.irmc, 'scci',
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
@mock.patch.object(irmc_common, 'get_irmc_report', spec_set=True,
autospec=True)
def test_management_interface_get_sensors_data_scci_ng_ipmi(
self, mock_get_irmc_report, mock_scci):
"""'irmc_sensor_method' = 'scci' specified and NG data."""
with open(os.path.join(os.path.dirname(__file__),
@ -420,6 +571,33 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
mock_scci.get_sensor_data.return_value = fake_xml.find(
"./System/SensorDataRecords")
self.node.set_driver_internal_info('irmc_fw_version', 'iRMC S5/2.00S')
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.driver_info['irmc_sensor_method'] = 'scci'
sensor_dict = irmc_management.IRMCManagement().get_sensors_data(
task)
self.assertEqual(len(sensor_dict), 0)
@mock.patch.object(irmc_management.irmc, 'scci',
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
@mock.patch.object(irmc_common, 'get_irmc_report', spec_set=True,
autospec=True)
def test_management_interface_get_sensors_data_scci_ng_redfish(
self, mock_get_irmc_report, mock_scci):
"""'irmc_sensor_method' = 'scci' specified and NG data."""
with open(os.path.join(os.path.dirname(__file__),
'fake_sensors_data_ng.xml'), "r") as report:
fake_txt = report.read()
fake_xml = ET.fromstring(fake_txt)
mock_get_irmc_report.return_value = fake_xml
mock_scci.get_sensor_data.return_value = fake_xml.find(
"./System/SensorDataRecords")
self.node.set_driver_internal_info('irmc_ipmi_succeed', False)
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.driver_info['irmc_sensor_method'] = 'scci'
sensor_dict = irmc_management.IRMCManagement().get_sensors_data(
@ -429,16 +607,31 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
@mock.patch.object(ipmitool.IPMIManagement, 'get_sensors_data',
spec_set=True, autospec=True)
def test_management_interface_get_sensors_data_ipmitool_ok(
def test_management_interface_get_sensors_data_ipmitool_ok_ipmi(
self,
get_sensors_data_mock):
"""'irmc_sensor_method' = 'ipmitool' specified."""
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.driver_info['irmc_sensor_method'] = 'ipmitool'
task.driver.management.get_sensors_data(task)
get_sensors_data_mock.assert_called_once_with(
task.driver.management, task)
@mock.patch.object(ipmitool.IPMIManagement, 'get_sensors_data',
spec_set=True, autospec=True)
def test_management_interface_get_sensors_data_ipmitool_ng_redfish(
self,
get_sensors_data_mock):
"""'irmc_sensor_method' = 'ipmitool' specified."""
self.node.set_driver_internal_info('irmc_ipmi_succeed', False)
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.driver_info['irmc_sensor_method'] = 'ipmitool'
self.assertRaises(exception.InvalidParameterValue,
task.driver.management.get_sensors_data, task)
@mock.patch.object(irmc_common, 'get_irmc_report', spec_set=True,
autospec=True)
def test_management_interface_get_sensors_data_exception(
@ -459,6 +652,36 @@ class IRMCManagementTestCase(test_common.BaseIRMCTest):
self.assertEqual("Failed to get sensor data for node %s. "
"Error: Fake Error" % self.node.uuid, str(e))
@mock.patch.object(redfish_management.RedfishManagement, 'detect_vendor',
spec_set=True, autospec=True)
@mock.patch.object(ipmitool.IPMIManagement, 'detect_vendor',
spec_set=True, autospec=True)
def test_management_interface_detect_vendor_ipmi(self,
ipmimgmt_detectv_mock,
redfishmgmt_detectv_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
irmc_mgmt_inst = irmc_management.IRMCManagement()
with task_manager.acquire(self.context, self.node.uuid) as task:
irmc_mgmt_inst.detect_vendor(task)
ipmimgmt_detectv_mock.assert_called_once_with(irmc_mgmt_inst, task)
redfishmgmt_detectv_mock.assert_not_called()
@mock.patch.object(redfish_management.RedfishManagement, 'detect_vendor',
spec_set=True, autospec=True)
@mock.patch.object(ipmitool.IPMIManagement, 'detect_vendor',
spec_set=True, autospec=True)
def test_management_interface_detect_vendor_redfish(
self, ipmimgmt_detectv_mock, redfishmgmt_detectv_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', False)
self.node.save()
ipmimgmt_detectv_mock.side_effect = exception.IPMIFailure
irmc_mgmt_inst = irmc_management.IRMCManagement()
with task_manager.acquire(self.context, self.node.uuid) as task:
irmc_mgmt_inst.detect_vendor(task)
redfishmgmt_detectv_mock.assert_called_once_with(
irmc_mgmt_inst, task)
@mock.patch.object(irmc_management.LOG, 'error', spec_set=True,
autospec=True)
@mock.patch.object(irmc_common, 'get_irmc_client', spec_set=True,

View File

@ -24,6 +24,8 @@ from ironic.conductor import task_manager
from ironic.drivers.modules.irmc import boot as irmc_boot
from ironic.drivers.modules.irmc import common as irmc_common
from ironic.drivers.modules.irmc import power as irmc_power
from ironic.drivers.modules.redfish import power as redfish_power
from ironic.drivers.modules.redfish import utils as redfish_util
from ironic.tests.unit.drivers.modules.irmc import test_common
@ -289,17 +291,32 @@ class IRMCPowerTestCase(test_common.BaseIRMCTest):
for prop in irmc_common.COMMON_PROPERTIES:
self.assertIn(prop, properties)
@mock.patch.object(redfish_util, 'parse_driver_info', autospec=True)
@mock.patch.object(irmc_common, 'parse_driver_info', spec_set=True,
autospec=True)
def test_validate(self, mock_drvinfo):
def test_validate_default(self, mock_drvinfo, redfish_parsedr_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.driver.power.validate(task)
mock_drvinfo.assert_called_once_with(task.node)
redfish_parsedr_mock.assert_not_called()
@mock.patch.object(redfish_util, 'parse_driver_info', autospec=True)
@mock.patch.object(irmc_common, 'parse_driver_info', spec_set=True,
autospec=True)
def test_validate_fail(self, mock_drvinfo):
def test_validate_ipmi(self, mock_drvinfo, redfish_parsedr_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.driver.power.validate(task)
mock_drvinfo.assert_called_once_with(task.node)
redfish_parsedr_mock.assert_not_called()
@mock.patch.object(redfish_util, 'parse_driver_info', autospec=True)
@mock.patch.object(irmc_common, 'parse_driver_info', spec_set=True,
autospec=True)
def test_validate_fail_ipmi(self, mock_drvinfo, redfish_parsedr_mock):
side_effect = exception.InvalidParameterValue("Invalid Input")
mock_drvinfo.side_effect = side_effect
with task_manager.acquire(self.context, self.node.uuid,
@ -307,10 +324,40 @@ class IRMCPowerTestCase(test_common.BaseIRMCTest):
self.assertRaises(exception.InvalidParameterValue,
task.driver.power.validate,
task)
redfish_parsedr_mock.assert_not_called()
@mock.patch.object(redfish_util, 'parse_driver_info', autospec=True)
@mock.patch.object(irmc_common, 'parse_driver_info', spec_set=True,
autospec=True)
def test_validate_redfish(self, mock_drvinfo, redfish_parsedr_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', False)
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.driver.power.validate(task)
mock_drvinfo.assert_called_once_with(task.node)
redfish_parsedr_mock.assert_called_once_with(task.node)
@mock.patch.object(redfish_util, 'parse_driver_info', autospec=True)
@mock.patch.object(irmc_common, 'parse_driver_info', spec_set=True,
autospec=True)
def test_validate_fail_redfish(self, mock_drvinfo, redfish_parsedr_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', False)
self.node.save()
side_effect = exception.InvalidParameterValue("Invalid Input")
redfish_parsedr_mock.side_effect = side_effect
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
self.assertRaises(exception.InvalidParameterValue,
task.driver.power.validate,
task)
mock_drvinfo.assert_called_once_with(task.node)
@mock.patch.object(redfish_power.RedfishPower, 'get_power_state',
autospec=True)
@mock.patch('ironic.drivers.modules.irmc.power.ipmitool.IPMIPower',
spec_set=True, autospec=True)
def test_get_power_state(self, mock_IPMIPower):
def test_get_power_state_default(self, mock_IPMIPower, redfish_getpw_mock):
ipmi_power = mock_IPMIPower.return_value
ipmi_power.get_power_state.return_value = states.POWER_ON
with task_manager.acquire(self.context, self.node.uuid,
@ -318,6 +365,41 @@ class IRMCPowerTestCase(test_common.BaseIRMCTest):
self.assertEqual(states.POWER_ON,
task.driver.power.get_power_state(task))
ipmi_power.get_power_state.assert_called_once_with(task)
redfish_getpw_mock.assert_not_called()
@mock.patch.object(redfish_power.RedfishPower, 'get_power_state',
autospec=True)
@mock.patch('ironic.drivers.modules.irmc.power.ipmitool.IPMIPower',
spec_set=True, autospec=True)
def test_get_power_state_ipmi(self, mock_IPMIPower, redfish_getpw_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', True)
self.node.save()
ipmi_power = mock_IPMIPower.return_value
ipmi_power.get_power_state.return_value = states.POWER_ON
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
self.assertEqual(states.POWER_ON,
task.driver.power.get_power_state(task))
ipmi_power.get_power_state.assert_called_once_with(task)
redfish_getpw_mock.assert_not_called()
@mock.patch.object(redfish_power.RedfishPower, 'get_power_state',
autospec=True)
@mock.patch('ironic.drivers.modules.irmc.power.ipmitool.IPMIPower',
spec_set=True, autospec=True)
def test_get_power_state_redfish(self, mock_IPMIPower, redfish_getpw_mock):
self.node.set_driver_internal_info('irmc_ipmi_succeed', False)
self.node.save()
ipmipw_instance = mock_IPMIPower()
ipmipw_instance.get_power_state.side_effect = exception.IPMIFailure
redfish_getpw_mock.return_value = states.POWER_ON
irmc_power_inst = irmc_power.IRMCPower()
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
self.assertEqual(states.POWER_ON,
irmc_power_inst.get_power_state(task))
ipmipw_instance.get_power_state.assert_called()
redfish_getpw_mock.assert_called_once_with(irmc_power_inst, task)
@mock.patch.object(irmc_power, '_set_power_state', spec_set=True,
autospec=True)

View File

@ -0,0 +1,15 @@
---
fixes:
- |
Fixes a firmware incompatibility issue with iRMC versions S6 2.00
and later now doesn't support IPMI over LAN by default.
To deal with this problem, irmc driver first tries IPMI operation then,
if IPMI operation fails, it tries Redfish API of Fujitsu server.
The operator must set Redfish parameters in the ``driver_info``
if iRMC disable or doesn't support IPMI over LAN.
upgrade:
- |
When Ironic operator uses irmc driver against Fujitsu server which runs
iRMC version S6 2.00 or later, operator may need to set Redfish parameters
in ``driver_info`` so this fix can operate properly or operator should
enable IPMI over LAN through BMC settings, if possible.