Use get_service_clients framework with basic Secure RBAC

The ironic tempest plugin was an early plugin and manually
invoked override plugin clients and then attached them in
the setup_clients method. However, the newer format is to
use get_service_clients, which creates and attach client
classes using the prepared credentials supplied by the
credentials attribute on the test classes.

In order to support even the most basic testing handling
and testing of Scope Enforcement as part of Secure RBAC,
then the we need to leverage the newer (last 3-4 years)
model of instantiating and leveraging clients in tempest.
This is because we need to be able to get a system scoped
admin token to be able to test actions as a system scoped
admin user. Not to be confused with "admin", which is
project scoped.

This newer style of client support does necessitate some
legacy style or direct client invocations to be retooled
so they do not attempt to directly invoke without the
required context.

Additionally, to support even the most basic handling of
the Secure RBAC's effort, we need to be able to know
when to leverage *and* then leverage that client.
We do that through the enforce_scope parameter
in upstream tempest.

Depends-On: https://review.opendev.org/c/openstack/tempest/+/798130
Change-Id: I5188fc756f1b524e9d1b32ef0474e29a9cf90b57
diff --git a/ironic_tempest_plugin/config.py b/ironic_tempest_plugin/config.py
index 7354ef7..de517dc 100644
--- a/ironic_tempest_plugin/config.py
+++ b/ironic_tempest_plugin/config.py
@@ -18,6 +18,8 @@
 from tempest import config  # noqa
 
 
+# NOTE(TheJulia): The following options are loaded into a tempest
+# plugin configuration option via plugin.py.
 ironic_service_option = cfg.BoolOpt('ironic',
                                     default=False,
                                     help='Whether or not ironic is expected '
@@ -28,6 +30,18 @@
                                        help="Whether or not ironic-inspector "
                                        "is expected to be available")
 
+ironic_scope_enforcement = cfg.BoolOpt('ironic',
+                                       default=False,
+                                       help='Wheter or not ironic is '
+                                            'exepcted to enforce auth '
+                                            'scope.')
+
+inspector_scope_enforcement = cfg.BoolOpt('ironic_inspector',
+                                          default=False,
+                                          help='Whether or not '
+                                               'ironic-inspector is expected '
+                                               'to enforce auth scope.')
+
 baremetal_group = cfg.OptGroup(name='baremetal',
                                title='Baremetal provisioning service options',
                                help='When enabling baremetal tests, Nova '
@@ -38,6 +52,8 @@
                                     'live_migration, pause, rescue, resize, '
                                     'shelve, snapshot, and suspend')
 
+# The bulk of the embedded configuration is below.
+
 baremetal_introspection_group = cfg.OptGroup(
     name="baremetal_introspection",
     title="Baremetal introspection service options",
diff --git a/ironic_tempest_plugin/manager.py b/ironic_tempest_plugin/manager.py
index 2977740..95fe207 100644
--- a/ironic_tempest_plugin/manager.py
+++ b/ironic_tempest_plugin/manager.py
@@ -42,7 +42,7 @@
 class ScenarioTest(tempest.test.BaseTestCase):
     """Base class for scenario tests. Uses tempest own clients. """
 
-    credentials = ['primary']
+    credentials = ['primary', 'admin', 'system_admin']
 
     @classmethod
     def setup_clients(cls):
@@ -92,7 +92,7 @@
     def _create_port(self, network_id, client=None, namestart='port-quotatest',
                      **kwargs):
         if not client:
-            client = self.ports_client
+            client = self.os_primary.ports_client
         name = data_utils.rand_name(namestart)
         result = client.create_port(
             name=name,
@@ -106,7 +106,7 @@
 
     def create_keypair(self, client=None):
         if not client:
-            client = self.keypairs_client
+            client = self.os_primary.keypairs_client
         name = data_utils.rand_name(self.__class__.__name__)
         # We don't need to create a keypair by pubkey in scenario
         body = client.create_keypair(name=name)
@@ -254,12 +254,13 @@
         if not CONF.compute_feature_enabled.console_output:
             LOG.debug('Console output not supported, cannot log')
             return
+        client = self.os_primary.servers_client
         if not servers:
-            servers = self.servers_client.list_servers()
+            servers = client.list_servers()
             servers = servers['servers']
         for server in servers:
             try:
-                console_output = self.servers_client.get_console_output(
+                console_output = client.get_console_output(
                     server['id'])['output']
                 LOG.debug('Console output for %s\nbody=\n%s',
                           server['id'], console_output)
@@ -277,12 +278,12 @@
 
         LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
                   server_id, image, preserve_ephemeral)
-        self.servers_client.rebuild_server(
+        self.os_primary.servers_client.rebuild_server(
             server_id=server_id, image_ref=image,
             preserve_ephemeral=preserve_ephemeral,
             **rebuild_kwargs)
         if wait:
-            waiters.wait_for_server_status(self.servers_client,
+            waiters.wait_for_server_status(self.os_primary.servers_client,
                                            server_id, 'ACTIVE')
 
     def ping_ip_address(self, ip_address, should_succeed=True,
@@ -357,12 +358,13 @@
 
         if not pool_name:
             pool_name = CONF.network.floating_network_name
-        floating_ip = (self.compute_floating_ips_client.
+        client = self.os_primary.compute_floating_ips_client
+        floating_ip = (client.
                        create_floating_ip(pool=pool_name)['floating_ip'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.compute_floating_ips_client.delete_floating_ip,
+                        client.delete_floating_ip,
                         floating_ip['id'])
-        self.compute_floating_ips_client.associate_floating_ip_to_server(
+        client.associate_floating_ip_to_server(
             floating_ip['ip'], thing['id'])
         return floating_ip
 
@@ -423,7 +425,7 @@
         routes traffic to the public network.
         """
         if not client:
-            client = self.routers_client
+            client = self.os_primary.routers_client
         if not tenant_id:
             tenant_id = client.tenant_id
         router_id = CONF.network.public_router_id
@@ -443,7 +445,7 @@
     def _create_router(self, client=None, tenant_id=None,
                        namestart='router-smoke'):
         if not client:
-            client = self.routers_client
+            client = self.os_primary.routers_client
         if not tenant_id:
             tenant_id = client.tenant_id
         name = data_utils.rand_name(namestart)
@@ -470,7 +472,7 @@
 
     """
 
-    credentials = ['primary', 'admin']
+    credentials = ['primary', 'admin', 'system_admin']
 
     @classmethod
     def skip_checks(cls):
@@ -483,9 +485,9 @@
                         namestart='network-smoke-',
                         port_security_enabled=True):
         if not networks_client:
-            networks_client = self.networks_client
+            networks_client = self.os_primary.networks_client
         if not tenant_id:
-            tenant_id = networks_client.tenant_id
+            tenant_id = self.os_primary.networks_client.tenant_id
         name = data_utils.rand_name(namestart)
         network_kwargs = dict(name=name, tenant_id=tenant_id)
         # Neutron disables port security by default so we have to check the
@@ -542,7 +544,7 @@
         if not external_network_id:
             external_network_id = CONF.network.public_network_id
         if not client:
-            client = self.floating_ips_client
+            client = self.os_primary.floating_ips_client
         if not port_id:
             port_id, ip4 = self._get_server_port_id_and_ip4(thing)
         else:
diff --git a/ironic_tempest_plugin/plugin.py b/ironic_tempest_plugin/plugin.py
index 43b2052..546509b 100644
--- a/ironic_tempest_plugin/plugin.py
+++ b/ironic_tempest_plugin/plugin.py
@@ -43,8 +43,43 @@
                           group='service_available')
         conf.register_opt(project_config.inspector_service_option,
                           group='service_available')
+        conf.register_opt(project_config.ironic_scope_enforcement,
+                          group='enforce_scope')
+        conf.register_opt(project_config.inspector_scope_enforcement,
+                          group='enforce_scope')
         for group, option in _opts:
             config.register_opt_group(conf, group, option)
 
     def get_opt_lists(self):
         return [(group.name, option) for group, option in _opts]
+
+    def get_service_clients(self):
+        ironic_config = config.service_client_config(
+            project_config.baremetal_group.name
+        )
+        baremetal_client = {
+            'name': 'baremetal',
+            'service_version': 'baremetal.v1',
+            'module_path': 'ironic_tempest_plugin.services.baremetal.v1.'
+                           'json.baremetal_client',
+            'client_names': [
+                'BaremetalClient',
+            ],
+        }
+        baremetal_client.update(ironic_config)
+
+        inspector_config = config.service_client_config(
+            project_config.baremetal_introspection_group.name
+        )
+        inspector_client = {
+            'name': 'introspection',
+            'service_version': 'baremetal_introspection.v1',
+            'module_path': 'ironic_tempest_plugin.services.'
+                           'introspection_client',
+            'client_names': [
+                'BaremetalIntrospectionClient',
+            ],
+        }
+        inspector_client.update(inspector_config)
+
+        return [baremetal_client, inspector_client]
diff --git a/ironic_tempest_plugin/tests/api/admin/base.py b/ironic_tempest_plugin/tests/api/admin/base.py
index 8cc8aec..d78f74a 100644
--- a/ironic_tempest_plugin/tests/api/admin/base.py
+++ b/ironic_tempest_plugin/tests/api/admin/base.py
@@ -17,7 +17,6 @@
 from tempest.lib import exceptions as lib_exc
 from tempest import test
 
-from ironic_tempest_plugin import clients
 from ironic_tempest_plugin.common import waiters
 from ironic_tempest_plugin.services.baremetal import base
 from ironic_tempest_plugin.tests.api.admin import api_microversion_fixture
@@ -58,7 +57,7 @@
                         test.BaseTestCase):
     """Base class for Baremetal API tests."""
 
-    credentials = ['admin']
+    credentials = ['admin', 'system_admin']
 
     @classmethod
     def skip_checks(cls):
@@ -86,12 +85,16 @@
                 CONF.baremetal.min_microversion))
         cls.services_microversion = {
             CONF.baremetal.catalog_type: cls.request_microversion}
+
         super(BaseBaremetalTest, cls).setup_credentials()
 
     @classmethod
     def setup_clients(cls):
         super(BaseBaremetalTest, cls).setup_clients()
-        cls.client = clients.Manager().baremetal_client
+        if CONF.enforce_scope.ironic:
+            cls.client = cls.os_system_admin.baremetal.BaremetalClient()
+        else:
+            cls.client = cls.os_admin.baremetal.BaremetalClient()
 
     @classmethod
     def resource_setup(cls):
diff --git a/ironic_tempest_plugin/tests/scenario/baremetal_manager.py b/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
index db0d200..4820c64 100644
--- a/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
+++ b/ironic_tempest_plugin/tests/scenario/baremetal_manager.py
@@ -22,7 +22,6 @@
 from tempest.lib.common.utils.linux import remote_client
 from tempest.lib import exceptions as lib_exc
 
-from ironic_tempest_plugin import clients
 from ironic_tempest_plugin.common import utils
 from ironic_tempest_plugin.common import waiters as ironic_waiters
 from ironic_tempest_plugin import manager
@@ -74,7 +73,7 @@
 
 class BaremetalScenarioTest(manager.ScenarioTest):
 
-    credentials = ['primary', 'admin']
+    credentials = ['primary', 'admin', 'system_admin']
     min_microversion = None
     max_microversion = api_version_utils.LATEST_MICROVERSION
 
@@ -93,8 +92,11 @@
     @classmethod
     def setup_clients(cls):
         super(BaremetalScenarioTest, cls).setup_clients()
-
-        cls.baremetal_client = clients.Manager().baremetal_client
+        if CONF.enforce_scope.ironic:
+            client = cls.os_system_admin.baremetal.BaremetalClient()
+        else:
+            client = cls.os_admin.baremetal.BaremetalClient()
+        cls.baremetal_client = client
 
     @classmethod
     def resource_setup(cls):
@@ -172,7 +174,7 @@
     def boot_instance(self, clients=None, keypair=None,
                       net_id=None, fixed_ip=None, **create_kwargs):
         if clients is None:
-            servers_client = self.servers_client
+            servers_client = self.os_primary.servers_client
         else:
             servers_client = clients.servers_client
         if keypair is None:
@@ -222,7 +224,7 @@
 
     def terminate_instance(self, instance, servers_client=None):
         if servers_client is None:
-            servers_client = self.servers_client
+            servers_client = self.os_primary.servers_client
 
         node = self.get_node(instance_id=instance['id'])
         servers_client.delete_server(instance['id'])
@@ -239,7 +241,7 @@
                         servers_client=None):
         """Rescue the instance, verify we can ping and SSH."""
         if servers_client is None:
-            servers_client = self.servers_client
+            servers_client = self.os_primary.servers_client
 
         rescuing_instance = servers_client.rescue_server(instance['id'])
         rescue_password = rescuing_instance['adminPass']
@@ -265,8 +267,8 @@
     def unrescue_instance(self, instance, node, server_ip,
                           servers_client=None):
         if servers_client is None:
-            servers_client = self.servers_client
-        self.servers_client.unrescue_server(instance['id'])
+            servers_client = self.os_primary.servers_client
+        self.os_primary.servers_client.unrescue_server(instance['id'])
         self.wait_provisioning_state(
             node['uuid'],
             BaremetalProvisionStates.ACTIVE,
diff --git a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
index 10a83a6..176f912 100644
--- a/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
+++ b/ironic_tempest_plugin/tests/scenario/baremetal_standalone_manager.py
@@ -31,7 +31,7 @@
 class BaremetalStandaloneManager(bm.BaremetalScenarioTest,
                                  manager.NetworkScenarioTest):
 
-    credentials = ['primary', 'admin']
+    credentials = ['primary', 'admin', 'system_admin']
     # NOTE(vsaienko): Standalone tests are using v1/node/<node_ident>/vifs to
     # attach VIF to a node.
     min_microversion = '1.28'
diff --git a/ironic_tempest_plugin/tests/scenario/introspection_manager.py b/ironic_tempest_plugin/tests/scenario/introspection_manager.py
index 71fa001..199bab1 100644
--- a/ironic_tempest_plugin/tests/scenario/introspection_manager.py
+++ b/ironic_tempest_plugin/tests/scenario/introspection_manager.py
@@ -23,7 +23,6 @@
 
 import ironic_tempest_plugin
 from ironic_tempest_plugin import exceptions
-from ironic_tempest_plugin.services import introspection_client
 from ironic_tempest_plugin.tests.api.admin.api_microversion_fixture import \
     APIMicroversionFixture as IronicMicroversionFixture
 from ironic_tempest_plugin.tests.scenario.baremetal_manager import \
@@ -40,7 +39,7 @@
 
     wait_provisioning_state_interval = 15
 
-    credentials = ['primary', 'admin']
+    credentials = ['admin', 'system_admin', 'primary']
 
     ironic_api_version = LATEST_MICROVERSION
 
@@ -53,8 +52,11 @@
     @classmethod
     def setup_clients(cls):
         super(InspectorScenarioTest, cls).setup_clients()
-        inspector_manager = introspection_client.Manager()
-        cls.introspection_client = inspector_manager.introspection_client
+        if CONF.enforce_scope.ironic_inspector:
+            oscli = cls.os_system_admin.introspection
+        else:
+            oscli = cls.os_admin.introspection
+        cls.introspection_client = oscli.BaremetalIntrospectionClient()
 
     def setUp(self):
         super(InspectorScenarioTest, self).setUp()
diff --git a/ironic_tempest_plugin/tests/scenario/test_baremetal_basic_ops.py b/ironic_tempest_plugin/tests/scenario/test_baremetal_basic_ops.py
index e3c1929..d4e8ae3 100644
--- a/ironic_tempest_plugin/tests/scenario/test_baremetal_basic_ops.py
+++ b/ironic_tempest_plugin/tests/scenario/test_baremetal_basic_ops.py
@@ -45,7 +45,7 @@
           expected state transitions
     """
 
-    credentials = ['primary', 'admin']
+    credentials = ['primary', 'admin', 'system_admin']
 
     TEST_RESCUE_MODE = False
     image_ref = None
diff --git a/ironic_tempest_plugin/tests/scenario/test_baremetal_boot_from_volume.py b/ironic_tempest_plugin/tests/scenario/test_baremetal_boot_from_volume.py
index 9908270..154fc96 100644
--- a/ironic_tempest_plugin/tests/scenario/test_baremetal_boot_from_volume.py
+++ b/ironic_tempest_plugin/tests/scenario/test_baremetal_boot_from_volume.py
@@ -34,7 +34,7 @@
     * Delete instance
     """
 
-    credentials = ['primary', 'admin']
+    credentials = ['primary', 'admin', 'system_admin']
 
     min_microversion = '1.32'
 
diff --git a/ironic_tempest_plugin/tests/scenario/test_baremetal_multitenancy.py b/ironic_tempest_plugin/tests/scenario/test_baremetal_multitenancy.py
index 60c3ed9..fc4c513 100644
--- a/ironic_tempest_plugin/tests/scenario/test_baremetal_multitenancy.py
+++ b/ironic_tempest_plugin/tests/scenario/test_baremetal_multitenancy.py
@@ -36,7 +36,7 @@
     * Delete both instances
     """
 
-    credentials = ['primary', 'alt', 'admin']
+    credentials = ['primary', 'alt', 'admin', 'system_admin']
 
     @classmethod
     def skip_checks(cls):
diff --git a/ironic_tempest_plugin/tests/scenario/test_baremetal_single_tenant.py b/ironic_tempest_plugin/tests/scenario/test_baremetal_single_tenant.py
index a873471..e06d43a 100644
--- a/ironic_tempest_plugin/tests/scenario/test_baremetal_single_tenant.py
+++ b/ironic_tempest_plugin/tests/scenario/test_baremetal_single_tenant.py
@@ -36,7 +36,7 @@
     * Delete both instances
     """
 
-    credentials = ['primary', 'alt', 'admin']
+    credentials = ['primary', 'alt', 'admin', 'system_admin']
 
     @classmethod
     def skip_checks(cls):