mirror of
https://github.com/ansible/awx.git
synced 2026-02-01 17:48:10 -03:30
964 lines
30 KiB
Python
964 lines
30 KiB
Python
# Copyright (c) 2015 Ansible, Inc.
|
|
# All Rights Reserved.
|
|
from collections import OrderedDict
|
|
import functools
|
|
import json
|
|
import operator
|
|
import os
|
|
import stat
|
|
import tempfile
|
|
|
|
# Jinja2
|
|
from jinja2 import Template
|
|
|
|
# Django
|
|
from django.db import models
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from django.core.exceptions import ValidationError
|
|
from django.utils.encoding import force_text
|
|
|
|
# AWX
|
|
from awx.api.versioning import reverse
|
|
from awx.main.fields import (ImplicitRoleField, CredentialInputField,
|
|
CredentialTypeInputField,
|
|
CredentialTypeInjectorField)
|
|
from awx.main.utils import decrypt_field
|
|
from awx.main.validators import validate_ssh_private_key
|
|
from awx.main.models.base import * # noqa
|
|
from awx.main.models.mixins import ResourceMixin
|
|
from awx.main.models.rbac import (
|
|
ROLE_SINGLETON_SYSTEM_ADMINISTRATOR,
|
|
ROLE_SINGLETON_SYSTEM_AUDITOR,
|
|
)
|
|
from awx.main.utils import encrypt_field
|
|
|
|
__all__ = ['Credential', 'CredentialType', 'V1Credential']
|
|
|
|
|
|
class V1Credential(object):
|
|
|
|
#
|
|
# API v1 backwards compat; as long as we continue to support the
|
|
# /api/v1/credentials/ endpoint, we'll keep these definitions around.
|
|
# The credential serializers are smart enough to detect the request
|
|
# version and use *these* fields for constructing the serializer if the URL
|
|
# starts with /api/v1/
|
|
#
|
|
PASSWORD_FIELDS = ('password', 'security_token', 'ssh_key_data',
|
|
'ssh_key_unlock', 'become_password',
|
|
'vault_password', 'secret', 'authorize_password')
|
|
KIND_CHOICES = [
|
|
('ssh', 'Machine'),
|
|
('net', 'Network'),
|
|
('scm', 'Source Control'),
|
|
('aws', 'Amazon Web Services'),
|
|
('vmware', 'VMware vCenter'),
|
|
('satellite6', 'Red Hat Satellite 6'),
|
|
('cloudforms', 'Red Hat CloudForms'),
|
|
('gce', 'Google Compute Engine'),
|
|
('azure', 'Microsoft Azure Classic (deprecated)'),
|
|
('azure_rm', 'Microsoft Azure Resource Manager'),
|
|
('openstack', 'OpenStack'),
|
|
]
|
|
FIELDS = {
|
|
'kind': models.CharField(
|
|
max_length=32,
|
|
choices=[
|
|
(kind[0], _(kind[1]))
|
|
for kind in KIND_CHOICES
|
|
],
|
|
default='ssh',
|
|
),
|
|
'cloud': models.BooleanField(
|
|
default=False,
|
|
editable=False,
|
|
),
|
|
'host': models.CharField(
|
|
blank=True,
|
|
default='',
|
|
max_length=1024,
|
|
verbose_name=_('Host'),
|
|
help_text=_('The hostname or IP address to use.'),
|
|
),
|
|
'username': models.CharField(
|
|
blank=True,
|
|
default='',
|
|
max_length=1024,
|
|
verbose_name=_('Username'),
|
|
help_text=_('Username for this credential.'),
|
|
),
|
|
'password': models.CharField(
|
|
blank=True,
|
|
default='',
|
|
max_length=1024,
|
|
verbose_name=_('Password'),
|
|
help_text=_('Password for this credential (or "ASK" to prompt the '
|
|
'user for machine credentials).'),
|
|
),
|
|
'security_token': models.CharField(
|
|
blank=True,
|
|
default='',
|
|
max_length=1024,
|
|
verbose_name=_('Security Token'),
|
|
help_text=_('Security Token for this credential'),
|
|
),
|
|
'project': models.CharField(
|
|
blank=True,
|
|
default='',
|
|
max_length=100,
|
|
verbose_name=_('Project'),
|
|
help_text=_('The identifier for the project.'),
|
|
),
|
|
'domain': models.CharField(
|
|
blank=True,
|
|
default='',
|
|
max_length=100,
|
|
verbose_name=_('Domain'),
|
|
help_text=_('The identifier for the domain.'),
|
|
),
|
|
'ssh_key_data': models.TextField(
|
|
blank=True,
|
|
default='',
|
|
verbose_name=_('SSH private key'),
|
|
help_text=_('RSA or DSA private key to be used instead of password.'),
|
|
),
|
|
'ssh_key_unlock': models.CharField(
|
|
max_length=1024,
|
|
blank=True,
|
|
default='',
|
|
verbose_name=_('SSH key unlock'),
|
|
help_text=_('Passphrase to unlock SSH private key if encrypted (or '
|
|
'"ASK" to prompt the user for machine credentials).'),
|
|
),
|
|
'become_method': models.CharField(
|
|
max_length=32,
|
|
blank=True,
|
|
default='',
|
|
choices=[
|
|
('', _('None')),
|
|
('sudo', _('Sudo')),
|
|
('su', _('Su')),
|
|
('pbrun', _('Pbrun')),
|
|
('pfexec', _('Pfexec')),
|
|
('dzdo', _('DZDO')),
|
|
('pmrun', _('Pmrun')),
|
|
],
|
|
help_text=_('Privilege escalation method.')
|
|
),
|
|
'become_username': models.CharField(
|
|
max_length=1024,
|
|
blank=True,
|
|
default='',
|
|
help_text=_('Privilege escalation username.'),
|
|
),
|
|
'become_password': models.CharField(
|
|
max_length=1024,
|
|
blank=True,
|
|
default='',
|
|
help_text=_('Password for privilege escalation method.')
|
|
),
|
|
'vault_password': models.CharField(
|
|
max_length=1024,
|
|
blank=True,
|
|
default='',
|
|
help_text=_('Vault password (or "ASK" to prompt the user).'),
|
|
),
|
|
'authorize': models.BooleanField(
|
|
default=False,
|
|
help_text=_('Whether to use the authorize mechanism.'),
|
|
),
|
|
'authorize_password': models.CharField(
|
|
max_length=1024,
|
|
blank=True,
|
|
default='',
|
|
help_text=_('Password used by the authorize mechanism.'),
|
|
),
|
|
'client': models.CharField(
|
|
max_length=128,
|
|
blank=True,
|
|
default='',
|
|
help_text=_('Client Id or Application Id for the credential'),
|
|
),
|
|
'secret': models.CharField(
|
|
max_length=1024,
|
|
blank=True,
|
|
default='',
|
|
help_text=_('Secret Token for this credential'),
|
|
),
|
|
'subscription': models.CharField(
|
|
max_length=1024,
|
|
blank=True,
|
|
default='',
|
|
help_text=_('Subscription identifier for this credential'),
|
|
),
|
|
'tenant': models.CharField(
|
|
max_length=1024,
|
|
blank=True,
|
|
default='',
|
|
help_text=_('Tenant identifier for this credential'),
|
|
)
|
|
}
|
|
|
|
|
|
|
|
class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
|
'''
|
|
A credential contains information about how to talk to a remote resource
|
|
Usually this is a SSH key location, and possibly an unlock password.
|
|
If used with sudo, a sudo password should be set if required.
|
|
'''
|
|
|
|
class Meta:
|
|
app_label = 'main'
|
|
ordering = ('name',)
|
|
unique_together = (('organization', 'name', 'credential_type'))
|
|
|
|
PASSWORD_FIELDS = ['inputs']
|
|
|
|
credential_type = models.ForeignKey(
|
|
'CredentialType',
|
|
related_name='credentials',
|
|
null=False,
|
|
help_text=_('Type for this credential. Credential Types define '
|
|
'valid fields (e.g,. "username", "password") and their '
|
|
'properties (e.g,. "username is required" or "password '
|
|
'should be stored with encryption").')
|
|
)
|
|
organization = models.ForeignKey(
|
|
'Organization',
|
|
null=True,
|
|
default=None,
|
|
blank=True,
|
|
on_delete=models.CASCADE,
|
|
related_name='credentials',
|
|
)
|
|
inputs = CredentialInputField(
|
|
blank=True,
|
|
default={},
|
|
help_text=_('Data structure used to specify input values (e.g., '
|
|
'{"username": "jane-doe", "password": "secret"}). Valid '
|
|
'fields and their requirements vary depending on the '
|
|
'fields defined on the chosen CredentialType.')
|
|
)
|
|
admin_role = ImplicitRoleField(
|
|
parent_role=[
|
|
'singleton:' + ROLE_SINGLETON_SYSTEM_ADMINISTRATOR,
|
|
'organization.admin_role',
|
|
],
|
|
)
|
|
use_role = ImplicitRoleField(
|
|
parent_role=[
|
|
'admin_role',
|
|
]
|
|
)
|
|
read_role = ImplicitRoleField(parent_role=[
|
|
'singleton:' + ROLE_SINGLETON_SYSTEM_AUDITOR,
|
|
'organization.auditor_role',
|
|
'use_role',
|
|
'admin_role',
|
|
])
|
|
|
|
def __getattr__(self, item):
|
|
if item in V1Credential.FIELDS:
|
|
return self.inputs.get(item, V1Credential.FIELDS[item].default)
|
|
elif item in self.inputs:
|
|
return self.inputs[item]
|
|
raise AttributeError(item)
|
|
|
|
def __setattr__(self, item, value):
|
|
if item in V1Credential.FIELDS and item in self.credential_type.defined_fields:
|
|
if value:
|
|
self.inputs[item] = value
|
|
elif item in self.inputs:
|
|
del self.inputs[item]
|
|
return
|
|
super(Credential, self).__setattr__(item, value)
|
|
|
|
@property
|
|
def kind(self):
|
|
# TODO 3.3: remove the need for this helper property by removing its
|
|
# usage throughout the codebase
|
|
type_ = self.credential_type
|
|
if type_.kind != 'cloud':
|
|
return type_.kind
|
|
for field in V1Credential.KIND_CHOICES:
|
|
kind, name = field
|
|
if name == type_.name:
|
|
return kind
|
|
|
|
@property
|
|
def cloud(self):
|
|
return self.credential_type.kind == 'cloud'
|
|
|
|
def get_absolute_url(self, request=None):
|
|
return reverse('api:credential_detail', kwargs={'pk': self.pk}, request=request)
|
|
|
|
#
|
|
# TODO: the SSH-related properties below are largely used for validation
|
|
# and for determining passwords necessary for job/ad-hoc launch
|
|
#
|
|
# These are SSH-specific; should we move them elsewhere?
|
|
#
|
|
@property
|
|
def needs_ssh_password(self):
|
|
return self.credential_type.kind == 'ssh' and self.password == 'ASK'
|
|
|
|
@property
|
|
def has_encrypted_ssh_key_data(self):
|
|
if self.pk:
|
|
ssh_key_data = decrypt_field(self, 'ssh_key_data')
|
|
else:
|
|
ssh_key_data = self.ssh_key_data
|
|
try:
|
|
pem_objects = validate_ssh_private_key(ssh_key_data)
|
|
for pem_object in pem_objects:
|
|
if pem_object.get('key_enc', False):
|
|
return True
|
|
except ValidationError:
|
|
pass
|
|
return False
|
|
|
|
@property
|
|
def needs_ssh_key_unlock(self):
|
|
if self.credential_type.kind == 'ssh' and self.ssh_key_unlock in ('ASK', ''):
|
|
return self.has_encrypted_ssh_key_data
|
|
return False
|
|
|
|
@property
|
|
def needs_become_password(self):
|
|
return self.credential_type.kind == 'ssh' and self.become_password == 'ASK'
|
|
|
|
@property
|
|
def needs_vault_password(self):
|
|
return self.credential_type.kind == 'vault' and self.vault_password == 'ASK'
|
|
|
|
@property
|
|
def passwords_needed(self):
|
|
needed = []
|
|
for field in ('ssh_password', 'become_password', 'ssh_key_unlock', 'vault_password'):
|
|
if getattr(self, 'needs_%s' % field):
|
|
needed.append(field)
|
|
return needed
|
|
|
|
def _password_field_allows_ask(self, field):
|
|
return field in self.credential_type.askable_fields
|
|
|
|
def save(self, *args, **kwargs):
|
|
self.PASSWORD_FIELDS = self.credential_type.secret_fields
|
|
|
|
if self.pk:
|
|
cred_before = Credential.objects.get(pk=self.pk)
|
|
inputs_before = cred_before.inputs
|
|
# Look up the currently persisted value so that we can replace
|
|
# $encrypted$ with the actual DB-backed value
|
|
for field in self.PASSWORD_FIELDS:
|
|
if self.inputs.get(field) == '$encrypted$':
|
|
self.inputs[field] = inputs_before[field]
|
|
|
|
super(Credential, self).save(*args, **kwargs)
|
|
|
|
def encrypt_field(self, field, ask):
|
|
encrypted = encrypt_field(self, field, ask=ask)
|
|
if encrypted:
|
|
self.inputs[field] = encrypted
|
|
elif field in self.inputs:
|
|
del self.inputs[field]
|
|
|
|
def mark_field_for_save(self, update_fields, field):
|
|
if field in self.credential_type.secret_fields:
|
|
# If we've encrypted a v1 field, we actually want to persist
|
|
# self.inputs
|
|
field = 'inputs'
|
|
super(Credential, self).mark_field_for_save(update_fields, field)
|
|
|
|
def display_inputs(self):
|
|
field_val = self.inputs.copy()
|
|
for k, v in field_val.items():
|
|
if force_text(v).startswith('$encrypted$'):
|
|
field_val[k] = '$encrypted$'
|
|
return field_val
|
|
|
|
|
|
class CredentialType(CommonModelNameNotUnique):
|
|
'''
|
|
A reusable schema for a credential.
|
|
|
|
Used to define a named credential type with fields (e.g., an API key) and
|
|
output injectors (i.e., an environment variable that uses the API key).
|
|
'''
|
|
|
|
defaults = OrderedDict()
|
|
|
|
ENV_BLACKLIST = set((
|
|
'VIRTUAL_ENV', 'PATH', 'PYTHONPATH', 'PROOT_TMP_DIR', 'JOB_ID',
|
|
'INVENTORY_ID', 'INVENTORY_SOURCE_ID', 'INVENTORY_UPDATE_ID',
|
|
'AD_HOC_COMMAND_ID', 'REST_API_URL', 'REST_API_TOKEN', 'TOWER_HOST',
|
|
'MAX_EVENT_RES', 'CALLBACK_QUEUE', 'CALLBACK_CONNECTION', 'CACHE',
|
|
'JOB_CALLBACK_DEBUG', 'INVENTORY_HOSTVARS', 'FACT_QUEUE',
|
|
))
|
|
|
|
class Meta:
|
|
app_label = 'main'
|
|
ordering = ('kind', 'name')
|
|
unique_together = (('name', 'kind'),)
|
|
|
|
KIND_CHOICES = (
|
|
('ssh', _('SSH')),
|
|
('vault', _('Vault')),
|
|
('net', _('Network')),
|
|
('scm', _('Source Control')),
|
|
('cloud', _('Cloud')),
|
|
('insights', _('Insights')),
|
|
)
|
|
|
|
kind = models.CharField(
|
|
max_length=32,
|
|
choices=KIND_CHOICES
|
|
)
|
|
managed_by_tower = models.BooleanField(
|
|
default=False,
|
|
editable=False
|
|
)
|
|
inputs = CredentialTypeInputField(
|
|
blank=True,
|
|
default={}
|
|
)
|
|
injectors = CredentialTypeInjectorField(
|
|
blank=True,
|
|
default={}
|
|
)
|
|
|
|
def get_absolute_url(self, request=None):
|
|
return reverse('api:credential_type_detail', kwargs={'pk': self.pk}, request=request)
|
|
|
|
@property
|
|
def unique_by_kind(self):
|
|
return self.kind != 'cloud'
|
|
|
|
@property
|
|
def defined_fields(self):
|
|
return [field.get('id') for field in self.inputs.get('fields', [])]
|
|
|
|
@property
|
|
def secret_fields(self):
|
|
return [
|
|
field['id'] for field in self.inputs.get('fields', [])
|
|
if field.get('secret', False) is True
|
|
]
|
|
|
|
@property
|
|
def askable_fields(self):
|
|
return [
|
|
field['id'] for field in self.inputs.get('fields', [])
|
|
if field.get('ask_at_runtime', False) is True
|
|
]
|
|
|
|
@classmethod
|
|
def default(cls, f):
|
|
func = functools.partial(f, cls)
|
|
cls.defaults[f.__name__] = func
|
|
return func
|
|
|
|
@classmethod
|
|
def setup_tower_managed_defaults(cls, persisted=True):
|
|
for default in cls.defaults.values():
|
|
default_ = default()
|
|
if persisted:
|
|
default_.save()
|
|
|
|
@classmethod
|
|
def from_v1_kind(cls, kind, data={}):
|
|
match = None
|
|
kind = kind or 'ssh'
|
|
kind_choices = dict(V1Credential.KIND_CHOICES)
|
|
requirements = {}
|
|
if kind == 'ssh':
|
|
if data.get('vault_password'):
|
|
requirements['kind'] = 'vault'
|
|
else:
|
|
requirements['kind'] = 'ssh'
|
|
elif kind in ('net', 'scm'):
|
|
requirements['kind'] = kind
|
|
elif kind in kind_choices:
|
|
requirements.update(dict(
|
|
kind='cloud',
|
|
name=kind_choices[kind]
|
|
))
|
|
if requirements:
|
|
requirements['managed_by_tower'] = True
|
|
match = cls.objects.filter(**requirements)[:1].get()
|
|
return match
|
|
|
|
def inject_credential(self, credential, env, safe_env, args, safe_args, private_data_dir):
|
|
"""
|
|
Inject credential data into the environment variables and arguments
|
|
passed to `ansible-playbook`
|
|
|
|
:param credential: a :class:`awx.main.models.Credential` instance
|
|
:param env: a dictionary of environment variables used in
|
|
the `ansible-playbook` call. This method adds
|
|
additional environment variables based on
|
|
custom `env` injectors defined on this
|
|
CredentialType.
|
|
:param safe_env: a dictionary of environment variables stored
|
|
in the database for the job run
|
|
(`UnifiedJob.job_env`); secret values should
|
|
be stripped
|
|
:param args: a list of arguments passed to
|
|
`ansible-playbook` in the style of
|
|
`subprocess.call(args)`. This method appends
|
|
additional arguments based on custom
|
|
`extra_vars` injectors defined on this
|
|
CredentialType.
|
|
:param safe_args: a list of arguments stored in the database for
|
|
the job run (`UnifiedJob.job_args`); secret
|
|
values should be stripped
|
|
:param private_data_dir: a temporary directory to store files generated
|
|
by `file` injectors (like config files or key
|
|
files)
|
|
"""
|
|
if not self.injectors:
|
|
return
|
|
|
|
class TowerNamespace:
|
|
filename = None
|
|
|
|
tower_namespace = TowerNamespace()
|
|
|
|
# maintain a normal namespace for building the ansible-playbook arguments (env and args)
|
|
namespace = {'tower': tower_namespace}
|
|
|
|
# maintain a sanitized namespace for building the DB-stored arguments (safe_env and safe_args)
|
|
safe_namespace = {'tower': tower_namespace}
|
|
|
|
# build a normal namespace with secret values decrypted (for
|
|
# ansible-playbook) and a safe namespace with secret values hidden (for
|
|
# DB storage)
|
|
for field_name, value in credential.inputs.items():
|
|
if field_name in self.secret_fields:
|
|
value = decrypt_field(credential, field_name)
|
|
safe_namespace[field_name] = '**********'
|
|
elif len(value):
|
|
safe_namespace[field_name] = value
|
|
if len(value):
|
|
namespace[field_name] = value
|
|
|
|
file_tmpl = self.injectors.get('file', {}).get('template')
|
|
if file_tmpl is not None:
|
|
# If a file template is provided, render the file and update the
|
|
# special `tower` template namespace so the filename can be
|
|
# referenced in other injectors
|
|
data = Template(file_tmpl).render(**namespace)
|
|
_, path = tempfile.mkstemp(dir=private_data_dir)
|
|
with open(path, 'w') as f:
|
|
f.write(data)
|
|
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
|
|
namespace['tower'].filename = path
|
|
|
|
for env_var, tmpl in self.injectors.get('env', {}).items():
|
|
if env_var.startswith('ANSIBLE_') or env_var in self.ENV_BLACKLIST:
|
|
continue
|
|
env[env_var] = Template(tmpl).render(**namespace)
|
|
safe_env[env_var] = Template(tmpl).render(**safe_namespace)
|
|
|
|
extra_vars = {}
|
|
safe_extra_vars = {}
|
|
for var_name, tmpl in self.injectors.get('extra_vars', {}).items():
|
|
extra_vars[var_name] = Template(tmpl).render(**namespace)
|
|
safe_extra_vars[var_name] = Template(tmpl).render(**safe_namespace)
|
|
|
|
if extra_vars:
|
|
args.extend(['-e', json.dumps(extra_vars)])
|
|
|
|
if safe_extra_vars:
|
|
safe_args.extend(['-e', json.dumps(safe_extra_vars)])
|
|
|
|
|
|
@CredentialType.default
|
|
def ssh(cls):
|
|
return cls(
|
|
kind='ssh',
|
|
name='SSH',
|
|
managed_by_tower=True,
|
|
inputs={
|
|
'fields': [{
|
|
'id': 'username',
|
|
'label': 'Username',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'password',
|
|
'label': 'Password',
|
|
'type': 'string',
|
|
'secret': True,
|
|
'ask_at_runtime': True
|
|
}, {
|
|
'id': 'ssh_key_data',
|
|
'label': 'SSH Private Key',
|
|
'type': 'string',
|
|
'format': 'ssh_private_key',
|
|
'secret': True,
|
|
'multiline': True
|
|
}, {
|
|
'id': 'ssh_key_unlock',
|
|
'label': 'Private Key Passphrase',
|
|
'type': 'string',
|
|
'secret': True,
|
|
'ask_at_runtime': True
|
|
}, {
|
|
'id': 'become_method',
|
|
'label': 'Privilege Escalation Method',
|
|
'choices': map(operator.itemgetter(0),
|
|
V1Credential.FIELDS['become_method'].choices)
|
|
}, {
|
|
'id': 'become_username',
|
|
'label': 'Privilege Escalation Username',
|
|
'type': 'string',
|
|
}, {
|
|
'id': 'become_password',
|
|
'label': 'Privilege Escalation Password',
|
|
'type': 'string',
|
|
'secret': True,
|
|
'ask_at_runtime': True
|
|
}]
|
|
}
|
|
)
|
|
|
|
|
|
@CredentialType.default
|
|
def scm(cls):
|
|
return cls(
|
|
kind='scm',
|
|
name='Source Control',
|
|
managed_by_tower=True,
|
|
inputs={
|
|
'fields': [{
|
|
'id': 'username',
|
|
'label': 'Username',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'password',
|
|
'label': 'Password',
|
|
'type': 'string',
|
|
'secret': True
|
|
}, {
|
|
'id': 'ssh_key_data',
|
|
'label': 'SCM Private Key',
|
|
'type': 'string',
|
|
'secret': True,
|
|
'multiline': True
|
|
}, {
|
|
'id': 'ssh_key_unlock',
|
|
'label': 'Private Key Passphrase',
|
|
'type': 'string',
|
|
'secret': True
|
|
}]
|
|
}
|
|
)
|
|
|
|
|
|
@CredentialType.default
|
|
def vault(cls):
|
|
return cls(
|
|
kind='vault',
|
|
name='Vault',
|
|
managed_by_tower=True,
|
|
inputs={
|
|
'fields': [{
|
|
'id': 'vault_password',
|
|
'label': 'Vault Password',
|
|
'type': 'string',
|
|
'secret': True,
|
|
'ask_at_runtime': True
|
|
}],
|
|
}
|
|
)
|
|
|
|
|
|
@CredentialType.default
|
|
def net(cls):
|
|
return cls(
|
|
kind='net',
|
|
name='Network',
|
|
managed_by_tower=True,
|
|
inputs={
|
|
'fields': [{
|
|
'id': 'username',
|
|
'label': 'Username',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'password',
|
|
'label': 'Password',
|
|
'type': 'string',
|
|
'secret': True,
|
|
}, {
|
|
'id': 'ssh_key_data',
|
|
'label': 'SSH Private Key',
|
|
'type': 'string',
|
|
'secret': True,
|
|
'multiline': True
|
|
}, {
|
|
'id': 'ssh_key_unlock',
|
|
'label': 'Private Key Passphrase',
|
|
'type': 'string',
|
|
'secret': True,
|
|
}, {
|
|
'id': 'authorize_password',
|
|
'label': 'Authorize Password',
|
|
'type': 'string',
|
|
'secret': True,
|
|
}]
|
|
}
|
|
)
|
|
|
|
|
|
@CredentialType.default
|
|
def aws(cls):
|
|
return cls(
|
|
kind='cloud',
|
|
name='Amazon Web Services',
|
|
managed_by_tower=True,
|
|
inputs={
|
|
'fields': [{
|
|
'id': 'username',
|
|
'label': 'Access Key',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'password',
|
|
'label': 'Secret Key',
|
|
'type': 'string',
|
|
'secret': True,
|
|
}, {
|
|
'id': 'security_token',
|
|
'label': 'STS Token',
|
|
'type': 'string',
|
|
'secret': True,
|
|
}],
|
|
'required': ['username', 'password']
|
|
}
|
|
)
|
|
|
|
|
|
@CredentialType.default
|
|
def openstack(cls):
|
|
return cls(
|
|
kind='cloud',
|
|
name='OpenStack',
|
|
managed_by_tower=True,
|
|
inputs={
|
|
'fields': [{
|
|
'id': 'username',
|
|
'label': 'Username',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'password',
|
|
'label': 'Password (API Key)',
|
|
'type': 'string',
|
|
'secret': True,
|
|
}, {
|
|
'id': 'host',
|
|
'label': 'Host (Authentication URL)',
|
|
'type': 'string',
|
|
}, {
|
|
'id': 'project',
|
|
'label': 'Project (Tenant Name)',
|
|
'type': 'string',
|
|
}, {
|
|
'id': 'domain',
|
|
'label': 'Domain Name',
|
|
'type': 'string'
|
|
}],
|
|
'required': ['username', 'password', 'host', 'project']
|
|
}
|
|
)
|
|
|
|
|
|
@CredentialType.default
|
|
def vmware(cls):
|
|
return cls(
|
|
kind='cloud',
|
|
name='VMware vCenter',
|
|
managed_by_tower=True,
|
|
inputs={
|
|
'fields': [{
|
|
'id': 'host',
|
|
'label': 'VCenter Host',
|
|
'type': 'string',
|
|
}, {
|
|
'id': 'username',
|
|
'label': 'Username',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'password',
|
|
'label': 'Password',
|
|
'type': 'string',
|
|
'secret': True,
|
|
}],
|
|
'required': ['host', 'username', 'password']
|
|
}
|
|
)
|
|
|
|
|
|
@CredentialType.default
|
|
def satellite6(cls):
|
|
return cls(
|
|
kind='cloud',
|
|
name='Red Hat Satellite 6',
|
|
managed_by_tower=True,
|
|
inputs={
|
|
'fields': [{
|
|
'id': 'host',
|
|
'label': 'Satellite 6 URL',
|
|
'type': 'string',
|
|
}, {
|
|
'id': 'username',
|
|
'label': 'Username',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'password',
|
|
'label': 'Password',
|
|
'type': 'string',
|
|
'secret': True,
|
|
}]
|
|
}
|
|
)
|
|
|
|
|
|
@CredentialType.default
|
|
def cloudforms(cls):
|
|
return cls(
|
|
kind='cloud',
|
|
name='Red Hat CloudForms',
|
|
managed_by_tower=True,
|
|
inputs={
|
|
'fields': [{
|
|
'id': 'host',
|
|
'label': 'CloudForms URL',
|
|
'type': 'string',
|
|
}, {
|
|
'id': 'username',
|
|
'label': 'Username',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'password',
|
|
'label': 'Password',
|
|
'type': 'string',
|
|
'secret': True,
|
|
}]
|
|
}
|
|
)
|
|
|
|
|
|
@CredentialType.default
|
|
def gce(cls):
|
|
return cls(
|
|
kind='cloud',
|
|
name='Google Compute Engine',
|
|
managed_by_tower=True,
|
|
inputs={
|
|
'fields': [{
|
|
'id': 'username',
|
|
'label': 'Service Account Email Address',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'project',
|
|
'label': 'Project',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'ssh_key_data',
|
|
'label': 'RSA Private Key',
|
|
'type': 'string',
|
|
'secret': True,
|
|
'multiline': True
|
|
}]
|
|
}
|
|
)
|
|
|
|
|
|
@CredentialType.default
|
|
def azure(cls):
|
|
return cls(
|
|
kind='cloud',
|
|
name='Microsoft Azure Classic (deprecated)',
|
|
managed_by_tower=True,
|
|
inputs={
|
|
'fields': [{
|
|
'id': 'username',
|
|
'label': 'Subscription ID',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'ssh_key_data',
|
|
'label': 'Management Certificate',
|
|
'type': 'string',
|
|
'secret': True,
|
|
'multiline': True
|
|
}]
|
|
}
|
|
)
|
|
|
|
|
|
@CredentialType.default
|
|
def azure_rm(cls):
|
|
return cls(
|
|
kind='cloud',
|
|
name='Microsoft Azure Resource Manager',
|
|
managed_by_tower=True,
|
|
inputs={
|
|
'fields': [{
|
|
'id': 'subscription',
|
|
'label': 'Subscription ID',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'username',
|
|
'label': 'Username',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'password',
|
|
'label': 'Password',
|
|
'type': 'string',
|
|
'secret': True,
|
|
}, {
|
|
'id': 'client',
|
|
'label': 'Client ID',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'secret',
|
|
'label': 'Client Secret',
|
|
'type': 'string',
|
|
'secret': True,
|
|
}, {
|
|
'id': 'tenant',
|
|
'label': 'Tenant ID',
|
|
'type': 'string'
|
|
}]
|
|
}
|
|
)
|
|
|
|
|
|
@CredentialType.default
|
|
def insights(cls):
|
|
return cls(
|
|
kind='insights',
|
|
name='Insights Basic Auth',
|
|
managed_by_tower=True,
|
|
inputs={
|
|
'fields': [{
|
|
'id': 'username',
|
|
'label': 'Basic Auth Username',
|
|
'type': 'string'
|
|
}, {
|
|
'id': 'password',
|
|
'label': 'Basic Auth Password',
|
|
'type': 'string',
|
|
'secret': True
|
|
}],
|
|
'required': ['username', 'password'],
|
|
},
|
|
injectors={
|
|
'extra_vars': {
|
|
"scm_username": "{{username}}",
|
|
"scm_password": "{{password}}",
|
|
},
|
|
},
|
|
)
|
|
|