summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Meyers <chris.meyers.fsu@gmail.com>2024-08-06 16:22:43 +0200
committerChris Meyers <chrismeyersfsu@users.noreply.github.com>2024-09-04 20:46:22 +0200
commit376cc35a925764b2535618d194c44f786bab8662 (patch)
tree0e25ba6258700758c8761ef292cd381caafd5594
parentRemove references to IRC & Google Groups (#15480) (diff)
downloadawx-376cc35a925764b2535618d194c44f786bab8662.tar.xz
awx-376cc35a925764b2535618d194c44f786bab8662.zip
move inv and cred plugins into awx_plugins
-rw-r--r--awx/main/credential_plugins/__init__.py0
-rw-r--r--awx/main/models/credential/__init__.py668
-rw-r--r--awx/main/models/inventory.py301
-rw-r--r--awx_plugins/credentials/aim.py (renamed from awx/main/credential_plugins/aim.py)2
-rw-r--r--awx_plugins/credentials/aws_secretsmanager.py (renamed from awx/main/credential_plugins/aws_secretsmanager.py)2
-rw-r--r--awx_plugins/credentials/azure_kv.py (renamed from awx/main/credential_plugins/azure_kv.py)2
-rw-r--r--awx_plugins/credentials/centrify_vault.py (renamed from awx/main/credential_plugins/centrify_vault.py)2
-rw-r--r--awx_plugins/credentials/conjur.py (renamed from awx/main/credential_plugins/conjur.py)2
-rw-r--r--awx_plugins/credentials/dsv.py (renamed from awx/main/credential_plugins/dsv.py)4
-rw-r--r--awx_plugins/credentials/hashivault.py (renamed from awx/main/credential_plugins/hashivault.py)2
-rw-r--r--awx_plugins/credentials/injectors.py (renamed from awx/main/models/credential/injectors.py)0
-rw-r--r--awx_plugins/credentials/plugin.py (renamed from awx/main/credential_plugins/plugin.py)13
-rw-r--r--awx_plugins/credentials/plugins.py665
-rw-r--r--awx_plugins/credentials/tss.py (renamed from awx/main/credential_plugins/tss.py)2
-rw-r--r--awx_plugins/inventory/plugins.py302
-rw-r--r--awx_plugins/tests/test_credential_plugins.py (renamed from awx/main/tests/functional/test_credential_plugins.py)8
-rw-r--r--setup.cfg20
17 files changed, 1010 insertions, 985 deletions
diff --git a/awx/main/credential_plugins/__init__.py b/awx/main/credential_plugins/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/awx/main/credential_plugins/__init__.py
+++ /dev/null
diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py
index e07553e1a9..8eee732c25 100644
--- a/awx/main/models/credential/__init__.py
+++ b/awx/main/models/credential/__init__.py
@@ -15,7 +15,7 @@ from jinja2 import sandbox
# Django
from django.db import models
-from django.utils.translation import gettext_lazy as _, gettext_noop
+from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError
from django.conf import settings
from django.utils.encoding import force_str
@@ -47,12 +47,12 @@ from awx.main.models.rbac import (
)
from awx.main.models import Team, Organization
from awx.main.utils import encrypt_field
-from . import injectors as builtin_injectors
+from awx_plugins.credentials import injectors as builtin_injectors
__all__ = ['Credential', 'CredentialType', 'CredentialInputSource', 'build_safe_env']
logger = logging.getLogger('awx.main.models.credential')
-credential_plugins = dict((ep.name, ep.load()) for ep in iter_entry_points('awx.credential_plugins'))
+credential_plugins = dict((ep.name, ep.load()) for ep in iter_entry_points('awx.plugins'))
HIDDEN_PASSWORD = '**********'
@@ -601,666 +601,6 @@ class ManagedCredentialType(SimpleNamespace):
return CredentialType(**self.get_creation_params())
-ManagedCredentialType(
- namespace='ssh',
- kind='ssh',
- name=gettext_noop('Machine'),
- inputs={
- 'fields': [
- {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
- {'id': 'password', 'label': gettext_noop('Password'), 'type': 'string', 'secret': True, 'ask_at_runtime': True},
- {'id': 'ssh_key_data', 'label': gettext_noop('SSH Private Key'), 'type': 'string', 'format': 'ssh_private_key', 'secret': True, 'multiline': True},
- {
- 'id': 'ssh_public_key_data',
- 'label': gettext_noop('Signed SSH Certificate'),
- 'type': 'string',
- 'multiline': True,
- 'secret': True,
- },
- {'id': 'ssh_key_unlock', 'label': gettext_noop('Private Key Passphrase'), 'type': 'string', 'secret': True, 'ask_at_runtime': True},
- {
- 'id': 'become_method',
- 'label': gettext_noop('Privilege Escalation Method'),
- 'type': 'string',
- 'help_text': gettext_noop('Specify a method for "become" operations. This is equivalent to specifying the --become-method Ansible parameter.'),
- },
- {
- 'id': 'become_username',
- 'label': gettext_noop('Privilege Escalation Username'),
- 'type': 'string',
- },
- {'id': 'become_password', 'label': gettext_noop('Privilege Escalation Password'), 'type': 'string', 'secret': True, 'ask_at_runtime': True},
- ],
- },
-)
-
-ManagedCredentialType(
- namespace='scm',
- kind='scm',
- name=gettext_noop('Source Control'),
- managed=True,
- inputs={
- 'fields': [
- {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
- {'id': 'password', 'label': gettext_noop('Password'), 'type': 'string', 'secret': True},
- {'id': 'ssh_key_data', 'label': gettext_noop('SCM Private Key'), 'type': 'string', 'format': 'ssh_private_key', 'secret': True, 'multiline': True},
- {'id': 'ssh_key_unlock', 'label': gettext_noop('Private Key Passphrase'), 'type': 'string', 'secret': True},
- ],
- },
-)
-
-ManagedCredentialType(
- namespace='vault',
- kind='vault',
- name=gettext_noop('Vault'),
- managed=True,
- inputs={
- 'fields': [
- {'id': 'vault_password', 'label': gettext_noop('Vault Password'), 'type': 'string', 'secret': True, 'ask_at_runtime': True},
- {
- 'id': 'vault_id',
- 'label': gettext_noop('Vault Identifier'),
- 'type': 'string',
- 'format': 'vault_id',
- 'help_text': gettext_noop(
- 'Specify an (optional) Vault ID. This is '
- 'equivalent to specifying the --vault-id '
- 'Ansible parameter for providing multiple Vault '
- 'passwords. Note: this feature only works in '
- 'Ansible 2.4+.'
- ),
- },
- ],
- 'required': ['vault_password'],
- },
-)
-
-ManagedCredentialType(
- namespace='net',
- kind='net',
- name=gettext_noop('Network'),
- managed=True,
- inputs={
- 'fields': [
- {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
- {
- 'id': 'password',
- 'label': gettext_noop('Password'),
- 'type': 'string',
- 'secret': True,
- },
- {'id': 'ssh_key_data', 'label': gettext_noop('SSH Private Key'), 'type': 'string', 'format': 'ssh_private_key', 'secret': True, 'multiline': True},
- {
- 'id': 'ssh_key_unlock',
- 'label': gettext_noop('Private Key Passphrase'),
- 'type': 'string',
- 'secret': True,
- },
- {
- 'id': 'authorize',
- 'label': gettext_noop('Authorize'),
- 'type': 'boolean',
- },
- {
- 'id': 'authorize_password',
- 'label': gettext_noop('Authorize Password'),
- 'type': 'string',
- 'secret': True,
- },
- ],
- 'dependencies': {
- 'authorize_password': ['authorize'],
- },
- 'required': ['username'],
- },
-)
-
-ManagedCredentialType(
- namespace='aws',
- kind='cloud',
- name=gettext_noop('Amazon Web Services'),
- managed=True,
- inputs={
- 'fields': [
- {'id': 'username', 'label': gettext_noop('Access Key'), 'type': 'string'},
- {
- 'id': 'password',
- 'label': gettext_noop('Secret Key'),
- 'type': 'string',
- 'secret': True,
- },
- {
- 'id': 'security_token',
- 'label': gettext_noop('STS Token'),
- 'type': 'string',
- 'secret': True,
- 'help_text': gettext_noop(
- 'Security Token Service (STS) is a web service '
- 'that enables you to request temporary, '
- 'limited-privilege credentials for AWS Identity '
- 'and Access Management (IAM) users.'
- ),
- },
- ],
- 'required': ['username', 'password'],
- },
-)
-
-ManagedCredentialType(
- namespace='openstack',
- kind='cloud',
- name=gettext_noop('OpenStack'),
- managed=True,
- inputs={
- 'fields': [
- {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
- {
- 'id': 'password',
- 'label': gettext_noop('Password (API Key)'),
- 'type': 'string',
- 'secret': True,
- },
- {
- 'id': 'host',
- 'label': gettext_noop('Host (Authentication URL)'),
- 'type': 'string',
- 'help_text': gettext_noop('The host to authenticate with. For example, https://openstack.business.com/v2.0/'),
- },
- {
- 'id': 'project',
- 'label': gettext_noop('Project (Tenant Name)'),
- 'type': 'string',
- },
- {
- 'id': 'project_domain_name',
- 'label': gettext_noop('Project (Domain Name)'),
- 'type': 'string',
- },
- {
- 'id': 'domain',
- 'label': gettext_noop('Domain Name'),
- 'type': 'string',
- 'help_text': gettext_noop(
- 'OpenStack domains define administrative boundaries. '
- 'It is only needed for Keystone v3 authentication '
- 'URLs. Refer to the documentation for '
- 'common scenarios.'
- ),
- },
- {
- 'id': 'region',
- 'label': gettext_noop('Region Name'),
- 'type': 'string',
- 'help_text': gettext_noop('For some cloud providers, like OVH, region must be specified'),
- },
- {
- 'id': 'verify_ssl',
- 'label': gettext_noop('Verify SSL'),
- 'type': 'boolean',
- 'default': True,
- },
- ],
- 'required': ['username', 'password', 'host', 'project'],
- },
-)
-
-ManagedCredentialType(
- namespace='vmware',
- kind='cloud',
- name=gettext_noop('VMware vCenter'),
- managed=True,
- inputs={
- 'fields': [
- {
- 'id': 'host',
- 'label': gettext_noop('VCenter Host'),
- 'type': 'string',
- 'help_text': gettext_noop('Enter the hostname or IP address that corresponds to your VMware vCenter.'),
- },
- {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
- {
- 'id': 'password',
- 'label': gettext_noop('Password'),
- 'type': 'string',
- 'secret': True,
- },
- ],
- 'required': ['host', 'username', 'password'],
- },
-)
-
-ManagedCredentialType(
- namespace='satellite6',
- kind='cloud',
- name=gettext_noop('Red Hat Satellite 6'),
- managed=True,
- inputs={
- 'fields': [
- {
- 'id': 'host',
- 'label': gettext_noop('Satellite 6 URL'),
- 'type': 'string',
- 'help_text': gettext_noop('Enter the URL that corresponds to your Red Hat Satellite 6 server. For example, https://satellite.example.org'),
- },
- {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
- {
- 'id': 'password',
- 'label': gettext_noop('Password'),
- 'type': 'string',
- 'secret': True,
- },
- ],
- 'required': ['host', 'username', 'password'],
- },
-)
-
-ManagedCredentialType(
- namespace='gce',
- kind='cloud',
- name=gettext_noop('Google Compute Engine'),
- managed=True,
- inputs={
- 'fields': [
- {
- 'id': 'username',
- 'label': gettext_noop('Service Account Email Address'),
- 'type': 'string',
- 'help_text': gettext_noop('The email address assigned to the Google Compute Engine service account.'),
- },
- {
- 'id': 'project',
- 'label': 'Project',
- 'type': 'string',
- 'help_text': gettext_noop(
- 'The Project ID is the GCE assigned identification. '
- 'It is often constructed as three words or two words '
- 'followed by a three-digit number. Examples: project-id-000 '
- 'and another-project-id'
- ),
- },
- {
- 'id': 'ssh_key_data',
- 'label': gettext_noop('RSA Private Key'),
- 'type': 'string',
- 'format': 'ssh_private_key',
- 'secret': True,
- 'multiline': True,
- 'help_text': gettext_noop('Paste the contents of the PEM file associated with the service account email.'),
- },
- ],
- 'required': ['username', 'ssh_key_data'],
- },
-)
-
-ManagedCredentialType(
- namespace='azure_rm',
- kind='cloud',
- name=gettext_noop('Microsoft Azure Resource Manager'),
- managed=True,
- inputs={
- 'fields': [
- {
- 'id': 'subscription',
- 'label': gettext_noop('Subscription ID'),
- 'type': 'string',
- 'help_text': gettext_noop('Subscription ID is an Azure construct, which is mapped to a username.'),
- },
- {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
- {
- 'id': 'password',
- 'label': gettext_noop('Password'),
- 'type': 'string',
- 'secret': True,
- },
- {'id': 'client', 'label': gettext_noop('Client ID'), 'type': 'string'},
- {
- 'id': 'secret',
- 'label': gettext_noop('Client Secret'),
- 'type': 'string',
- 'secret': True,
- },
- {'id': 'tenant', 'label': gettext_noop('Tenant ID'), 'type': 'string'},
- {
- 'id': 'cloud_environment',
- 'label': gettext_noop('Azure Cloud Environment'),
- 'type': 'string',
- 'help_text': gettext_noop('Environment variable AZURE_CLOUD_ENVIRONMENT when using Azure GovCloud or Azure stack.'),
- },
- ],
- 'required': ['subscription'],
- },
-)
-
-ManagedCredentialType(
- namespace='github_token',
- kind='token',
- name=gettext_noop('GitHub Personal Access Token'),
- managed=True,
- inputs={
- 'fields': [
- {
- 'id': 'token',
- 'label': gettext_noop('Token'),
- 'type': 'string',
- 'secret': True,
- 'help_text': gettext_noop('This token needs to come from your profile settings in GitHub'),
- }
- ],
- 'required': ['token'],
- },
-)
-
-ManagedCredentialType(
- namespace='gitlab_token',
- kind='token',
- name=gettext_noop('GitLab Personal Access Token'),
- managed=True,
- inputs={
- 'fields': [
- {
- 'id': 'token',
- 'label': gettext_noop('Token'),
- 'type': 'string',
- 'secret': True,
- 'help_text': gettext_noop('This token needs to come from your profile settings in GitLab'),
- }
- ],
- 'required': ['token'],
- },
-)
-
-ManagedCredentialType(
- namespace='bitbucket_dc_token',
- kind='token',
- name=gettext_noop('Bitbucket Data Center HTTP Access Token'),
- managed=True,
- inputs={
- 'fields': [
- {
- 'id': 'token',
- 'label': gettext_noop('Token'),
- 'type': 'string',
- 'secret': True,
- 'help_text': gettext_noop('This token needs to come from your user settings in Bitbucket'),
- }
- ],
- 'required': ['token'],
- },
-)
-
-ManagedCredentialType(
- namespace='insights',
- kind='insights',
- name=gettext_noop('Insights'),
- managed=True,
- inputs={
- 'fields': [
- {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
- {'id': 'password', 'label': gettext_noop('Password'), 'type': 'string', 'secret': True},
- ],
- 'required': ['username', 'password'],
- },
- injectors={
- 'extra_vars': {
- "scm_username": "{{username}}",
- "scm_password": "{{password}}",
- },
- 'env': {
- 'INSIGHTS_USER': '{{username}}',
- 'INSIGHTS_PASSWORD': '{{password}}',
- },
- },
-)
-
-ManagedCredentialType(
- namespace='rhv',
- kind='cloud',
- name=gettext_noop('Red Hat Virtualization'),
- managed=True,
- inputs={
- 'fields': [
- {'id': 'host', 'label': gettext_noop('Host (Authentication URL)'), 'type': 'string', 'help_text': gettext_noop('The host to authenticate with.')},
- {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
- {
- 'id': 'password',
- 'label': gettext_noop('Password'),
- 'type': 'string',
- 'secret': True,
- },
- {
- 'id': 'ca_file',
- 'label': gettext_noop('CA File'),
- 'type': 'string',
- 'help_text': gettext_noop('Absolute file path to the CA file to use (optional)'),
- },
- ],
- 'required': ['host', 'username', 'password'],
- },
- injectors={
- # The duplication here is intentional; the ovirt4 inventory plugin
- # writes a .ini file for authentication, while the ansible modules for
- # ovirt4 use a separate authentication process that support
- # environment variables; by injecting both, we support both
- 'file': {
- 'template': '\n'.join(
- [
- '[ovirt]',
- 'ovirt_url={{host}}',
- 'ovirt_username={{username}}',
- 'ovirt_password={{password}}',
- '{% if ca_file %}ovirt_ca_file={{ca_file}}{% endif %}',
- ]
- )
- },
- 'env': {'OVIRT_INI_PATH': '{{tower.filename}}', 'OVIRT_URL': '{{host}}', 'OVIRT_USERNAME': '{{username}}', 'OVIRT_PASSWORD': '{{password}}'},
- },
-)
-
-ManagedCredentialType(
- namespace='controller',
- kind='cloud',
- name=gettext_noop('Red Hat Ansible Automation Platform'),
- managed=True,
- inputs={
- 'fields': [
- {
- 'id': 'host',
- 'label': gettext_noop('Red Hat Ansible Automation Platform'),
- 'type': 'string',
- 'help_text': gettext_noop('Red Hat Ansible Automation Platform base URL to authenticate with.'),
- },
- {
- 'id': 'username',
- 'label': gettext_noop('Username'),
- 'type': 'string',
- 'help_text': gettext_noop(
- 'Red Hat Ansible Automation Platform username id to authenticate as.This should not be set if an OAuth token is being used.'
- ),
- },
- {
- 'id': 'password',
- 'label': gettext_noop('Password'),
- 'type': 'string',
- 'secret': True,
- },
- {
- 'id': 'oauth_token',
- 'label': gettext_noop('OAuth Token'),
- 'type': 'string',
- 'secret': True,
- 'help_text': gettext_noop('An OAuth token to use to authenticate with.This should not be set if username/password are being used.'),
- },
- {'id': 'verify_ssl', 'label': gettext_noop('Verify SSL'), 'type': 'boolean', 'secret': False},
- ],
- 'required': ['host'],
- },
- injectors={
- 'env': {
- 'TOWER_HOST': '{{host}}',
- 'TOWER_USERNAME': '{{username}}',
- 'TOWER_PASSWORD': '{{password}}',
- 'TOWER_VERIFY_SSL': '{{verify_ssl}}',
- 'TOWER_OAUTH_TOKEN': '{{oauth_token}}',
- 'CONTROLLER_HOST': '{{host}}',
- 'CONTROLLER_USERNAME': '{{username}}',
- 'CONTROLLER_PASSWORD': '{{password}}',
- 'CONTROLLER_VERIFY_SSL': '{{verify_ssl}}',
- 'CONTROLLER_OAUTH_TOKEN': '{{oauth_token}}',
- }
- },
-)
-
-ManagedCredentialType(
- namespace='kubernetes_bearer_token',
- kind='kubernetes',
- name=gettext_noop('OpenShift or Kubernetes API Bearer Token'),
- inputs={
- 'fields': [
- {
- 'id': 'host',
- 'label': gettext_noop('OpenShift or Kubernetes API Endpoint'),
- 'type': 'string',
- 'help_text': gettext_noop('The OpenShift or Kubernetes API Endpoint to authenticate with.'),
- },
- {
- 'id': 'bearer_token',
- 'label': gettext_noop('API authentication bearer token'),
- 'type': 'string',
- 'secret': True,
- },
- {
- 'id': 'verify_ssl',
- 'label': gettext_noop('Verify SSL'),
- 'type': 'boolean',
- 'default': True,
- },
- {
- 'id': 'ssl_ca_cert',
- 'label': gettext_noop('Certificate Authority data'),
- 'type': 'string',
- 'secret': True,
- 'multiline': True,
- },
- ],
- 'required': ['host', 'bearer_token'],
- },
-)
-
-ManagedCredentialType(
- namespace='registry',
- kind='registry',
- name=gettext_noop('Container Registry'),
- inputs={
- 'fields': [
- {
- 'id': 'host',
- 'label': gettext_noop('Authentication URL'),
- 'type': 'string',
- 'help_text': gettext_noop('Authentication endpoint for the container registry.'),
- 'default': 'quay.io',
- },
- {
- 'id': 'username',
- 'label': gettext_noop('Username'),
- 'type': 'string',
- },
- {
- 'id': 'password',
- 'label': gettext_noop('Password or Token'),
- 'type': 'string',
- 'secret': True,
- 'help_text': gettext_noop('A password or token used to authenticate with'),
- },
- {
- 'id': 'verify_ssl',
- 'label': gettext_noop('Verify SSL'),
- 'type': 'boolean',
- 'default': True,
- },
- ],
- 'required': ['host'],
- },
-)
-
-
-ManagedCredentialType(
- namespace='galaxy_api_token',
- kind='galaxy',
- name=gettext_noop('Ansible Galaxy/Automation Hub API Token'),
- inputs={
- 'fields': [
- {
- 'id': 'url',
- 'label': gettext_noop('Galaxy Server URL'),
- 'type': 'string',
- 'help_text': gettext_noop('The URL of the Galaxy instance to connect to.'),
- },
- {
- 'id': 'auth_url',
- 'label': gettext_noop('Auth Server URL'),
- 'type': 'string',
- 'help_text': gettext_noop('The URL of a Keycloak server token_endpoint, if using SSO auth.'),
- },
- {
- 'id': 'token',
- 'label': gettext_noop('API Token'),
- 'type': 'string',
- 'secret': True,
- 'help_text': gettext_noop('A token to use for authentication against the Galaxy instance.'),
- },
- ],
- 'required': ['url'],
- },
-)
-
-ManagedCredentialType(
- namespace='gpg_public_key',
- kind='cryptography',
- name=gettext_noop('GPG Public Key'),
- inputs={
- 'fields': [
- {
- 'id': 'gpg_public_key',
- 'label': gettext_noop('GPG Public Key'),
- 'type': 'string',
- 'secret': True,
- 'multiline': True,
- 'help_text': gettext_noop('GPG Public Key used to validate content signatures.'),
- },
- ],
- 'required': ['gpg_public_key'],
- },
-)
-
-ManagedCredentialType(
- namespace='terraform',
- kind='cloud',
- name=gettext_noop('Terraform backend configuration'),
- managed=True,
- inputs={
- 'fields': [
- {
- 'id': 'configuration',
- 'label': gettext_noop('Backend configuration'),
- 'type': 'string',
- 'secret': True,
- 'multiline': True,
- 'help_text': gettext_noop('Terraform backend config as Hashicorp configuration language.'),
- },
- {
- 'id': 'gce_credentials',
- 'label': gettext_noop('Google Cloud Platform account credentials'),
- 'type': 'string',
- 'secret': True,
- 'multiline': True,
- 'help_text': gettext_noop('Google Cloud Platform account credentials in JSON format.'),
- },
- ],
- 'required': ['configuration'],
- },
-)
-
-
class CredentialInputSource(PrimordialModel):
class Meta:
app_label = 'main'
@@ -1325,5 +665,7 @@ class CredentialInputSource(PrimordialModel):
return reverse(view_name, kwargs={'pk': self.pk}, request=request)
+from awx_plugins.credentials.plugins import * # noqa
+
for ns, plugin in credential_plugins.items():
CredentialType.load_plugin(ns, plugin)
diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py
index 2b96ed549f..acc900250c 100644
--- a/awx/main/models/inventory.py
+++ b/awx/main/models/inventory.py
@@ -10,10 +10,6 @@ import copy
import os.path
from urllib.parse import urljoin
-import yaml
-import tempfile
-import stat
-
# Django
from django.conf import settings
from django.db import models, connection
@@ -28,6 +24,7 @@ from django.db.models import Q
from rest_framework.exceptions import ParseError
from ansible_base.lib.utils.models import prevent_search
+from awx_plugins.inventory.plugins import PluginFileInjector
# AWX
from awx.api.versioning import reverse
@@ -52,11 +49,9 @@ from awx.main.models.notifications import (
NotificationTemplate,
JobNotificationMixin,
)
-from awx.main.models.credential.injectors import _openstack_data
from awx.main.utils import _inventory_updates
from awx.main.utils.safe_yaml import sanitize_jinja
-from awx.main.utils.execution_environments import to_container_path, get_control_plane_execution_environment
-from awx.main.utils.licensing import server_product_name
+from awx.main.utils.execution_environments import get_control_plane_execution_environment
__all__ = ['Inventory', 'Host', 'Group', 'InventorySource', 'InventoryUpdate', 'SmartInventoryMembership', 'HostMetric', 'HostMetricSummaryMonthly']
@@ -1427,297 +1422,5 @@ class CustomInventoryScript(CommonModelNameNotUnique):
return reverse('api:inventory_script_detail', kwargs={'pk': self.pk}, request=request)
-class PluginFileInjector(object):
- plugin_name = None # Ansible core name used to reference plugin
- # base injector should be one of None, "managed", or "template"
- # this dictates which logic to borrow from playbook injectors
- base_injector = None
- # every source should have collection, these are for the collection name
- namespace = None
- collection = None
- collection_migration = '2.9' # Starting with this version, we use collections
- use_fqcn = False # plugin: name versus plugin: namespace.collection.name
-
- # TODO: delete this method and update unit tests
- @classmethod
- def get_proper_name(cls):
- if cls.plugin_name is None:
- return None
- return f'{cls.namespace}.{cls.collection}.{cls.plugin_name}'
-
- @property
- def filename(self):
- """Inventory filename for using the inventory plugin
- This is created dynamically, but the auto plugin requires this exact naming
- """
- return '{0}.yml'.format(self.plugin_name)
-
- def inventory_contents(self, inventory_update, private_data_dir):
- """Returns a string that is the content for the inventory file for the inventory plugin"""
- return yaml.safe_dump(self.inventory_as_dict(inventory_update, private_data_dir), default_flow_style=False, width=1000)
-
- def inventory_as_dict(self, inventory_update, private_data_dir):
- source_vars = dict(inventory_update.source_vars_dict) # make a copy
- '''
- None conveys that we should use the user-provided plugin.
- Note that a plugin value of '' should still be overridden.
- '''
- if self.plugin_name is not None:
- if hasattr(self, 'downstream_namespace') and server_product_name() != 'AWX':
- source_vars['plugin'] = f'{self.downstream_namespace}.{self.downstream_collection}.{self.plugin_name}'
- elif self.use_fqcn:
- source_vars['plugin'] = f'{self.namespace}.{self.collection}.{self.plugin_name}'
- else:
- source_vars['plugin'] = self.plugin_name
- return source_vars
-
- def build_env(self, inventory_update, env, private_data_dir, private_data_files):
- injector_env = self.get_plugin_env(inventory_update, private_data_dir, private_data_files)
- env.update(injector_env)
- # All CLOUD_PROVIDERS sources implement as inventory plugin from collection
- env['ANSIBLE_INVENTORY_ENABLED'] = 'auto'
- return env
-
- def _get_shared_env(self, inventory_update, private_data_dir, private_data_files):
- """By default, we will apply the standard managed injectors"""
- injected_env = {}
- credential = inventory_update.get_cloud_credential()
- # some sources may have no credential, specifically ec2
- if credential is None:
- return injected_env
- if self.base_injector in ('managed', 'template'):
- injected_env['INVENTORY_UPDATE_ID'] = str(inventory_update.pk) # so injector knows this is inventory
- if self.base_injector == 'managed':
- from awx.main.models.credential import injectors as builtin_injectors
-
- cred_kind = inventory_update.source.replace('ec2', 'aws')
- if cred_kind in dir(builtin_injectors):
- getattr(builtin_injectors, cred_kind)(credential, injected_env, private_data_dir)
- elif self.base_injector == 'template':
- safe_env = injected_env.copy()
- args = []
- credential.credential_type.inject_credential(credential, injected_env, safe_env, args, private_data_dir)
- # NOTE: safe_env is handled externally to injector class by build_safe_env static method
- # that means that managed injectors must only inject detectable env keys
- # enforcement of this is accomplished by tests
- return injected_env
-
- def get_plugin_env(self, inventory_update, private_data_dir, private_data_files):
- env = self._get_shared_env(inventory_update, private_data_dir, private_data_files)
- return env
-
- def build_private_data(self, inventory_update, private_data_dir):
- return self.build_plugin_private_data(inventory_update, private_data_dir)
-
- def build_plugin_private_data(self, inventory_update, private_data_dir):
- return None
-
-
-class azure_rm(PluginFileInjector):
- plugin_name = 'azure_rm'
- base_injector = 'managed'
- namespace = 'azure'
- collection = 'azcollection'
-
- def get_plugin_env(self, *args, **kwargs):
- ret = super(azure_rm, self).get_plugin_env(*args, **kwargs)
- # We need native jinja2 types so that tags can give JSON null value
- ret['ANSIBLE_JINJA2_NATIVE'] = str(True)
- return ret
-
-
-class ec2(PluginFileInjector):
- plugin_name = 'aws_ec2'
- base_injector = 'managed'
- namespace = 'amazon'
- collection = 'aws'
-
- def get_plugin_env(self, *args, **kwargs):
- ret = super(ec2, self).get_plugin_env(*args, **kwargs)
- # We need native jinja2 types so that ec2_state_code will give integer
- ret['ANSIBLE_JINJA2_NATIVE'] = str(True)
- return ret
-
-
-class gce(PluginFileInjector):
- plugin_name = 'gcp_compute'
- base_injector = 'managed'
- namespace = 'google'
- collection = 'cloud'
-
- def get_plugin_env(self, *args, **kwargs):
- ret = super(gce, self).get_plugin_env(*args, **kwargs)
- # We need native jinja2 types so that ip addresses can give JSON null value
- ret['ANSIBLE_JINJA2_NATIVE'] = str(True)
- return ret
-
- def inventory_as_dict(self, inventory_update, private_data_dir):
- ret = super().inventory_as_dict(inventory_update, private_data_dir)
- credential = inventory_update.get_cloud_credential()
- # InventorySource.source_vars take precedence over ENV vars
- if 'projects' not in ret:
- ret['projects'] = [credential.get_input('project', default='')]
- return ret
-
-
-class vmware(PluginFileInjector):
- plugin_name = 'vmware_vm_inventory'
- base_injector = 'managed'
- namespace = 'community'
- collection = 'vmware'
-
-
-class openstack(PluginFileInjector):
- plugin_name = 'openstack'
- namespace = 'openstack'
- collection = 'cloud'
-
- def _get_clouds_dict(self, inventory_update, cred, private_data_dir):
- openstack_data = _openstack_data(cred)
-
- openstack_data['clouds']['devstack']['private'] = inventory_update.source_vars_dict.get('private', True)
- ansible_variables = {
- 'use_hostnames': True,
- 'expand_hostvars': False,
- 'fail_on_errors': True,
- }
- provided_count = 0
- for var_name in ansible_variables:
- if var_name in inventory_update.source_vars_dict:
- ansible_variables[var_name] = inventory_update.source_vars_dict[var_name]
- provided_count += 1
- if provided_count:
- # Must we provide all 3 because the user provides any 1 of these??
- # this probably results in some incorrect mangling of the defaults
- openstack_data['ansible'] = ansible_variables
- return openstack_data
-
- def build_plugin_private_data(self, inventory_update, private_data_dir):
- credential = inventory_update.get_cloud_credential()
- private_data = {'credentials': {}}
-
- openstack_data = self._get_clouds_dict(inventory_update, credential, private_data_dir)
- private_data['credentials'][credential] = yaml.safe_dump(openstack_data, default_flow_style=False, allow_unicode=True)
- return private_data
-
- def get_plugin_env(self, inventory_update, private_data_dir, private_data_files):
- env = super(openstack, self).get_plugin_env(inventory_update, private_data_dir, private_data_files)
- credential = inventory_update.get_cloud_credential()
- cred_data = private_data_files['credentials']
- env['OS_CLIENT_CONFIG_FILE'] = to_container_path(cred_data[credential], private_data_dir)
- return env
-
-
-class rhv(PluginFileInjector):
- """ovirt uses the custom credential templating, and that is all"""
-
- plugin_name = 'ovirt'
- base_injector = 'template'
- initial_version = '2.9'
- namespace = 'ovirt'
- collection = 'ovirt'
- downstream_namespace = 'redhat'
- downstream_collection = 'rhv'
- use_fqcn = True
-
-
-class satellite6(PluginFileInjector):
- plugin_name = 'foreman'
- namespace = 'theforeman'
- collection = 'foreman'
- downstream_namespace = 'redhat'
- downstream_collection = 'satellite'
- use_fqcn = True
-
- def get_plugin_env(self, inventory_update, private_data_dir, private_data_files):
- # this assumes that this is merged
- # https://github.com/ansible/ansible/pull/52693
- credential = inventory_update.get_cloud_credential()
- ret = super(satellite6, self).get_plugin_env(inventory_update, private_data_dir, private_data_files)
- if credential:
- ret['FOREMAN_SERVER'] = credential.get_input('host', default='')
- ret['FOREMAN_USER'] = credential.get_input('username', default='')
- ret['FOREMAN_PASSWORD'] = credential.get_input('password', default='')
- return ret
-
-
-class terraform(PluginFileInjector):
- plugin_name = 'terraform_state'
- namespace = 'cloud'
- collection = 'terraform'
- use_fqcn = True
-
- def inventory_as_dict(self, inventory_update, private_data_dir):
- ret = super().inventory_as_dict(inventory_update, private_data_dir)
- credential = inventory_update.get_cloud_credential()
- config_cred = credential.get_input('configuration')
- if config_cred:
- handle, path = tempfile.mkstemp(dir=os.path.join(private_data_dir, 'env'))
- with os.fdopen(handle, 'w') as f:
- os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
- f.write(config_cred)
- ret['backend_config_files'] = to_container_path(path, private_data_dir)
- return ret
-
- def build_plugin_private_data(self, inventory_update, private_data_dir):
- credential = inventory_update.get_cloud_credential()
-
- private_data = {'credentials': {}}
- gce_cred = credential.get_input('gce_credentials', default=None)
- if gce_cred:
- private_data['credentials'][credential] = gce_cred
- return private_data
-
- def get_plugin_env(self, inventory_update, private_data_dir, private_data_files):
- env = super(terraform, self).get_plugin_env(inventory_update, private_data_dir, private_data_files)
- credential = inventory_update.get_cloud_credential()
- cred_data = private_data_files['credentials']
- if credential in cred_data:
- env['GOOGLE_BACKEND_CREDENTIALS'] = to_container_path(cred_data[credential], private_data_dir)
- return env
-
-
-class controller(PluginFileInjector):
- plugin_name = 'tower' # TODO: relying on routing for now, update after EEs pick up revised collection
- base_injector = 'template'
- namespace = 'awx'
- collection = 'awx'
- downstream_namespace = 'ansible'
- downstream_collection = 'controller'
-
-
-class insights(PluginFileInjector):
- plugin_name = 'insights'
- base_injector = 'template'
- namespace = 'redhatinsights'
- collection = 'insights'
- downstream_namespace = 'redhat'
- downstream_collection = 'insights'
- use_fqcn = True
-
-
-class openshift_virtualization(PluginFileInjector):
- plugin_name = 'kubevirt'
- base_injector = 'template'
- namespace = 'kubevirt'
- collection = 'core'
- downstream_namespace = 'redhat'
- downstream_collection = 'openshift_virtualization'
- use_fqcn = True
-
-
-class constructed(PluginFileInjector):
- plugin_name = 'constructed'
- namespace = 'ansible'
- collection = 'builtin'
-
- def build_env(self, *args, **kwargs):
- env = super().build_env(*args, **kwargs)
- # Enable script inventory plugin so we pick up the script files from source inventories
- env['ANSIBLE_INVENTORY_ENABLED'] += ',script'
- env['ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED'] = 'True'
- return env
-
-
for cls in PluginFileInjector.__subclasses__():
InventorySourceOptions.injectors[cls.__name__] = cls
diff --git a/awx/main/credential_plugins/aim.py b/awx_plugins/credentials/aim.py
index 2476042b5f..dc06b0ea6f 100644
--- a/awx/main/credential_plugins/aim.py
+++ b/awx_plugins/credentials/aim.py
@@ -2,7 +2,7 @@ from .plugin import CredentialPlugin, CertFiles, raise_for_status
from urllib.parse import quote, urlencode, urljoin
-from django.utils.translation import gettext_lazy as _
+from .plugin import translate_function as _
import requests
aim_inputs = {
diff --git a/awx/main/credential_plugins/aws_secretsmanager.py b/awx_plugins/credentials/aws_secretsmanager.py
index fa85f5e52a..335113b2a8 100644
--- a/awx/main/credential_plugins/aws_secretsmanager.py
+++ b/awx_plugins/credentials/aws_secretsmanager.py
@@ -2,7 +2,7 @@ import boto3
from botocore.exceptions import ClientError
from .plugin import CredentialPlugin
-from django.utils.translation import gettext_lazy as _
+from .plugin import translate_function as _
secrets_manager_inputs = {
diff --git a/awx/main/credential_plugins/azure_kv.py b/awx_plugins/credentials/azure_kv.py
index 8910a0726d..7579dbee3d 100644
--- a/awx/main/credential_plugins/azure_kv.py
+++ b/awx_plugins/credentials/azure_kv.py
@@ -4,7 +4,7 @@ from msrestazure import azure_cloud
from .plugin import CredentialPlugin
-from django.utils.translation import gettext_lazy as _
+from .plugin import translate_function as _
# https://github.com/Azure/msrestazure-for-python/blob/master/msrestazure/azure_cloud.py
diff --git a/awx/main/credential_plugins/centrify_vault.py b/awx_plugins/credentials/centrify_vault.py
index 1e05625e71..b2d97a1db3 100644
--- a/awx/main/credential_plugins/centrify_vault.py
+++ b/awx_plugins/credentials/centrify_vault.py
@@ -1,5 +1,5 @@
from .plugin import CredentialPlugin, raise_for_status
-from django.utils.translation import gettext_lazy as _
+from .plugin import translate_function as _
from urllib.parse import urljoin
import requests
diff --git a/awx/main/credential_plugins/conjur.py b/awx_plugins/credentials/conjur.py
index e6984bed46..a7fd3a3a65 100644
--- a/awx/main/credential_plugins/conjur.py
+++ b/awx_plugins/credentials/conjur.py
@@ -2,7 +2,7 @@ from .plugin import CredentialPlugin, CertFiles, raise_for_status
from urllib.parse import urljoin, quote
-from django.utils.translation import gettext_lazy as _
+from .plugin import translate_function as _
import requests
import base64
import binascii
diff --git a/awx/main/credential_plugins/dsv.py b/awx_plugins/credentials/dsv.py
index 7dc74cab91..8296779bde 100644
--- a/awx/main/credential_plugins/dsv.py
+++ b/awx_plugins/credentials/dsv.py
@@ -1,7 +1,7 @@
from .plugin import CredentialPlugin
-from django.conf import settings
-from django.utils.translation import gettext_lazy as _
+from .plugin import settings
+from .plugin import translate_function as _
from delinea.secrets.vault import PasswordGrantAuthorizer, SecretsVault
from base64 import b64decode
diff --git a/awx/main/credential_plugins/hashivault.py b/awx_plugins/credentials/hashivault.py
index f3dcd53b5d..81f7770f51 100644
--- a/awx/main/credential_plugins/hashivault.py
+++ b/awx_plugins/credentials/hashivault.py
@@ -7,7 +7,7 @@ from urllib.parse import urljoin
from .plugin import CredentialPlugin, CertFiles, raise_for_status
import requests
-from django.utils.translation import gettext_lazy as _
+from .plugin import translate_function as _
base_inputs = {
'fields': [
diff --git a/awx/main/models/credential/injectors.py b/awx_plugins/credentials/injectors.py
index 29a438f919..29a438f919 100644
--- a/awx/main/models/credential/injectors.py
+++ b/awx_plugins/credentials/injectors.py
diff --git a/awx/main/credential_plugins/plugin.py b/awx_plugins/credentials/plugin.py
index 7219231efc..b8aa294544 100644
--- a/awx/main/credential_plugins/plugin.py
+++ b/awx_plugins/credentials/plugin.py
@@ -8,6 +8,19 @@ from requests.exceptions import HTTPError
CredentialPlugin = namedtuple('CredentialPlugin', ['name', 'inputs', 'backend'])
+try:
+ from django.utils.translation import gettext_lazy as translate_function
+except ModuleNotFoundError:
+ translate_function = lambda *args, **kwargs: None
+
+
+class Settings():
+ DEBUG = False
+
+
+settings = Settings()
+
+
def raise_for_status(resp):
resp.raise_for_status()
if resp.status_code >= 300:
diff --git a/awx_plugins/credentials/plugins.py b/awx_plugins/credentials/plugins.py
new file mode 100644
index 0000000000..debc5c7032
--- /dev/null
+++ b/awx_plugins/credentials/plugins.py
@@ -0,0 +1,665 @@
+# Django
+from django.utils.translation import gettext_noop
+
+# AWX
+from awx.main.models.credential import ManagedCredentialType
+
+
+ManagedCredentialType(
+ namespace='ssh',
+ kind='ssh',
+ name=gettext_noop('Machine'),
+ inputs={
+ 'fields': [
+ {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
+ {'id': 'password', 'label': gettext_noop('Password'), 'type': 'string', 'secret': True, 'ask_at_runtime': True},
+ {'id': 'ssh_key_data', 'label': gettext_noop('SSH Private Key'), 'type': 'string', 'format': 'ssh_private_key', 'secret': True, 'multiline': True},
+ {
+ 'id': 'ssh_public_key_data',
+ 'label': gettext_noop('Signed SSH Certificate'),
+ 'type': 'string',
+ 'multiline': True,
+ 'secret': True,
+ },
+ {'id': 'ssh_key_unlock', 'label': gettext_noop('Private Key Passphrase'), 'type': 'string', 'secret': True, 'ask_at_runtime': True},
+ {
+ 'id': 'become_method',
+ 'label': gettext_noop('Privilege Escalation Method'),
+ 'type': 'string',
+ 'help_text': gettext_noop('Specify a method for "become" operations. This is equivalent to specifying the --become-method Ansible parameter.'),
+ },
+ {
+ 'id': 'become_username',
+ 'label': gettext_noop('Privilege Escalation Username'),
+ 'type': 'string',
+ },
+ {'id': 'become_password', 'label': gettext_noop('Privilege Escalation Password'), 'type': 'string', 'secret': True, 'ask_at_runtime': True},
+ ],
+ },
+)
+
+ManagedCredentialType(
+ namespace='scm',
+ kind='scm',
+ name=gettext_noop('Source Control'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
+ {'id': 'password', 'label': gettext_noop('Password'), 'type': 'string', 'secret': True},
+ {'id': 'ssh_key_data', 'label': gettext_noop('SCM Private Key'), 'type': 'string', 'format': 'ssh_private_key', 'secret': True, 'multiline': True},
+ {'id': 'ssh_key_unlock', 'label': gettext_noop('Private Key Passphrase'), 'type': 'string', 'secret': True},
+ ],
+ },
+)
+
+ManagedCredentialType(
+ namespace='vault',
+ kind='vault',
+ name=gettext_noop('Vault'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {'id': 'vault_password', 'label': gettext_noop('Vault Password'), 'type': 'string', 'secret': True, 'ask_at_runtime': True},
+ {
+ 'id': 'vault_id',
+ 'label': gettext_noop('Vault Identifier'),
+ 'type': 'string',
+ 'format': 'vault_id',
+ 'help_text': gettext_noop(
+ 'Specify an (optional) Vault ID. This is '
+ 'equivalent to specifying the --vault-id '
+ 'Ansible parameter for providing multiple Vault '
+ 'passwords. Note: this feature only works in '
+ 'Ansible 2.4+.'
+ ),
+ },
+ ],
+ 'required': ['vault_password'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='net',
+ kind='net',
+ name=gettext_noop('Network'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
+ {
+ 'id': 'password',
+ 'label': gettext_noop('Password'),
+ 'type': 'string',
+ 'secret': True,
+ },
+ {'id': 'ssh_key_data', 'label': gettext_noop('SSH Private Key'), 'type': 'string', 'format': 'ssh_private_key', 'secret': True, 'multiline': True},
+ {
+ 'id': 'ssh_key_unlock',
+ 'label': gettext_noop('Private Key Passphrase'),
+ 'type': 'string',
+ 'secret': True,
+ },
+ {
+ 'id': 'authorize',
+ 'label': gettext_noop('Authorize'),
+ 'type': 'boolean',
+ },
+ {
+ 'id': 'authorize_password',
+ 'label': gettext_noop('Authorize Password'),
+ 'type': 'string',
+ 'secret': True,
+ },
+ ],
+ 'dependencies': {
+ 'authorize_password': ['authorize'],
+ },
+ 'required': ['username'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='aws',
+ kind='cloud',
+ name=gettext_noop('Amazon Web Services'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {'id': 'username', 'label': gettext_noop('Access Key'), 'type': 'string'},
+ {
+ 'id': 'password',
+ 'label': gettext_noop('Secret Key'),
+ 'type': 'string',
+ 'secret': True,
+ },
+ {
+ 'id': 'security_token',
+ 'label': gettext_noop('STS Token'),
+ 'type': 'string',
+ 'secret': True,
+ 'help_text': gettext_noop(
+ 'Security Token Service (STS) is a web service '
+ 'that enables you to request temporary, '
+ 'limited-privilege credentials for AWS Identity '
+ 'and Access Management (IAM) users.'
+ ),
+ },
+ ],
+ 'required': ['username', 'password'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='openstack',
+ kind='cloud',
+ name=gettext_noop('OpenStack'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
+ {
+ 'id': 'password',
+ 'label': gettext_noop('Password (API Key)'),
+ 'type': 'string',
+ 'secret': True,
+ },
+ {
+ 'id': 'host',
+ 'label': gettext_noop('Host (Authentication URL)'),
+ 'type': 'string',
+ 'help_text': gettext_noop('The host to authenticate with. For example, https://openstack.business.com/v2.0/'),
+ },
+ {
+ 'id': 'project',
+ 'label': gettext_noop('Project (Tenant Name)'),
+ 'type': 'string',
+ },
+ {
+ 'id': 'project_domain_name',
+ 'label': gettext_noop('Project (Domain Name)'),
+ 'type': 'string',
+ },
+ {
+ 'id': 'domain',
+ 'label': gettext_noop('Domain Name'),
+ 'type': 'string',
+ 'help_text': gettext_noop(
+ 'OpenStack domains define administrative boundaries. '
+ 'It is only needed for Keystone v3 authentication '
+ 'URLs. Refer to the documentation for '
+ 'common scenarios.'
+ ),
+ },
+ {
+ 'id': 'region',
+ 'label': gettext_noop('Region Name'),
+ 'type': 'string',
+ 'help_text': gettext_noop('For some cloud providers, like OVH, region must be specified'),
+ },
+ {
+ 'id': 'verify_ssl',
+ 'label': gettext_noop('Verify SSL'),
+ 'type': 'boolean',
+ 'default': True,
+ },
+ ],
+ 'required': ['username', 'password', 'host', 'project'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='vmware',
+ kind='cloud',
+ name=gettext_noop('VMware vCenter'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {
+ 'id': 'host',
+ 'label': gettext_noop('VCenter Host'),
+ 'type': 'string',
+ 'help_text': gettext_noop('Enter the hostname or IP address that corresponds to your VMware vCenter.'),
+ },
+ {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
+ {
+ 'id': 'password',
+ 'label': gettext_noop('Password'),
+ 'type': 'string',
+ 'secret': True,
+ },
+ ],
+ 'required': ['host', 'username', 'password'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='satellite6',
+ kind='cloud',
+ name=gettext_noop('Red Hat Satellite 6'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {
+ 'id': 'host',
+ 'label': gettext_noop('Satellite 6 URL'),
+ 'type': 'string',
+ 'help_text': gettext_noop('Enter the URL that corresponds to your Red Hat Satellite 6 server. For example, https://satellite.example.org'),
+ },
+ {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
+ {
+ 'id': 'password',
+ 'label': gettext_noop('Password'),
+ 'type': 'string',
+ 'secret': True,
+ },
+ ],
+ 'required': ['host', 'username', 'password'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='gce',
+ kind='cloud',
+ name=gettext_noop('Google Compute Engine'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {
+ 'id': 'username',
+ 'label': gettext_noop('Service Account Email Address'),
+ 'type': 'string',
+ 'help_text': gettext_noop('The email address assigned to the Google Compute Engine service account.'),
+ },
+ {
+ 'id': 'project',
+ 'label': 'Project',
+ 'type': 'string',
+ 'help_text': gettext_noop(
+ 'The Project ID is the GCE assigned identification. '
+ 'It is often constructed as three words or two words '
+ 'followed by a three-digit number. Examples: project-id-000 '
+ 'and another-project-id'
+ ),
+ },
+ {
+ 'id': 'ssh_key_data',
+ 'label': gettext_noop('RSA Private Key'),
+ 'type': 'string',
+ 'format': 'ssh_private_key',
+ 'secret': True,
+ 'multiline': True,
+ 'help_text': gettext_noop('Paste the contents of the PEM file associated with the service account email.'),
+ },
+ ],
+ 'required': ['username', 'ssh_key_data'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='azure_rm',
+ kind='cloud',
+ name=gettext_noop('Microsoft Azure Resource Manager'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {
+ 'id': 'subscription',
+ 'label': gettext_noop('Subscription ID'),
+ 'type': 'string',
+ 'help_text': gettext_noop('Subscription ID is an Azure construct, which is mapped to a username.'),
+ },
+ {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
+ {
+ 'id': 'password',
+ 'label': gettext_noop('Password'),
+ 'type': 'string',
+ 'secret': True,
+ },
+ {'id': 'client', 'label': gettext_noop('Client ID'), 'type': 'string'},
+ {
+ 'id': 'secret',
+ 'label': gettext_noop('Client Secret'),
+ 'type': 'string',
+ 'secret': True,
+ },
+ {'id': 'tenant', 'label': gettext_noop('Tenant ID'), 'type': 'string'},
+ {
+ 'id': 'cloud_environment',
+ 'label': gettext_noop('Azure Cloud Environment'),
+ 'type': 'string',
+ 'help_text': gettext_noop('Environment variable AZURE_CLOUD_ENVIRONMENT when using Azure GovCloud or Azure stack.'),
+ },
+ ],
+ 'required': ['subscription'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='github_token',
+ kind='token',
+ name=gettext_noop('GitHub Personal Access Token'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {
+ 'id': 'token',
+ 'label': gettext_noop('Token'),
+ 'type': 'string',
+ 'secret': True,
+ 'help_text': gettext_noop('This token needs to come from your profile settings in GitHub'),
+ }
+ ],
+ 'required': ['token'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='gitlab_token',
+ kind='token',
+ name=gettext_noop('GitLab Personal Access Token'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {
+ 'id': 'token',
+ 'label': gettext_noop('Token'),
+ 'type': 'string',
+ 'secret': True,
+ 'help_text': gettext_noop('This token needs to come from your profile settings in GitLab'),
+ }
+ ],
+ 'required': ['token'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='bitbucket_dc_token',
+ kind='token',
+ name=gettext_noop('Bitbucket Data Center HTTP Access Token'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {
+ 'id': 'token',
+ 'label': gettext_noop('Token'),
+ 'type': 'string',
+ 'secret': True,
+ 'help_text': gettext_noop('This token needs to come from your user settings in Bitbucket'),
+ }
+ ],
+ 'required': ['token'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='insights',
+ kind='insights',
+ name=gettext_noop('Insights'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
+ {'id': 'password', 'label': gettext_noop('Password'), 'type': 'string', 'secret': True},
+ ],
+ 'required': ['username', 'password'],
+ },
+ injectors={
+ 'extra_vars': {
+ "scm_username": "{{username}}",
+ "scm_password": "{{password}}",
+ },
+ 'env': {
+ 'INSIGHTS_USER': '{{username}}',
+ 'INSIGHTS_PASSWORD': '{{password}}',
+ },
+ },
+)
+
+ManagedCredentialType(
+ namespace='rhv',
+ kind='cloud',
+ name=gettext_noop('Red Hat Virtualization'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {'id': 'host', 'label': gettext_noop('Host (Authentication URL)'), 'type': 'string', 'help_text': gettext_noop('The host to authenticate with.')},
+ {'id': 'username', 'label': gettext_noop('Username'), 'type': 'string'},
+ {
+ 'id': 'password',
+ 'label': gettext_noop('Password'),
+ 'type': 'string',
+ 'secret': True,
+ },
+ {
+ 'id': 'ca_file',
+ 'label': gettext_noop('CA File'),
+ 'type': 'string',
+ 'help_text': gettext_noop('Absolute file path to the CA file to use (optional)'),
+ },
+ ],
+ 'required': ['host', 'username', 'password'],
+ },
+ injectors={
+ # The duplication here is intentional; the ovirt4 inventory plugin
+ # writes a .ini file for authentication, while the ansible modules for
+ # ovirt4 use a separate authentication process that support
+ # environment variables; by injecting both, we support both
+ 'file': {
+ 'template': '\n'.join(
+ [
+ '[ovirt]',
+ 'ovirt_url={{host}}',
+ 'ovirt_username={{username}}',
+ 'ovirt_password={{password}}',
+ '{% if ca_file %}ovirt_ca_file={{ca_file}}{% endif %}',
+ ]
+ )
+ },
+ 'env': {'OVIRT_INI_PATH': '{{tower.filename}}', 'OVIRT_URL': '{{host}}', 'OVIRT_USERNAME': '{{username}}', 'OVIRT_PASSWORD': '{{password}}'},
+ },
+)
+
+ManagedCredentialType(
+ namespace='controller',
+ kind='cloud',
+ name=gettext_noop('Red Hat Ansible Automation Platform'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {
+ 'id': 'host',
+ 'label': gettext_noop('Red Hat Ansible Automation Platform'),
+ 'type': 'string',
+ 'help_text': gettext_noop('Red Hat Ansible Automation Platform base URL to authenticate with.'),
+ },
+ {
+ 'id': 'username',
+ 'label': gettext_noop('Username'),
+ 'type': 'string',
+ 'help_text': gettext_noop(
+ 'Red Hat Ansible Automation Platform username id to authenticate as.This should not be set if an OAuth token is being used.'
+ ),
+ },
+ {
+ 'id': 'password',
+ 'label': gettext_noop('Password'),
+ 'type': 'string',
+ 'secret': True,
+ },
+ {
+ 'id': 'oauth_token',
+ 'label': gettext_noop('OAuth Token'),
+ 'type': 'string',
+ 'secret': True,
+ 'help_text': gettext_noop('An OAuth token to use to authenticate with.This should not be set if username/password are being used.'),
+ },
+ {'id': 'verify_ssl', 'label': gettext_noop('Verify SSL'), 'type': 'boolean', 'secret': False},
+ ],
+ 'required': ['host'],
+ },
+ injectors={
+ 'env': {
+ 'TOWER_HOST': '{{host}}',
+ 'TOWER_USERNAME': '{{username}}',
+ 'TOWER_PASSWORD': '{{password}}',
+ 'TOWER_VERIFY_SSL': '{{verify_ssl}}',
+ 'TOWER_OAUTH_TOKEN': '{{oauth_token}}',
+ 'CONTROLLER_HOST': '{{host}}',
+ 'CONTROLLER_USERNAME': '{{username}}',
+ 'CONTROLLER_PASSWORD': '{{password}}',
+ 'CONTROLLER_VERIFY_SSL': '{{verify_ssl}}',
+ 'CONTROLLER_OAUTH_TOKEN': '{{oauth_token}}',
+ }
+ },
+)
+
+ManagedCredentialType(
+ namespace='kubernetes_bearer_token',
+ kind='kubernetes',
+ name=gettext_noop('OpenShift or Kubernetes API Bearer Token'),
+ inputs={
+ 'fields': [
+ {
+ 'id': 'host',
+ 'label': gettext_noop('OpenShift or Kubernetes API Endpoint'),
+ 'type': 'string',
+ 'help_text': gettext_noop('The OpenShift or Kubernetes API Endpoint to authenticate with.'),
+ },
+ {
+ 'id': 'bearer_token',
+ 'label': gettext_noop('API authentication bearer token'),
+ 'type': 'string',
+ 'secret': True,
+ },
+ {
+ 'id': 'verify_ssl',
+ 'label': gettext_noop('Verify SSL'),
+ 'type': 'boolean',
+ 'default': True,
+ },
+ {
+ 'id': 'ssl_ca_cert',
+ 'label': gettext_noop('Certificate Authority data'),
+ 'type': 'string',
+ 'secret': True,
+ 'multiline': True,
+ },
+ ],
+ 'required': ['host', 'bearer_token'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='registry',
+ kind='registry',
+ name=gettext_noop('Container Registry'),
+ inputs={
+ 'fields': [
+ {
+ 'id': 'host',
+ 'label': gettext_noop('Authentication URL'),
+ 'type': 'string',
+ 'help_text': gettext_noop('Authentication endpoint for the container registry.'),
+ 'default': 'quay.io',
+ },
+ {
+ 'id': 'username',
+ 'label': gettext_noop('Username'),
+ 'type': 'string',
+ },
+ {
+ 'id': 'password',
+ 'label': gettext_noop('Password or Token'),
+ 'type': 'string',
+ 'secret': True,
+ 'help_text': gettext_noop('A password or token used to authenticate with'),
+ },
+ {
+ 'id': 'verify_ssl',
+ 'label': gettext_noop('Verify SSL'),
+ 'type': 'boolean',
+ 'default': True,
+ },
+ ],
+ 'required': ['host'],
+ },
+)
+
+
+ManagedCredentialType(
+ namespace='galaxy_api_token',
+ kind='galaxy',
+ name=gettext_noop('Ansible Galaxy/Automation Hub API Token'),
+ inputs={
+ 'fields': [
+ {
+ 'id': 'url',
+ 'label': gettext_noop('Galaxy Server URL'),
+ 'type': 'string',
+ 'help_text': gettext_noop('The URL of the Galaxy instance to connect to.'),
+ },
+ {
+ 'id': 'auth_url',
+ 'label': gettext_noop('Auth Server URL'),
+ 'type': 'string',
+ 'help_text': gettext_noop('The URL of a Keycloak server token_endpoint, if using SSO auth.'),
+ },
+ {
+ 'id': 'token',
+ 'label': gettext_noop('API Token'),
+ 'type': 'string',
+ 'secret': True,
+ 'help_text': gettext_noop('A token to use for authentication against the Galaxy instance.'),
+ },
+ ],
+ 'required': ['url'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='gpg_public_key',
+ kind='cryptography',
+ name=gettext_noop('GPG Public Key'),
+ inputs={
+ 'fields': [
+ {
+ 'id': 'gpg_public_key',
+ 'label': gettext_noop('GPG Public Key'),
+ 'type': 'string',
+ 'secret': True,
+ 'multiline': True,
+ 'help_text': gettext_noop('GPG Public Key used to validate content signatures.'),
+ },
+ ],
+ 'required': ['gpg_public_key'],
+ },
+)
+
+ManagedCredentialType(
+ namespace='terraform',
+ kind='cloud',
+ name=gettext_noop('Terraform backend configuration'),
+ managed=True,
+ inputs={
+ 'fields': [
+ {
+ 'id': 'configuration',
+ 'label': gettext_noop('Backend configuration'),
+ 'type': 'string',
+ 'secret': True,
+ 'multiline': True,
+ 'help_text': gettext_noop('Terraform backend config as Hashicorp configuration language.'),
+ },
+ {
+ 'id': 'gce_credentials',
+ 'label': gettext_noop('Google Cloud Platform account credentials'),
+ 'type': 'string',
+ 'secret': True,
+ 'multiline': True,
+ 'help_text': gettext_noop('Google Cloud Platform account credentials in JSON format.'),
+ },
+ ],
+ 'required': ['configuration'],
+ },
+)
diff --git a/awx/main/credential_plugins/tss.py b/awx_plugins/credentials/tss.py
index 682c6c8639..e295072233 100644
--- a/awx/main/credential_plugins/tss.py
+++ b/awx_plugins/credentials/tss.py
@@ -1,5 +1,5 @@
from .plugin import CredentialPlugin
-from django.utils.translation import gettext_lazy as _
+from .plugin import translate_function as _
try:
from delinea.secrets.server import DomainPasswordGrantAuthorizer, PasswordGrantAuthorizer, SecretServer, ServerSecret
diff --git a/awx_plugins/inventory/plugins.py b/awx_plugins/inventory/plugins.py
new file mode 100644
index 0000000000..9a42173482
--- /dev/null
+++ b/awx_plugins/inventory/plugins.py
@@ -0,0 +1,302 @@
+import yaml
+import stat
+import tempfile
+
+import os.path
+
+from awx_plugins.credentials.injectors import _openstack_data
+from awx.main.utils.execution_environments import to_container_path
+
+from awx.main.utils.licensing import server_product_name
+
+
+class PluginFileInjector(object):
+ plugin_name = None # Ansible core name used to reference plugin
+ # base injector should be one of None, "managed", or "template"
+ # this dictates which logic to borrow from playbook injectors
+ base_injector = None
+ # every source should have collection, these are for the collection name
+ namespace = None
+ collection = None
+ collection_migration = '2.9' # Starting with this version, we use collections
+ use_fqcn = False # plugin: name versus plugin: namespace.collection.name
+
+ # TODO: delete this method and update unit tests
+ @classmethod
+ def get_proper_name(cls):
+ if cls.plugin_name is None:
+ return None
+ return f'{cls.namespace}.{cls.collection}.{cls.plugin_name}'
+
+ @property
+ def filename(self):
+ """Inventory filename for using the inventory plugin
+ This is created dynamically, but the auto plugin requires this exact naming
+ """
+ return '{0}.yml'.format(self.plugin_name)
+
+ def inventory_contents(self, inventory_update, private_data_dir):
+ """Returns a string that is the content for the inventory file for the inventory plugin"""
+ return yaml.safe_dump(self.inventory_as_dict(inventory_update, private_data_dir), default_flow_style=False, width=1000)
+
+ def inventory_as_dict(self, inventory_update, private_data_dir):
+ source_vars = dict(inventory_update.source_vars_dict) # make a copy
+ '''
+ None conveys that we should use the user-provided plugin.
+ Note that a plugin value of '' should still be overridden.
+ '''
+ if self.plugin_name is not None:
+ if hasattr(self, 'downstream_namespace') and server_product_name() != 'AWX':
+ source_vars['plugin'] = f'{self.downstream_namespace}.{self.downstream_collection}.{self.plugin_name}'
+ elif self.use_fqcn:
+ source_vars['plugin'] = f'{self.namespace}.{self.collection}.{self.plugin_name}'
+ else:
+ source_vars['plugin'] = self.plugin_name
+ return source_vars
+
+ def build_env(self, inventory_update, env, private_data_dir, private_data_files):
+ injector_env = self.get_plugin_env(inventory_update, private_data_dir, private_data_files)
+ env.update(injector_env)
+ # All CLOUD_PROVIDERS sources implement as inventory plugin from collection
+ env['ANSIBLE_INVENTORY_ENABLED'] = 'auto'
+ return env
+
+ def _get_shared_env(self, inventory_update, private_data_dir, private_data_files):
+ """By default, we will apply the standard managed injectors"""
+ injected_env = {}
+ credential = inventory_update.get_cloud_credential()
+ # some sources may have no credential, specifically ec2
+ if credential is None:
+ return injected_env
+ if self.base_injector in ('managed', 'template'):
+ injected_env['INVENTORY_UPDATE_ID'] = str(inventory_update.pk) # so injector knows this is inventory
+ if self.base_injector == 'managed':
+ from awx_plugins.credentials import injectors as builtin_injectors
+
+ cred_kind = inventory_update.source.replace('ec2', 'aws')
+ if cred_kind in dir(builtin_injectors):
+ getattr(builtin_injectors, cred_kind)(credential, injected_env, private_data_dir)
+ elif self.base_injector == 'template':
+ safe_env = injected_env.copy()
+ args = []
+ credential.credential_type.inject_credential(credential, injected_env, safe_env, args, private_data_dir)
+ # NOTE: safe_env is handled externally to injector class by build_safe_env static method
+ # that means that managed injectors must only inject detectable env keys
+ # enforcement of this is accomplished by tests
+ return injected_env
+
+ def get_plugin_env(self, inventory_update, private_data_dir, private_data_files):
+ env = self._get_shared_env(inventory_update, private_data_dir, private_data_files)
+ return env
+
+ def build_private_data(self, inventory_update, private_data_dir):
+ return self.build_plugin_private_data(inventory_update, private_data_dir)
+
+ def build_plugin_private_data(self, inventory_update, private_data_dir):
+ return None
+
+
+class azure_rm(PluginFileInjector):
+ plugin_name = 'azure_rm'
+ base_injector = 'managed'
+ namespace = 'azure'
+ collection = 'azcollection'
+
+ def get_plugin_env(self, *args, **kwargs):
+ ret = super(azure_rm, self).get_plugin_env(*args, **kwargs)
+ # We need native jinja2 types so that tags can give JSON null value
+ ret['ANSIBLE_JINJA2_NATIVE'] = str(True)
+ return ret
+
+
+class ec2(PluginFileInjector):
+ plugin_name = 'aws_ec2'
+ base_injector = 'managed'
+ namespace = 'amazon'
+ collection = 'aws'
+
+ def get_plugin_env(self, *args, **kwargs):
+ ret = super(ec2, self).get_plugin_env(*args, **kwargs)
+ # We need native jinja2 types so that ec2_state_code will give integer
+ ret['ANSIBLE_JINJA2_NATIVE'] = str(True)
+ return ret
+
+
+class gce(PluginFileInjector):
+ plugin_name = 'gcp_compute'
+ base_injector = 'managed'
+ namespace = 'google'
+ collection = 'cloud'
+
+ def get_plugin_env(self, *args, **kwargs):
+ ret = super(gce, self).get_plugin_env(*args, **kwargs)
+ # We need native jinja2 types so that ip addresses can give JSON null value
+ ret['ANSIBLE_JINJA2_NATIVE'] = str(True)
+ return ret
+
+ def inventory_as_dict(self, inventory_update, private_data_dir):
+ ret = super().inventory_as_dict(inventory_update, private_data_dir)
+ credential = inventory_update.get_cloud_credential()
+ # InventorySource.source_vars take precedence over ENV vars
+ if 'projects' not in ret:
+ ret['projects'] = [credential.get_input('project', default='')]
+ return ret
+
+
+class vmware(PluginFileInjector):
+ plugin_name = 'vmware_vm_inventory'
+ base_injector = 'managed'
+ namespace = 'community'
+ collection = 'vmware'
+
+
+class openstack(PluginFileInjector):
+ plugin_name = 'openstack'
+ namespace = 'openstack'
+ collection = 'cloud'
+
+ def _get_clouds_dict(self, inventory_update, cred, private_data_dir):
+ openstack_data = _openstack_data(cred)
+
+ openstack_data['clouds']['devstack']['private'] = inventory_update.source_vars_dict.get('private', True)
+ ansible_variables = {
+ 'use_hostnames': True,
+ 'expand_hostvars': False,
+ 'fail_on_errors': True,
+ }
+ provided_count = 0
+ for var_name in ansible_variables:
+ if var_name in inventory_update.source_vars_dict:
+ ansible_variables[var_name] = inventory_update.source_vars_dict[var_name]
+ provided_count += 1
+ if provided_count:
+ # Must we provide all 3 because the user provides any 1 of these??
+ # this probably results in some incorrect mangling of the defaults
+ openstack_data['ansible'] = ansible_variables
+ return openstack_data
+
+ def build_plugin_private_data(self, inventory_update, private_data_dir):
+ credential = inventory_update.get_cloud_credential()
+ private_data = {'credentials': {}}
+
+ openstack_data = self._get_clouds_dict(inventory_update, credential, private_data_dir)
+ private_data['credentials'][credential] = yaml.safe_dump(openstack_data, default_flow_style=False, allow_unicode=True)
+ return private_data
+
+ def get_plugin_env(self, inventory_update, private_data_dir, private_data_files):
+ env = super(openstack, self).get_plugin_env(inventory_update, private_data_dir, private_data_files)
+ credential = inventory_update.get_cloud_credential()
+ cred_data = private_data_files['credentials']
+ env['OS_CLIENT_CONFIG_FILE'] = to_container_path(cred_data[credential], private_data_dir)
+ return env
+
+
+class rhv(PluginFileInjector):
+ """ovirt uses the custom credential templating, and that is all"""
+
+ plugin_name = 'ovirt'
+ base_injector = 'template'
+ initial_version = '2.9'
+ namespace = 'ovirt'
+ collection = 'ovirt'
+ downstream_namespace = 'redhat'
+ downstream_collection = 'rhv'
+ use_fqcn = True
+
+
+class satellite6(PluginFileInjector):
+ plugin_name = 'foreman'
+ namespace = 'theforeman'
+ collection = 'foreman'
+ downstream_namespace = 'redhat'
+ downstream_collection = 'satellite'
+ use_fqcn = True
+
+ def get_plugin_env(self, inventory_update, private_data_dir, private_data_files):
+ # this assumes that this is merged
+ # https://github.com/ansible/ansible/pull/52693
+ credential = inventory_update.get_cloud_credential()
+ ret = super(satellite6, self).get_plugin_env(inventory_update, private_data_dir, private_data_files)
+ if credential:
+ ret['FOREMAN_SERVER'] = credential.get_input('host', default='')
+ ret['FOREMAN_USER'] = credential.get_input('username', default='')
+ ret['FOREMAN_PASSWORD'] = credential.get_input('password', default='')
+ return ret
+
+
+class terraform(PluginFileInjector):
+ plugin_name = 'terraform_state'
+ namespace = 'cloud'
+ collection = 'terraform'
+ use_fqcn = True
+
+ def inventory_as_dict(self, inventory_update, private_data_dir):
+ ret = super().inventory_as_dict(inventory_update, private_data_dir)
+ credential = inventory_update.get_cloud_credential()
+ config_cred = credential.get_input('configuration')
+ if config_cred:
+ handle, path = tempfile.mkstemp(dir=os.path.join(private_data_dir, 'env'))
+ with os.fdopen(handle, 'w') as f:
+ os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
+ f.write(config_cred)
+ ret['backend_config_files'] = to_container_path(path, private_data_dir)
+ return ret
+
+ def build_plugin_private_data(self, inventory_update, private_data_dir):
+ credential = inventory_update.get_cloud_credential()
+
+ private_data = {'credentials': {}}
+ gce_cred = credential.get_input('gce_credentials', default=None)
+ if gce_cred:
+ private_data['credentials'][credential] = gce_cred
+ return private_data
+
+ def get_plugin_env(self, inventory_update, private_data_dir, private_data_files):
+ env = super(terraform, self).get_plugin_env(inventory_update, private_data_dir, private_data_files)
+ credential = inventory_update.get_cloud_credential()
+ cred_data = private_data_files['credentials']
+ if credential in cred_data:
+ env['GOOGLE_BACKEND_CREDENTIALS'] = to_container_path(cred_data[credential], private_data_dir)
+ return env
+
+
+class controller(PluginFileInjector):
+ plugin_name = 'tower' # TODO: relying on routing for now, update after EEs pick up revised collection
+ base_injector = 'template'
+ namespace = 'awx'
+ collection = 'awx'
+ downstream_namespace = 'ansible'
+ downstream_collection = 'controller'
+
+
+class insights(PluginFileInjector):
+ plugin_name = 'insights'
+ base_injector = 'template'
+ namespace = 'redhatinsights'
+ collection = 'insights'
+ downstream_namespace = 'redhat'
+ downstream_collection = 'insights'
+ use_fqcn = True
+
+
+class openshift_virtualization(PluginFileInjector):
+ plugin_name = 'kubevirt'
+ base_injector = 'template'
+ namespace = 'kubevirt'
+ collection = 'core'
+ downstream_namespace = 'redhat'
+ downstream_collection = 'openshift_virtualization'
+ use_fqcn = True
+
+
+class constructed(PluginFileInjector):
+ plugin_name = 'constructed'
+ namespace = 'ansible'
+ collection = 'builtin'
+
+ def build_env(self, *args, **kwargs):
+ env = super().build_env(*args, **kwargs)
+ # Enable script inventory plugin so we pick up the script files from source inventories
+ env['ANSIBLE_INVENTORY_ENABLED'] += ',script'
+ env['ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED'] = 'True'
+ return env
diff --git a/awx/main/tests/functional/test_credential_plugins.py b/awx_plugins/tests/test_credential_plugins.py
index 3ee29e9ce3..660fdf756b 100644
--- a/awx/main/tests/functional/test_credential_plugins.py
+++ b/awx_plugins/tests/test_credential_plugins.py
@@ -1,10 +1,10 @@
import pytest
from unittest import mock
-from awx.main.credential_plugins import hashivault
+from awx_plugins.credentials import hashivault
def test_imported_azure_cloud_sdk_vars():
- from awx.main.credential_plugins import azure_kv
+ from awx_plugins.credentials import azure_kv
assert len(azure_kv.clouds) > 0
assert all([hasattr(c, 'name') for c in azure_kv.clouds])
@@ -129,13 +129,13 @@ class TestDelineaImports:
"""
def test_dsv_import(self):
- from awx.main.credential_plugins.dsv import SecretsVault # noqa
+ from awx_plugins.credentials.dsv import SecretsVault # noqa
# assert this module as opposed to older thycotic.secrets.vault
assert SecretsVault.__module__ == 'delinea.secrets.vault'
def test_tss_import(self):
- from awx.main.credential_plugins.tss import DomainPasswordGrantAuthorizer, PasswordGrantAuthorizer, SecretServer, ServerSecret # noqa
+ from awx_plugins.credentials.tss import DomainPasswordGrantAuthorizer, PasswordGrantAuthorizer, SecretServer, ServerSecret # noqa
for cls in (DomainPasswordGrantAuthorizer, PasswordGrantAuthorizer, SecretServer, ServerSecret):
# assert this module as opposed to older thycotic.secrets.server
diff --git a/setup.cfg b/setup.cfg
index c861d3ae92..cb1903c84d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -13,13 +13,13 @@ include_package_data = True
[options.entry_points]
console_scripts =
awx-manage = awx:manage
-awx.credential_plugins =
- conjur = awx.main.credential_plugins.conjur:conjur_plugin
- hashivault_kv = awx.main.credential_plugins.hashivault:hashivault_kv_plugin
- hashivault_ssh = awx.main.credential_plugins.hashivault:hashivault_ssh_plugin
- azure_kv = awx.main.credential_plugins.azure_kv:azure_keyvault_plugin
- aim = awx.main.credential_plugins.aim:aim_plugin
- centrify_vault_kv = awx.main.credential_plugins.centrify_vault:centrify_plugin
- thycotic_dsv = awx.main.credential_plugins.dsv:dsv_plugin
- thycotic_tss = awx.main.credential_plugins.tss:tss_plugin
- aws_secretsmanager_credential = awx.main.credential_plugins.aws_secretsmanager:aws_secretmanager_plugin
+awx.plugins =
+ conjur = awx_plugins.credentials.conjur:conjur_plugin
+ hashivault_kv = awx_plugins.credentials.hashivault:hashivault_kv_plugin
+ hashivault_ssh = awx_plugins.credentials.hashivault:hashivault_ssh_plugin
+ azure_kv = awx_plugins.credentials.azure_kv:azure_keyvault_plugin
+ aim = awx_plugins.credentials.aim:aim_plugin
+ centrify_vault_kv = awx_plugins.credentials.centrify_vault:centrify_plugin
+ thycotic_dsv = awx_plugins.credentials.dsv:dsv_plugin
+ thycotic_tss = awx_plugins.credentials.tss:tss_plugin
+ aws_secretsmanager_credential = awx_plugins.credentials.aws_secretsmanager:aws_secretmanager_plugin