summaryrefslogtreecommitdiff
path: root/ironic/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'ironic/drivers')
-rw-r--r--ironic/drivers/drac.py5
-rw-r--r--ironic/drivers/modules/drac/raid.py164
-rw-r--r--ironic/drivers/modules/redfish/raid.py26
3 files changed, 193 insertions, 2 deletions
diff --git a/ironic/drivers/drac.py b/ironic/drivers/drac.py
index 87d7e7217..266b21bee 100644
--- a/ironic/drivers/drac.py
+++ b/ironic/drivers/drac.py
@@ -74,8 +74,9 @@ class IDRACHardware(generic.GenericHardware):
@property
def supported_raid_interfaces(self):
"""List of supported raid interfaces."""
- return [raid.DracWSManRAID, raid.DracRAID] + super(
- IDRACHardware, self).supported_raid_interfaces
+ return [raid.DracWSManRAID, raid.DracRAID,
+ raid.DracRedfishRAID] + super(
+ IDRACHardware, self).supported_raid_interfaces
@property
def supported_vendor_interfaces(self):
diff --git a/ironic/drivers/modules/drac/raid.py b/ironic/drivers/modules/drac/raid.py
index f7319a140..2d3e639a8 100644
--- a/ironic/drivers/modules/drac/raid.py
+++ b/ironic/drivers/modules/drac/raid.py
@@ -23,6 +23,7 @@ from ironic_lib import metrics_utils
from oslo_log import log as logging
from oslo_utils import importutils
from oslo_utils import units
+import tenacity
from ironic.common import exception
from ironic.common.i18n import _
@@ -34,9 +35,12 @@ from ironic.drivers import base
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.drac import common as drac_common
from ironic.drivers.modules.drac import job as drac_job
+from ironic.drivers.modules.redfish import raid as redfish_raid
+from ironic.drivers.modules.redfish import utils as redfish_utils
drac_exceptions = importutils.try_import('dracclient.exceptions')
drac_constants = importutils.try_import('dracclient.constants')
+sushy = importutils.try_import('sushy')
LOG = logging.getLogger(__name__)
@@ -1160,6 +1164,166 @@ def _get_disk_free_size_mb(disk, pending_delete):
return disk.size_mb if pending_delete else disk.free_size_mb
+def _wait_till_realtime_ready(task):
+ """Waits till real time operations are ready to be executed.
+
+ Useful for RAID operations where almost all controllers support
+ real time configuration, but controllers might not be ready for
+ it by the time IPA starts executing steps. It can take minute or
+ bit more to be ready for real time configuration.
+
+ :param task: TaskManager object containing the node.
+ :raises RedfishError: If can't find OEM extension or it fails to
+ execute
+ """
+ try:
+ _retry_till_realtime_ready(task)
+ except tenacity.RetryError:
+ LOG.debug('Retries exceeded while waiting for real-time ready '
+ 'for node %(node)s. Will proceed with out real-time '
+ 'ready state', {'node': task.node.uuid})
+
+
+@tenacity.retry(
+ stop=(tenacity.stop_after_attempt(30)),
+ wait=tenacity.wait_fixed(10),
+ retry=tenacity.retry_if_result(lambda result: not result))
+def _retry_till_realtime_ready(task):
+ """Retries till real time operations are ready to be executed.
+
+ :param task: TaskManager object containing the node.
+ :raises RedfishError: If can't find OEM extension or it fails to
+ execute
+ :raises RetryError: If retries exceeded and still not ready for real-time
+ """
+ return _is_realtime_ready(task)
+
+
+def _is_realtime_ready(task):
+ """Gets is real time ready status
+
+ Uses sushy-oem-idrac extension.
+
+ :param task: TaskManager object containing the node.
+ :returns: True, if real time operations are ready, otherwise False.
+ :raises RedfishError: If can't find OEM extension or it fails to
+ execute
+ """
+ system = redfish_utils.get_system(task.node)
+ for manager in system.managers:
+ try:
+ manager_oem = manager.get_oem_extension('Dell')
+ except sushy.exceptions.OEMExtensionNotFoundError as e:
+ error_msg = (_("Search for Sushy OEM extension Python package "
+ "'sushy-oem-idrac' failed for node %(node)s. "
+ "Ensure it is installed. Error: %(error)s") %
+ {'node': task.node.uuid, 'error': e})
+ LOG.error(error_msg)
+ raise exception.RedfishError(error=error_msg)
+
+ try:
+ return manager_oem.lifecycle_service.is_realtime_ready()
+ except sushy.exceptions.SushyError as e:
+ LOG.debug("Failed to get real time ready status with system "
+ "%(system)s manager %(manager)s for node %(node)s. Will "
+ "try next manager, if available. Error: %(error)s",
+ {'system': system.uuid if system.uuid else
+ system.identity,
+ 'manager': manager.uuid if manager.uuid else
+ manager.identity,
+ 'node': task.node.uuid,
+ 'error': e})
+ continue
+ break
+
+ else:
+ error_msg = (_("iDRAC Redfish get real time ready status failed for "
+ "node %(node)s, because system %(system)s has no "
+ "manager%(no_manager)s.") %
+ {'node': task.node.uuid,
+ 'system': system.uuid if system.uuid else
+ system.identity,
+ 'no_manager': '' if not system.managers else
+ ' which could'})
+ LOG.error(error_msg)
+ raise exception.RedfishError(error=error_msg)
+
+
+class DracRedfishRAID(redfish_raid.RedfishRAID):
+ """iDRAC Redfish interface for RAID related actions.
+
+ Includes iDRAC specific adjustments for RAID related actions.
+ """
+
+ @base.clean_step(priority=0, abortable=False, argsinfo={
+ 'create_root_volume': {
+ 'description': (
+ 'This specifies whether to create the root volume. '
+ 'Defaults to `True`.'
+ ),
+ 'required': False
+ },
+ 'create_nonroot_volumes': {
+ 'description': (
+ 'This specifies whether to create the non-root volumes. '
+ 'Defaults to `True`.'
+ ),
+ 'required': False
+ },
+ 'delete_existing': {
+ 'description': (
+ 'Setting this to `True` indicates to delete existing RAID '
+ 'configuration prior to creating the new configuration. '
+ 'Default value is `False`.'
+ ),
+ 'required': False,
+ }
+ })
+ def create_configuration(self, task, create_root_volume=True,
+ create_nonroot_volumes=True,
+ delete_existing=False):
+ """Create RAID configuration on the node.
+
+ This method creates the RAID configuration as read from
+ node.target_raid_config. This method
+ by default will create all logical disks.
+
+ :param task: TaskManager object containing the node.
+ :param create_root_volume: Setting this to False indicates
+ not to create root volume that is specified in the node's
+ target_raid_config. Default value is True.
+ :param create_nonroot_volumes: Setting this to False indicates
+ not to create non-root volumes (all except the root volume) in
+ the node's target_raid_config. Default value is True.
+ :param delete_existing: Setting this to True indicates to delete RAID
+ configuration prior to creating the new configuration. Default is
+ False.
+ :returns: states.CLEANWAIT if RAID configuration is in progress
+ asynchronously or None if it is complete.
+ :raises: RedfishError if there is an error creating the configuration
+ """
+ _wait_till_realtime_ready(task)
+ return super(DracRedfishRAID, self).create_configuration(
+ task, create_root_volume, create_nonroot_volumes,
+ delete_existing)
+
+ @base.clean_step(priority=0)
+ @base.deploy_step(priority=0)
+ def delete_configuration(self, task):
+ """Delete RAID configuration on the node.
+
+ :param task: TaskManager object containing the node.
+ :returns: states.CLEANWAIT (cleaning) or states.DEPLOYWAIT (deployment)
+ if deletion is in progress asynchronously or None if it is
+ complete.
+ """
+ _wait_till_realtime_ready(task)
+ return super(DracRedfishRAID, self).delete_configuration(task)
+
+ def _validate_vendor(self, task):
+ pass # for now assume idrac-redfish is used with iDRAC BMC, thus pass
+
+
class DracWSManRAID(base.RAIDInterface):
def get_properties(self):
diff --git a/ironic/drivers/modules/redfish/raid.py b/ironic/drivers/modules/redfish/raid.py
index 7e14735e2..f9b84bb86 100644
--- a/ironic/drivers/modules/redfish/raid.py
+++ b/ironic/drivers/modules/redfish/raid.py
@@ -687,6 +687,32 @@ class RedfishRAID(base.RAIDInterface):
"""
return redfish_utils.COMMON_PROPERTIES.copy()
+ def _validate_vendor(self, task):
+ vendor = task.node.properties.get('vendor')
+ if not vendor:
+ return
+
+ if 'dell' in vendor.lower().split():
+ raise exception.InvalidParameterValue(
+ _("The %(iface)s raid interface is not suitable for node "
+ "%(node)s with vendor %(vendor)s, use idrac-redfish instead")
+ % {'iface': task.node.raid_interface,
+ 'node': task.node.uuid, 'vendor': vendor})
+
+ def validate(self, task):
+ """Validates the RAID Interface.
+
+ This method validates the properties defined by Ironic for RAID
+ configuration. Driver implementations of this interface can override
+ this method for doing more validations (such as BMC's credentials).
+
+ :param task: A TaskManager instance.
+ :raises: InvalidParameterValue, if the RAID configuration is invalid.
+ :raises: MissingParameterValue, if some parameters are missing.
+ """
+ self._validate_vendor(task)
+ super(RedfishRAID, self).validate(task)
+
@base.deploy_step(priority=0,
argsinfo=base.RAID_APPLY_CONFIGURATION_ARGSINFO)
def apply_configuration(self, task, raid_config, create_root_volume=True,