From d75fecc974bb9233cf41a457af35e1911232a2d9 Mon Sep 17 00:00:00 2001
From: Richard Pioso <richard.pioso@dell.com>
Date: Sun, 6 Aug 2017 13:01:43 -0400
Subject: [PATCH] Add 'hardware type' for Dell EMC iDRACs

The ironic project is engaged in a driver composition reform [0] which
introduces dynamic drivers. That reform places hardware vendors in
charge of defining a new form of drivers for their hardware. The new
form is called 'hardware type'. The reform will deprecate [1] the
existing ironic drivers, which are now referred to as 'classic drivers'.

This change adds a single new 'hardware type', 'idrac', for servers
controlled by Dell EMC integrated Dell Remote Access Controllers
(iDRAC).

[0]
http://specs.openstack.org/openstack/ironic-specs/specs/7.0/driver-composition-reform.html
[1]
http://specs.openstack.org/openstack/ironic-specs/specs/not-implemented/classic-drivers-future.html

Change-Id: Ie332a5adebfc229a79663c771c9f4409174dd8c9
Closes-Bug: #1705741
Related-Bug: #1676387
---
 ironic/drivers/drac.py                        | 38 ++++++++++++++
 ironic/tests/unit/drivers/test_drac.py        | 49 +++++++++++++++++++
 .../idrac-hardware-type-54383960af3459d0.yaml | 17 +++++++
 setup.cfg                                     |  6 +++
 4 files changed, 110 insertions(+)
 create mode 100644 releasenotes/notes/idrac-hardware-type-54383960af3459d0.yaml

diff --git a/ironic/drivers/drac.py b/ironic/drivers/drac.py
index ea0abb0654..12262d489e 100644
--- a/ironic/drivers/drac.py
+++ b/ironic/drivers/drac.py
@@ -20,6 +20,7 @@ from oslo_utils import importutils
 from ironic.common import exception
 from ironic.common.i18n import _
 from ironic.drivers import base
+from ironic.drivers import generic
 from ironic.drivers.modules.drac import inspect as drac_inspect
 from ironic.drivers.modules.drac import management
 from ironic.drivers.modules.drac import power
@@ -27,9 +28,46 @@ from ironic.drivers.modules.drac import raid
 from ironic.drivers.modules.drac import vendor_passthru
 from ironic.drivers.modules import inspector
 from ironic.drivers.modules import iscsi_deploy
+from ironic.drivers.modules import noop
 from ironic.drivers.modules import pxe
 
 
+class IDRACHardware(generic.GenericHardware):
+    """integrated Dell Remote Access Controller hardware type"""
+
+    # Required hardware interfaces
+
+    @property
+    def supported_management_interfaces(self):
+        """List of supported management interfaces."""
+        return [management.DracManagement]
+
+    @property
+    def supported_power_interfaces(self):
+        """List of supported power interfaces."""
+        return [power.DracPower]
+
+    # Optional hardware interfaces
+
+    @property
+    def supported_inspect_interfaces(self):
+        """List of supported inspect interfaces."""
+        # Inspector support should have a higher priority than NoInspect
+        # if it is enabled by an operator (implying that the service is
+        # installed).
+        return [drac_inspect.DracInspect, inspector.Inspector, noop.NoInspect]
+
+    @property
+    def supported_raid_interfaces(self):
+        """List of supported raid interfaces."""
+        return [raid.DracRAID, noop.NoRAID]
+
+    @property
+    def supported_vendor_interfaces(self):
+        """List of supported vendor interfaces."""
+        return [vendor_passthru.DracVendorPassthru]
+
+
 class PXEDracDriver(base.BaseDriver):
     """DRAC driver using PXE for deploy."""
 
diff --git a/ironic/tests/unit/drivers/test_drac.py b/ironic/tests/unit/drivers/test_drac.py
index 4d37947be4..d5e610a745 100644
--- a/ironic/tests/unit/drivers/test_drac.py
+++ b/ironic/tests/unit/drivers/test_drac.py
@@ -19,14 +19,18 @@ import mock
 from oslo_utils import importutils
 
 from ironic.common import exception
+from ironic.conductor import task_manager
 from ironic.drivers import drac as drac_drivers
+from ironic.drivers.modules import agent
 from ironic.drivers.modules import drac
 from ironic.drivers.modules import inspector
 from ironic.drivers.modules import iscsi_deploy
 from ironic.drivers.modules.network import flat as flat_net
+from ironic.drivers.modules import noop
 from ironic.drivers.modules import pxe
 from ironic.drivers.modules.storage import noop as noop_storage
 from ironic.tests.unit.db import base as db_base
+from ironic.tests.unit.objects import utils as obj_utils
 
 
 class BaseIDRACTestCase(db_base.DbTestCase):
@@ -82,6 +86,51 @@ class BaseIDRACTestCase(db_base.DbTestCase):
             kwargs.get('vendor', drac.vendor_passthru.DracVendorPassthru))
 
 
+class IDRACHardwareTestCase(BaseIDRACTestCase):
+
+    def setUp(self):
+        super(IDRACHardwareTestCase, self).setUp()
+        self.config(enabled_hardware_types=['idrac'],
+                    enabled_management_interfaces=['idrac'],
+                    enabled_power_interfaces=['idrac'],
+                    enabled_inspect_interfaces=[
+                        'idrac', 'inspector', 'no-inspect'],
+                    enabled_network_interfaces=['flat', 'neutron', 'noop'],
+                    enabled_raid_interfaces=['idrac', 'no-raid'],
+                    enabled_vendor_interfaces=['idrac'])
+
+    def test_default_interfaces(self):
+        node = obj_utils.create_test_node(self.context, driver='idrac')
+        with task_manager.acquire(self.context, node.id) as task:
+            self._validate_interfaces(task.driver, console=noop.NoConsole)
+
+    def test_override_with_inspector(self):
+        node = obj_utils.create_test_node(self.context, driver='idrac',
+                                          inspect_interface='inspector')
+        with task_manager.acquire(self.context, node.id) as task:
+            self._validate_interfaces(task.driver,
+                                      console=noop.NoConsole,
+                                      inspect=inspector.Inspector)
+
+    def test_override_with_agent(self):
+        node = obj_utils.create_test_node(self.context, driver='idrac',
+                                          deploy_interface='direct',
+                                          inspect_interface='inspector')
+        with task_manager.acquire(self.context, node.id) as task:
+            self._validate_interfaces(task.driver,
+                                      console=noop.NoConsole,
+                                      deploy=agent.AgentDeploy,
+                                      inspect=inspector.Inspector)
+
+    def test_override_with_raid(self):
+        node = obj_utils.create_test_node(self.context, driver='idrac',
+                                          raid_interface='no-raid')
+        with task_manager.acquire(self.context, node.id) as task:
+            self._validate_interfaces(task.driver,
+                                      console=noop.NoConsole,
+                                      raid=noop.NoRAID)
+
+
 @mock.patch.object(importutils, 'try_import', spec_set=True, autospec=True)
 class DracClassicDriversTestCase(BaseIDRACTestCase):
 
diff --git a/releasenotes/notes/idrac-hardware-type-54383960af3459d0.yaml b/releasenotes/notes/idrac-hardware-type-54383960af3459d0.yaml
new file mode 100644
index 0000000000..9bda40ddaf
--- /dev/null
+++ b/releasenotes/notes/idrac-hardware-type-54383960af3459d0.yaml
@@ -0,0 +1,17 @@
+---
+features:
+  - |
+    Adds a new hardware type, ``idrac``, for Dell EMC integrated Dell Remote
+    Access Controllers (iDRAC). ``idrac`` hardware type supports PXE-based
+    provisioning using an iDRAC. It supports the following driver interfaces:
+
+    * boot: ``pxe``
+    * console: ``no-console``
+    * deploy: ``iscsi`` and ``direct``
+    * inspect: ``idrac``, ``inspector``, and ``no-inspect``
+    * management: ``idrac``
+    * network: ``flat``, ``neutron``, and ``noop``
+    * power:  ``idrac``
+    * raid: ``idrac`` and ``no-raid``
+    * storage: ``noop`` and ``cinder``
+    * vendor: ``idrac``
diff --git a/setup.cfg b/setup.cfg
index c5ff2e928b..268571b855 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -103,6 +103,7 @@ ironic.hardware.interfaces.deploy =
 
 ironic.hardware.interfaces.inspect =
     fake = ironic.drivers.modules.fake:FakeInspect
+    idrac = ironic.drivers.modules.drac.inspect:DracInspect
     ilo = ironic.drivers.modules.ilo.inspect:IloInspect
     inspector = ironic.drivers.modules.inspector:Inspector
     irmc = ironic.drivers.modules.irmc.inspect:IRMCInspect
@@ -112,6 +113,7 @@ ironic.hardware.interfaces.inspect =
 ironic.hardware.interfaces.management =
     cimc = ironic.drivers.modules.cimc.management:CIMCManagement
     fake = ironic.drivers.modules.fake:FakeManagement
+    idrac = ironic.drivers.modules.drac.management:DracManagement
     ilo = ironic.drivers.modules.ilo.management:IloManagement
     ipmitool = ironic.drivers.modules.ipmitool:IPMIManagement
     irmc = ironic.drivers.modules.irmc.management:IRMCManagement
@@ -127,6 +129,7 @@ ironic.hardware.interfaces.network =
 ironic.hardware.interfaces.power =
     cimc = ironic.drivers.modules.cimc.power:Power
     fake = ironic.drivers.modules.fake:FakePower
+    idrac = ironic.drivers.modules.drac.power:DracPower
     ilo = ironic.drivers.modules.ilo.power:IloPower
     ipmitool = ironic.drivers.modules.ipmitool:IPMIPower
     irmc = ironic.drivers.modules.irmc.power:IRMCPower
@@ -138,6 +141,7 @@ ironic.hardware.interfaces.power =
 ironic.hardware.interfaces.raid =
     agent = ironic.drivers.modules.agent:AgentRAID
     fake = ironic.drivers.modules.fake:FakeRAID
+    idrac = ironic.drivers.modules.drac.raid:DracRAID
     no-raid = ironic.drivers.modules.noop:NoRAID
 
 ironic.hardware.interfaces.rescue =
@@ -150,6 +154,7 @@ ironic.hardware.interfaces.storage =
 
 ironic.hardware.interfaces.vendor =
     fake = ironic.drivers.modules.fake:FakeVendorB
+    idrac = ironic.drivers.modules.drac.vendor_passthru:DracVendorPassthru
     ipmitool = ironic.drivers.modules.ipmitool:VendorPassthru
     no-vendor = ironic.drivers.modules.noop:NoVendor
 
@@ -157,6 +162,7 @@ ironic.hardware.types =
     cisco-ucs-managed = ironic.drivers.cisco_ucs:CiscoUCSManaged
     cisco-ucs-standalone = ironic.drivers.cisco_ucs:CiscoUCSStandalone
     fake-hardware = ironic.drivers.fake_hardware:FakeHardware
+    idrac = ironic.drivers.drac:IDRACHardware
     ilo = ironic.drivers.ilo:IloHardware
     ipmi = ironic.drivers.ipmi:IPMIHardware
     irmc = ironic.drivers.irmc:IRMCHardware