Merge "[iRMC] Handle IPMI incompatibility in iRMC S6 2.x"
This commit is contained in:
commit
571d0223ba
@ -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:
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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)):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
@ -297,6 +365,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
|
||||
@ -321,6 +392,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,
|
||||
@ -353,7 +427,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)
|
||||
@ -421,6 +495,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']
|
||||
@ -455,6 +532,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 = []
|
||||
@ -479,6 +559,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']
|
||||
@ -508,6 +591,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 = []
|
||||
@ -538,6 +624,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']
|
||||
@ -569,6 +658,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']
|
||||
@ -599,6 +691,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']
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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.
|
Loading…
x
Reference in New Issue
Block a user