Merge "Set minimal tempest version to 27.0.0"
diff --git a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
index da7db4c..e4d8717 100644
--- a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
+++ b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
@@ -125,6 +125,16 @@
         return body
 
     @classmethod
+    def _disassociate_instance_with_node(cls, node_id):
+        """Disassociate instance_uuid from attached node.
+
+        :param node_id: Name or UUID of the node.
+        """
+        cls.update_node(node_id, [{'op': 'replace',
+                                   'path': '/instance_uuid',
+                                   'value': None}])
+
+    @classmethod
     def get_node_vifs(cls, node_id):
         """Return a list of VIFs for a given node.
 
@@ -233,6 +243,28 @@
         return nodes[0]
 
     @classmethod
+    def unreserve_node(cls, node):
+        """Unreserves node by disassociating instance_uuid attached to node.
+
+        :param node: Ironic node to disassociate instance_uuid from.
+        """
+
+        def _try_to_disassociate_instance():
+            _, node_prop = cls.baremetal_client.show_node(node['uuid'])
+            if node_prop['instance_uuid']:
+                try:
+                    cls._disassociate_instance_with_node(node['uuid'])
+                except lib_exc.Conflict:
+                    return False
+            return True
+        if (not test_utils.call_until_true(
+                _try_to_disassociate_instance,
+                duration=CONF.baremetal.association_timeout, sleep_for=1)):
+            msg = ('Timed out waiting to disassociate instance from '
+                   'ironic node uuid %s' % node['instance_uuid'])
+            raise lib_exc.TimeoutException(msg)
+
+    @classmethod
     def boot_node(cls, image_ref=None, image_checksum=None,
                   boot_option=None):
         """Boot ironic node.
@@ -600,6 +632,7 @@
             except lib_exc.NotFound:
                 pass
         cls.terminate_node(cls.node['uuid'])
+        cls.unreserve_node(cls.node)
         base.reset_baremetal_api_microversion()
         super(BaremetalStandaloneManager, cls).resource_cleanup()
 
diff --git a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py
index 6fa91a8..c12d3d7 100644
--- a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py
+++ b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_basic_ops.py
@@ -436,3 +436,149 @@
     @utils.services('network')
     def test_ip_access_to_server(self):
         self.boot_and_verify_node()
+
+
+class BaremetalRedfishDirectPxeWholedisk(
+        bsm.BaremetalStandaloneScenarioTest):
+
+    mandatory_attr = ['driver']
+    api_microversion = '1.31'  # to set the deploy_interface
+    driver = 'redfish'
+    image_ref = CONF.baremetal.whole_disk_image_ref
+    wholedisk_image = True
+    boot_interface = 'pxe'
+    deploy_interface = 'direct'
+
+    @classmethod
+    def skip_checks(cls):
+        super(BaremetalRedfishDirectPxeWholedisk, cls).skip_checks()
+        if CONF.baremetal_feature_enabled.ipxe_enabled:
+            skip_msg = ("Skipping the test case since ipxe is enabled")
+            raise cls.skipException(skip_msg)
+
+    @utils.services('image', 'network')
+    @decorators.idempotent_id('2bf34541-5514-4ea7-b073-7707d408ce99')
+    def test_deploy_node(self):
+        self.boot_and_verify_node()
+
+
+class BaremetalIdracDirectIPxeWholedisk(
+        bsm.BaremetalStandaloneScenarioTest):
+
+    mandatory_attr = ['driver']
+    api_microversion = '1.31'  # to set the deploy_interface
+    driver = 'idrac'
+    image_ref = CONF.baremetal.whole_disk_image_ref
+    wholedisk_image = True
+    boot_interface = 'ipxe'
+    deploy_interface = 'direct'
+
+    @classmethod
+    def skip_checks(cls):
+        super(BaremetalIdracDirectIPxeWholedisk, cls).skip_checks()
+        if not CONF.baremetal_feature_enabled.ipxe_enabled:
+            skip_msg = ("Skipping the test case since ipxe is disabled")
+            raise cls.skipException(skip_msg)
+
+    @utils.services('image', 'network')
+    @decorators.idempotent_id('b467889b-aba4-4696-9e6f-4ef60a3f78a4')
+    def test_deploy_node(self):
+        self.boot_and_verify_node()
+
+
+class BaremetalIdracDirectPxeWholedisk(
+        bsm.BaremetalStandaloneScenarioTest):
+
+    mandatory_attr = ['driver']
+    api_microversion = '1.31'  # to set the deploy_interface
+    driver = 'idrac'
+    image_ref = CONF.baremetal.whole_disk_image_ref
+    wholedisk_image = True
+    boot_interface = 'pxe'
+    deploy_interface = 'direct'
+
+    @classmethod
+    def skip_checks(cls):
+        super(BaremetalIdracDirectPxeWholedisk, cls).skip_checks()
+        if CONF.baremetal_feature_enabled.ipxe_enabled:
+            skip_msg = ("Skipping the test case since ipxe is enabled")
+            raise cls.skipException(skip_msg)
+
+    @utils.services('image', 'network')
+    @decorators.idempotent_id('20c0f2f4-723b-4d1e-a4f2-bea11ffb1513')
+    def test_deploy_node(self):
+        self.boot_and_verify_node()
+
+
+class BaremetalIdracWSManDirectIPxeWholedisk(
+        BaremetalIdracDirectIPxeWholedisk):
+    power_interface = 'idrac-wsman'
+    management_interface = 'idrac-wsman'
+
+
+class BaremetalIdracWSManDirectPxeWholedisk(
+        BaremetalIdracDirectPxeWholedisk):
+    power_interface = 'idrac-wsman'
+    management_interface = 'idrac-wsman'
+
+
+class BaremetalIdracRedfishDirectIPxeWholedisk(
+        BaremetalIdracDirectIPxeWholedisk):
+
+    power_interface = 'idrac-redfish'
+    management_interface = 'idrac-redfish'
+
+
+class BaremetalIdracRedfishDirectPxeWholedisk(
+        BaremetalIdracDirectPxeWholedisk):
+
+    power_interface = 'idrac-redfish'
+    management_interface = 'idrac-redfish'
+
+
+class BaremetalIpmiDirectIPxeWholedisk(
+        bsm.BaremetalStandaloneScenarioTest):
+
+    mandatory_attr = ['driver']
+    api_microversion = '1.31'  # to set the deploy_interface
+    driver = 'ipmi'
+    image_ref = CONF.baremetal.whole_disk_image_ref
+    wholedisk_image = True
+    boot_interface = 'ipxe'
+    deploy_interface = 'direct'
+
+    @classmethod
+    def skip_checks(cls):
+        super(BaremetalIpmiDirectIPxeWholedisk, cls).skip_checks()
+        if not CONF.baremetal_feature_enabled.ipxe_enabled:
+            skip_msg = ("Skipping the test case since ipxe is disabled")
+            raise cls.skipException(skip_msg)
+
+    @utils.services('image', 'network')
+    @decorators.idempotent_id('9cd5a47b-ef96-4d64-9118-be467b24d1bf')
+    def test_deploy_node(self):
+        self.boot_and_verify_node()
+
+
+class BaremetalIpmiDirectPxeWholedisk(
+        bsm.BaremetalStandaloneScenarioTest):
+
+    mandatory_attr = ['driver']
+    api_microversion = '1.31'  # to set the deploy_interface
+    driver = 'ipmi'
+    image_ref = CONF.baremetal.whole_disk_image_ref
+    wholedisk_image = True
+    boot_interface = 'pxe'
+    deploy_interface = 'direct'
+
+    @classmethod
+    def skip_checks(cls):
+        super(BaremetalIpmiDirectPxeWholedisk, cls).skip_checks()
+        if CONF.baremetal_feature_enabled.ipxe_enabled:
+            skip_msg = ("Skipping the test case since ipxe is enabled")
+            raise cls.skipException(skip_msg)
+
+    @utils.services('image', 'network')
+    @decorators.idempotent_id('d68a38aa-b731-403a-9d40-b3b49ea75e9b')
+    def test_deploy_node(self):
+        self.boot_and_verify_node()
diff --git a/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_inspection_basic.py b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_inspection_basic.py
new file mode 100644
index 0000000..d6e2af1
--- /dev/null
+++ b/ironic_tempest_plugin/tests/scenario/ironic_standalone/test_inspection_basic.py
@@ -0,0 +1,67 @@
+#    Copyright (c) 2022 Dell Inc. or its subsidiaries.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib import decorators
+
+from ironic_tempest_plugin.tests.scenario import \
+    baremetal_standalone_manager as bsm
+
+
+class BaremetalIdracInspect(bsm.BaremetalStandaloneScenarioTest):
+
+    driver = 'idrac'
+    mandatory_attr = ['driver', 'inspect_interface']
+    # The test cases clean up at the end by detaching the VIF.
+    # Support for VIFs was introduced by version 1.28
+    # (# v1.28: Add vifs subcontroller to node).
+    api_microversion = '1.28'
+    delete_node = False
+
+    def _verify_node_inspection_data(self):
+        _, node = self.baremetal_client.show_node(self.node['uuid'])
+
+        self.assertEqual(node['properties']['cpu_arch'], 'x86_64')
+        self.assertGreater(int(node['properties']['memory_mb']), 0)
+        self.assertGreater(int(node['properties']['cpus']), 0)
+        self.assertGreater(int(node['properties']['local_gb']), 0)
+
+    @decorators.idempotent_id('47ea4487-4720-43e8-a024-53ae82f8c264')
+    def test_baremetal_inspect(self):
+        """This test case follows this set of operations:
+
+            * Sets nodes to manageable state
+            * Inspects nodes
+            * Verifies inspection data
+            * Sets node to available state
+        """
+        self.baremetal_client.set_node_provision_state(self.node['uuid'],
+                                                       'manage')
+        self.baremetal_client.set_node_provision_state(self.node['uuid'],
+                                                       'inspect')
+
+        self.wait_provisioning_state(self.node['uuid'], 'manageable')
+
+        self._verify_node_inspection_data()
+
+        self.baremetal_client.set_node_provision_state(self.node['uuid'],
+                                                       'provide')
+        self.wait_provisioning_state(self.node['uuid'], 'available')
+
+
+class BaremetalIdracRedfishInspect(BaremetalIdracInspect):
+    inspect_interface = 'idrac-redfish'
+
+
+class BaremetalIdracWSManInspect(BaremetalIdracInspect):
+    inspect_interface = 'idrac-wsman'