.. _cred_providers:

Credential Providers
====================

These library interfaces are used to deal with allocating credentials on demand
either dynamically by calling Keystone to allocate new credentials, or from
a list of preprovisioned credentials. These 2 modules are implementations of
the same abstract credential providers class and can be used interchangeably.
However, each implementation has some additional parameters that are used to
influence the behavior of the modules. The API reference at the bottom of this
doc shows the interface definitions for both modules, however, that may be a bit
opaque. You can see some examples of how to leverage this interface below.

Initialization Example
----------------------
This example is from Tempest itself (from tempest/common/credentials_factory.py
just modified slightly) and is how it initializes the credential provider based
on config::

  from tempest import config
  from tempest.lib.common import dynamic_creds
  from tempest.lib.common import preprov_creds

  CONF = config.CONF

  def get_credentials_provider(name, network_resources=None,
                               force_tenant_isolation=False,
                               identity_version=None):
      # If a test requires a new account to work, it can have it via forcing
      # dynamic credentials. A new account will be produced only for that test.
      # In case admin credentials are not available for the account creation,
      # the test should be skipped else it will fail.
      identity_version = identity_version or CONF.identity.auth_version
      if CONF.auth.use_dynamic_credentials or force_tenant_isolation:
          admin_creds = get_configured_admin_credentials(
              fill_in=True, identity_version=identity_version)
          return dynamic_creds.DynamicCredentialProvider(
              name=name,
              network_resources=network_resources,
              identity_version=identity_version,
              admin_creds=admin_creds,
              identity_admin_domain_scope=CONF.identity.admin_domain_scope,
              identity_admin_role=CONF.identity.admin_role,
              extra_roles=CONF.auth.tempest_roles,
              neutron_available=CONF.service_available.neutron,
              project_network_cidr=CONF.network.project_network_cidr,
              project_network_mask_bits=CONF.network.project_network_mask_bits,
              public_network_id=CONF.network.public_network_id,
              create_networks=(CONF.auth.create_isolated_networks and not
                               CONF.network.shared_physical_network),
              resource_prefix='tempest',
              credentials_domain=CONF.auth.default_credentials_domain_name,
              admin_role=CONF.identity.admin_role,
              identity_uri=CONF.identity.uri_v3,
              identity_admin_endpoint_type=CONF.identity.v3_endpoint_type)
      else:
          if CONF.auth.test_accounts_file:
              # Most params are not relevant for pre-created accounts
              return preprov_creds.PreProvisionedCredentialProvider(
                  name=name, identity_version=identity_version,
                  accounts_lock_dir=lockutils.get_lock_path(CONF),
                  test_accounts_file=CONF.auth.test_accounts_file,
                  object_storage_operator_role=CONF.object_storage.operator_role,
                  object_storage_reseller_admin_role=reseller_admin_role,
                  credentials_domain=CONF.auth.default_credentials_domain_name,
                  admin_role=CONF.identity.admin_role,
                  identity_uri=CONF.identity.uri_v3,
                  identity_admin_endpoint_type=CONF.identity.v3_endpoint_type)
          else:
              raise exceptions.InvalidConfiguration(
                  'A valid credential provider is needed')

This function just returns an initialized credential provider class based on the
config file. The consumer of this function treats the output as the same
regardless of whether it's a dynamic or preprovisioned provider object.

Dealing with Credentials
------------------------

Once you have a credential provider object created the access patterns for
allocating and removing credentials are the same across both the dynamic
and preprovisioned credentials. These are defined in the abstract
CredentialProvider class. At a high level, the credentials provider enables
you to get 3 basic types of credentials at once (per object): primary, alt,
and admin. You're also able to allocate a credential by role. These credentials
are tracked by the provider object and delete must be called manually,
otherwise, the created resources will not be deleted (or returned to the pool
in the case of preprovisioned creds).

Examples
''''''''

Continuing from the example above, to allocate credentials by the 3 basic types
you can do the following::

  provider = get_credentials_provider('my_tests')
  primary_creds = provider.get_primary_creds()
  alt_creds = provider.get_alt_creds()
  admin_creds = provider.get_admin_creds()
  # Make sure to delete the credentials when you're finished
  provider.clear_creds()

To create and interact with credentials by role you can do the following::

  provider = get_credentials_provider('my_tests')
  my_role_creds = provider.get_creds_by_role({'roles': ['my_role']})
  # provider.clear_creds() will clear all creds including those allocated by
  # role
  provider.clear_creds()

When multiple roles are specified a set of creds with all the roles assigned
will be allocated::

  provider = get_credentials_provider('my_tests')
  my_role_creds = provider.get_creds_by_role({'roles': ['my_role',
                                                        'my_other_role']})
  # provider.clear_creds() will clear all creds including those allocated by
  # role
  provider.clear_creds()

If you need multiple sets of credentials with the same roles you can also do
this by leveraging the ``force_new`` kwarg::

  provider = get_credentials_provider('my_tests')
  my_role_creds = provider.get_creds_by_role({'roles': ['my_role']})
  my_role_other_creds = provider.get_creds_by_role({'roles': ['my_role']},
                                                   force_new=True)
  # provider.clear_creds() will clear all creds including those allocated by
  # role
  provider.clear_creds()


API Reference
-------------

The dynamic credentials module
''''''''''''''''''''''''''''''

.. automodule:: tempest.lib.common.dynamic_creds
   :members:

The pre-provisioned credentials module
''''''''''''''''''''''''''''''''''''''

.. automodule:: tempest.lib.common.preprov_creds
   :members:
