Merge "Add iDRAC Redfish sync boot mode test"
diff --git a/ironic_tempest_plugin/config.py b/ironic_tempest_plugin/config.py
index c812300..19070b3 100644
--- a/ironic_tempest_plugin/config.py
+++ b/ironic_tempest_plugin/config.py
@@ -168,6 +168,18 @@
                 help="List of Ironic enabled power interfaces."),
     cfg.StrOpt('default_rescue_interface',
                help="Ironic default rescue interface."),
+    cfg.StrOpt('firmware_image_url',
+               help="Image URL of firmware image file supported by "
+                    "update_firmware clean step."),
+    cfg.StrOpt('firmware_image_checksum',
+               help="SHA1 checksum of firmware image file."),
+    cfg.StrOpt('firmware_rollback_image_url',
+               help="Image URL of firmware update cleaning step's "
+                    "rollback image. Optional. If not provided, "
+                    "rollback is skipped."),
+    cfg.StrOpt('firmware_rollback_image_checksum',
+               help="SHA1 checksum of firmware rollback image file. "
+                    "This is required if firmware_rollback_image_url is set."),
     cfg.IntOpt('adjusted_root_disk_size_gb',
                min=0,
                help="Ironic adjusted disk size to use in the standalone tests "
diff --git a/ironic_tempest_plugin/tests/api/admin/test_nodes.py b/ironic_tempest_plugin/tests/api/admin/test_nodes.py
index 6534466..6d26a26 100644
--- a/ironic_tempest_plugin/tests/api/admin/test_nodes.py
+++ b/ironic_tempest_plugin/tests/api/admin/test_nodes.py
@@ -911,13 +911,13 @@
         self.deploy_node(self.node['uuid'])
         self.client.update_node(self.node['uuid'], protected=True,
                                 maintenance=True)
-
+        # undo maintenance because we can't teardown something
+        # in maintenance.
+        self.addCleanup(self.client.update_node, self.node['uuid'],
+                        maintenance=False)
         self.assertRaises(lib_exc.Forbidden,
                           self.client.delete_node,
                           self.node['uuid'])
-        # undo maintenance because we can't teardown something
-        # in maintenance.
-        self.client.update_node(self.node['uuid'], maintenance=False)
 
     @decorators.attr(type='negative')
     @decorators.idempotent_id('1c819f4c-6c1d-4150-ba4a-3b0dcb3c8694')
diff --git a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_cleaning.py b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_cleaning.py
index e8bcd14..f03d665 100644
--- a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_cleaning.py
+++ b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_cleaning.py
@@ -352,3 +352,67 @@
 class BaremetalIdracWSManRaidCleaning(
         BaremetalIdracRaidCleaning):
     raid_interface = 'idrac-wsman'
+
+
+class BaremetalRedfishFirmwareUpdate(bsm.BaremetalStandaloneScenarioTest):
+
+    api_microversion = '1.68'  # to support redfish firmware update
+    driver = 'redfish'
+    delete_node = False
+    image_ref = CONF.baremetal.whole_disk_image_ref
+    wholedisk_image = True
+
+    @classmethod
+    def skip_checks(cls):
+        super(BaremetalRedfishFirmwareUpdate, cls).skip_checks()
+        if not CONF.baremetal.firmware_image_url:
+            raise cls.skipException("Firmware image URL is not "
+                                    "provided. Skipping test case.")
+        if not CONF.baremetal.firmware_image_checksum:
+            raise cls.skipException("Firmware image SHA1 checksum is not "
+                                    "provided. Skipping test case.")
+
+    def _firmware_update(self, fw_image_url, fw_image_checksum):
+        steps = [
+            {
+                "interface": "management",
+                "step": "update_firmware",
+                "args": {
+                    "firmware_images": [
+                        {
+                            "url": fw_image_url,
+                            "checksum": fw_image_checksum,
+                            "wait": 300
+                        }
+                    ]
+                }
+            }
+        ]
+        self.manual_cleaning(self.node, clean_steps=steps)
+
+    @utils.services('network')
+    @decorators.idempotent_id('360e0c0e-3c17-4d2e-b052-55a932c1a4c7')
+    def test_firmware_update(self):
+        # WARNING: Removing power from a server while it is in the process of
+        # updating firmware may result in devices in the server, or the server
+        # itself becoming inoperable.
+        # Execution of firmware test case needs careful execution as it
+        # changes state of server, may result in break down of server on
+        # interruption. As it deals with firmware of component, make sure
+        # to provide proper image url path while testing with correct SHA1
+        # checksum of image.
+        self._firmware_update(CONF.baremetal.firmware_image_url,
+                              CONF.baremetal.firmware_image_checksum)
+        if (CONF.baremetal.firmware_rollback_image_url
+                and CONF.baremetal.firmware_rollback_image_checksum):
+            self.addCleanup(self._firmware_update,
+                            CONF.baremetal.firmware_rollback_image_url,
+                            CONF.baremetal.firmware_rollback_image_checksum)
+
+
+class BaremetalIdracRedfishFirmwareUpdate(BaremetalRedfishFirmwareUpdate):
+
+    driver = 'idrac'
+    boot_interface = 'ipxe'
+    management_interface = 'idrac-redfish'
+    power_interface = 'idrac-redfish'