diff --git a/awx/api/serializers.py b/awx/api/serializers.py index a234240a7e..70a1eaded3 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1924,6 +1924,17 @@ class CredentialTypeSerializer(BaseSerializer): ) return res + def to_representation(self, data): + value = super(CredentialTypeSerializer, self).to_representation(data) + + # translate labels and help_text for credential fields "managed by Tower" + if value.get('managed_by_tower'): + for field in value.get('inputs', {}).get('fields', []): + field['label'] = _(field['label']) + if 'help_text' in field: + field['help_text'] = _(field['help_text']) + return value + # TODO: remove when API v1 is removed @six.add_metaclass(BaseSerializerMetaclass) diff --git a/awx/main/migrations/0040_v320_add_credentialtype_model.py b/awx/main/migrations/0040_v320_add_credentialtype_model.py index 226c160fbf..626f061d10 100644 --- a/awx/main/migrations/0040_v320_add_credentialtype_model.py +++ b/awx/main/migrations/0040_v320_add_credentialtype_model.py @@ -25,8 +25,8 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=512)), ('kind', models.CharField(max_length=32, choices=[(b'ssh', 'SSH'), (b'vault', 'Vault'), (b'net', 'Network'), (b'scm', 'Source Control'), (b'cloud', 'Cloud'), (b'insights', 'Insights')])), ('managed_by_tower', models.BooleanField(default=False, editable=False)), - ('inputs', awx.main.fields.CredentialTypeInputField(default={}, blank=True)), - ('injectors', awx.main.fields.CredentialTypeInjectorField(default={}, blank=True)), + ('inputs', awx.main.fields.CredentialTypeInputField(default={}, blank=True, help_text='Enter inputs using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.')), + ('injectors', awx.main.fields.CredentialTypeInjectorField(default={}, blank=True, help_text='Enter injectors using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.')), ('created_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_created+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)), ('modified_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_modified+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)), ('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')), diff --git a/awx/main/migrations/0042_v320_drop_v1_credential_fields.py b/awx/main/migrations/0042_v320_drop_v1_credential_fields.py index b1eb8d6489..a618e952a7 100644 --- a/awx/main/migrations/0042_v320_drop_v1_credential_fields.py +++ b/awx/main/migrations/0042_v320_drop_v1_credential_fields.py @@ -99,12 +99,12 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='credential', name='credential_type', - field=models.ForeignKey(related_name='credentials', to='main.CredentialType', 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").') + field=models.ForeignKey(related_name='credentials', to='main.CredentialType', null=False, help_text='Specify the type of credential you want to create. Refer to the Ansible Tower documentation for details on each type.') ), migrations.AlterField( model_name='credential', name='inputs', - field=awx.main.fields.CredentialInputField(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.', blank=True), + field=awx.main.fields.CredentialInputField(default={}, help_text='Enter inputs using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.', blank=True), ), migrations.RemoveField( model_name='job', diff --git a/awx/main/models/credential.py b/awx/main/models/credential.py index 4565e9f737..79f110e4ed 100644 --- a/awx/main/models/credential.py +++ b/awx/main/models/credential.py @@ -219,10 +219,8 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin): '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").') + help_text=_('Specify the type of credential you want to create. Refer ' + 'to the Ansible Tower documentation for details on each type.') ) organization = models.ForeignKey( 'Organization', @@ -235,10 +233,9 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin): 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.') + help_text=_('Enter inputs using either JSON or YAML syntax. Use the ' + 'radio button to toggle between the two. Refer to the ' + 'Ansible Tower documentation for example syntax.') ) admin_role = ImplicitRoleField( parent_role=[ @@ -421,11 +418,17 @@ class CredentialType(CommonModelNameNotUnique): ) inputs = CredentialTypeInputField( blank=True, - default={} + default={}, + help_text=_('Enter inputs using either JSON or YAML syntax. Use the ' + 'radio button to toggle between the two. Refer to the ' + 'Ansible Tower documentation for example syntax.') ) injectors = CredentialTypeInjectorField( blank=True, - default={} + default={}, + help_text=_('Enter injectors using either JSON or YAML syntax. Use the ' + 'radio button to toggle between the two. Refer to the ' + 'Ansible Tower documentation for example syntax.') ) def get_absolute_url(self, request=None): @@ -621,7 +624,10 @@ def ssh(cls): 'id': 'become_method', 'label': 'Privilege Escalation Method', 'choices': map(operator.itemgetter(0), - V1Credential.FIELDS['become_method'].choices) + V1Credential.FIELDS['become_method'].choices), + 'help_text': ('Specify a method for "become" operations. This is ' + 'equivalent to specifying the --become-method ' + 'Ansible parameter.') }, { 'id': 'become_username', 'label': 'Privilege Escalation Username', @@ -751,6 +757,10 @@ def aws(cls): 'label': 'STS Token', 'type': 'string', 'secret': True, + 'help_text': ('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'] } @@ -777,6 +787,8 @@ def openstack(cls): 'id': 'host', 'label': 'Host (Authentication URL)', 'type': 'string', + 'help_text': ('The host to authenticate with. For example, ' + 'https://openstack.business.com/v2.0/') }, { 'id': 'project', 'label': 'Project (Tenant Name)', @@ -784,7 +796,11 @@ def openstack(cls): }, { 'id': 'domain', 'label': 'Domain Name', - 'type': 'string' + 'type': 'string', + 'help_text': ('OpenStack domains define administrative boundaries. ' + 'It is only needed for Keystone v3 authentication ' + 'URLs. Refer to Ansible Tower documentation for ' + 'common scenarios.') }], 'required': ['username', 'password', 'host', 'project'] } @@ -802,6 +818,8 @@ def vmware(cls): 'id': 'host', 'label': 'VCenter Host', 'type': 'string', + 'help_text': ('Enter the hostname or IP address which corresponds ' + 'to your VMware vCenter.') }, { 'id': 'username', 'label': 'Username', @@ -828,6 +846,8 @@ def satellite6(cls): 'id': 'host', 'label': 'Satellite 6 URL', 'type': 'string', + 'help_text': ('Enter the URL which corresponds to your Red Hat ' + 'Satellite 6 server. For example, https://satellite.example.org') }, { 'id': 'username', 'label': 'Username', @@ -853,6 +873,9 @@ def cloudforms(cls): 'id': 'host', 'label': 'CloudForms URL', 'type': 'string', + 'help_text': ('Enter the URL for the virtual machine which ' + 'corresponds to your CloudForm instance. ' + 'For example, https://cloudforms.example.org') }, { 'id': 'username', 'label': 'Username', @@ -877,18 +900,25 @@ def gce(cls): 'fields': [{ 'id': 'username', 'label': 'Service Account Email Address', - 'type': 'string' + 'type': 'string', + 'help_text': ('The email address assigned to the Google Compute ' + 'Engine service account.') }, { 'id': 'project', 'label': 'Project', - 'type': 'string' + 'type': 'string', + 'help_text': ('The Project ID is the GCE assigned identification. ' + 'It is constructed as two words followed by a three ' + 'digit number. Example: adjective-noun-000') }, { 'id': 'ssh_key_data', 'label': 'RSA Private Key', 'type': 'string', 'format': 'ssh_private_key', 'secret': True, - 'multiline': True + 'multiline': True, + 'help_text': ('Paste the contents of the PEM file associated ' + 'with the service account email.') }] } ) @@ -904,14 +934,19 @@ def azure(cls): 'fields': [{ 'id': 'username', 'label': 'Subscription ID', - 'type': 'string' + 'type': 'string', + 'help_text': ('Subscription ID is an Azure construct, which is ' + 'mapped to a username.') }, { 'id': 'ssh_key_data', 'label': 'Management Certificate', 'type': 'string', 'format': 'ssh_private_key', 'secret': True, - 'multiline': True + 'multiline': True, + 'help_text': ('Paste the contents of the PEM file that corresponds ' + 'to the certificate you uploaded in the Microsoft ' + 'Azure console.') }] } ) @@ -927,7 +962,9 @@ def azure_rm(cls): 'fields': [{ 'id': 'subscription', 'label': 'Subscription ID', - 'type': 'string' + 'type': 'string', + 'help_text': ('Subscription ID is an Azure construct, which is ' + 'mapped to a username.') }, { 'id': 'username', 'label': 'Username',