diff --git a/CHANGELOG.md b/CHANGELOG.md index ffad4cfb70..afa7697114 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,25 @@ This is a list of high-level changes for each release of AWX. A full list of commits can be found at `https://github.com/ansible/awx/releases/tag/`. +## 15.0.0 (September 30, 2020) +- AWX now utilizes a version of certifi that auto-discovers certificates in the system certificate store - https://github.com/ansible/awx/pull/8242 +- Added support for arbitrary custom inventory plugin configuration: https://github.com/ansible/awx/issues/5150 +- Added improved support for fetching Ansible collections from private Galaxy content sources (such as https://github.com/ansible/galaxy_ng) - https://github.com/ansible/awx/issues/7813 +- Added an optional setting to disable the auto-creation of organizations and teams on successful SAML login. - https://github.com/ansible/awx/pull/8069 +- Added a number of optimizations to AWX's callback receiver to improve the speed of stdout processing for simultaneous playbooks runs - https://github.com/ansible/awx/pull/8193 https://github.com/ansible/awx/pull/8191 +- Added the ability to use `!include` and `!import` constructors when constructing YAML for use with the AWX CLI - https://github.com/ansible/awx/issues/8135 +- Fixed a bug that prevented certain users from being able to edit approval nodes in Workflows - https://github.com/ansible/awx/pull/8253 +- Fixed a bug that broke password prompting for credentials in certain cases - https://github.com/ansible/awx/issues/8202 +- Fixed a bug which can cause PostgreSQL deadlocks when running many parallel playbooks against large shared inventories - https://github.com/ansible/awx/issues/8145 +- Fixed a bug which can cause delays in AWX's task manager when large numbers of simultaneous jobs are scheduled - https://github.com/ansible/awx/issues/7655 +- Fixed a bug which can cause certain scheduled jobs - those that run every X minute(s) or hour(s) - to fail to run at the proper time - https://github.com/ansible/awx/issues/8071 +- Fixed a performance issue for playbooks that store large amounts of data using the `set_stats` module - https://github.com/ansible/awx/issues/8006 +- Fixed a bug related to AWX's handling of the auth_path argument for the HashiVault KeyValue credential plugin - https://github.com/ansible/awx/pull/7991 +- Fixed a bug that broke support for Remote Archive SCM Type project syncs on platforms that utilize Python2 - https://github.com/ansible/awx/pull/8057 +- Updated to the latest version of Django Rest Framework to address CVE-2020-25626 +- Updated to the latest version of Django to address CVE-2020-24583 and CVE-2020-24584 +- Updated to the latest verson of channels_redis to address a bug that slowly causes Daphne processes to leak memory over time - https://github.com/django/channels_redis/issues/212 + ## 14.1.0 (Aug 25, 2020) - AWX images can now be built on ARM64 - https://github.com/ansible/awx/pull/7607 - Added the Remote Archive SCM Type to support using immutable artifacts and releases (such as tarballs and zip files) as projects - https://github.com/ansible/awx/issues/7954 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 42516b9a15..bd3da38b51 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -80,7 +80,7 @@ For Linux platforms, refer to the following from Docker: If you're not using Docker for Mac, or Docker for Windows, you may need, or choose to, install the Docker compose Python module separately, in which case you'll need to run the following: ```bash -(host)$ pip install docker-compose +(host)$ pip3 install docker-compose ``` #### Frontend Development diff --git a/VERSION b/VERSION index 7b3b6e02bb..94188a7483 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -14.1.0 +15.0.0 diff --git a/awx/api/conf.py b/awx/api/conf.py index 493eed6981..f7da952004 100644 --- a/awx/api/conf.py +++ b/awx/api/conf.py @@ -16,6 +16,7 @@ register( help_text=_('Number of seconds that a user is inactive before they will need to login again.'), category=_('Authentication'), category_slug='authentication', + unit=_('seconds'), ) register( 'SESSIONS_PER_USER', @@ -49,6 +50,7 @@ register( 'in the number of seconds.'), category=_('Authentication'), category_slug='authentication', + unit=_('seconds'), ) register( 'ALLOW_OAUTH2_FOR_EXTERNAL_USERS', diff --git a/awx/api/metadata.py b/awx/api/metadata.py index 847e353890..0b60f9a1ef 100644 --- a/awx/api/metadata.py +++ b/awx/api/metadata.py @@ -39,7 +39,7 @@ class Metadata(metadata.SimpleMetadata): 'min_length', 'max_length', 'min_value', 'max_value', 'category', 'category_slug', - 'defined_in_file' + 'defined_in_file', 'unit', ] for attr in text_attrs: diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 83575025e7..2bb25f3de2 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1269,6 +1269,7 @@ class OrganizationSerializer(BaseSerializer): object_roles = self.reverse('api:organization_object_roles_list', kwargs={'pk': obj.pk}), access_list = self.reverse('api:organization_access_list', kwargs={'pk': obj.pk}), instance_groups = self.reverse('api:organization_instance_groups_list', kwargs={'pk': obj.pk}), + galaxy_credentials = self.reverse('api:organization_galaxy_credentials_list', kwargs={'pk': obj.pk}), )) return res @@ -2536,10 +2537,11 @@ class CredentialTypeSerializer(BaseSerializer): class CredentialSerializer(BaseSerializer): show_capabilities = ['edit', 'delete', 'copy', 'use'] capabilities_prefetch = ['admin', 'use'] + managed_by_tower = serializers.ReadOnlyField() class Meta: model = Credential - fields = ('*', 'organization', 'credential_type', 'inputs', 'kind', 'cloud', 'kubernetes') + fields = ('*', 'organization', 'credential_type', 'managed_by_tower', 'inputs', 'kind', 'cloud', 'kubernetes') extra_kwargs = { 'credential_type': { 'label': _('Credential Type'), @@ -2603,6 +2605,13 @@ class CredentialSerializer(BaseSerializer): return summary_dict + def validate(self, attrs): + if self.instance and self.instance.managed_by_tower: + raise PermissionDenied( + detail=_("Modifications not allowed for managed credentials") + ) + return super(CredentialSerializer, self).validate(attrs) + def get_validation_exclusions(self, obj=None): ret = super(CredentialSerializer, self).get_validation_exclusions(obj) for field in ('credential_type', 'inputs'): @@ -2610,6 +2619,17 @@ class CredentialSerializer(BaseSerializer): ret.remove(field) return ret + def validate_organization(self, org): + if ( + self.instance and + self.instance.credential_type.kind == 'galaxy' and + org is None + ): + raise serializers.ValidationError(_( + "Galaxy credentials must be owned by an Organization." + )) + return org + def validate_credential_type(self, credential_type): if self.instance and credential_type.pk != self.instance.credential_type.pk: for related_objects in ( @@ -2674,6 +2694,15 @@ class CredentialSerializerCreate(CredentialSerializer): if attrs.get('team'): attrs['organization'] = attrs['team'].organization + if ( + 'credential_type' in attrs and + attrs['credential_type'].kind == 'galaxy' and + list(owner_fields) != ['organization'] + ): + raise serializers.ValidationError({"organization": _( + "Galaxy credentials must be owned by an Organization." + )}) + return super(CredentialSerializerCreate, self).validate(attrs) def create(self, validated_data): @@ -4128,7 +4157,10 @@ class JobLaunchSerializer(BaseSerializer): # verify that credentials (either provided or existing) don't # require launch-time passwords that have not been provided if 'credentials' in accepted: - launch_credentials = accepted['credentials'] + launch_credentials = Credential.unique_dict( + list(template_credentials.all()) + + list(accepted['credentials']) + ).values() else: launch_credentials = template_credentials passwords = attrs.get('credential_passwords', {}) # get from original attrs diff --git a/awx/api/urls/organization.py b/awx/api/urls/organization.py index 3d172f1360..12b2807905 100644 --- a/awx/api/urls/organization.py +++ b/awx/api/urls/organization.py @@ -21,6 +21,7 @@ from awx.api.views import ( OrganizationNotificationTemplatesSuccessList, OrganizationNotificationTemplatesApprovalList, OrganizationInstanceGroupsList, + OrganizationGalaxyCredentialsList, OrganizationObjectRolesList, OrganizationAccessList, OrganizationApplicationList, @@ -49,6 +50,7 @@ urls = [ url(r'^(?P[0-9]+)/notification_templates_approvals/$', OrganizationNotificationTemplatesApprovalList.as_view(), name='organization_notification_templates_approvals_list'), url(r'^(?P[0-9]+)/instance_groups/$', OrganizationInstanceGroupsList.as_view(), name='organization_instance_groups_list'), + url(r'^(?P[0-9]+)/galaxy_credentials/$', OrganizationGalaxyCredentialsList.as_view(), name='organization_galaxy_credentials_list'), url(r'^(?P[0-9]+)/object_roles/$', OrganizationObjectRolesList.as_view(), name='organization_object_roles_list'), url(r'^(?P[0-9]+)/access_list/$', OrganizationAccessList.as_view(), name='organization_access_list'), url(r'^(?P[0-9]+)/applications/$', OrganizationApplicationList.as_view(), name='organization_applications_list'), diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index c5b22d105a..4f436c8f0e 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -124,6 +124,7 @@ from awx.api.views.organization import ( # noqa OrganizationNotificationTemplatesSuccessList, OrganizationNotificationTemplatesApprovalList, OrganizationInstanceGroupsList, + OrganizationGalaxyCredentialsList, OrganizationAccessList, OrganizationObjectRolesList, ) @@ -1355,6 +1356,13 @@ class CredentialDetail(RetrieveUpdateDestroyAPIView): model = models.Credential serializer_class = serializers.CredentialSerializer + def destroy(self, request, *args, **kwargs): + instance = self.get_object() + if instance.managed_by_tower: + raise PermissionDenied(detail=_("Deletion not allowed for managed credentials")) + return super(CredentialDetail, self).destroy(request, *args, **kwargs) + + class CredentialActivityStreamList(SubListAPIView): diff --git a/awx/api/views/metrics.py b/awx/api/views/metrics.py index 8d78dea21f..39744e1bcd 100644 --- a/awx/api/views/metrics.py +++ b/awx/api/views/metrics.py @@ -22,7 +22,7 @@ from awx.api.generics import ( ) -logger = logging.getLogger('awx.main.analytics') +logger = logging.getLogger('awx.analytics') class MetricsView(APIView): diff --git a/awx/api/views/organization.py b/awx/api/views/organization.py index cb929ec5b5..06172af79f 100644 --- a/awx/api/views/organization.py +++ b/awx/api/views/organization.py @@ -7,6 +7,7 @@ import logging # Django from django.db.models import Count from django.contrib.contenttypes.models import ContentType +from django.utils.translation import ugettext_lazy as _ # AWX from awx.main.models import ( @@ -20,7 +21,8 @@ from awx.main.models import ( Role, User, Team, - InstanceGroup + InstanceGroup, + Credential ) from awx.api.generics import ( ListCreateAPIView, @@ -42,7 +44,8 @@ from awx.api.serializers import ( RoleSerializer, NotificationTemplateSerializer, InstanceGroupSerializer, - ProjectSerializer, JobTemplateSerializer, WorkflowJobTemplateSerializer + ProjectSerializer, JobTemplateSerializer, WorkflowJobTemplateSerializer, + CredentialSerializer ) from awx.api.views.mixin import ( RelatedJobsPreventDeleteMixin, @@ -214,6 +217,20 @@ class OrganizationInstanceGroupsList(SubListAttachDetachAPIView): relationship = 'instance_groups' +class OrganizationGalaxyCredentialsList(SubListAttachDetachAPIView): + + model = Credential + serializer_class = CredentialSerializer + parent_model = Organization + relationship = 'galaxy_credentials' + + def is_valid_relation(self, parent, sub, created=False): + if sub.kind != 'galaxy_api_token': + return {'msg': _( + f"Credential must be a Galaxy credential, not {sub.credential_type.name}." + )} + + class OrganizationAccessList(ResourceAccessList): model = User # needs to be User for AccessLists's diff --git a/awx/api/views/root.py b/awx/api/views/root.py index 4a15936e9b..aeda19cdeb 100644 --- a/awx/api/views/root.py +++ b/awx/api/views/root.py @@ -21,6 +21,7 @@ import requests from awx.api.generics import APIView from awx.conf.registry import settings_registry +from awx.main.analytics import all_collectors from awx.main.ha import is_ha_environment from awx.main.utils import ( get_awx_version, @@ -252,6 +253,7 @@ class ApiV2ConfigView(APIView): ansible_version=get_ansible_version(), eula=render_to_string("eula.md") if license_data.get('license_type', 'UNLICENSED') != 'open' else '', analytics_status=pendo_state, + analytics_collectors=all_collectors(), become_methods=PRIVILEGE_ESCALATION_METHODS, ) diff --git a/awx/conf/registry.py b/awx/conf/registry.py index 63336fc55e..e8e52fe695 100644 --- a/awx/conf/registry.py +++ b/awx/conf/registry.py @@ -129,12 +129,14 @@ class SettingsRegistry(object): placeholder = field_kwargs.pop('placeholder', empty) encrypted = bool(field_kwargs.pop('encrypted', False)) defined_in_file = bool(field_kwargs.pop('defined_in_file', False)) + unit = field_kwargs.pop('unit', None) if getattr(field_kwargs.get('child', None), 'source', None) is not None: field_kwargs['child'].source = None field_instance = field_class(**field_kwargs) field_instance.category_slug = category_slug field_instance.category = category field_instance.depends_on = depends_on + field_instance.unit = unit if placeholder is not empty: field_instance.placeholder = placeholder field_instance.defined_in_file = defined_in_file diff --git a/awx/conf/settings.py b/awx/conf/settings.py index 98a39978d3..d2733ce879 100644 --- a/awx/conf/settings.py +++ b/awx/conf/settings.py @@ -17,6 +17,8 @@ from django.utils.functional import cached_property # Django REST Framework from rest_framework.fields import empty, SkipField +import cachetools + # Tower from awx.main.utils import encrypt_field, decrypt_field from awx.conf import settings_registry @@ -28,6 +30,8 @@ from awx.conf.migrations._reencrypt import decrypt_field as old_decrypt_field logger = logging.getLogger('awx.conf.settings') +SETTING_MEMORY_TTL = 5 if 'callback_receiver' in ' '.join(sys.argv) else 0 + # Store a special value to indicate when a setting is not set in the database. SETTING_CACHE_NOTSET = '___notset___' @@ -406,6 +410,7 @@ class SettingsWrapper(UserSettingsHolder): def SETTINGS_MODULE(self): return self._get_default('SETTINGS_MODULE') + @cachetools.cached(cache=cachetools.TTLCache(maxsize=2048, ttl=SETTING_MEMORY_TTL)) def __getattr__(self, name): value = empty if name in self.all_supported_settings: diff --git a/awx/locale/django.pot b/awx/locale/django.pot index dfdd2e72ef..3d2cf41999 100644 --- a/awx/locale/django.pot +++ b/awx/locale/django.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-16 02:52+0000\n" +"POT-Creation-Date: 2020-10-05 19:43+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -27,35 +27,42 @@ msgid "" "again." msgstr "" -#: awx/api/conf.py:17 awx/api/conf.py:26 awx/api/conf.py:34 awx/api/conf.py:50 -#: awx/api/conf.py:62 awx/api/conf.py:74 awx/sso/conf.py:97 awx/sso/conf.py:108 +#: awx/api/conf.py:17 awx/api/conf.py:27 awx/api/conf.py:35 awx/api/conf.py:51 +#: awx/api/conf.py:64 awx/api/conf.py:76 awx/sso/conf.py:97 awx/sso/conf.py:108 #: awx/sso/conf.py:120 awx/sso/conf.py:135 msgid "Authentication" msgstr "" -#: awx/api/conf.py:24 -msgid "Maximum number of simultaneous logged in sessions" +#: awx/api/conf.py:19 awx/api/conf.py:53 awx/main/conf.py:256 +#: awx/main/conf.py:268 awx/main/conf.py:281 awx/main/conf.py:503 +#: awx/main/conf.py:516 awx/main/conf.py:529 awx/main/conf.py:544 +#: awx/main/conf.py:682 awx/main/conf.py:764 awx/sso/conf.py:518 +msgid "seconds" msgstr "" #: awx/api/conf.py:25 +msgid "Maximum number of simultaneous logged in sessions" +msgstr "" + +#: awx/api/conf.py:26 msgid "" "Maximum number of simultaneous logged in sessions a user may have. To " "disable enter -1." msgstr "" -#: awx/api/conf.py:32 +#: awx/api/conf.py:33 msgid "Enable HTTP Basic Auth" msgstr "" -#: awx/api/conf.py:33 +#: awx/api/conf.py:34 msgid "Enable HTTP Basic Auth for the API Browser." msgstr "" -#: awx/api/conf.py:43 +#: awx/api/conf.py:44 msgid "OAuth 2 Timeout Settings" msgstr "" -#: awx/api/conf.py:44 +#: awx/api/conf.py:45 msgid "" "Dictionary for customizing OAuth 2 timeouts, available items are " "`ACCESS_TOKEN_EXPIRE_SECONDS`, the duration of access tokens in the number " @@ -65,11 +72,11 @@ msgid "" "expired access tokens, in the number of seconds." msgstr "" -#: awx/api/conf.py:57 +#: awx/api/conf.py:59 msgid "Allow External Users to Create OAuth2 Tokens" msgstr "" -#: awx/api/conf.py:58 +#: awx/api/conf.py:60 msgid "" "For security reasons, users from external auth providers (LDAP, SAML, SSO, " "Radius, and others) are not allowed to create OAuth2 tokens. To change this " @@ -77,11 +84,11 @@ msgid "" "setting is toggled off." msgstr "" -#: awx/api/conf.py:71 +#: awx/api/conf.py:73 msgid "Login redirect override URL" msgstr "" -#: awx/api/conf.py:72 +#: awx/api/conf.py:74 msgid "" "URL to which unauthorized users will be redirected to log in. If blank, " "users will be sent to the Tower login page." @@ -126,27 +133,27 @@ msgstr "" msgid "Invalid {field_name} id: {field_id}" msgstr "" -#: awx/api/filters.py:333 +#: awx/api/filters.py:338 msgid "" "Cannot apply role_level filter to this list because its model does not use " "roles for access control." msgstr "" -#: awx/api/generics.py:182 +#: awx/api/generics.py:183 msgid "" "You did not use correct Content-Type in your HTTP request. If you are using " "our REST API, the Content-Type must be application/json" msgstr "" -#: awx/api/generics.py:623 awx/api/generics.py:685 +#: awx/api/generics.py:647 awx/api/generics.py:709 msgid "\"id\" field must be an integer." msgstr "" -#: awx/api/generics.py:682 +#: awx/api/generics.py:706 msgid "\"id\" is required to disassociate" msgstr "" -#: awx/api/generics.py:733 +#: awx/api/generics.py:757 msgid "{} 'id' field is missing." msgstr "" @@ -248,1127 +255,1161 @@ msgid "" "saved to the database." msgstr "" -#: awx/api/serializers.py:878 +#: awx/api/serializers.py:880 msgid "Write-only field used to change the password." msgstr "" -#: awx/api/serializers.py:880 +#: awx/api/serializers.py:882 msgid "Set if the account is managed by an external service" msgstr "" -#: awx/api/serializers.py:907 +#: awx/api/serializers.py:909 msgid "Password required for new User." msgstr "" -#: awx/api/serializers.py:992 +#: awx/api/serializers.py:994 #, python-format msgid "Unable to change %s on user managed by LDAP." msgstr "" -#: awx/api/serializers.py:1088 +#: awx/api/serializers.py:1090 msgid "Must be a simple space-separated string with allowed scopes {}." msgstr "" -#: awx/api/serializers.py:1186 +#: awx/api/serializers.py:1188 msgid "Authorization Grant Type" msgstr "" -#: awx/api/serializers.py:1188 awx/main/credential_plugins/azure_kv.py:30 -#: awx/main/models/credential/__init__.py:964 +#: awx/api/serializers.py:1190 awx/main/credential_plugins/azure_kv.py:30 +#: awx/main/models/credential/__init__.py:972 msgid "Client Secret" msgstr "" -#: awx/api/serializers.py:1191 +#: awx/api/serializers.py:1193 msgid "Client Type" msgstr "" -#: awx/api/serializers.py:1194 +#: awx/api/serializers.py:1196 msgid "Redirect URIs" msgstr "" -#: awx/api/serializers.py:1197 +#: awx/api/serializers.py:1199 msgid "Skip Authorization" msgstr "" -#: awx/api/serializers.py:1303 +#: awx/api/serializers.py:1306 msgid "Cannot change max_hosts." msgstr "" -#: awx/api/serializers.py:1336 +#: awx/api/serializers.py:1339 msgid "This path is already being used by another manual project." msgstr "" -#: awx/api/serializers.py:1338 +#: awx/api/serializers.py:1341 +msgid "SCM branch cannot be used with archive projects." +msgstr "" + +#: awx/api/serializers.py:1343 msgid "SCM refspec can only be used with git projects." msgstr "" -#: awx/api/serializers.py:1415 +#: awx/api/serializers.py:1420 msgid "" "One or more job templates depend on branch override behavior for this " "project (ids: {})." msgstr "" -#: awx/api/serializers.py:1422 +#: awx/api/serializers.py:1427 msgid "Update options must be set to false for manual projects." msgstr "" -#: awx/api/serializers.py:1428 +#: awx/api/serializers.py:1433 msgid "Array of playbooks available within this project." msgstr "" -#: awx/api/serializers.py:1447 +#: awx/api/serializers.py:1452 msgid "" "Array of inventory files and directories available within this project, not " "comprehensive." msgstr "" -#: awx/api/serializers.py:1495 awx/api/serializers.py:3034 -#: awx/api/serializers.py:3246 +#: awx/api/serializers.py:1500 awx/api/serializers.py:3089 +#: awx/api/serializers.py:3301 msgid "A count of hosts uniquely assigned to each status." msgstr "" -#: awx/api/serializers.py:1498 awx/api/serializers.py:3037 +#: awx/api/serializers.py:1503 awx/api/serializers.py:3092 msgid "A count of all plays and tasks for the job run." msgstr "" -#: awx/api/serializers.py:1625 +#: awx/api/serializers.py:1630 msgid "Smart inventories must specify host_filter" msgstr "" -#: awx/api/serializers.py:1713 +#: awx/api/serializers.py:1722 #, python-format msgid "Invalid port specification: %s" msgstr "" -#: awx/api/serializers.py:1724 +#: awx/api/serializers.py:1733 msgid "Cannot create Host for Smart Inventory" msgstr "" -#: awx/api/serializers.py:1808 +#: awx/api/serializers.py:1751 +msgid "A Group with that name already exists." +msgstr "" + +#: awx/api/serializers.py:1822 +msgid "A Host with that name already exists." +msgstr "" + +#: awx/api/serializers.py:1827 msgid "Invalid group name." msgstr "" -#: awx/api/serializers.py:1813 +#: awx/api/serializers.py:1832 msgid "Cannot create Group for Smart Inventory" msgstr "" -#: awx/api/serializers.py:1888 +#: awx/api/serializers.py:1907 msgid "" "Script must begin with a hashbang sequence: i.e.... #!/usr/bin/env python" msgstr "" -#: awx/api/serializers.py:1917 +#: awx/api/serializers.py:1936 msgid "Cloud credential to use for inventory updates." msgstr "" -#: awx/api/serializers.py:1938 +#: awx/api/serializers.py:1957 msgid "`{}` is a prohibited environment variable" msgstr "" -#: awx/api/serializers.py:1949 +#: awx/api/serializers.py:1968 msgid "If 'source' is 'custom', 'source_script' must be provided." msgstr "" -#: awx/api/serializers.py:1955 +#: awx/api/serializers.py:1974 msgid "Must provide an inventory." msgstr "" -#: awx/api/serializers.py:1959 +#: awx/api/serializers.py:1978 msgid "" "The 'source_script' does not belong to the same organization as the " "inventory." msgstr "" -#: awx/api/serializers.py:1961 +#: awx/api/serializers.py:1980 msgid "'source_script' doesn't exist." msgstr "" -#: awx/api/serializers.py:2063 +#: awx/api/serializers.py:2082 msgid "Cannot use manual project for SCM-based inventory." msgstr "" -#: awx/api/serializers.py:2068 +#: awx/api/serializers.py:2087 msgid "Setting not compatible with existing schedules." msgstr "" -#: awx/api/serializers.py:2073 +#: awx/api/serializers.py:2092 msgid "Cannot create Inventory Source for Smart Inventory" msgstr "" -#: awx/api/serializers.py:2121 +#: awx/api/serializers.py:2140 msgid "Project required for scm type sources." msgstr "" -#: awx/api/serializers.py:2130 +#: awx/api/serializers.py:2149 #, python-format msgid "Cannot set %s if not SCM type." msgstr "" -#: awx/api/serializers.py:2200 +#: awx/api/serializers.py:2219 msgid "The project used for this job." msgstr "" -#: awx/api/serializers.py:2456 +#: awx/api/serializers.py:2475 msgid "Modifications not allowed for managed credential types" msgstr "" -#: awx/api/serializers.py:2468 +#: awx/api/serializers.py:2487 msgid "" "Modifications to inputs are not allowed for credential types that are in use" msgstr "" -#: awx/api/serializers.py:2473 +#: awx/api/serializers.py:2492 #, python-format msgid "Must be 'cloud' or 'net', not %s" msgstr "" -#: awx/api/serializers.py:2479 +#: awx/api/serializers.py:2498 msgid "'ask_at_runtime' is not supported for custom credentials." msgstr "" -#: awx/api/serializers.py:2527 +#: awx/api/serializers.py:2547 msgid "Credential Type" msgstr "" -#: awx/api/serializers.py:2608 +#: awx/api/serializers.py:2611 +msgid "Modifications not allowed for managed credentials" +msgstr "" + +#: awx/api/serializers.py:2629 awx/api/serializers.py:2703 +msgid "Galaxy credentials must be owned by an Organization." +msgstr "" + +#: awx/api/serializers.py:2646 msgid "" "You cannot change the credential type of the credential, as it may break the " "functionality of the resources using it." msgstr "" -#: awx/api/serializers.py:2620 +#: awx/api/serializers.py:2658 msgid "" "Write-only field used to add user to owner role. If provided, do not give " "either team or organization. Only valid for creation." msgstr "" -#: awx/api/serializers.py:2625 +#: awx/api/serializers.py:2663 msgid "" "Write-only field used to add team to owner role. If provided, do not give " "either user or organization. Only valid for creation." msgstr "" -#: awx/api/serializers.py:2630 +#: awx/api/serializers.py:2668 msgid "" "Inherit permissions from organization roles. If provided on creation, do not " "give either user or team." msgstr "" -#: awx/api/serializers.py:2646 +#: awx/api/serializers.py:2685 msgid "Missing 'user', 'team', or 'organization'." msgstr "" -#: awx/api/serializers.py:2663 +#: awx/api/serializers.py:2690 +msgid "" +"Only one of 'user', 'team', or 'organization' should be provided, received " +"{} fields." +msgstr "" + +#: awx/api/serializers.py:2718 msgid "" "Credential organization must be set and match before assigning to a team" msgstr "" -#: awx/api/serializers.py:2789 +#: awx/api/serializers.py:2844 msgid "This field is required." msgstr "" -#: awx/api/serializers.py:2798 +#: awx/api/serializers.py:2853 msgid "Playbook not found for project." msgstr "" -#: awx/api/serializers.py:2800 +#: awx/api/serializers.py:2855 msgid "Must select playbook for project." msgstr "" -#: awx/api/serializers.py:2802 awx/api/serializers.py:2804 +#: awx/api/serializers.py:2857 awx/api/serializers.py:2859 msgid "Project does not allow overriding branch." msgstr "" -#: awx/api/serializers.py:2841 +#: awx/api/serializers.py:2896 msgid "Must be a Personal Access Token." msgstr "" -#: awx/api/serializers.py:2844 +#: awx/api/serializers.py:2899 msgid "Must match the selected webhook service." msgstr "" -#: awx/api/serializers.py:2915 +#: awx/api/serializers.py:2970 msgid "Cannot enable provisioning callback without an inventory set." msgstr "" -#: awx/api/serializers.py:2918 +#: awx/api/serializers.py:2973 msgid "Must either set a default value or ask to prompt on launch." msgstr "" -#: awx/api/serializers.py:2920 awx/main/models/jobs.py:299 +#: awx/api/serializers.py:2975 awx/main/models/jobs.py:299 msgid "Job Templates must have a project assigned." msgstr "" -#: awx/api/serializers.py:3078 +#: awx/api/serializers.py:3133 msgid "No change to job limit" msgstr "" -#: awx/api/serializers.py:3079 +#: awx/api/serializers.py:3134 msgid "All failed and unreachable hosts" msgstr "" -#: awx/api/serializers.py:3094 +#: awx/api/serializers.py:3149 msgid "Missing passwords needed to start: {}" msgstr "" -#: awx/api/serializers.py:3113 +#: awx/api/serializers.py:3168 msgid "Relaunch by host status not available until job finishes running." msgstr "" -#: awx/api/serializers.py:3127 +#: awx/api/serializers.py:3182 msgid "Job Template Project is missing or undefined." msgstr "" -#: awx/api/serializers.py:3129 +#: awx/api/serializers.py:3184 msgid "Job Template Inventory is missing or undefined." msgstr "" -#: awx/api/serializers.py:3167 +#: awx/api/serializers.py:3222 msgid "Unknown, job may have been ran before launch configurations were saved." msgstr "" -#: awx/api/serializers.py:3238 awx/main/tasks.py:2799 awx/main/tasks.py:2817 +#: awx/api/serializers.py:3293 awx/main/tasks.py:2838 awx/main/tasks.py:2856 msgid "{} are prohibited from use in ad hoc commands." msgstr "" -#: awx/api/serializers.py:3326 awx/api/views/__init__.py:4169 +#: awx/api/serializers.py:3381 awx/api/views/__init__.py:4211 #, python-brace-format msgid "" "Standard Output too large to display ({text_size} bytes), only download " "supported for sizes over {supported_size} bytes." msgstr "" -#: awx/api/serializers.py:3639 +#: awx/api/serializers.py:3694 msgid "Provided variable {} has no database value to replace with." msgstr "" -#: awx/api/serializers.py:3657 +#: awx/api/serializers.py:3712 msgid "\"$encrypted$ is a reserved keyword, may not be used for {}.\"" msgstr "" -#: awx/api/serializers.py:4064 +#: awx/api/serializers.py:4119 msgid "A project is required to run a job." msgstr "" -#: awx/api/serializers.py:4066 +#: awx/api/serializers.py:4121 msgid "Missing a revision to run due to failed project update." msgstr "" -#: awx/api/serializers.py:4070 +#: awx/api/serializers.py:4125 msgid "The inventory associated with this Job Template is being deleted." msgstr "" -#: awx/api/serializers.py:4072 awx/api/serializers.py:4188 +#: awx/api/serializers.py:4127 awx/api/serializers.py:4247 msgid "The provided inventory is being deleted." msgstr "" -#: awx/api/serializers.py:4080 +#: awx/api/serializers.py:4135 msgid "Cannot assign multiple {} credentials." msgstr "" -#: awx/api/serializers.py:4084 +#: awx/api/serializers.py:4140 msgid "Cannot assign a Credential of kind `{}`" msgstr "" -#: awx/api/serializers.py:4097 +#: awx/api/serializers.py:4153 msgid "" "Removing {} credential at launch time without replacement is not supported. " "Provided list lacked credential(s): {}." msgstr "" -#: awx/api/serializers.py:4186 +#: awx/api/serializers.py:4245 msgid "The inventory associated with this Workflow is being deleted." msgstr "" -#: awx/api/serializers.py:4257 +#: awx/api/serializers.py:4316 msgid "Message type '{}' invalid, must be either 'message' or 'body'" msgstr "" -#: awx/api/serializers.py:4263 +#: awx/api/serializers.py:4322 msgid "Expected string for '{}', found {}, " msgstr "" -#: awx/api/serializers.py:4267 +#: awx/api/serializers.py:4326 msgid "Messages cannot contain newlines (found newline in {} event)" msgstr "" -#: awx/api/serializers.py:4273 +#: awx/api/serializers.py:4332 msgid "Expected dict for 'messages' field, found {}" msgstr "" -#: awx/api/serializers.py:4277 +#: awx/api/serializers.py:4336 msgid "" "Event '{}' invalid, must be one of 'started', 'success', 'error', or " "'workflow_approval'" msgstr "" -#: awx/api/serializers.py:4283 +#: awx/api/serializers.py:4342 msgid "Expected dict for event '{}', found {}" msgstr "" -#: awx/api/serializers.py:4288 +#: awx/api/serializers.py:4347 msgid "" "Workflow Approval event '{}' invalid, must be one of 'running', 'approved', " "'timed_out', or 'denied'" msgstr "" -#: awx/api/serializers.py:4295 +#: awx/api/serializers.py:4354 msgid "Expected dict for workflow approval event '{}', found {}" msgstr "" -#: awx/api/serializers.py:4322 +#: awx/api/serializers.py:4381 msgid "Unable to render message '{}': {}" msgstr "" -#: awx/api/serializers.py:4324 +#: awx/api/serializers.py:4383 msgid "Field '{}' unavailable" msgstr "" -#: awx/api/serializers.py:4326 +#: awx/api/serializers.py:4385 msgid "Security error due to field '{}'" msgstr "" -#: awx/api/serializers.py:4346 +#: awx/api/serializers.py:4405 msgid "Webhook body for '{}' should be a json dictionary. Found type '{}'." msgstr "" -#: awx/api/serializers.py:4349 +#: awx/api/serializers.py:4408 msgid "Webhook body for '{}' is not a valid json dictionary ({})." msgstr "" -#: awx/api/serializers.py:4367 +#: awx/api/serializers.py:4426 msgid "" "Missing required fields for Notification Configuration: notification_type" msgstr "" -#: awx/api/serializers.py:4394 +#: awx/api/serializers.py:4453 msgid "No values specified for field '{}'" msgstr "" -#: awx/api/serializers.py:4399 +#: awx/api/serializers.py:4458 msgid "HTTP method must be either 'POST' or 'PUT'." msgstr "" -#: awx/api/serializers.py:4401 +#: awx/api/serializers.py:4460 msgid "Missing required fields for Notification Configuration: {}." msgstr "" -#: awx/api/serializers.py:4404 +#: awx/api/serializers.py:4463 msgid "Configuration field '{}' incorrect type, expected {}." msgstr "" -#: awx/api/serializers.py:4421 +#: awx/api/serializers.py:4480 msgid "Notification body" msgstr "" -#: awx/api/serializers.py:4501 +#: awx/api/serializers.py:4560 msgid "" "Valid DTSTART required in rrule. Value should start with: DTSTART:" "YYYYMMDDTHHMMSSZ" msgstr "" -#: awx/api/serializers.py:4503 +#: awx/api/serializers.py:4562 msgid "" "DTSTART cannot be a naive datetime. Specify ;TZINFO= or YYYYMMDDTHHMMSSZZ." msgstr "" -#: awx/api/serializers.py:4505 +#: awx/api/serializers.py:4564 msgid "Multiple DTSTART is not supported." msgstr "" -#: awx/api/serializers.py:4507 +#: awx/api/serializers.py:4566 msgid "RRULE required in rrule." msgstr "" -#: awx/api/serializers.py:4509 +#: awx/api/serializers.py:4568 msgid "Multiple RRULE is not supported." msgstr "" -#: awx/api/serializers.py:4511 +#: awx/api/serializers.py:4570 msgid "INTERVAL required in rrule." msgstr "" -#: awx/api/serializers.py:4513 +#: awx/api/serializers.py:4572 msgid "SECONDLY is not supported." msgstr "" -#: awx/api/serializers.py:4515 +#: awx/api/serializers.py:4574 msgid "Multiple BYMONTHDAYs not supported." msgstr "" -#: awx/api/serializers.py:4517 +#: awx/api/serializers.py:4576 msgid "Multiple BYMONTHs not supported." msgstr "" -#: awx/api/serializers.py:4519 +#: awx/api/serializers.py:4578 msgid "BYDAY with numeric prefix not supported." msgstr "" -#: awx/api/serializers.py:4521 +#: awx/api/serializers.py:4580 msgid "BYYEARDAY not supported." msgstr "" -#: awx/api/serializers.py:4523 +#: awx/api/serializers.py:4582 msgid "BYWEEKNO not supported." msgstr "" -#: awx/api/serializers.py:4525 +#: awx/api/serializers.py:4584 msgid "RRULE may not contain both COUNT and UNTIL" msgstr "" -#: awx/api/serializers.py:4529 +#: awx/api/serializers.py:4588 msgid "COUNT > 999 is unsupported." msgstr "" -#: awx/api/serializers.py:4535 +#: awx/api/serializers.py:4594 msgid "rrule parsing failed validation: {}" msgstr "" -#: awx/api/serializers.py:4597 +#: awx/api/serializers.py:4656 msgid "Inventory Source must be a cloud resource." msgstr "" -#: awx/api/serializers.py:4599 +#: awx/api/serializers.py:4658 msgid "Manual Project cannot have a schedule set." msgstr "" -#: awx/api/serializers.py:4602 +#: awx/api/serializers.py:4661 msgid "" "Inventory sources with `update_on_project_update` cannot be scheduled. " "Schedule its source project `{}` instead." msgstr "" -#: awx/api/serializers.py:4612 +#: awx/api/serializers.py:4671 msgid "" "Count of jobs in the running or waiting state that are targeted for this " "instance" msgstr "" -#: awx/api/serializers.py:4617 +#: awx/api/serializers.py:4676 msgid "Count of all jobs that target this instance" msgstr "" -#: awx/api/serializers.py:4650 +#: awx/api/serializers.py:4711 msgid "" "Count of jobs in the running or waiting state that are targeted for this " "instance group" msgstr "" -#: awx/api/serializers.py:4655 +#: awx/api/serializers.py:4716 msgid "Count of all jobs that target this instance group" msgstr "" -#: awx/api/serializers.py:4660 +#: awx/api/serializers.py:4721 msgid "Indicates whether instance group controls any other group" msgstr "" -#: awx/api/serializers.py:4664 +#: awx/api/serializers.py:4725 msgid "" "Indicates whether instances in this group are isolated.Isolated groups have " "a designated controller group." msgstr "" -#: awx/api/serializers.py:4669 +#: awx/api/serializers.py:4730 msgid "" "Indicates whether instances in this group are containerized.Containerized " "groups have a designated Openshift or Kubernetes cluster." msgstr "" -#: awx/api/serializers.py:4677 +#: awx/api/serializers.py:4738 msgid "Policy Instance Percentage" msgstr "" -#: awx/api/serializers.py:4678 +#: awx/api/serializers.py:4739 msgid "" "Minimum percentage of all instances that will be automatically assigned to " "this group when new instances come online." msgstr "" -#: awx/api/serializers.py:4683 +#: awx/api/serializers.py:4744 msgid "Policy Instance Minimum" msgstr "" -#: awx/api/serializers.py:4684 +#: awx/api/serializers.py:4745 msgid "" "Static minimum number of Instances that will be automatically assign to this " "group when new instances come online." msgstr "" -#: awx/api/serializers.py:4689 +#: awx/api/serializers.py:4750 msgid "Policy Instance List" msgstr "" -#: awx/api/serializers.py:4690 +#: awx/api/serializers.py:4751 msgid "List of exact-match Instances that will be assigned to this group" msgstr "" -#: awx/api/serializers.py:4716 +#: awx/api/serializers.py:4777 msgid "Duplicate entry {}." msgstr "" -#: awx/api/serializers.py:4718 +#: awx/api/serializers.py:4779 msgid "{} is not a valid hostname of an existing instance." msgstr "" -#: awx/api/serializers.py:4720 awx/api/views/mixin.py:98 +#: awx/api/serializers.py:4781 awx/api/views/mixin.py:98 msgid "" "Isolated instances may not be added or removed from instances groups via the " "API." msgstr "" -#: awx/api/serializers.py:4722 awx/api/views/mixin.py:102 +#: awx/api/serializers.py:4783 awx/api/views/mixin.py:102 msgid "Isolated instance group membership may not be managed via the API." msgstr "" -#: awx/api/serializers.py:4724 awx/api/serializers.py:4729 -#: awx/api/serializers.py:4734 +#: awx/api/serializers.py:4785 awx/api/serializers.py:4790 +#: awx/api/serializers.py:4795 msgid "Containerized instances may not be managed via the API" msgstr "" -#: awx/api/serializers.py:4739 +#: awx/api/serializers.py:4800 msgid "tower instance group name may not be changed." msgstr "" -#: awx/api/serializers.py:4744 +#: awx/api/serializers.py:4805 msgid "Only Kubernetes credentials can be associated with an Instance Group" msgstr "" -#: awx/api/serializers.py:4783 +#: awx/api/serializers.py:4844 msgid "" "When present, shows the field name of the role or relationship that changed." msgstr "" -#: awx/api/serializers.py:4785 +#: awx/api/serializers.py:4846 msgid "" "When present, shows the model on which the role or relationship was defined." msgstr "" -#: awx/api/serializers.py:4818 +#: awx/api/serializers.py:4879 msgid "" "A summary of the new and changed values when an object is created, updated, " "or deleted" msgstr "" -#: awx/api/serializers.py:4820 +#: awx/api/serializers.py:4881 msgid "" "For create, update, and delete events this is the object type that was " "affected. For associate and disassociate events this is the object type " "associated or disassociated with object2." msgstr "" -#: awx/api/serializers.py:4823 +#: awx/api/serializers.py:4884 msgid "" "Unpopulated for create, update, and delete events. For associate and " "disassociate events this is the object type that object1 is being associated " "with." msgstr "" -#: awx/api/serializers.py:4826 +#: awx/api/serializers.py:4887 msgid "The action taken with respect to the given object(s)." msgstr "" -#: awx/api/views/__init__.py:181 +#: awx/api/views/__init__.py:185 +msgid "Not found." +msgstr "" + +#: awx/api/views/__init__.py:193 msgid "Dashboard" msgstr "" -#: awx/api/views/__init__.py:271 +#: awx/api/views/__init__.py:290 msgid "Dashboard Jobs Graphs" msgstr "" -#: awx/api/views/__init__.py:307 +#: awx/api/views/__init__.py:326 #, python-format msgid "Unknown period \"%s\"" msgstr "" -#: awx/api/views/__init__.py:321 +#: awx/api/views/__init__.py:340 msgid "Instances" msgstr "" -#: awx/api/views/__init__.py:329 +#: awx/api/views/__init__.py:348 msgid "Instance Detail" msgstr "" -#: awx/api/views/__init__.py:346 +#: awx/api/views/__init__.py:365 msgid "Instance Jobs" msgstr "" -#: awx/api/views/__init__.py:360 +#: awx/api/views/__init__.py:379 msgid "Instance's Instance Groups" msgstr "" -#: awx/api/views/__init__.py:369 +#: awx/api/views/__init__.py:388 msgid "Instance Groups" msgstr "" -#: awx/api/views/__init__.py:377 +#: awx/api/views/__init__.py:396 msgid "Instance Group Detail" msgstr "" -#: awx/api/views/__init__.py:392 +#: awx/api/views/__init__.py:411 msgid "Isolated Groups can not be removed from the API" msgstr "" -#: awx/api/views/__init__.py:394 +#: awx/api/views/__init__.py:413 msgid "" "Instance Groups acting as a controller for an Isolated Group can not be " "removed from the API" msgstr "" -#: awx/api/views/__init__.py:400 +#: awx/api/views/__init__.py:419 msgid "Instance Group Running Jobs" msgstr "" -#: awx/api/views/__init__.py:409 +#: awx/api/views/__init__.py:428 msgid "Instance Group's Instances" msgstr "" -#: awx/api/views/__init__.py:419 +#: awx/api/views/__init__.py:438 msgid "Schedules" msgstr "" -#: awx/api/views/__init__.py:433 +#: awx/api/views/__init__.py:452 msgid "Schedule Recurrence Rule Preview" msgstr "" -#: awx/api/views/__init__.py:480 +#: awx/api/views/__init__.py:499 msgid "Cannot assign credential when related template is null." msgstr "" -#: awx/api/views/__init__.py:485 +#: awx/api/views/__init__.py:504 msgid "Related template cannot accept {} on launch." msgstr "" -#: awx/api/views/__init__.py:487 +#: awx/api/views/__init__.py:506 msgid "" "Credential that requires user input on launch cannot be used in saved launch " "configuration." msgstr "" -#: awx/api/views/__init__.py:493 +#: awx/api/views/__init__.py:512 msgid "Related template is not configured to accept credentials on launch." msgstr "" -#: awx/api/views/__init__.py:495 +#: awx/api/views/__init__.py:514 #, python-brace-format msgid "" "This launch configuration already provides a {credential_type} credential." msgstr "" -#: awx/api/views/__init__.py:498 +#: awx/api/views/__init__.py:517 #, python-brace-format msgid "Related template already uses {credential_type} credential." msgstr "" -#: awx/api/views/__init__.py:516 +#: awx/api/views/__init__.py:535 msgid "Schedule Jobs List" msgstr "" -#: awx/api/views/__init__.py:600 awx/api/views/__init__.py:4378 +#: awx/api/views/__init__.py:619 awx/api/views/__init__.py:4420 msgid "" "You cannot assign an Organization participation role as a child role for a " "Team." msgstr "" -#: awx/api/views/__init__.py:604 awx/api/views/__init__.py:4392 +#: awx/api/views/__init__.py:623 awx/api/views/__init__.py:4434 msgid "You cannot grant system-level permissions to a team." msgstr "" -#: awx/api/views/__init__.py:611 awx/api/views/__init__.py:4384 +#: awx/api/views/__init__.py:630 awx/api/views/__init__.py:4426 msgid "" "You cannot grant credential access to a team when the Organization field " "isn't set, or belongs to a different organization" msgstr "" -#: awx/api/views/__init__.py:713 +#: awx/api/views/__init__.py:732 msgid "Project Schedules" msgstr "" -#: awx/api/views/__init__.py:724 +#: awx/api/views/__init__.py:743 msgid "Project SCM Inventory Sources" msgstr "" -#: awx/api/views/__init__.py:825 +#: awx/api/views/__init__.py:844 msgid "Project Update Events List" msgstr "" -#: awx/api/views/__init__.py:839 +#: awx/api/views/__init__.py:858 msgid "System Job Events List" msgstr "" -#: awx/api/views/__init__.py:873 +#: awx/api/views/__init__.py:892 msgid "Project Update SCM Inventory Updates" msgstr "" -#: awx/api/views/__init__.py:918 +#: awx/api/views/__init__.py:937 msgid "Me" msgstr "" -#: awx/api/views/__init__.py:927 +#: awx/api/views/__init__.py:946 msgid "OAuth 2 Applications" msgstr "" -#: awx/api/views/__init__.py:936 +#: awx/api/views/__init__.py:955 msgid "OAuth 2 Application Detail" msgstr "" -#: awx/api/views/__init__.py:949 +#: awx/api/views/__init__.py:968 msgid "OAuth 2 Application Tokens" msgstr "" -#: awx/api/views/__init__.py:971 +#: awx/api/views/__init__.py:990 msgid "OAuth2 Tokens" msgstr "" -#: awx/api/views/__init__.py:980 +#: awx/api/views/__init__.py:999 msgid "OAuth2 User Tokens" msgstr "" -#: awx/api/views/__init__.py:992 +#: awx/api/views/__init__.py:1011 msgid "OAuth2 User Authorized Access Tokens" msgstr "" -#: awx/api/views/__init__.py:1007 +#: awx/api/views/__init__.py:1026 msgid "Organization OAuth2 Applications" msgstr "" -#: awx/api/views/__init__.py:1019 +#: awx/api/views/__init__.py:1038 msgid "OAuth2 Personal Access Tokens" msgstr "" -#: awx/api/views/__init__.py:1034 +#: awx/api/views/__init__.py:1053 msgid "OAuth Token Detail" msgstr "" -#: awx/api/views/__init__.py:1096 awx/api/views/__init__.py:4345 +#: awx/api/views/__init__.py:1115 awx/api/views/__init__.py:4387 msgid "" "You cannot grant credential access to a user not in the credentials' " "organization" msgstr "" -#: awx/api/views/__init__.py:1100 awx/api/views/__init__.py:4349 +#: awx/api/views/__init__.py:1119 awx/api/views/__init__.py:4391 msgid "You cannot grant private credential access to another user" msgstr "" -#: awx/api/views/__init__.py:1198 +#: awx/api/views/__init__.py:1217 #, python-format msgid "Cannot change %s." msgstr "" -#: awx/api/views/__init__.py:1204 +#: awx/api/views/__init__.py:1223 msgid "Cannot delete user." msgstr "" -#: awx/api/views/__init__.py:1228 +#: awx/api/views/__init__.py:1247 msgid "Deletion not allowed for managed credential types" msgstr "" -#: awx/api/views/__init__.py:1230 +#: awx/api/views/__init__.py:1249 msgid "Credential types that are in use cannot be deleted" msgstr "" -#: awx/api/views/__init__.py:1381 +#: awx/api/views/__init__.py:1362 +msgid "Deletion not allowed for managed credentials" +msgstr "" + +#: awx/api/views/__init__.py:1407 msgid "External Credential Test" msgstr "" -#: awx/api/views/__init__.py:1408 +#: awx/api/views/__init__.py:1442 msgid "Credential Input Source Detail" msgstr "" -#: awx/api/views/__init__.py:1416 awx/api/views/__init__.py:1424 +#: awx/api/views/__init__.py:1450 awx/api/views/__init__.py:1458 msgid "Credential Input Sources" msgstr "" -#: awx/api/views/__init__.py:1439 +#: awx/api/views/__init__.py:1473 msgid "External Credential Type Test" msgstr "" -#: awx/api/views/__init__.py:1497 +#: awx/api/views/__init__.py:1539 msgid "The inventory for this host is already being deleted." msgstr "" -#: awx/api/views/__init__.py:1614 +#: awx/api/views/__init__.py:1656 msgid "SSLError while trying to connect to {}" msgstr "" -#: awx/api/views/__init__.py:1616 +#: awx/api/views/__init__.py:1658 msgid "Request to {} timed out." msgstr "" -#: awx/api/views/__init__.py:1618 +#: awx/api/views/__init__.py:1660 msgid "Unknown exception {} while trying to GET {}" msgstr "" -#: awx/api/views/__init__.py:1622 +#: awx/api/views/__init__.py:1664 msgid "" "Unauthorized access. Please check your Insights Credential username and " "password." msgstr "" -#: awx/api/views/__init__.py:1626 +#: awx/api/views/__init__.py:1668 msgid "" "Failed to access the Insights API at URL {}. Server responded with {} status " "code and message {}" msgstr "" -#: awx/api/views/__init__.py:1635 +#: awx/api/views/__init__.py:1677 msgid "Expected JSON response from Insights at URL {} but instead got {}" msgstr "" -#: awx/api/views/__init__.py:1653 +#: awx/api/views/__init__.py:1695 msgid "Could not translate Insights system ID {} into an Insights platform ID." msgstr "" -#: awx/api/views/__init__.py:1695 +#: awx/api/views/__init__.py:1737 msgid "This host is not recognized as an Insights host." msgstr "" -#: awx/api/views/__init__.py:1703 +#: awx/api/views/__init__.py:1745 msgid "The Insights Credential for \"{}\" was not found." msgstr "" -#: awx/api/views/__init__.py:1782 +#: awx/api/views/__init__.py:1824 msgid "Cyclical Group association." msgstr "" -#: awx/api/views/__init__.py:1948 +#: awx/api/views/__init__.py:1990 msgid "Inventory subset argument must be a string." msgstr "" -#: awx/api/views/__init__.py:1952 +#: awx/api/views/__init__.py:1994 msgid "Subset does not use any supported syntax." msgstr "" -#: awx/api/views/__init__.py:2002 +#: awx/api/views/__init__.py:2044 msgid "Inventory Source List" msgstr "" -#: awx/api/views/__init__.py:2014 +#: awx/api/views/__init__.py:2056 msgid "Inventory Sources Update" msgstr "" -#: awx/api/views/__init__.py:2047 +#: awx/api/views/__init__.py:2089 msgid "Could not start because `can_update` returned False" msgstr "" -#: awx/api/views/__init__.py:2055 +#: awx/api/views/__init__.py:2097 msgid "No inventory sources to update." msgstr "" -#: awx/api/views/__init__.py:2077 +#: awx/api/views/__init__.py:2119 msgid "Inventory Source Schedules" msgstr "" -#: awx/api/views/__init__.py:2104 +#: awx/api/views/__init__.py:2146 msgid "Notification Templates can only be assigned when source is one of {}." msgstr "" -#: awx/api/views/__init__.py:2202 +#: awx/api/views/__init__.py:2244 msgid "Source already has credential assigned." msgstr "" -#: awx/api/views/__init__.py:2418 +#: awx/api/views/__init__.py:2460 msgid "Job Template Schedules" msgstr "" -#: awx/api/views/__init__.py:2467 +#: awx/api/views/__init__.py:2509 msgid "Field '{}' is missing from survey spec." msgstr "" -#: awx/api/views/__init__.py:2469 +#: awx/api/views/__init__.py:2511 msgid "Expected {} for field '{}', received {} type." msgstr "" -#: awx/api/views/__init__.py:2473 +#: awx/api/views/__init__.py:2515 msgid "'spec' doesn't contain any items." msgstr "" -#: awx/api/views/__init__.py:2487 +#: awx/api/views/__init__.py:2529 #, python-format msgid "Survey question %s is not a json object." msgstr "" -#: awx/api/views/__init__.py:2490 +#: awx/api/views/__init__.py:2532 #, python-brace-format msgid "'{field_name}' missing from survey question {idx}" msgstr "" -#: awx/api/views/__init__.py:2500 +#: awx/api/views/__init__.py:2542 #, python-brace-format msgid "'{field_name}' in survey question {idx} expected to be {type_label}." msgstr "" -#: awx/api/views/__init__.py:2504 +#: awx/api/views/__init__.py:2546 #, python-format msgid "'variable' '%(item)s' duplicated in survey question %(survey)s." msgstr "" -#: awx/api/views/__init__.py:2514 +#: awx/api/views/__init__.py:2556 #, python-brace-format msgid "" "'{survey_item[type]}' in survey question {idx} is not one of " "'{allowed_types}' allowed question types." msgstr "" -#: awx/api/views/__init__.py:2524 +#: awx/api/views/__init__.py:2566 #, python-brace-format msgid "" "Default value {survey_item[default]} in survey question {idx} expected to be " "{type_label}." msgstr "" -#: awx/api/views/__init__.py:2534 +#: awx/api/views/__init__.py:2576 #, python-brace-format msgid "The {min_or_max} limit in survey question {idx} expected to be integer." msgstr "" -#: awx/api/views/__init__.py:2544 +#: awx/api/views/__init__.py:2586 #, python-brace-format msgid "Survey question {idx} of type {survey_item[type]} must specify choices." msgstr "" -#: awx/api/views/__init__.py:2558 +#: awx/api/views/__init__.py:2600 msgid "Multiple Choice (Single Select) can only have one default value." msgstr "" -#: awx/api/views/__init__.py:2562 +#: awx/api/views/__init__.py:2604 msgid "Default choice must be answered from the choices listed." msgstr "" -#: awx/api/views/__init__.py:2571 +#: awx/api/views/__init__.py:2613 #, python-brace-format msgid "" "$encrypted$ is a reserved keyword for password question defaults, survey " "question {idx} is type {survey_item[type]}." msgstr "" -#: awx/api/views/__init__.py:2585 +#: awx/api/views/__init__.py:2627 #, python-brace-format msgid "" "$encrypted$ is a reserved keyword, may not be used for new default in " "position {idx}." msgstr "" -#: awx/api/views/__init__.py:2657 +#: awx/api/views/__init__.py:2699 #, python-brace-format msgid "Cannot assign multiple {credential_type} credentials." msgstr "" -#: awx/api/views/__init__.py:2661 +#: awx/api/views/__init__.py:2703 msgid "Cannot assign a Credential of kind `{}`." msgstr "" -#: awx/api/views/__init__.py:2684 +#: awx/api/views/__init__.py:2726 msgid "Maximum number of labels for {} reached." msgstr "" -#: awx/api/views/__init__.py:2807 +#: awx/api/views/__init__.py:2849 msgid "No matching host could be found!" msgstr "" -#: awx/api/views/__init__.py:2810 +#: awx/api/views/__init__.py:2852 msgid "Multiple hosts matched the request!" msgstr "" -#: awx/api/views/__init__.py:2815 +#: awx/api/views/__init__.py:2857 msgid "Cannot start automatically, user input required!" msgstr "" -#: awx/api/views/__init__.py:2823 +#: awx/api/views/__init__.py:2865 msgid "Host callback job already pending." msgstr "" -#: awx/api/views/__init__.py:2839 awx/api/views/__init__.py:3590 +#: awx/api/views/__init__.py:2881 awx/api/views/__init__.py:3632 msgid "Error starting job!" msgstr "" -#: awx/api/views/__init__.py:2963 awx/api/views/__init__.py:2983 +#: awx/api/views/__init__.py:3005 awx/api/views/__init__.py:3025 msgid "Cycle detected." msgstr "" -#: awx/api/views/__init__.py:2975 +#: awx/api/views/__init__.py:3017 msgid "Relationship not allowed." msgstr "" -#: awx/api/views/__init__.py:3204 +#: awx/api/views/__init__.py:3246 msgid "Cannot relaunch slice workflow job orphaned from job template." msgstr "" -#: awx/api/views/__init__.py:3206 +#: awx/api/views/__init__.py:3248 msgid "Cannot relaunch sliced workflow job after slice count has changed." msgstr "" -#: awx/api/views/__init__.py:3239 +#: awx/api/views/__init__.py:3281 msgid "Workflow Job Template Schedules" msgstr "" -#: awx/api/views/__init__.py:3382 awx/api/views/__init__.py:4013 +#: awx/api/views/__init__.py:3424 awx/api/views/__init__.py:4055 msgid "Superuser privileges needed." msgstr "" -#: awx/api/views/__init__.py:3415 +#: awx/api/views/__init__.py:3457 msgid "System Job Template Schedules" msgstr "" -#: awx/api/views/__init__.py:3573 +#: awx/api/views/__init__.py:3615 #, python-brace-format msgid "Wait until job finishes before retrying on {status_value} hosts." msgstr "" -#: awx/api/views/__init__.py:3578 +#: awx/api/views/__init__.py:3620 #, python-brace-format msgid "Cannot retry on {status_value} hosts, playbook stats not available." msgstr "" -#: awx/api/views/__init__.py:3583 +#: awx/api/views/__init__.py:3625 #, python-brace-format msgid "Cannot relaunch because previous job had 0 {status_value} hosts." msgstr "" -#: awx/api/views/__init__.py:3612 +#: awx/api/views/__init__.py:3654 msgid "Cannot create schedule because job requires credential passwords." msgstr "" -#: awx/api/views/__init__.py:3617 +#: awx/api/views/__init__.py:3659 msgid "Cannot create schedule because job was launched by legacy method." msgstr "" -#: awx/api/views/__init__.py:3619 +#: awx/api/views/__init__.py:3661 msgid "Cannot create schedule because a related resource is missing." msgstr "" -#: awx/api/views/__init__.py:3674 +#: awx/api/views/__init__.py:3716 msgid "Job Host Summaries List" msgstr "" -#: awx/api/views/__init__.py:3728 +#: awx/api/views/__init__.py:3770 msgid "Job Event Children List" msgstr "" -#: awx/api/views/__init__.py:3744 +#: awx/api/views/__init__.py:3786 msgid "Job Event Hosts List" msgstr "" -#: awx/api/views/__init__.py:3759 +#: awx/api/views/__init__.py:3801 msgid "Job Events List" msgstr "" -#: awx/api/views/__init__.py:3970 +#: awx/api/views/__init__.py:4012 msgid "Ad Hoc Command Events List" msgstr "" -#: awx/api/views/__init__.py:4215 +#: awx/api/views/__init__.py:4257 msgid "Delete not allowed while there are pending notifications" msgstr "" -#: awx/api/views/__init__.py:4223 +#: awx/api/views/__init__.py:4265 msgid "Notification Template Test" msgstr "" -#: awx/api/views/__init__.py:4483 awx/api/views/__init__.py:4498 +#: awx/api/views/__init__.py:4525 awx/api/views/__init__.py:4540 msgid "User does not have permission to approve or deny this workflow." msgstr "" -#: awx/api/views/__init__.py:4485 awx/api/views/__init__.py:4500 +#: awx/api/views/__init__.py:4527 awx/api/views/__init__.py:4542 msgid "This workflow step has already been approved or denied." msgstr "" @@ -1380,7 +1421,11 @@ msgstr "" msgid "Cannot delete inventory script." msgstr "" -#: awx/api/views/inventory.py:149 +#: awx/api/views/inventory.py:137 +msgid "You cannot turn a regular inventory into a \"smart\" inventory." +msgstr "" + +#: awx/api/views/inventory.py:150 #, python-brace-format msgid "{0}" msgstr "" @@ -1405,71 +1450,76 @@ msgstr "" msgid "Related job {} is still processing events." msgstr "" -#: awx/api/views/root.py:49 awx/templates/rest_framework/api.html:28 +#: awx/api/views/organization.py:230 +#, python-brace-format +msgid "Credential must be a Galaxy credential, not {sub.credential_type.name}." +msgstr "" + +#: awx/api/views/root.py:50 awx/templates/rest_framework/api.html:28 msgid "REST API" msgstr "" -#: awx/api/views/root.py:59 awx/templates/rest_framework/api.html:4 +#: awx/api/views/root.py:60 awx/templates/rest_framework/api.html:4 msgid "AWX REST API" msgstr "" -#: awx/api/views/root.py:72 +#: awx/api/views/root.py:73 msgid "API OAuth 2 Authorization Root" msgstr "" -#: awx/api/views/root.py:139 +#: awx/api/views/root.py:140 msgid "Version 2" msgstr "" -#: awx/api/views/root.py:148 +#: awx/api/views/root.py:149 msgid "Ping" msgstr "" -#: awx/api/views/root.py:180 awx/api/views/root.py:225 awx/conf/apps.py:10 +#: awx/api/views/root.py:181 awx/api/views/root.py:226 awx/conf/apps.py:10 msgid "Configuration" msgstr "" -#: awx/api/views/root.py:202 awx/api/views/root.py:308 +#: awx/api/views/root.py:203 awx/api/views/root.py:310 msgid "Invalid License" msgstr "" -#: awx/api/views/root.py:207 +#: awx/api/views/root.py:208 msgid "The provided credentials are invalid (HTTP 401)." msgstr "" -#: awx/api/views/root.py:209 +#: awx/api/views/root.py:210 msgid "Unable to connect to proxy server." msgstr "" -#: awx/api/views/root.py:211 +#: awx/api/views/root.py:212 msgid "Could not connect to subscription service." msgstr "" -#: awx/api/views/root.py:284 +#: awx/api/views/root.py:286 msgid "Invalid license data" msgstr "" -#: awx/api/views/root.py:286 +#: awx/api/views/root.py:288 msgid "Missing 'eula_accepted' property" msgstr "" -#: awx/api/views/root.py:290 +#: awx/api/views/root.py:292 msgid "'eula_accepted' value is invalid" msgstr "" -#: awx/api/views/root.py:293 +#: awx/api/views/root.py:295 msgid "'eula_accepted' must be True" msgstr "" -#: awx/api/views/root.py:300 +#: awx/api/views/root.py:302 msgid "Invalid JSON" msgstr "" -#: awx/api/views/root.py:319 +#: awx/api/views/root.py:321 msgid "Invalid license" msgstr "" -#: awx/api/views/root.py:327 +#: awx/api/views/root.py:329 msgid "Failed to remove license." msgstr "" @@ -1660,11 +1710,11 @@ msgstr "" msgid "Expected a list of tuples of max length 2 but got {input_type} instead." msgstr "" -#: awx/conf/registry.py:73 awx/conf/tests/unit/test_registry.py:155 +#: awx/conf/registry.py:73 awx/conf/tests/unit/test_registry.py:156 msgid "All" msgstr "" -#: awx/conf/registry.py:74 awx/conf/tests/unit/test_registry.py:156 +#: awx/conf/registry.py:74 awx/conf/tests/unit/test_registry.py:157 msgid "Changed" msgstr "" @@ -1672,59 +1722,57 @@ msgstr "" msgid "User-Defaults" msgstr "" -#: awx/conf/registry.py:143 +#: awx/conf/registry.py:145 msgid "This value has been set manually in a settings file." msgstr "" -#: awx/conf/tests/unit/test_registry.py:46 -#: awx/conf/tests/unit/test_registry.py:56 -#: awx/conf/tests/unit/test_registry.py:72 -#: awx/conf/tests/unit/test_registry.py:87 -#: awx/conf/tests/unit/test_registry.py:100 -#: awx/conf/tests/unit/test_registry.py:106 -#: awx/conf/tests/unit/test_registry.py:126 -#: awx/conf/tests/unit/test_registry.py:132 -#: awx/conf/tests/unit/test_registry.py:145 -#: awx/conf/tests/unit/test_registry.py:157 -#: awx/conf/tests/unit/test_registry.py:166 -#: awx/conf/tests/unit/test_registry.py:172 -#: awx/conf/tests/unit/test_registry.py:184 -#: awx/conf/tests/unit/test_registry.py:191 -#: awx/conf/tests/unit/test_registry.py:233 -#: awx/conf/tests/unit/test_registry.py:251 -#: awx/conf/tests/unit/test_settings.py:79 -#: awx/conf/tests/unit/test_settings.py:97 -#: awx/conf/tests/unit/test_settings.py:112 -#: awx/conf/tests/unit/test_settings.py:127 -#: awx/conf/tests/unit/test_settings.py:143 -#: awx/conf/tests/unit/test_settings.py:156 -#: awx/conf/tests/unit/test_settings.py:173 -#: awx/conf/tests/unit/test_settings.py:189 -#: awx/conf/tests/unit/test_settings.py:200 -#: awx/conf/tests/unit/test_settings.py:216 -#: awx/conf/tests/unit/test_settings.py:237 -#: awx/conf/tests/unit/test_settings.py:259 -#: awx/conf/tests/unit/test_settings.py:285 -#: awx/conf/tests/unit/test_settings.py:299 -#: awx/conf/tests/unit/test_settings.py:323 +#: awx/conf/tests/unit/test_registry.py:47 +#: awx/conf/tests/unit/test_registry.py:57 +#: awx/conf/tests/unit/test_registry.py:73 +#: awx/conf/tests/unit/test_registry.py:88 +#: awx/conf/tests/unit/test_registry.py:101 +#: awx/conf/tests/unit/test_registry.py:107 +#: awx/conf/tests/unit/test_registry.py:127 +#: awx/conf/tests/unit/test_registry.py:133 +#: awx/conf/tests/unit/test_registry.py:146 +#: awx/conf/tests/unit/test_registry.py:158 +#: awx/conf/tests/unit/test_registry.py:167 +#: awx/conf/tests/unit/test_registry.py:173 +#: awx/conf/tests/unit/test_registry.py:185 +#: awx/conf/tests/unit/test_registry.py:192 +#: awx/conf/tests/unit/test_registry.py:234 +#: awx/conf/tests/unit/test_registry.py:252 +#: awx/conf/tests/unit/test_settings.py:73 +#: awx/conf/tests/unit/test_settings.py:91 +#: awx/conf/tests/unit/test_settings.py:106 +#: awx/conf/tests/unit/test_settings.py:121 +#: awx/conf/tests/unit/test_settings.py:137 +#: awx/conf/tests/unit/test_settings.py:150 +#: awx/conf/tests/unit/test_settings.py:167 +#: awx/conf/tests/unit/test_settings.py:183 +#: awx/conf/tests/unit/test_settings.py:194 +#: awx/conf/tests/unit/test_settings.py:210 +#: awx/conf/tests/unit/test_settings.py:231 +#: awx/conf/tests/unit/test_settings.py:254 +#: awx/conf/tests/unit/test_settings.py:268 +#: awx/conf/tests/unit/test_settings.py:292 +#: awx/conf/tests/unit/test_settings.py:312 +#: awx/conf/tests/unit/test_settings.py:329 #: awx/conf/tests/unit/test_settings.py:343 -#: awx/conf/tests/unit/test_settings.py:360 -#: awx/conf/tests/unit/test_settings.py:374 -#: awx/conf/tests/unit/test_settings.py:398 -#: awx/conf/tests/unit/test_settings.py:411 -#: awx/conf/tests/unit/test_settings.py:430 -#: awx/conf/tests/unit/test_settings.py:466 awx/main/conf.py:24 -#: awx/main/conf.py:33 awx/main/conf.py:43 awx/main/conf.py:53 -#: awx/main/conf.py:65 awx/main/conf.py:78 awx/main/conf.py:91 -#: awx/main/conf.py:116 awx/main/conf.py:129 awx/main/conf.py:142 -#: awx/main/conf.py:154 awx/main/conf.py:162 awx/main/conf.py:173 -#: awx/main/conf.py:405 awx/main/conf.py:830 awx/main/conf.py:840 -#: awx/main/conf.py:852 +#: awx/conf/tests/unit/test_settings.py:367 +#: awx/conf/tests/unit/test_settings.py:380 +#: awx/conf/tests/unit/test_settings.py:399 +#: awx/conf/tests/unit/test_settings.py:435 awx/main/conf.py:23 +#: awx/main/conf.py:32 awx/main/conf.py:42 awx/main/conf.py:52 +#: awx/main/conf.py:64 awx/main/conf.py:77 awx/main/conf.py:90 +#: awx/main/conf.py:115 awx/main/conf.py:128 awx/main/conf.py:141 +#: awx/main/conf.py:153 awx/main/conf.py:161 awx/main/conf.py:172 +#: awx/main/conf.py:395 awx/main/conf.py:750 awx/main/conf.py:762 msgid "System" msgstr "" -#: awx/conf/tests/unit/test_registry.py:151 -#: awx/conf/tests/unit/test_registry.py:158 +#: awx/conf/tests/unit/test_registry.py:152 +#: awx/conf/tests/unit/test_registry.py:159 msgid "OtherSystem" msgstr "" @@ -1791,112 +1839,172 @@ msgstr "" msgid "Unable to change inventory on a group." msgstr "" -#: awx/main/access.py:1264 +#: awx/main/access.py:1261 msgid "Unable to change organization on a team." msgstr "" -#: awx/main/access.py:1280 +#: awx/main/access.py:1277 msgid "The {} role cannot be assigned to a team" msgstr "" -#: awx/main/access.py:1474 +#: awx/main/access.py:1471 msgid "Insufficient access to Job Template credentials." msgstr "" -#: awx/main/access.py:1639 awx/main/access.py:2063 +#: awx/main/access.py:1635 awx/main/access.py:2059 msgid "Job was launched with secret prompts provided by another user." msgstr "" -#: awx/main/access.py:1648 +#: awx/main/access.py:1644 msgid "Job has been orphaned from its job template and organization." msgstr "" -#: awx/main/access.py:1650 +#: awx/main/access.py:1646 msgid "Job was launched with prompted fields you do not have access to." msgstr "" -#: awx/main/access.py:1652 +#: awx/main/access.py:1648 msgid "" "Job was launched with unknown prompted fields. Organization admin " "permissions required." msgstr "" -#: awx/main/access.py:2053 +#: awx/main/access.py:2049 msgid "Workflow Job was launched with unknown prompts." msgstr "" -#: awx/main/access.py:2065 +#: awx/main/access.py:2061 msgid "Job was launched with prompts you lack access to." msgstr "" -#: awx/main/access.py:2067 +#: awx/main/access.py:2063 msgid "Job was launched with prompts no longer accepted." msgstr "" -#: awx/main/access.py:2079 +#: awx/main/access.py:2075 msgid "" "You do not have permission to the workflow job resources required for " "relaunch." msgstr "" +#: awx/main/analytics/collectors.py:36 +msgid "General platform configuration." +msgstr "" + +#: awx/main/analytics/collectors.py:68 +msgid "Counts of objects such as organizations, inventories, and projects" +msgstr "" + +#: awx/main/analytics/collectors.py:103 +msgid "Counts of users and teams by organization" +msgstr "" + +#: awx/main/analytics/collectors.py:115 +msgid "Counts of credentials by credential type" +msgstr "" + +#: awx/main/analytics/collectors.py:127 +msgid "Inventories, their inventory sources, and host counts" +msgstr "" + +#: awx/main/analytics/collectors.py:152 +msgid "Counts of projects by source control type" +msgstr "" + +#: awx/main/analytics/collectors.py:171 +msgid "Cluster topology and capacity" +msgstr "" + +#: awx/main/analytics/collectors.py:197 +msgid "Counts of jobs by status" +msgstr "" + +#: awx/main/analytics/collectors.py:207 +msgid "Counts of jobs by execution node" +msgstr "" + +#: awx/main/analytics/collectors.py:222 +msgid "Metadata about the analytics collected" +msgstr "" + +#: awx/main/analytics/collectors.py:285 +msgid "Automation task records" +msgstr "" + +#: awx/main/analytics/collectors.py:314 +msgid "Data on jobs run" +msgstr "" + +#: awx/main/analytics/collectors.py:351 +msgid "Data on job templates" +msgstr "" + +#: awx/main/analytics/collectors.py:374 +msgid "Data on workflow runs" +msgstr "" + +#: awx/main/analytics/collectors.py:410 +msgid "Data on workflows" +msgstr "" + #: awx/main/apps.py:8 msgid "Main" msgstr "" -#: awx/main/conf.py:22 +#: awx/main/conf.py:21 msgid "Enable Activity Stream" msgstr "" -#: awx/main/conf.py:23 +#: awx/main/conf.py:22 msgid "Enable capturing activity for the activity stream." msgstr "" -#: awx/main/conf.py:31 +#: awx/main/conf.py:30 msgid "Enable Activity Stream for Inventory Sync" msgstr "" -#: awx/main/conf.py:32 +#: awx/main/conf.py:31 msgid "" "Enable capturing activity for the activity stream when running inventory " "sync." msgstr "" -#: awx/main/conf.py:40 +#: awx/main/conf.py:39 msgid "All Users Visible to Organization Admins" msgstr "" -#: awx/main/conf.py:41 +#: awx/main/conf.py:40 msgid "" "Controls whether any Organization Admin can view all users and teams, even " "those not associated with their Organization." msgstr "" -#: awx/main/conf.py:50 +#: awx/main/conf.py:49 msgid "Organization Admins Can Manage Users and Teams" msgstr "" -#: awx/main/conf.py:51 +#: awx/main/conf.py:50 msgid "" "Controls whether any Organization Admin has the privileges to create and " "manage users and teams. You may want to disable this ability if you are " "using an LDAP or SAML integration." msgstr "" -#: awx/main/conf.py:62 +#: awx/main/conf.py:61 msgid "Base URL of the Tower host" msgstr "" -#: awx/main/conf.py:63 +#: awx/main/conf.py:62 msgid "" "This setting is used by services like notifications to render a valid url to " "the Tower host." msgstr "" -#: awx/main/conf.py:72 +#: awx/main/conf.py:71 msgid "Remote Host Headers" msgstr "" -#: awx/main/conf.py:73 +#: awx/main/conf.py:72 msgid "" "HTTP headers and meta keys to search to determine remote host name or IP. " "Add additional items to this list, such as \"HTTP_X_FORWARDED_FOR\", if " @@ -1904,114 +2012,112 @@ msgid "" "Adminstrator guide for more details." msgstr "" -#: awx/main/conf.py:85 -msgid "Proxy IP Whitelist" +#: awx/main/conf.py:84 +msgid "Proxy IP Allowed List" msgstr "" -#: awx/main/conf.py:86 +#: awx/main/conf.py:85 msgid "" "If Tower is behind a reverse proxy/load balancer, use this setting to " -"whitelist the proxy IP addresses from which Tower should trust custom " +"configure the proxy IP addresses from which Tower should trust custom " "REMOTE_HOST_HEADERS header values. If this setting is an empty list (the " "default), the headers specified by REMOTE_HOST_HEADERS will be trusted " "unconditionally')" msgstr "" -#: awx/main/conf.py:112 +#: awx/main/conf.py:111 msgid "License" msgstr "" -#: awx/main/conf.py:113 +#: awx/main/conf.py:112 msgid "" "The license controls which features and functionality are enabled. Use /api/" "v2/config/ to update or change the license." msgstr "" -#: awx/main/conf.py:127 +#: awx/main/conf.py:126 msgid "Red Hat customer username" msgstr "" -#: awx/main/conf.py:128 +#: awx/main/conf.py:127 msgid "" "This username is used to retrieve license information and to send Automation " "Analytics" msgstr "" -#: awx/main/conf.py:140 +#: awx/main/conf.py:139 msgid "Red Hat customer password" msgstr "" -#: awx/main/conf.py:141 +#: awx/main/conf.py:140 msgid "" "This password is used to retrieve license information and to send Automation " "Analytics" msgstr "" -#: awx/main/conf.py:152 -msgid "Automation Analytics upload URL." +#: awx/main/conf.py:151 +msgid "Automation Analytics upload URL" msgstr "" -#: awx/main/conf.py:153 +#: awx/main/conf.py:152 msgid "" "This setting is used to to configure data collection for the Automation " "Analytics dashboard" msgstr "" -#: awx/main/conf.py:161 +#: awx/main/conf.py:160 msgid "Unique identifier for an AWX/Tower installation" msgstr "" -#: awx/main/conf.py:170 +#: awx/main/conf.py:169 msgid "Custom virtual environment paths" msgstr "" -#: awx/main/conf.py:171 +#: awx/main/conf.py:170 msgid "" "Paths where Tower will look for custom virtual environments (in addition to /" "var/lib/awx/venv/). Enter one path per line." msgstr "" -#: awx/main/conf.py:181 +#: awx/main/conf.py:180 msgid "Ansible Modules Allowed for Ad Hoc Jobs" msgstr "" -#: awx/main/conf.py:182 +#: awx/main/conf.py:181 msgid "List of modules allowed to be used by ad-hoc jobs." msgstr "" -#: awx/main/conf.py:183 awx/main/conf.py:205 awx/main/conf.py:214 -#: awx/main/conf.py:225 awx/main/conf.py:235 awx/main/conf.py:245 -#: awx/main/conf.py:256 awx/main/conf.py:267 awx/main/conf.py:278 -#: awx/main/conf.py:290 awx/main/conf.py:299 awx/main/conf.py:312 -#: awx/main/conf.py:325 awx/main/conf.py:337 awx/main/conf.py:348 -#: awx/main/conf.py:359 awx/main/conf.py:371 awx/main/conf.py:383 -#: awx/main/conf.py:394 awx/main/conf.py:414 awx/main/conf.py:424 -#: awx/main/conf.py:434 awx/main/conf.py:450 awx/main/conf.py:463 -#: awx/main/conf.py:477 awx/main/conf.py:491 awx/main/conf.py:503 -#: awx/main/conf.py:513 awx/main/conf.py:524 awx/main/conf.py:534 -#: awx/main/conf.py:545 awx/main/conf.py:555 awx/main/conf.py:565 -#: awx/main/conf.py:577 awx/main/conf.py:589 awx/main/conf.py:601 -#: awx/main/conf.py:615 awx/main/conf.py:627 +#: awx/main/conf.py:182 awx/main/conf.py:204 awx/main/conf.py:213 +#: awx/main/conf.py:224 awx/main/conf.py:234 awx/main/conf.py:244 +#: awx/main/conf.py:254 awx/main/conf.py:266 awx/main/conf.py:279 +#: awx/main/conf.py:289 awx/main/conf.py:302 awx/main/conf.py:315 +#: awx/main/conf.py:327 awx/main/conf.py:338 awx/main/conf.py:349 +#: awx/main/conf.py:361 awx/main/conf.py:373 awx/main/conf.py:384 +#: awx/main/conf.py:404 awx/main/conf.py:414 awx/main/conf.py:424 +#: awx/main/conf.py:437 awx/main/conf.py:448 awx/main/conf.py:458 +#: awx/main/conf.py:469 awx/main/conf.py:479 awx/main/conf.py:489 +#: awx/main/conf.py:501 awx/main/conf.py:514 awx/main/conf.py:527 +#: awx/main/conf.py:542 awx/main/conf.py:555 msgid "Jobs" msgstr "" -#: awx/main/conf.py:192 +#: awx/main/conf.py:191 msgid "Always" msgstr "" -#: awx/main/conf.py:193 +#: awx/main/conf.py:192 msgid "Never" msgstr "" -#: awx/main/conf.py:194 +#: awx/main/conf.py:193 msgid "Only On Job Template Definitions" msgstr "" -#: awx/main/conf.py:197 +#: awx/main/conf.py:196 msgid "When can extra variables contain Jinja templates?" msgstr "" -#: awx/main/conf.py:199 +#: awx/main/conf.py:198 msgid "" "Ansible allows variable substitution via the Jinja2 templating language for " "--extra-vars. This poses a potential security risk where Tower users with " @@ -2020,358 +2126,294 @@ msgid "" "to \"template\" or \"never\"." msgstr "" -#: awx/main/conf.py:212 +#: awx/main/conf.py:211 msgid "Enable job isolation" msgstr "" -#: awx/main/conf.py:213 +#: awx/main/conf.py:212 msgid "" "Isolates an Ansible job from protected parts of the system to prevent " "exposing sensitive information." msgstr "" -#: awx/main/conf.py:221 +#: awx/main/conf.py:220 msgid "Job execution path" msgstr "" -#: awx/main/conf.py:222 +#: awx/main/conf.py:221 msgid "" "The directory in which Tower will create new temporary directories for job " "execution and isolation (such as credential files and custom inventory " "scripts)." msgstr "" -#: awx/main/conf.py:233 +#: awx/main/conf.py:232 msgid "Paths to hide from isolated jobs" msgstr "" -#: awx/main/conf.py:234 +#: awx/main/conf.py:233 msgid "" "Additional paths to hide from isolated processes. Enter one path per line." msgstr "" -#: awx/main/conf.py:243 +#: awx/main/conf.py:242 msgid "Paths to expose to isolated jobs" msgstr "" -#: awx/main/conf.py:244 +#: awx/main/conf.py:243 msgid "" -"Whitelist of paths that would otherwise be hidden to expose to isolated " -"jobs. Enter one path per line." +"List of paths that would otherwise be hidden to expose to isolated jobs. " +"Enter one path per line." msgstr "" -#: awx/main/conf.py:254 -msgid "Verbosity level for isolated node management tasks" -msgstr "" - -#: awx/main/conf.py:255 -msgid "" -"This can be raised to aid in debugging connection issues for isolated task " -"execution" -msgstr "" - -#: awx/main/conf.py:265 +#: awx/main/conf.py:252 msgid "Isolated status check interval" msgstr "" -#: awx/main/conf.py:266 +#: awx/main/conf.py:253 msgid "" "The number of seconds to sleep between status checks for jobs running on " "isolated instances." msgstr "" -#: awx/main/conf.py:275 +#: awx/main/conf.py:263 msgid "Isolated launch timeout" msgstr "" -#: awx/main/conf.py:276 +#: awx/main/conf.py:264 msgid "" "The timeout (in seconds) for launching jobs on isolated instances. This " "includes the time needed to copy source control files (playbooks) to the " "isolated instance." msgstr "" -#: awx/main/conf.py:287 +#: awx/main/conf.py:276 msgid "Isolated connection timeout" msgstr "" -#: awx/main/conf.py:288 +#: awx/main/conf.py:277 msgid "" "Ansible SSH connection timeout (in seconds) to use when communicating with " "isolated instances. Value should be substantially greater than expected " "network latency." msgstr "" -#: awx/main/conf.py:297 +#: awx/main/conf.py:287 msgid "Isolated host key checking" msgstr "" -#: awx/main/conf.py:298 +#: awx/main/conf.py:288 msgid "" "When set to True, AWX will enforce strict host key checking for " "communication with isolated nodes." msgstr "" -#: awx/main/conf.py:308 +#: awx/main/conf.py:298 msgid "Generate RSA keys for isolated instances" msgstr "" -#: awx/main/conf.py:309 +#: awx/main/conf.py:299 msgid "" "If set, a random RSA key will be generated and distributed to isolated " "instances. To disable this behavior and manage authentication for isolated " "instances outside of Tower, disable this setting." msgstr "" -#: awx/main/conf.py:323 awx/main/conf.py:324 +#: awx/main/conf.py:313 awx/main/conf.py:314 msgid "The RSA private key for SSH traffic to isolated instances" msgstr "" -#: awx/main/conf.py:335 awx/main/conf.py:336 +#: awx/main/conf.py:325 awx/main/conf.py:326 msgid "The RSA public key for SSH traffic to isolated instances" msgstr "" -#: awx/main/conf.py:345 +#: awx/main/conf.py:335 msgid "Enable detailed resource profiling on all playbook runs" msgstr "" -#: awx/main/conf.py:346 +#: awx/main/conf.py:336 msgid "" "If set, detailed resource profiling data will be collected on all jobs. This " "data can be gathered with `sosreport`." msgstr "" -#: awx/main/conf.py:356 +#: awx/main/conf.py:346 msgid "Interval (in seconds) between polls for cpu usage." msgstr "" -#: awx/main/conf.py:357 +#: awx/main/conf.py:347 msgid "" "Interval (in seconds) between polls for cpu usage. Setting this lower than " "the default will affect playbook performance." msgstr "" -#: awx/main/conf.py:368 +#: awx/main/conf.py:358 msgid "Interval (in seconds) between polls for memory usage." msgstr "" -#: awx/main/conf.py:369 +#: awx/main/conf.py:359 msgid "" "Interval (in seconds) between polls for memory usage. Setting this lower " "than the default will affect playbook performance." msgstr "" -#: awx/main/conf.py:380 +#: awx/main/conf.py:370 msgid "Interval (in seconds) between polls for PID count." msgstr "" -#: awx/main/conf.py:381 +#: awx/main/conf.py:371 msgid "" "Interval (in seconds) between polls for PID count. Setting this lower than " "the default will affect playbook performance." msgstr "" -#: awx/main/conf.py:392 +#: awx/main/conf.py:382 msgid "Extra Environment Variables" msgstr "" -#: awx/main/conf.py:393 +#: awx/main/conf.py:383 msgid "" "Additional environment variables set for playbook runs, inventory updates, " "project updates, and notification sending." msgstr "" -#: awx/main/conf.py:403 +#: awx/main/conf.py:393 msgid "Gather data for Automation Analytics" msgstr "" -#: awx/main/conf.py:404 +#: awx/main/conf.py:394 msgid "Enables Tower to gather data on automation and send it to Red Hat." msgstr "" -#: awx/main/conf.py:412 +#: awx/main/conf.py:402 msgid "Run Project Updates With Higher Verbosity" msgstr "" -#: awx/main/conf.py:413 +#: awx/main/conf.py:403 msgid "" "Adds the CLI -vvv flag to ansible-playbook runs of project_update.yml used " "for project updates." msgstr "" -#: awx/main/conf.py:422 +#: awx/main/conf.py:412 msgid "Enable Role Download" msgstr "" -#: awx/main/conf.py:423 +#: awx/main/conf.py:413 msgid "" "Allows roles to be dynamically downloaded from a requirements.yml file for " "SCM projects." msgstr "" -#: awx/main/conf.py:432 +#: awx/main/conf.py:422 msgid "Enable Collection(s) Download" msgstr "" -#: awx/main/conf.py:433 +#: awx/main/conf.py:423 msgid "" "Allows collections to be dynamically downloaded from a requirements.yml file " "for SCM projects." msgstr "" -#: awx/main/conf.py:443 -msgid "Primary Galaxy Server URL" +#: awx/main/conf.py:432 +msgid "Follow symlinks" +msgstr "" + +#: awx/main/conf.py:434 +msgid "" +"Follow symbolic links when scanning for playbooks. Be aware that setting " +"this to True can lead to infinite recursion if a link points to a parent " +"directory of itself." msgstr "" #: awx/main/conf.py:445 -msgid "" -"For organizations that run their own Galaxy service, this gives the option " -"to specify a host as the primary galaxy server. Requirements will be " -"downloaded from the primary if the specific role or collection is available " -"there. If the content is not avilable in the primary, or if this field is " -"left blank, it will default to galaxy.ansible.com." -msgstr "" - -#: awx/main/conf.py:459 -msgid "Primary Galaxy Server Username" -msgstr "" - -#: awx/main/conf.py:460 -msgid "" -"For using a galaxy server at higher precedence than the public Ansible " -"Galaxy. The username to use for basic authentication against the Galaxy " -"instance, this is mutually exclusive with PRIMARY_GALAXY_TOKEN." -msgstr "" - -#: awx/main/conf.py:473 -msgid "Primary Galaxy Server Password" -msgstr "" - -#: awx/main/conf.py:474 -msgid "" -"For using a galaxy server at higher precedence than the public Ansible " -"Galaxy. The password to use for basic authentication against the Galaxy " -"instance, this is mutually exclusive with PRIMARY_GALAXY_TOKEN." -msgstr "" - -#: awx/main/conf.py:487 -msgid "Primary Galaxy Server Token" -msgstr "" - -#: awx/main/conf.py:488 -msgid "" -"For using a galaxy server at higher precedence than the public Ansible " -"Galaxy. The token to use for connecting with the Galaxy instance, this is " -"mutually exclusive with corresponding username and password settings." -msgstr "" - -#: awx/main/conf.py:500 -msgid "Primary Galaxy Authentication URL" -msgstr "" - -#: awx/main/conf.py:501 -msgid "" -"For using a galaxy server at higher precedence than the public Ansible " -"Galaxy. The token_endpoint of a Keycloak server." -msgstr "" - -#: awx/main/conf.py:511 -msgid "Allow Access to Public Galaxy" -msgstr "" - -#: awx/main/conf.py:512 -msgid "" -"Allow or deny access to the public Ansible Galaxy during project updates." -msgstr "" - -#: awx/main/conf.py:521 msgid "Ignore Ansible Galaxy SSL Certificate Verification" msgstr "" -#: awx/main/conf.py:522 +#: awx/main/conf.py:446 msgid "" -"If set to true, certificate validation will not be done wheninstalling " +"If set to true, certificate validation will not be done when installing " "content from any Galaxy server." msgstr "" -#: awx/main/conf.py:532 +#: awx/main/conf.py:456 msgid "Standard Output Maximum Display Size" msgstr "" -#: awx/main/conf.py:533 +#: awx/main/conf.py:457 msgid "" "Maximum Size of Standard Output in bytes to display before requiring the " "output be downloaded." msgstr "" -#: awx/main/conf.py:542 +#: awx/main/conf.py:466 msgid "Job Event Standard Output Maximum Display Size" msgstr "" -#: awx/main/conf.py:544 +#: awx/main/conf.py:468 msgid "" "Maximum Size of Standard Output in bytes to display for a single job or ad " "hoc command event. `stdout` will end with `…` when truncated." msgstr "" -#: awx/main/conf.py:553 +#: awx/main/conf.py:477 msgid "Maximum Scheduled Jobs" msgstr "" -#: awx/main/conf.py:554 +#: awx/main/conf.py:478 msgid "" "Maximum number of the same job template that can be waiting to run when " "launching from a schedule before no more are created." msgstr "" -#: awx/main/conf.py:563 +#: awx/main/conf.py:487 msgid "Ansible Callback Plugins" msgstr "" -#: awx/main/conf.py:564 +#: awx/main/conf.py:488 msgid "" "List of paths to search for extra callback plugins to be used when running " "jobs. Enter one path per line." msgstr "" -#: awx/main/conf.py:574 +#: awx/main/conf.py:498 msgid "Default Job Timeout" msgstr "" -#: awx/main/conf.py:575 +#: awx/main/conf.py:499 msgid "" "Maximum time in seconds to allow jobs to run. Use value of 0 to indicate " "that no timeout should be imposed. A timeout set on an individual job " "template will override this." msgstr "" -#: awx/main/conf.py:586 +#: awx/main/conf.py:511 msgid "Default Inventory Update Timeout" msgstr "" -#: awx/main/conf.py:587 +#: awx/main/conf.py:512 msgid "" "Maximum time in seconds to allow inventory updates to run. Use value of 0 to " "indicate that no timeout should be imposed. A timeout set on an individual " "inventory source will override this." msgstr "" -#: awx/main/conf.py:598 +#: awx/main/conf.py:524 msgid "Default Project Update Timeout" msgstr "" -#: awx/main/conf.py:599 +#: awx/main/conf.py:525 msgid "" "Maximum time in seconds to allow project updates to run. Use value of 0 to " "indicate that no timeout should be imposed. A timeout set on an individual " "project will override this." msgstr "" -#: awx/main/conf.py:610 +#: awx/main/conf.py:537 msgid "Per-Host Ansible Fact Cache Timeout" msgstr "" -#: awx/main/conf.py:611 +#: awx/main/conf.py:538 msgid "" "Maximum time, in seconds, that stored Ansible facts are considered valid " "since the last time they were modified. Only valid, non-stale, facts will be " @@ -2380,74 +2422,74 @@ msgid "" "timeout should be imposed." msgstr "" -#: awx/main/conf.py:624 -msgid "Maximum number of forks per job." +#: awx/main/conf.py:552 +msgid "Maximum number of forks per job" msgstr "" -#: awx/main/conf.py:625 +#: awx/main/conf.py:553 msgid "" "Saving a Job Template with more than this number of forks will result in an " "error. When set to 0, no limit is applied." msgstr "" -#: awx/main/conf.py:636 +#: awx/main/conf.py:564 msgid "Logging Aggregator" msgstr "" -#: awx/main/conf.py:637 +#: awx/main/conf.py:565 msgid "Hostname/IP where external logs will be sent to." msgstr "" -#: awx/main/conf.py:638 awx/main/conf.py:649 awx/main/conf.py:661 -#: awx/main/conf.py:671 awx/main/conf.py:683 awx/main/conf.py:698 -#: awx/main/conf.py:710 awx/main/conf.py:719 awx/main/conf.py:729 -#: awx/main/conf.py:741 awx/main/conf.py:752 awx/main/conf.py:764 -#: awx/main/conf.py:777 awx/main/conf.py:787 awx/main/conf.py:799 -#: awx/main/conf.py:810 awx/main/conf.py:820 +#: awx/main/conf.py:566 awx/main/conf.py:577 awx/main/conf.py:589 +#: awx/main/conf.py:599 awx/main/conf.py:611 awx/main/conf.py:626 +#: awx/main/conf.py:638 awx/main/conf.py:647 awx/main/conf.py:657 +#: awx/main/conf.py:669 awx/main/conf.py:680 awx/main/conf.py:693 +#: awx/main/conf.py:706 awx/main/conf.py:718 awx/main/conf.py:729 +#: awx/main/conf.py:739 msgid "Logging" msgstr "" -#: awx/main/conf.py:646 +#: awx/main/conf.py:574 msgid "Logging Aggregator Port" msgstr "" -#: awx/main/conf.py:647 +#: awx/main/conf.py:575 msgid "" "Port on Logging Aggregator to send logs to (if required and not provided in " "Logging Aggregator)." msgstr "" -#: awx/main/conf.py:659 +#: awx/main/conf.py:587 msgid "Logging Aggregator Type" msgstr "" -#: awx/main/conf.py:660 +#: awx/main/conf.py:588 msgid "Format messages for the chosen log aggregator." msgstr "" -#: awx/main/conf.py:669 +#: awx/main/conf.py:597 msgid "Logging Aggregator Username" msgstr "" -#: awx/main/conf.py:670 +#: awx/main/conf.py:598 msgid "Username for external log aggregator (if required; HTTP/s only)." msgstr "" -#: awx/main/conf.py:681 +#: awx/main/conf.py:609 msgid "Logging Aggregator Password/Token" msgstr "" -#: awx/main/conf.py:682 +#: awx/main/conf.py:610 msgid "" "Password or authentication token for external log aggregator (if required; " "HTTP/s only)." msgstr "" -#: awx/main/conf.py:691 +#: awx/main/conf.py:619 msgid "Loggers Sending Data to Log Aggregator Form" msgstr "" -#: awx/main/conf.py:692 +#: awx/main/conf.py:620 msgid "" "List of loggers that will send HTTP logs to the collector, these can include " "any or all of: \n" @@ -2457,11 +2499,11 @@ msgid "" "system_tracking - facts gathered from scan jobs." msgstr "" -#: awx/main/conf.py:705 +#: awx/main/conf.py:633 msgid "Log System Tracking Facts Individually" msgstr "" -#: awx/main/conf.py:706 +#: awx/main/conf.py:634 msgid "" "If set, system tracking facts will be sent for each package, service, or " "other item found in a scan, allowing for greater search query granularity. " @@ -2469,47 +2511,47 @@ msgid "" "efficiency in fact processing." msgstr "" -#: awx/main/conf.py:717 +#: awx/main/conf.py:645 msgid "Enable External Logging" msgstr "" -#: awx/main/conf.py:718 +#: awx/main/conf.py:646 msgid "Enable sending logs to external log aggregator." msgstr "" -#: awx/main/conf.py:727 +#: awx/main/conf.py:655 msgid "Cluster-wide Tower unique identifier." msgstr "" -#: awx/main/conf.py:728 +#: awx/main/conf.py:656 msgid "Useful to uniquely identify Tower instances." msgstr "" -#: awx/main/conf.py:737 +#: awx/main/conf.py:665 msgid "Logging Aggregator Protocol" msgstr "" -#: awx/main/conf.py:738 +#: awx/main/conf.py:666 msgid "" "Protocol used to communicate with log aggregator. HTTPS/HTTP assumes HTTPS " "unless http:// is explicitly used in the Logging Aggregator hostname." msgstr "" -#: awx/main/conf.py:748 +#: awx/main/conf.py:676 msgid "TCP Connection Timeout" msgstr "" -#: awx/main/conf.py:749 +#: awx/main/conf.py:677 msgid "" "Number of seconds for a TCP connection to external log aggregator to " "timeout. Applies to HTTPS and TCP log aggregator protocols." msgstr "" -#: awx/main/conf.py:759 +#: awx/main/conf.py:688 msgid "Enable/disable HTTPS certificate verification" msgstr "" -#: awx/main/conf.py:760 +#: awx/main/conf.py:689 msgid "" "Flag to control enable/disable of certificate verification when " "LOG_AGGREGATOR_PROTOCOL is \"https\". If enabled, Tower's log handler will " @@ -2517,11 +2559,11 @@ msgid "" "connection." msgstr "" -#: awx/main/conf.py:772 +#: awx/main/conf.py:701 msgid "Logging Aggregator Level Threshold" msgstr "" -#: awx/main/conf.py:773 +#: awx/main/conf.py:702 msgid "" "Level threshold used by log handler. Severities from lowest to highest are " "DEBUG, INFO, WARNING, ERROR, CRITICAL. Messages less severe than the " @@ -2529,198 +2571,144 @@ msgid "" "anlytics ignore this setting)" msgstr "" -#: awx/main/conf.py:785 -msgid "Enabled external log aggregation auditing" -msgstr "" - -#: awx/main/conf.py:786 -msgid "" -"When enabled, all external logs emitted by Tower will also be written to /" -"var/log/tower/external.log. This is an experimental setting intended to be " -"used for debugging external log aggregation issues (and may be subject to " -"change in the future)." -msgstr "" - -#: awx/main/conf.py:795 +#: awx/main/conf.py:714 msgid "Maximum disk persistance for external log aggregation (in GB)" msgstr "" -#: awx/main/conf.py:796 +#: awx/main/conf.py:715 msgid "" "Amount of data to store (in gigabytes) during an outage of the external log " "aggregator (defaults to 1). Equivalent to the rsyslogd queue.maxdiskspace " "setting." msgstr "" -#: awx/main/conf.py:806 +#: awx/main/conf.py:725 msgid "File system location for rsyslogd disk persistence" msgstr "" -#: awx/main/conf.py:807 +#: awx/main/conf.py:726 msgid "" "Location to persist logs that should be retried after an outage of the " "external log aggregator (defaults to /var/lib/awx). Equivalent to the " "rsyslogd queue.spoolDirectory setting." msgstr "" -#: awx/main/conf.py:817 +#: awx/main/conf.py:736 msgid "Enable rsyslogd debugging" msgstr "" -#: awx/main/conf.py:818 +#: awx/main/conf.py:737 msgid "" "Enabled high verbosity debugging for rsyslogd. Useful for debugging " "connection issues for external log aggregation." msgstr "" -#: awx/main/conf.py:828 -msgid "Message Durability" -msgstr "" - -#: awx/main/conf.py:829 -msgid "" -"When set (the default), underlying queues will be persisted to disk. " -"Disable this to enable higher message bus throughput." -msgstr "" - -#: awx/main/conf.py:838 +#: awx/main/conf.py:748 msgid "Last gather date for Automation Analytics." msgstr "" -#: awx/main/conf.py:848 +#: awx/main/conf.py:758 msgid "Automation Analytics Gather Interval" msgstr "" -#: awx/main/conf.py:849 +#: awx/main/conf.py:759 msgid "Interval (in seconds) between data gathering." msgstr "" -#: awx/main/conf.py:871 awx/sso/conf.py:1239 +#: awx/main/conf.py:782 awx/sso/conf.py:1251 msgid "\n" msgstr "" -#: awx/main/conf.py:892 -msgid "" -"A URL for Primary Galaxy must be defined before disabling public Galaxy." -msgstr "" - -#: awx/main/conf.py:912 -msgid "Cannot provide field if PRIMARY_GALAXY_URL is not set." -msgstr "" - -#: awx/main/conf.py:925 -#, python-brace-format -msgid "" -"Galaxy server settings are not available until Ansible {min_version}, you " -"are running {current_version}." -msgstr "" - -#: awx/main/conf.py:934 -msgid "" -"Setting Galaxy token and authentication URL is mutually exclusive with " -"username and password." -msgstr "" - -#: awx/main/conf.py:937 -msgid "If authenticating via username and password, both must be provided." -msgstr "" - -#: awx/main/conf.py:943 -msgid "" -"If authenticating via token, both token and authentication URL must be " -"provided." -msgstr "" - -#: awx/main/constants.py:17 +#: awx/main/constants.py:16 msgid "Sudo" msgstr "" -#: awx/main/constants.py:17 +#: awx/main/constants.py:16 msgid "Su" msgstr "" -#: awx/main/constants.py:17 +#: awx/main/constants.py:16 msgid "Pbrun" msgstr "" -#: awx/main/constants.py:17 +#: awx/main/constants.py:16 msgid "Pfexec" msgstr "" -#: awx/main/constants.py:18 +#: awx/main/constants.py:17 msgid "DZDO" msgstr "" -#: awx/main/constants.py:18 +#: awx/main/constants.py:17 msgid "Pmrun" msgstr "" -#: awx/main/constants.py:18 +#: awx/main/constants.py:17 msgid "Runas" msgstr "" -#: awx/main/constants.py:19 +#: awx/main/constants.py:18 msgid "Enable" msgstr "" -#: awx/main/constants.py:19 +#: awx/main/constants.py:18 msgid "Doas" msgstr "" -#: awx/main/constants.py:19 +#: awx/main/constants.py:18 msgid "Ksu" msgstr "" -#: awx/main/constants.py:20 +#: awx/main/constants.py:19 msgid "Machinectl" msgstr "" -#: awx/main/constants.py:20 +#: awx/main/constants.py:19 msgid "Sesu" msgstr "" -#: awx/main/constants.py:22 +#: awx/main/constants.py:21 msgid "None" msgstr "" -#: awx/main/credential_plugins/aim.py:16 +#: awx/main/credential_plugins/aim.py:11 msgid "CyberArk AIM URL" msgstr "" -#: awx/main/credential_plugins/aim.py:21 +#: awx/main/credential_plugins/aim.py:16 msgid "Application ID" msgstr "" -#: awx/main/credential_plugins/aim.py:26 +#: awx/main/credential_plugins/aim.py:21 msgid "Client Key" msgstr "" -#: awx/main/credential_plugins/aim.py:32 +#: awx/main/credential_plugins/aim.py:27 msgid "Client Certificate" msgstr "" -#: awx/main/credential_plugins/aim.py:38 +#: awx/main/credential_plugins/aim.py:33 msgid "Verify SSL Certificates" msgstr "" -#: awx/main/credential_plugins/aim.py:44 +#: awx/main/credential_plugins/aim.py:39 msgid "Object Query" msgstr "" -#: awx/main/credential_plugins/aim.py:46 +#: awx/main/credential_plugins/aim.py:41 msgid "" "Lookup query for the object. Ex: Safe=TestSafe;Object=testAccountName123" msgstr "" -#: awx/main/credential_plugins/aim.py:49 +#: awx/main/credential_plugins/aim.py:44 msgid "Object Query Format" msgstr "" -#: awx/main/credential_plugins/aim.py:55 +#: awx/main/credential_plugins/aim.py:50 msgid "Reason" msgstr "" -#: awx/main/credential_plugins/aim.py:57 +#: awx/main/credential_plugins/aim.py:52 msgid "" "Object request reason. This is only needed if it is required by the object's " "policy." @@ -2731,12 +2719,12 @@ msgid "Vault URL (DNS Name)" msgstr "" #: awx/main/credential_plugins/azure_kv.py:26 -#: awx/main/models/credential/__init__.py:960 +#: awx/main/models/credential/__init__.py:968 msgid "Client ID" msgstr "" #: awx/main/credential_plugins/azure_kv.py:35 -#: awx/main/models/credential/__init__.py:969 +#: awx/main/models/credential/__init__.py:977 msgid "Tenant ID" msgstr "" @@ -2757,166 +2745,177 @@ msgid "The name of the secret to look up." msgstr "" #: awx/main/credential_plugins/azure_kv.py:51 -#: awx/main/credential_plugins/conjur.py:47 +#: awx/main/credential_plugins/conjur.py:42 msgid "Secret Version" msgstr "" #: awx/main/credential_plugins/azure_kv.py:53 -#: awx/main/credential_plugins/conjur.py:49 -#: awx/main/credential_plugins/hashivault.py:86 +#: awx/main/credential_plugins/conjur.py:44 +#: awx/main/credential_plugins/hashivault.py:89 msgid "" "Used to specify a specific secret version (if left empty, the latest version " "will be used)." msgstr "" -#: awx/main/credential_plugins/conjur.py:18 +#: awx/main/credential_plugins/conjur.py:13 msgid "Conjur URL" msgstr "" -#: awx/main/credential_plugins/conjur.py:23 +#: awx/main/credential_plugins/conjur.py:18 msgid "API Key" msgstr "" -#: awx/main/credential_plugins/conjur.py:28 awx/main/models/inventory.py:1018 +#: awx/main/credential_plugins/conjur.py:23 +#: awx/main/migrations/_inventory_source_vars.py:142 msgid "Account" msgstr "" -#: awx/main/credential_plugins/conjur.py:32 -#: awx/main/models/credential/__init__.py:598 -#: awx/main/models/credential/__init__.py:654 -#: awx/main/models/credential/__init__.py:712 -#: awx/main/models/credential/__init__.py:785 -#: awx/main/models/credential/__init__.py:838 -#: awx/main/models/credential/__init__.py:864 -#: awx/main/models/credential/__init__.py:891 -#: awx/main/models/credential/__init__.py:951 -#: awx/main/models/credential/__init__.py:1024 -#: awx/main/models/credential/__init__.py:1055 -#: awx/main/models/credential/__init__.py:1105 +#: awx/main/credential_plugins/conjur.py:27 +#: awx/main/models/credential/__init__.py:606 +#: awx/main/models/credential/__init__.py:662 +#: awx/main/models/credential/__init__.py:720 +#: awx/main/models/credential/__init__.py:793 +#: awx/main/models/credential/__init__.py:846 +#: awx/main/models/credential/__init__.py:872 +#: awx/main/models/credential/__init__.py:899 +#: awx/main/models/credential/__init__.py:959 +#: awx/main/models/credential/__init__.py:1032 +#: awx/main/models/credential/__init__.py:1063 +#: awx/main/models/credential/__init__.py:1113 msgid "Username" msgstr "" -#: awx/main/credential_plugins/conjur.py:36 +#: awx/main/credential_plugins/conjur.py:31 msgid "Public Key Certificate" msgstr "" -#: awx/main/credential_plugins/conjur.py:42 +#: awx/main/credential_plugins/conjur.py:37 msgid "Secret Identifier" msgstr "" -#: awx/main/credential_plugins/conjur.py:44 +#: awx/main/credential_plugins/conjur.py:39 msgid "The identifier for the secret e.g., /some/identifier" msgstr "" -#: awx/main/credential_plugins/hashivault.py:19 +#: awx/main/credential_plugins/hashivault.py:14 msgid "Server URL" msgstr "" -#: awx/main/credential_plugins/hashivault.py:22 +#: awx/main/credential_plugins/hashivault.py:17 msgid "The URL to the HashiCorp Vault" msgstr "" -#: awx/main/credential_plugins/hashivault.py:25 -#: awx/main/models/credential/__init__.py:990 -#: awx/main/models/credential/__init__.py:1007 +#: awx/main/credential_plugins/hashivault.py:20 +#: awx/main/models/credential/__init__.py:998 +#: awx/main/models/credential/__init__.py:1015 msgid "Token" msgstr "" -#: awx/main/credential_plugins/hashivault.py:28 +#: awx/main/credential_plugins/hashivault.py:23 msgid "The access token used to authenticate to the Vault server" msgstr "" -#: awx/main/credential_plugins/hashivault.py:31 +#: awx/main/credential_plugins/hashivault.py:26 msgid "CA Certificate" msgstr "" -#: awx/main/credential_plugins/hashivault.py:34 +#: awx/main/credential_plugins/hashivault.py:29 msgid "" "The CA certificate used to verify the SSL certificate of the Vault server" msgstr "" -#: awx/main/credential_plugins/hashivault.py:37 +#: awx/main/credential_plugins/hashivault.py:32 msgid "AppRole role_id" msgstr "" -#: awx/main/credential_plugins/hashivault.py:40 +#: awx/main/credential_plugins/hashivault.py:35 msgid "The Role ID for AppRole Authentication" msgstr "" -#: awx/main/credential_plugins/hashivault.py:43 +#: awx/main/credential_plugins/hashivault.py:38 msgid "AppRole secret_id" msgstr "" -#: awx/main/credential_plugins/hashivault.py:47 +#: awx/main/credential_plugins/hashivault.py:42 msgid "The Secret ID for AppRole Authentication" msgstr "" -#: awx/main/credential_plugins/hashivault.py:52 -msgid "Path to Secret" +#: awx/main/credential_plugins/hashivault.py:45 +msgid "Path to Approle Auth" +msgstr "" + +#: awx/main/credential_plugins/hashivault.py:49 +msgid "" +"The AppRole Authentication path to use if one isn't provided in the metadata " +"when linking to an input field. Defaults to 'approle'" msgstr "" #: awx/main/credential_plugins/hashivault.py:54 +msgid "Path to Secret" +msgstr "" + +#: awx/main/credential_plugins/hashivault.py:56 msgid "The path to the secret stored in the secret backend e.g, /some/secret/" msgstr "" -#: awx/main/credential_plugins/hashivault.py:57 +#: awx/main/credential_plugins/hashivault.py:59 msgid "Path to Auth" msgstr "" -#: awx/main/credential_plugins/hashivault.py:59 +#: awx/main/credential_plugins/hashivault.py:62 msgid "The path where the Authentication method is mounted e.g, approle" msgstr "" -#: awx/main/credential_plugins/hashivault.py:67 +#: awx/main/credential_plugins/hashivault.py:70 msgid "API Version" msgstr "" -#: awx/main/credential_plugins/hashivault.py:69 +#: awx/main/credential_plugins/hashivault.py:72 msgid "" "API v1 is for static key/value lookups. API v2 is for versioned key/value " "lookups." msgstr "" -#: awx/main/credential_plugins/hashivault.py:74 +#: awx/main/credential_plugins/hashivault.py:77 msgid "Name of Secret Backend" msgstr "" -#: awx/main/credential_plugins/hashivault.py:76 +#: awx/main/credential_plugins/hashivault.py:79 msgid "" "The name of the kv secret backend (if left empty, the first segment of the " "secret path will be used)." msgstr "" -#: awx/main/credential_plugins/hashivault.py:79 -#: awx/main/models/inventory.py:1023 +#: awx/main/credential_plugins/hashivault.py:82 +#: awx/main/migrations/_inventory_source_vars.py:147 msgid "Key Name" msgstr "" -#: awx/main/credential_plugins/hashivault.py:81 +#: awx/main/credential_plugins/hashivault.py:84 msgid "The name of the key to look up in the secret." msgstr "" -#: awx/main/credential_plugins/hashivault.py:84 +#: awx/main/credential_plugins/hashivault.py:87 msgid "Secret Version (v2 only)" msgstr "" -#: awx/main/credential_plugins/hashivault.py:93 +#: awx/main/credential_plugins/hashivault.py:96 msgid "Unsigned Public Key" msgstr "" -#: awx/main/credential_plugins/hashivault.py:98 +#: awx/main/credential_plugins/hashivault.py:101 msgid "Role Name" msgstr "" -#: awx/main/credential_plugins/hashivault.py:100 +#: awx/main/credential_plugins/hashivault.py:103 msgid "The name of the role used to sign." msgstr "" -#: awx/main/credential_plugins/hashivault.py:103 +#: awx/main/credential_plugins/hashivault.py:106 msgid "Valid Principals" msgstr "" -#: awx/main/credential_plugins/hashivault.py:105 +#: awx/main/credential_plugins/hashivault.py:108 msgid "" "Valid principals (either usernames or hostnames) that the certificate should " "be signed for." @@ -2951,70 +2950,74 @@ msgstr "" msgid "secret values must be of type string, not {}" msgstr "" -#: awx/main/fields.py:667 +#: awx/main/fields.py:675 #, python-format msgid "cannot be set unless \"%s\" is set" msgstr "" -#: awx/main/fields.py:702 +#: awx/main/fields.py:710 msgid "must be set when SSH key is encrypted." msgstr "" -#: awx/main/fields.py:710 +#: awx/main/fields.py:718 msgid "should not be set when SSH key is not encrypted." msgstr "" -#: awx/main/fields.py:769 +#: awx/main/fields.py:777 msgid "'dependencies' is not supported for custom credentials." msgstr "" -#: awx/main/fields.py:783 +#: awx/main/fields.py:791 msgid "\"tower\" is a reserved field name" msgstr "" -#: awx/main/fields.py:790 +#: awx/main/fields.py:798 #, python-format msgid "field IDs must be unique (%s)" msgstr "" -#: awx/main/fields.py:805 +#: awx/main/fields.py:813 msgid "{} is not a {}" msgstr "" -#: awx/main/fields.py:811 +#: awx/main/fields.py:819 #, python-brace-format msgid "{sub_key} not allowed for {element_type} type ({element_id})" msgstr "" -#: awx/main/fields.py:869 +#: awx/main/fields.py:877 msgid "" "Environment variable {} may affect Ansible configuration so its use is not " "allowed in credentials." msgstr "" -#: awx/main/fields.py:875 -msgid "Environment variable {} is blacklisted from use in credentials." +#: awx/main/fields.py:883 +msgid "Environment variable {} is not allowed to be used in credentials." msgstr "" -#: awx/main/fields.py:903 +#: awx/main/fields.py:911 msgid "" "Must define unnamed file injector in order to reference `tower.filename`." msgstr "" -#: awx/main/fields.py:910 +#: awx/main/fields.py:918 msgid "Cannot directly reference reserved `tower` namespace container." msgstr "" -#: awx/main/fields.py:920 +#: awx/main/fields.py:928 msgid "Must use multi-file syntax when injecting multiple files" msgstr "" -#: awx/main/fields.py:940 +#: awx/main/fields.py:948 #, python-brace-format msgid "{sub_key} uses an undefined field ({error_msg})" msgstr "" -#: awx/main/fields.py:947 +#: awx/main/fields.py:955 +msgid "Encountered unsafe code execution: {}" +msgstr "" + +#: awx/main/fields.py:959 #, python-brace-format msgid "" "Syntax error rendering template for {sub_key} inside of {type} ({error_msg})" @@ -3044,6 +3047,50 @@ msgid "" "this list to programmatically generate named URLs for resources" msgstr "" +#: awx/main/migrations/_inventory_source_vars.py:140 +msgid "Image ID" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:141 +msgid "Availability Zone" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:143 +msgid "Instance ID" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:144 +msgid "Instance State" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:145 +msgid "Platform" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:146 +msgid "Instance Type" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:148 +msgid "Region" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:149 +msgid "Security Group" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:150 +msgid "Tags" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:151 +msgid "Tag None" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:152 +msgid "VPC ID" +msgstr "" + #: awx/main/models/activity_stream.py:28 msgid "Entity Created" msgstr "" @@ -3090,17 +3137,17 @@ msgstr "" msgid "No argument passed to %s module." msgstr "" -#: awx/main/models/base.py:33 awx/main/models/base.py:39 -#: awx/main/models/base.py:44 awx/main/models/base.py:49 +#: awx/main/models/base.py:34 awx/main/models/base.py:40 +#: awx/main/models/base.py:45 awx/main/models/base.py:50 msgid "Run" msgstr "" -#: awx/main/models/base.py:34 awx/main/models/base.py:40 -#: awx/main/models/base.py:45 awx/main/models/base.py:50 +#: awx/main/models/base.py:35 awx/main/models/base.py:41 +#: awx/main/models/base.py:46 awx/main/models/base.py:51 msgid "Check" msgstr "" -#: awx/main/models/base.py:35 +#: awx/main/models/base.py:36 msgid "Scan" msgstr "" @@ -3110,819 +3157,822 @@ msgid "" "Tower documentation for details on each type." msgstr "" -#: awx/main/models/credential/__init__.py:110 -#: awx/main/models/credential/__init__.py:353 +#: awx/main/models/credential/__init__.py:114 +#: awx/main/models/credential/__init__.py:358 msgid "" "Enter inputs using either JSON or YAML syntax. Refer to the Ansible Tower " "documentation for example syntax." msgstr "" -#: awx/main/models/credential/__init__.py:325 -#: awx/main/models/credential/__init__.py:594 +#: awx/main/models/credential/__init__.py:329 +#: awx/main/models/credential/__init__.py:602 msgid "Machine" msgstr "" -#: awx/main/models/credential/__init__.py:326 -#: awx/main/models/credential/__init__.py:680 +#: awx/main/models/credential/__init__.py:330 +#: awx/main/models/credential/__init__.py:688 msgid "Vault" msgstr "" -#: awx/main/models/credential/__init__.py:327 -#: awx/main/models/credential/__init__.py:707 +#: awx/main/models/credential/__init__.py:331 +#: awx/main/models/credential/__init__.py:715 msgid "Network" msgstr "" -#: awx/main/models/credential/__init__.py:328 -#: awx/main/models/credential/__init__.py:649 +#: awx/main/models/credential/__init__.py:332 +#: awx/main/models/credential/__init__.py:657 msgid "Source Control" msgstr "" -#: awx/main/models/credential/__init__.py:329 +#: awx/main/models/credential/__init__.py:333 msgid "Cloud" msgstr "" -#: awx/main/models/credential/__init__.py:330 +#: awx/main/models/credential/__init__.py:334 msgid "Personal Access Token" msgstr "" -#: awx/main/models/credential/__init__.py:331 -#: awx/main/models/credential/__init__.py:1019 +#: awx/main/models/credential/__init__.py:335 +#: awx/main/models/credential/__init__.py:1027 msgid "Insights" msgstr "" -#: awx/main/models/credential/__init__.py:332 +#: awx/main/models/credential/__init__.py:336 msgid "External" msgstr "" -#: awx/main/models/credential/__init__.py:333 +#: awx/main/models/credential/__init__.py:337 msgid "Kubernetes" msgstr "" -#: awx/main/models/credential/__init__.py:359 +#: awx/main/models/credential/__init__.py:338 +msgid "Galaxy/Automation Hub" +msgstr "" + +#: awx/main/models/credential/__init__.py:364 msgid "" "Enter injectors using either JSON or YAML syntax. Refer to the Ansible Tower " "documentation for example syntax." msgstr "" -#: awx/main/models/credential/__init__.py:428 +#: awx/main/models/credential/__init__.py:433 #, python-format msgid "adding %s credential type" msgstr "" -#: awx/main/models/credential/__init__.py:602 -#: awx/main/models/credential/__init__.py:658 -#: awx/main/models/credential/__init__.py:716 -#: awx/main/models/credential/__init__.py:842 -#: awx/main/models/credential/__init__.py:868 -#: awx/main/models/credential/__init__.py:895 -#: awx/main/models/credential/__init__.py:955 -#: awx/main/models/credential/__init__.py:1028 -#: awx/main/models/credential/__init__.py:1059 -#: awx/main/models/credential/__init__.py:1109 +#: awx/main/models/credential/__init__.py:610 +#: awx/main/models/credential/__init__.py:666 +#: awx/main/models/credential/__init__.py:724 +#: awx/main/models/credential/__init__.py:850 +#: awx/main/models/credential/__init__.py:876 +#: awx/main/models/credential/__init__.py:903 +#: awx/main/models/credential/__init__.py:963 +#: awx/main/models/credential/__init__.py:1036 +#: awx/main/models/credential/__init__.py:1067 +#: awx/main/models/credential/__init__.py:1119 msgid "Password" msgstr "" -#: awx/main/models/credential/__init__.py:608 -#: awx/main/models/credential/__init__.py:721 +#: awx/main/models/credential/__init__.py:616 +#: awx/main/models/credential/__init__.py:729 msgid "SSH Private Key" msgstr "" -#: awx/main/models/credential/__init__.py:615 +#: awx/main/models/credential/__init__.py:623 msgid "Signed SSH Certificate" msgstr "" -#: awx/main/models/credential/__init__.py:621 -#: awx/main/models/credential/__init__.py:670 -#: awx/main/models/credential/__init__.py:728 +#: awx/main/models/credential/__init__.py:629 +#: awx/main/models/credential/__init__.py:678 +#: awx/main/models/credential/__init__.py:736 msgid "Private Key Passphrase" msgstr "" -#: awx/main/models/credential/__init__.py:627 +#: awx/main/models/credential/__init__.py:635 msgid "Privilege Escalation Method" msgstr "" -#: awx/main/models/credential/__init__.py:629 +#: awx/main/models/credential/__init__.py:637 msgid "" "Specify a method for \"become\" operations. This is equivalent to specifying " "the --become-method Ansible parameter." msgstr "" -#: awx/main/models/credential/__init__.py:634 +#: awx/main/models/credential/__init__.py:642 msgid "Privilege Escalation Username" msgstr "" -#: awx/main/models/credential/__init__.py:638 +#: awx/main/models/credential/__init__.py:646 msgid "Privilege Escalation Password" msgstr "" -#: awx/main/models/credential/__init__.py:663 +#: awx/main/models/credential/__init__.py:671 msgid "SCM Private Key" msgstr "" -#: awx/main/models/credential/__init__.py:685 +#: awx/main/models/credential/__init__.py:693 msgid "Vault Password" msgstr "" -#: awx/main/models/credential/__init__.py:691 +#: awx/main/models/credential/__init__.py:699 msgid "Vault Identifier" msgstr "" -#: awx/main/models/credential/__init__.py:694 +#: awx/main/models/credential/__init__.py:702 msgid "" "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+." msgstr "" -#: awx/main/models/credential/__init__.py:733 +#: awx/main/models/credential/__init__.py:741 msgid "Authorize" msgstr "" -#: awx/main/models/credential/__init__.py:737 +#: awx/main/models/credential/__init__.py:745 msgid "Authorize Password" msgstr "" -#: awx/main/models/credential/__init__.py:751 +#: awx/main/models/credential/__init__.py:759 msgid "Amazon Web Services" msgstr "" -#: awx/main/models/credential/__init__.py:756 +#: awx/main/models/credential/__init__.py:764 msgid "Access Key" msgstr "" -#: awx/main/models/credential/__init__.py:760 +#: awx/main/models/credential/__init__.py:768 msgid "Secret Key" msgstr "" -#: awx/main/models/credential/__init__.py:765 +#: awx/main/models/credential/__init__.py:773 msgid "STS Token" msgstr "" -#: awx/main/models/credential/__init__.py:768 +#: awx/main/models/credential/__init__.py:776 msgid "" "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." msgstr "" -#: awx/main/models/credential/__init__.py:780 awx/main/models/inventory.py:833 +#: awx/main/models/credential/__init__.py:788 awx/main/models/inventory.py:826 msgid "OpenStack" msgstr "" -#: awx/main/models/credential/__init__.py:789 +#: awx/main/models/credential/__init__.py:797 msgid "Password (API Key)" msgstr "" -#: awx/main/models/credential/__init__.py:794 -#: awx/main/models/credential/__init__.py:1050 +#: awx/main/models/credential/__init__.py:802 +#: awx/main/models/credential/__init__.py:1058 msgid "Host (Authentication URL)" msgstr "" -#: awx/main/models/credential/__init__.py:796 +#: awx/main/models/credential/__init__.py:804 msgid "" "The host to authenticate with. For example, https://openstack.business.com/" "v2.0/" msgstr "" -#: awx/main/models/credential/__init__.py:800 +#: awx/main/models/credential/__init__.py:808 msgid "Project (Tenant Name)" msgstr "" -#: awx/main/models/credential/__init__.py:804 +#: awx/main/models/credential/__init__.py:812 msgid "Project (Domain Name)" msgstr "" -#: awx/main/models/credential/__init__.py:808 +#: awx/main/models/credential/__init__.py:816 msgid "Domain Name" msgstr "" -#: awx/main/models/credential/__init__.py:810 +#: awx/main/models/credential/__init__.py:818 msgid "" "OpenStack domains define administrative boundaries. It is only needed for " "Keystone v3 authentication URLs. Refer to Ansible Tower documentation for " "common scenarios." msgstr "" -#: awx/main/models/credential/__init__.py:816 -#: awx/main/models/credential/__init__.py:1114 -#: awx/main/models/credential/__init__.py:1148 +#: awx/main/models/credential/__init__.py:824 +#: awx/main/models/credential/__init__.py:1131 +#: awx/main/models/credential/__init__.py:1166 msgid "Verify SSL" msgstr "" -#: awx/main/models/credential/__init__.py:827 awx/main/models/inventory.py:830 +#: awx/main/models/credential/__init__.py:835 awx/main/models/inventory.py:824 msgid "VMware vCenter" msgstr "" -#: awx/main/models/credential/__init__.py:832 +#: awx/main/models/credential/__init__.py:840 msgid "VCenter Host" msgstr "" -#: awx/main/models/credential/__init__.py:834 +#: awx/main/models/credential/__init__.py:842 msgid "" "Enter the hostname or IP address that corresponds to your VMware vCenter." msgstr "" -#: awx/main/models/credential/__init__.py:853 awx/main/models/inventory.py:831 +#: awx/main/models/credential/__init__.py:861 awx/main/models/inventory.py:825 msgid "Red Hat Satellite 6" msgstr "" -#: awx/main/models/credential/__init__.py:858 +#: awx/main/models/credential/__init__.py:866 msgid "Satellite 6 URL" msgstr "" -#: awx/main/models/credential/__init__.py:860 +#: awx/main/models/credential/__init__.py:868 msgid "" "Enter the URL that corresponds to your Red Hat Satellite 6 server. For " "example, https://satellite.example.org" msgstr "" -#: awx/main/models/credential/__init__.py:879 awx/main/models/inventory.py:832 +#: awx/main/models/credential/__init__.py:887 msgid "Red Hat CloudForms" msgstr "" -#: awx/main/models/credential/__init__.py:884 +#: awx/main/models/credential/__init__.py:892 msgid "CloudForms URL" msgstr "" -#: awx/main/models/credential/__init__.py:886 +#: awx/main/models/credential/__init__.py:894 msgid "" "Enter the URL for the virtual machine that corresponds to your CloudForms " "instance. For example, https://cloudforms.example.org" msgstr "" -#: awx/main/models/credential/__init__.py:906 awx/main/models/inventory.py:828 +#: awx/main/models/credential/__init__.py:914 awx/main/models/inventory.py:822 msgid "Google Compute Engine" msgstr "" -#: awx/main/models/credential/__init__.py:911 +#: awx/main/models/credential/__init__.py:919 msgid "Service Account Email Address" msgstr "" -#: awx/main/models/credential/__init__.py:913 +#: awx/main/models/credential/__init__.py:921 msgid "" "The email address assigned to the Google Compute Engine service account." msgstr "" -#: awx/main/models/credential/__init__.py:919 +#: awx/main/models/credential/__init__.py:927 msgid "" "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" msgstr "" -#: awx/main/models/credential/__init__.py:925 +#: awx/main/models/credential/__init__.py:933 msgid "RSA Private Key" msgstr "" -#: awx/main/models/credential/__init__.py:930 +#: awx/main/models/credential/__init__.py:938 msgid "" "Paste the contents of the PEM file associated with the service account email." msgstr "" -#: awx/main/models/credential/__init__.py:940 awx/main/models/inventory.py:829 +#: awx/main/models/credential/__init__.py:948 awx/main/models/inventory.py:823 msgid "Microsoft Azure Resource Manager" msgstr "" -#: awx/main/models/credential/__init__.py:945 +#: awx/main/models/credential/__init__.py:953 msgid "Subscription ID" msgstr "" -#: awx/main/models/credential/__init__.py:947 +#: awx/main/models/credential/__init__.py:955 msgid "Subscription ID is an Azure construct, which is mapped to a username." msgstr "" -#: awx/main/models/credential/__init__.py:973 +#: awx/main/models/credential/__init__.py:981 msgid "Azure Cloud Environment" msgstr "" -#: awx/main/models/credential/__init__.py:975 +#: awx/main/models/credential/__init__.py:983 msgid "" "Environment variable AZURE_CLOUD_ENVIRONMENT when using Azure GovCloud or " "Azure stack." msgstr "" -#: awx/main/models/credential/__init__.py:985 +#: awx/main/models/credential/__init__.py:993 msgid "GitHub Personal Access Token" msgstr "" -#: awx/main/models/credential/__init__.py:993 +#: awx/main/models/credential/__init__.py:1001 msgid "This token needs to come from your profile settings in GitHub" msgstr "" -#: awx/main/models/credential/__init__.py:1002 +#: awx/main/models/credential/__init__.py:1010 msgid "GitLab Personal Access Token" msgstr "" -#: awx/main/models/credential/__init__.py:1010 +#: awx/main/models/credential/__init__.py:1018 msgid "This token needs to come from your profile settings in GitLab" msgstr "" -#: awx/main/models/credential/__init__.py:1045 awx/main/models/inventory.py:834 +#: awx/main/models/credential/__init__.py:1053 awx/main/models/inventory.py:827 msgid "Red Hat Virtualization" msgstr "" -#: awx/main/models/credential/__init__.py:1052 +#: awx/main/models/credential/__init__.py:1060 msgid "The host to authenticate with." msgstr "" -#: awx/main/models/credential/__init__.py:1064 +#: awx/main/models/credential/__init__.py:1072 msgid "CA File" msgstr "" -#: awx/main/models/credential/__init__.py:1066 +#: awx/main/models/credential/__init__.py:1074 msgid "Absolute file path to the CA file to use (optional)" msgstr "" -#: awx/main/models/credential/__init__.py:1095 awx/main/models/inventory.py:835 +#: awx/main/models/credential/__init__.py:1103 awx/main/models/inventory.py:828 msgid "Ansible Tower" msgstr "" -#: awx/main/models/credential/__init__.py:1100 +#: awx/main/models/credential/__init__.py:1108 msgid "Ansible Tower Hostname" msgstr "" -#: awx/main/models/credential/__init__.py:1102 +#: awx/main/models/credential/__init__.py:1110 msgid "The Ansible Tower base URL to authenticate with." msgstr "" -#: awx/main/models/credential/__init__.py:1134 +#: awx/main/models/credential/__init__.py:1115 +msgid "" +"The Ansible Tower user to authenticate as.This should not be set if an OAuth " +"token is being used." +msgstr "" + +#: awx/main/models/credential/__init__.py:1124 +msgid "OAuth Token" +msgstr "" + +#: awx/main/models/credential/__init__.py:1127 +msgid "" +"An OAuth token to use to authenticate to Tower with.This should not be set " +"if username/password are being used." +msgstr "" + +#: awx/main/models/credential/__init__.py:1152 msgid "OpenShift or Kubernetes API Bearer Token" msgstr "" -#: awx/main/models/credential/__init__.py:1138 +#: awx/main/models/credential/__init__.py:1156 msgid "OpenShift or Kubernetes API Endpoint" msgstr "" -#: awx/main/models/credential/__init__.py:1140 +#: awx/main/models/credential/__init__.py:1158 msgid "The OpenShift or Kubernetes API Endpoint to authenticate with." msgstr "" -#: awx/main/models/credential/__init__.py:1143 +#: awx/main/models/credential/__init__.py:1161 msgid "API authentication bearer token" msgstr "" -#: awx/main/models/credential/__init__.py:1153 +#: awx/main/models/credential/__init__.py:1171 msgid "Certificate Authority data" msgstr "" -#: awx/main/models/credential/__init__.py:1194 +#: awx/main/models/credential/__init__.py:1184 +msgid "Ansible Galaxy/Automation Hub API Token" +msgstr "" + +#: awx/main/models/credential/__init__.py:1188 +msgid "Galaxy Server URL" +msgstr "" + +#: awx/main/models/credential/__init__.py:1190 +msgid "The URL of the Galaxy instance to connect to." +msgstr "" + +#: awx/main/models/credential/__init__.py:1193 +msgid "Auth Server URL" +msgstr "" + +#: awx/main/models/credential/__init__.py:1196 +msgid "The URL of a Keycloak server token_endpoint, if using SSO auth." +msgstr "" + +#: awx/main/models/credential/__init__.py:1201 +msgid "API Token" +msgstr "" + +#: awx/main/models/credential/__init__.py:1205 +msgid "A token to use for authentication against the Galaxy instance." +msgstr "" + +#: awx/main/models/credential/__init__.py:1244 msgid "Target must be a non-external credential" msgstr "" -#: awx/main/models/credential/__init__.py:1199 +#: awx/main/models/credential/__init__.py:1249 msgid "Source must be an external credential" msgstr "" -#: awx/main/models/credential/__init__.py:1206 +#: awx/main/models/credential/__init__.py:1256 msgid "Input field must be defined on target credential (options are {})." msgstr "" -#: awx/main/models/events.py:152 awx/main/models/events.py:674 +#: awx/main/models/events.py:165 awx/main/models/events.py:707 msgid "Host Failed" msgstr "" -#: awx/main/models/events.py:153 +#: awx/main/models/events.py:166 msgid "Host Started" msgstr "" -#: awx/main/models/events.py:154 awx/main/models/events.py:675 +#: awx/main/models/events.py:167 awx/main/models/events.py:708 msgid "Host OK" msgstr "" -#: awx/main/models/events.py:155 +#: awx/main/models/events.py:168 msgid "Host Failure" msgstr "" -#: awx/main/models/events.py:156 awx/main/models/events.py:681 +#: awx/main/models/events.py:169 awx/main/models/events.py:714 msgid "Host Skipped" msgstr "" -#: awx/main/models/events.py:157 awx/main/models/events.py:676 +#: awx/main/models/events.py:170 awx/main/models/events.py:709 msgid "Host Unreachable" msgstr "" -#: awx/main/models/events.py:158 awx/main/models/events.py:172 +#: awx/main/models/events.py:171 awx/main/models/events.py:185 msgid "No Hosts Remaining" msgstr "" -#: awx/main/models/events.py:159 +#: awx/main/models/events.py:172 msgid "Host Polling" msgstr "" -#: awx/main/models/events.py:160 +#: awx/main/models/events.py:173 msgid "Host Async OK" msgstr "" -#: awx/main/models/events.py:161 +#: awx/main/models/events.py:174 msgid "Host Async Failure" msgstr "" -#: awx/main/models/events.py:162 +#: awx/main/models/events.py:175 msgid "Item OK" msgstr "" -#: awx/main/models/events.py:163 +#: awx/main/models/events.py:176 msgid "Item Failed" msgstr "" -#: awx/main/models/events.py:164 +#: awx/main/models/events.py:177 msgid "Item Skipped" msgstr "" -#: awx/main/models/events.py:165 +#: awx/main/models/events.py:178 msgid "Host Retry" msgstr "" -#: awx/main/models/events.py:167 +#: awx/main/models/events.py:180 msgid "File Difference" msgstr "" -#: awx/main/models/events.py:168 +#: awx/main/models/events.py:181 msgid "Playbook Started" msgstr "" -#: awx/main/models/events.py:169 +#: awx/main/models/events.py:182 msgid "Running Handlers" msgstr "" -#: awx/main/models/events.py:170 +#: awx/main/models/events.py:183 msgid "Including File" msgstr "" -#: awx/main/models/events.py:171 +#: awx/main/models/events.py:184 msgid "No Hosts Matched" msgstr "" -#: awx/main/models/events.py:173 +#: awx/main/models/events.py:186 msgid "Task Started" msgstr "" -#: awx/main/models/events.py:175 +#: awx/main/models/events.py:188 msgid "Variables Prompted" msgstr "" -#: awx/main/models/events.py:176 +#: awx/main/models/events.py:189 msgid "Gathering Facts" msgstr "" -#: awx/main/models/events.py:177 +#: awx/main/models/events.py:190 msgid "internal: on Import for Host" msgstr "" -#: awx/main/models/events.py:178 +#: awx/main/models/events.py:191 msgid "internal: on Not Import for Host" msgstr "" -#: awx/main/models/events.py:179 +#: awx/main/models/events.py:192 msgid "Play Started" msgstr "" -#: awx/main/models/events.py:180 +#: awx/main/models/events.py:193 msgid "Playbook Complete" msgstr "" -#: awx/main/models/events.py:184 awx/main/models/events.py:691 +#: awx/main/models/events.py:197 awx/main/models/events.py:724 msgid "Debug" msgstr "" -#: awx/main/models/events.py:185 awx/main/models/events.py:692 +#: awx/main/models/events.py:198 awx/main/models/events.py:725 msgid "Verbose" msgstr "" -#: awx/main/models/events.py:186 awx/main/models/events.py:693 +#: awx/main/models/events.py:199 awx/main/models/events.py:726 msgid "Deprecated" msgstr "" -#: awx/main/models/events.py:187 awx/main/models/events.py:694 +#: awx/main/models/events.py:200 awx/main/models/events.py:727 msgid "Warning" msgstr "" -#: awx/main/models/events.py:188 awx/main/models/events.py:695 +#: awx/main/models/events.py:201 awx/main/models/events.py:728 msgid "System Warning" msgstr "" -#: awx/main/models/events.py:189 awx/main/models/events.py:696 +#: awx/main/models/events.py:202 awx/main/models/events.py:729 #: awx/main/models/unified_jobs.py:75 msgid "Error" msgstr "" -#: awx/main/models/ha.py:175 +#: awx/main/models/ha.py:184 msgid "Instances that are members of this InstanceGroup" msgstr "" -#: awx/main/models/ha.py:180 +#: awx/main/models/ha.py:189 msgid "Instance Group to remotely control this group." msgstr "" -#: awx/main/models/ha.py:200 +#: awx/main/models/ha.py:209 msgid "Percentage of Instances to automatically assign to this group" msgstr "" -#: awx/main/models/ha.py:204 +#: awx/main/models/ha.py:213 msgid "" "Static minimum number of Instances to automatically assign to this group" msgstr "" -#: awx/main/models/ha.py:209 +#: awx/main/models/ha.py:218 msgid "" "List of exact-match Instances that will always be automatically assigned to " "this group" msgstr "" -#: awx/main/models/inventory.py:80 +#: awx/main/models/inventory.py:74 msgid "Hosts have a direct link to this inventory." msgstr "" -#: awx/main/models/inventory.py:81 +#: awx/main/models/inventory.py:75 msgid "Hosts for inventory generated using the host_filter property." msgstr "" -#: awx/main/models/inventory.py:86 +#: awx/main/models/inventory.py:80 msgid "inventories" msgstr "" -#: awx/main/models/inventory.py:93 +#: awx/main/models/inventory.py:87 msgid "Organization containing this inventory." msgstr "" -#: awx/main/models/inventory.py:100 +#: awx/main/models/inventory.py:94 msgid "Inventory variables in JSON or YAML format." msgstr "" -#: awx/main/models/inventory.py:105 +#: awx/main/models/inventory.py:99 msgid "" "This field is deprecated and will be removed in a future release. Flag " "indicating whether any hosts in this inventory have failed." msgstr "" -#: awx/main/models/inventory.py:111 +#: awx/main/models/inventory.py:105 msgid "" "This field is deprecated and will be removed in a future release. Total " "number of hosts in this inventory." msgstr "" -#: awx/main/models/inventory.py:117 +#: awx/main/models/inventory.py:111 msgid "" "This field is deprecated and will be removed in a future release. Number of " "hosts in this inventory with active failures." msgstr "" -#: awx/main/models/inventory.py:123 +#: awx/main/models/inventory.py:117 msgid "" "This field is deprecated and will be removed in a future release. Total " "number of groups in this inventory." msgstr "" -#: awx/main/models/inventory.py:129 +#: awx/main/models/inventory.py:123 msgid "" "This field is deprecated and will be removed in a future release. Flag " "indicating whether this inventory has any external inventory sources." msgstr "" -#: awx/main/models/inventory.py:135 +#: awx/main/models/inventory.py:129 msgid "" "Total number of external inventory sources configured within this inventory." msgstr "" -#: awx/main/models/inventory.py:140 +#: awx/main/models/inventory.py:134 msgid "Number of external inventory sources in this inventory with failures." msgstr "" -#: awx/main/models/inventory.py:147 +#: awx/main/models/inventory.py:141 msgid "Kind of inventory being represented." msgstr "" -#: awx/main/models/inventory.py:153 +#: awx/main/models/inventory.py:147 msgid "Filter that will be applied to the hosts of this inventory." msgstr "" -#: awx/main/models/inventory.py:181 +#: awx/main/models/inventory.py:175 msgid "" "Credentials to be used by hosts belonging to this inventory when accessing " "Red Hat Insights API." msgstr "" -#: awx/main/models/inventory.py:190 +#: awx/main/models/inventory.py:184 msgid "Flag indicating the inventory is being deleted." msgstr "" -#: awx/main/models/inventory.py:245 +#: awx/main/models/inventory.py:239 msgid "Could not parse subset as slice specification." msgstr "" -#: awx/main/models/inventory.py:249 +#: awx/main/models/inventory.py:243 msgid "Slice number must be less than total number of slices." msgstr "" -#: awx/main/models/inventory.py:251 +#: awx/main/models/inventory.py:245 msgid "Slice number must be 1 or higher." msgstr "" -#: awx/main/models/inventory.py:388 +#: awx/main/models/inventory.py:382 msgid "Assignment not allowed for Smart Inventory" msgstr "" -#: awx/main/models/inventory.py:390 awx/main/models/projects.py:166 +#: awx/main/models/inventory.py:384 awx/main/models/projects.py:167 msgid "Credential kind must be 'insights'." msgstr "" -#: awx/main/models/inventory.py:475 +#: awx/main/models/inventory.py:469 msgid "Is this host online and available for running jobs?" msgstr "" -#: awx/main/models/inventory.py:481 +#: awx/main/models/inventory.py:475 msgid "" "The value used by the remote inventory source to uniquely identify the host" msgstr "" -#: awx/main/models/inventory.py:486 +#: awx/main/models/inventory.py:480 msgid "Host variables in JSON or YAML format." msgstr "" -#: awx/main/models/inventory.py:509 +#: awx/main/models/inventory.py:503 msgid "Inventory source(s) that created or modified this host." msgstr "" -#: awx/main/models/inventory.py:514 +#: awx/main/models/inventory.py:508 msgid "Arbitrary JSON structure of most recent ansible_facts, per-host." msgstr "" -#: awx/main/models/inventory.py:520 +#: awx/main/models/inventory.py:514 msgid "The date and time ansible_facts was last modified." msgstr "" -#: awx/main/models/inventory.py:527 +#: awx/main/models/inventory.py:521 msgid "Red Hat Insights host unique identifier." msgstr "" -#: awx/main/models/inventory.py:641 +#: awx/main/models/inventory.py:635 msgid "Group variables in JSON or YAML format." msgstr "" -#: awx/main/models/inventory.py:647 +#: awx/main/models/inventory.py:641 msgid "Hosts associated directly with this group." msgstr "" -#: awx/main/models/inventory.py:653 +#: awx/main/models/inventory.py:647 msgid "Inventory source(s) that created or modified this group." msgstr "" -#: awx/main/models/inventory.py:825 +#: awx/main/models/inventory.py:819 msgid "File, Directory or Script" msgstr "" -#: awx/main/models/inventory.py:826 +#: awx/main/models/inventory.py:820 msgid "Sourced from a Project" msgstr "" -#: awx/main/models/inventory.py:827 +#: awx/main/models/inventory.py:821 msgid "Amazon EC2" msgstr "" -#: awx/main/models/inventory.py:836 +#: awx/main/models/inventory.py:829 msgid "Custom Script" msgstr "" -#: awx/main/models/inventory.py:953 +#: awx/main/models/inventory.py:863 msgid "Inventory source variables in YAML or JSON format." msgstr "" -#: awx/main/models/inventory.py:964 +#: awx/main/models/inventory.py:868 msgid "" -"Comma-separated list of filter expressions (EC2 only). Hosts are imported " -"when ANY of the filters match." +"Retrieve the enabled state from the given dict of host variables. The " +"enabled variable may be specified as \"foo.bar\", in which case the lookup " +"will traverse into nested dicts, equivalent to: from_dict.get(\"foo\", {})." +"get(\"bar\", default)" msgstr "" -#: awx/main/models/inventory.py:970 -msgid "Limit groups automatically created from inventory source (EC2 only)." +#: awx/main/models/inventory.py:876 +msgid "" +"Only used when enabled_var is set. Value when the host is considered " +"enabled. For example if enabled_var=\"status.power_state\"and enabled_value=" +"\"powered_on\" with host variables:{ \"status\": { \"power_state\": " +"\"powered_on\", \"created\": \"2020-08-04T18:13:04+00:00\", \"healthy" +"\": true }, \"name\": \"foobar\", \"ip_address\": \"192.168.2.1\"}" +"The host would be marked enabled. If power_state where any value other than " +"powered_on then the host would be disabled when imported into Tower. If the " +"key is not found then the host will be enabled" msgstr "" -#: awx/main/models/inventory.py:974 +#: awx/main/models/inventory.py:896 +msgid "Regex where only matching hosts will be imported into Tower." +msgstr "" + +#: awx/main/models/inventory.py:900 msgid "Overwrite local groups and hosts from remote inventory source." msgstr "" -#: awx/main/models/inventory.py:978 +#: awx/main/models/inventory.py:904 msgid "Overwrite local variables from remote inventory source." msgstr "" -#: awx/main/models/inventory.py:983 awx/main/models/jobs.py:154 -#: awx/main/models/projects.py:135 +#: awx/main/models/inventory.py:909 awx/main/models/jobs.py:154 +#: awx/main/models/projects.py:136 msgid "The amount of time (in seconds) to run before the task is canceled." msgstr "" -#: awx/main/models/inventory.py:1016 -msgid "Image ID" -msgstr "" - -#: awx/main/models/inventory.py:1017 -msgid "Availability Zone" -msgstr "" - -#: awx/main/models/inventory.py:1019 -msgid "Instance ID" -msgstr "" - -#: awx/main/models/inventory.py:1020 -msgid "Instance State" -msgstr "" - -#: awx/main/models/inventory.py:1021 -msgid "Platform" -msgstr "" - -#: awx/main/models/inventory.py:1022 -msgid "Instance Type" -msgstr "" - -#: awx/main/models/inventory.py:1024 -msgid "Region" -msgstr "" - -#: awx/main/models/inventory.py:1025 -msgid "Security Group" -msgstr "" - -#: awx/main/models/inventory.py:1026 -msgid "Tags" -msgstr "" - -#: awx/main/models/inventory.py:1027 -msgid "Tag None" -msgstr "" - -#: awx/main/models/inventory.py:1028 -msgid "VPC ID" -msgstr "" - -#: awx/main/models/inventory.py:1096 +#: awx/main/models/inventory.py:926 #, python-format msgid "" "Cloud-based inventory sources (such as %s) require credentials for the " "matching cloud service." msgstr "" -#: awx/main/models/inventory.py:1102 +#: awx/main/models/inventory.py:932 msgid "Credential is required for a cloud source." msgstr "" -#: awx/main/models/inventory.py:1105 +#: awx/main/models/inventory.py:935 msgid "" "Credentials of type machine, source control, insights and vault are " "disallowed for custom inventory sources." msgstr "" -#: awx/main/models/inventory.py:1110 +#: awx/main/models/inventory.py:940 msgid "" "Credentials of type insights and vault are disallowed for scm inventory " "sources." msgstr "" -#: awx/main/models/inventory.py:1170 -#, python-format -msgid "Invalid %(source)s region: %(region)s" -msgstr "" - -#: awx/main/models/inventory.py:1194 -#, python-format -msgid "Invalid filter expression: %(filter)s" -msgstr "" - -#: awx/main/models/inventory.py:1215 -#, python-format -msgid "Invalid group by choice: %(choice)s" -msgstr "" - -#: awx/main/models/inventory.py:1243 +#: awx/main/models/inventory.py:1004 msgid "Project containing inventory file used as source." msgstr "" -#: awx/main/models/inventory.py:1416 +#: awx/main/models/inventory.py:1177 msgid "" "More than one SCM-based inventory source with update on project update per-" "inventory not allowed." msgstr "" -#: awx/main/models/inventory.py:1423 +#: awx/main/models/inventory.py:1184 msgid "" "Cannot update SCM-based inventory source on launch if set to update on " "project update. Instead, configure the corresponding source project to " "update on launch." msgstr "" -#: awx/main/models/inventory.py:1429 +#: awx/main/models/inventory.py:1190 msgid "Cannot set source_path if not SCM type." msgstr "" -#: awx/main/models/inventory.py:1472 +#: awx/main/models/inventory.py:1233 msgid "" "Inventory files from this Project Update were used for the inventory update." msgstr "" -#: awx/main/models/inventory.py:1583 +#: awx/main/models/inventory.py:1344 msgid "Inventory script contents" msgstr "" -#: awx/main/models/inventory.py:1588 +#: awx/main/models/inventory.py:1349 msgid "Organization owning this inventory script" msgstr "" @@ -3980,8 +4030,8 @@ msgstr "" msgid "Job Template {} is missing or undefined." msgstr "" -#: awx/main/models/jobs.py:570 awx/main/models/projects.py:278 -#: awx/main/models/projects.py:497 +#: awx/main/models/jobs.py:570 awx/main/models/projects.py:284 +#: awx/main/models/projects.py:508 msgid "SCM Revision" msgstr "" @@ -4081,63 +4131,59 @@ msgstr "" msgid "Unique identifier of the event that triggered this webhook" msgstr "" -#: awx/main/models/notifications.py:42 +#: awx/main/models/notifications.py:41 msgid "Email" msgstr "" -#: awx/main/models/notifications.py:43 +#: awx/main/models/notifications.py:42 msgid "Slack" msgstr "" -#: awx/main/models/notifications.py:44 +#: awx/main/models/notifications.py:43 msgid "Twilio" msgstr "" -#: awx/main/models/notifications.py:45 +#: awx/main/models/notifications.py:44 msgid "Pagerduty" msgstr "" -#: awx/main/models/notifications.py:46 +#: awx/main/models/notifications.py:45 msgid "Grafana" msgstr "" -#: awx/main/models/notifications.py:47 -msgid "HipChat" -msgstr "" - -#: awx/main/models/notifications.py:48 awx/main/models/unified_jobs.py:544 +#: awx/main/models/notifications.py:46 awx/main/models/unified_jobs.py:544 msgid "Webhook" msgstr "" -#: awx/main/models/notifications.py:49 +#: awx/main/models/notifications.py:47 msgid "Mattermost" msgstr "" -#: awx/main/models/notifications.py:50 +#: awx/main/models/notifications.py:48 msgid "Rocket.Chat" msgstr "" -#: awx/main/models/notifications.py:51 +#: awx/main/models/notifications.py:49 msgid "IRC" msgstr "" -#: awx/main/models/notifications.py:82 +#: awx/main/models/notifications.py:80 msgid "Optional custom messages for notification template." msgstr "" -#: awx/main/models/notifications.py:212 awx/main/models/unified_jobs.py:70 +#: awx/main/models/notifications.py:210 awx/main/models/unified_jobs.py:70 msgid "Pending" msgstr "" -#: awx/main/models/notifications.py:213 awx/main/models/unified_jobs.py:73 +#: awx/main/models/notifications.py:211 awx/main/models/unified_jobs.py:73 msgid "Successful" msgstr "" -#: awx/main/models/notifications.py:214 awx/main/models/unified_jobs.py:74 +#: awx/main/models/notifications.py:212 awx/main/models/unified_jobs.py:74 msgid "Failed" msgstr "" -#: awx/main/models/notifications.py:467 +#: awx/main/models/notifications.py:470 msgid "status must be either running, succeeded or failed" msgstr "" @@ -4206,7 +4252,7 @@ msgid "" "authentication provider ({})" msgstr "" -#: awx/main/models/organization.py:51 +#: awx/main/models/organization.py:57 msgid "Maximum number of hosts allowed to be managed by this organization." msgstr "" @@ -4230,118 +4276,122 @@ msgstr "" msgid "Red Hat Insights" msgstr "" -#: awx/main/models/projects.py:83 +#: awx/main/models/projects.py:58 +msgid "Remote Archive" +msgstr "" + +#: awx/main/models/projects.py:84 msgid "" "Local path (relative to PROJECTS_ROOT) containing playbooks and related " "files for this project." msgstr "" -#: awx/main/models/projects.py:92 +#: awx/main/models/projects.py:93 msgid "SCM Type" msgstr "" -#: awx/main/models/projects.py:93 +#: awx/main/models/projects.py:94 msgid "Specifies the source control system used to store the project." msgstr "" -#: awx/main/models/projects.py:99 +#: awx/main/models/projects.py:100 msgid "SCM URL" msgstr "" -#: awx/main/models/projects.py:100 +#: awx/main/models/projects.py:101 msgid "The location where the project is stored." msgstr "" -#: awx/main/models/projects.py:106 +#: awx/main/models/projects.py:107 msgid "SCM Branch" msgstr "" -#: awx/main/models/projects.py:107 +#: awx/main/models/projects.py:108 msgid "Specific branch, tag or commit to checkout." msgstr "" -#: awx/main/models/projects.py:113 +#: awx/main/models/projects.py:114 msgid "SCM refspec" msgstr "" -#: awx/main/models/projects.py:114 +#: awx/main/models/projects.py:115 msgid "For git projects, an additional refspec to fetch." msgstr "" -#: awx/main/models/projects.py:118 +#: awx/main/models/projects.py:119 msgid "Discard any local changes before syncing the project." msgstr "" -#: awx/main/models/projects.py:122 +#: awx/main/models/projects.py:123 msgid "Delete the project before syncing." msgstr "" -#: awx/main/models/projects.py:151 +#: awx/main/models/projects.py:152 msgid "Invalid SCM URL." msgstr "" -#: awx/main/models/projects.py:154 +#: awx/main/models/projects.py:155 msgid "SCM URL is required." msgstr "" -#: awx/main/models/projects.py:162 +#: awx/main/models/projects.py:163 msgid "Insights Credential is required for an Insights Project." msgstr "" -#: awx/main/models/projects.py:168 +#: awx/main/models/projects.py:169 msgid "Credential kind must be 'scm'." msgstr "" -#: awx/main/models/projects.py:185 +#: awx/main/models/projects.py:186 msgid "Invalid credential." msgstr "" -#: awx/main/models/projects.py:259 +#: awx/main/models/projects.py:265 msgid "Update the project when a job is launched that uses the project." msgstr "" -#: awx/main/models/projects.py:264 +#: awx/main/models/projects.py:270 msgid "" "The number of seconds after the last project update ran that a new project " "update will be launched as a job dependency." msgstr "" -#: awx/main/models/projects.py:269 +#: awx/main/models/projects.py:275 msgid "" "Allow changing the SCM branch or revision in a job template that uses this " "project." msgstr "" -#: awx/main/models/projects.py:279 +#: awx/main/models/projects.py:285 msgid "The last revision fetched by a project update" msgstr "" -#: awx/main/models/projects.py:286 +#: awx/main/models/projects.py:292 msgid "Playbook Files" msgstr "" -#: awx/main/models/projects.py:287 +#: awx/main/models/projects.py:293 msgid "List of playbooks found in the project" msgstr "" -#: awx/main/models/projects.py:294 +#: awx/main/models/projects.py:300 msgid "Inventory Files" msgstr "" -#: awx/main/models/projects.py:295 +#: awx/main/models/projects.py:301 msgid "" "Suggested list of content that could be Ansible inventory in the project" msgstr "" -#: awx/main/models/projects.py:332 +#: awx/main/models/projects.py:338 msgid "Organization cannot be changed when in use by job templates." msgstr "" -#: awx/main/models/projects.py:490 +#: awx/main/models/projects.py:501 msgid "Parts of the project update playbook that will be run." msgstr "" -#: awx/main/models/projects.py:498 +#: awx/main/models/projects.py:509 msgid "" "The SCM Revision discovered by this update for the given project and branch." msgstr "" @@ -4697,41 +4747,33 @@ msgid "" "Shows when an approval node (with a timeout assigned to it) has timed out." msgstr "" -#: awx/main/notifications/grafana_backend.py:86 +#: awx/main/notifications/grafana_backend.py:81 msgid "Error converting time {} or timeEnd {} to int." msgstr "" -#: awx/main/notifications/grafana_backend.py:88 +#: awx/main/notifications/grafana_backend.py:83 msgid "Error converting time {} and/or timeEnd {} to int." msgstr "" -#: awx/main/notifications/grafana_backend.py:102 -#: awx/main/notifications/grafana_backend.py:104 +#: awx/main/notifications/grafana_backend.py:97 +#: awx/main/notifications/grafana_backend.py:99 msgid "Error sending notification grafana: {}" msgstr "" -#: awx/main/notifications/hipchat_backend.py:50 -msgid "Error sending messages: {}" -msgstr "" - -#: awx/main/notifications/hipchat_backend.py:52 -msgid "Error sending message to hipchat: {}" -msgstr "" - #: awx/main/notifications/irc_backend.py:56 msgid "Exception connecting to irc server: {}" msgstr "" -#: awx/main/notifications/mattermost_backend.py:50 -#: awx/main/notifications/mattermost_backend.py:52 +#: awx/main/notifications/mattermost_backend.py:49 +#: awx/main/notifications/mattermost_backend.py:51 msgid "Error sending notification mattermost: {}" msgstr "" -#: awx/main/notifications/pagerduty_backend.py:64 +#: awx/main/notifications/pagerduty_backend.py:75 msgid "Exception connecting to PagerDuty: {}" msgstr "" -#: awx/main/notifications/pagerduty_backend.py:73 +#: awx/main/notifications/pagerduty_backend.py:84 #: awx/main/notifications/slack_backend.py:58 #: awx/main/notifications/twilio_backend.py:48 msgid "Exception sending messages: {}" @@ -4758,46 +4800,52 @@ msgid "" "job node(s) missing unified job template and error handling path [{no_ufjt}]." msgstr "" -#: awx/main/scheduler/task_manager.py:118 +#: awx/main/scheduler/task_manager.py:127 msgid "" "Workflow Job spawned from workflow could not start because it would result " "in recursion (spawn order, most recent first: {})" msgstr "" -#: awx/main/scheduler/task_manager.py:126 +#: awx/main/scheduler/task_manager.py:135 msgid "" "Job spawned from workflow could not start because it was missing a related " "resource such as project or inventory" msgstr "" -#: awx/main/scheduler/task_manager.py:135 +#: awx/main/scheduler/task_manager.py:144 msgid "" "Job spawned from workflow could not start because it was not in the right " "state or required manual credentials" msgstr "" -#: awx/main/scheduler/task_manager.py:176 +#: awx/main/scheduler/task_manager.py:185 msgid "No error handling paths found, marking workflow as failed" msgstr "" -#: awx/main/scheduler/task_manager.py:508 +#: awx/main/scheduler/task_manager.py:523 #, python-brace-format msgid "The approval node {name} ({pk}) has expired after {timeout} seconds." msgstr "" -#: awx/main/tasks.py:1046 +#: awx/main/tasks.py:599 +msgid "" +"Scheduled job could not start because it was not in the " +"right state or required manual credentials" +msgstr "" + +#: awx/main/tasks.py:1070 msgid "Invalid virtual environment selected: {}" msgstr "" -#: awx/main/tasks.py:1849 +#: awx/main/tasks.py:1857 msgid "Job could not start because it does not have a valid inventory." msgstr "" -#: awx/main/tasks.py:1853 +#: awx/main/tasks.py:1861 msgid "Job could not start because it does not have a valid project." msgstr "" -#: awx/main/tasks.py:1858 +#: awx/main/tasks.py:1866 msgid "" "The project revision for this job template is unknown due to a failed update." msgstr "" @@ -4822,53 +4870,53 @@ msgstr "" msgid "Unable to convert \"%s\" to boolean" msgstr "" -#: awx/main/utils/common.py:261 +#: awx/main/utils/common.py:248 #, python-format msgid "Unsupported SCM type \"%s\"" msgstr "" -#: awx/main/utils/common.py:268 awx/main/utils/common.py:280 -#: awx/main/utils/common.py:299 +#: awx/main/utils/common.py:255 awx/main/utils/common.py:267 +#: awx/main/utils/common.py:286 #, python-format msgid "Invalid %s URL" msgstr "" -#: awx/main/utils/common.py:270 awx/main/utils/common.py:309 +#: awx/main/utils/common.py:257 awx/main/utils/common.py:297 #, python-format msgid "Unsupported %s URL" msgstr "" -#: awx/main/utils/common.py:311 +#: awx/main/utils/common.py:299 #, python-format msgid "Unsupported host \"%s\" for file:// URL" msgstr "" -#: awx/main/utils/common.py:313 +#: awx/main/utils/common.py:301 #, python-format msgid "Host is required for %s URL" msgstr "" -#: awx/main/utils/common.py:331 +#: awx/main/utils/common.py:319 #, python-format msgid "Username must be \"git\" for SSH access to %s." msgstr "" -#: awx/main/utils/common.py:337 +#: awx/main/utils/common.py:325 #, python-format msgid "Username must be \"hg\" for SSH access to %s." msgstr "" -#: awx/main/utils/common.py:668 +#: awx/main/utils/common.py:656 #, python-brace-format msgid "Input type `{data_type}` is not a dictionary" msgstr "" -#: awx/main/utils/common.py:701 +#: awx/main/utils/common.py:689 #, python-brace-format msgid "Variables not compatible with JSON standard (error: {json_error})" msgstr "" -#: awx/main/utils/common.py:707 +#: awx/main/utils/common.py:695 #, python-brace-format msgid "" "Cannot parse as JSON (error: {json_error}) or YAML (error: {yaml_error})." @@ -4980,290 +5028,6 @@ msgstr "" msgid "A server error has occurred." msgstr "" -#: awx/settings/defaults.py:683 -msgid "US East (Northern Virginia)" -msgstr "" - -#: awx/settings/defaults.py:684 -msgid "US East (Ohio)" -msgstr "" - -#: awx/settings/defaults.py:685 -msgid "US West (Oregon)" -msgstr "" - -#: awx/settings/defaults.py:686 -msgid "US West (Northern California)" -msgstr "" - -#: awx/settings/defaults.py:687 -msgid "Canada (Central)" -msgstr "" - -#: awx/settings/defaults.py:688 -msgid "EU (Frankfurt)" -msgstr "" - -#: awx/settings/defaults.py:689 -msgid "EU (Ireland)" -msgstr "" - -#: awx/settings/defaults.py:690 -msgid "EU (London)" -msgstr "" - -#: awx/settings/defaults.py:691 -msgid "Asia Pacific (Singapore)" -msgstr "" - -#: awx/settings/defaults.py:692 -msgid "Asia Pacific (Sydney)" -msgstr "" - -#: awx/settings/defaults.py:693 -msgid "Asia Pacific (Tokyo)" -msgstr "" - -#: awx/settings/defaults.py:694 -msgid "Asia Pacific (Seoul)" -msgstr "" - -#: awx/settings/defaults.py:695 -msgid "Asia Pacific (Mumbai)" -msgstr "" - -#: awx/settings/defaults.py:696 -msgid "South America (Sao Paulo)" -msgstr "" - -#: awx/settings/defaults.py:697 -msgid "US West (GovCloud)" -msgstr "" - -#: awx/settings/defaults.py:698 -msgid "China (Beijing)" -msgstr "" - -#: awx/settings/defaults.py:747 -msgid "US East 1 (B)" -msgstr "" - -#: awx/settings/defaults.py:748 -msgid "US East 1 (C)" -msgstr "" - -#: awx/settings/defaults.py:749 -msgid "US East 1 (D)" -msgstr "" - -#: awx/settings/defaults.py:750 -msgid "US East 4 (A)" -msgstr "" - -#: awx/settings/defaults.py:751 -msgid "US East 4 (B)" -msgstr "" - -#: awx/settings/defaults.py:752 -msgid "US East 4 (C)" -msgstr "" - -#: awx/settings/defaults.py:753 -msgid "US Central (A)" -msgstr "" - -#: awx/settings/defaults.py:754 -msgid "US Central (B)" -msgstr "" - -#: awx/settings/defaults.py:755 -msgid "US Central (C)" -msgstr "" - -#: awx/settings/defaults.py:756 -msgid "US Central (F)" -msgstr "" - -#: awx/settings/defaults.py:757 -msgid "US West (A)" -msgstr "" - -#: awx/settings/defaults.py:758 -msgid "US West (B)" -msgstr "" - -#: awx/settings/defaults.py:759 -msgid "US West (C)" -msgstr "" - -#: awx/settings/defaults.py:760 -msgid "Europe West 1 (B)" -msgstr "" - -#: awx/settings/defaults.py:761 -msgid "Europe West 1 (C)" -msgstr "" - -#: awx/settings/defaults.py:762 -msgid "Europe West 1 (D)" -msgstr "" - -#: awx/settings/defaults.py:763 -msgid "Europe West 2 (A)" -msgstr "" - -#: awx/settings/defaults.py:764 -msgid "Europe West 2 (B)" -msgstr "" - -#: awx/settings/defaults.py:765 -msgid "Europe West 2 (C)" -msgstr "" - -#: awx/settings/defaults.py:766 -msgid "Asia East (A)" -msgstr "" - -#: awx/settings/defaults.py:767 -msgid "Asia East (B)" -msgstr "" - -#: awx/settings/defaults.py:768 -msgid "Asia East (C)" -msgstr "" - -#: awx/settings/defaults.py:769 -msgid "Asia Southeast (A)" -msgstr "" - -#: awx/settings/defaults.py:770 -msgid "Asia Southeast (B)" -msgstr "" - -#: awx/settings/defaults.py:771 -msgid "Asia Northeast (A)" -msgstr "" - -#: awx/settings/defaults.py:772 -msgid "Asia Northeast (B)" -msgstr "" - -#: awx/settings/defaults.py:773 -msgid "Asia Northeast (C)" -msgstr "" - -#: awx/settings/defaults.py:774 -msgid "Australia Southeast (A)" -msgstr "" - -#: awx/settings/defaults.py:775 -msgid "Australia Southeast (B)" -msgstr "" - -#: awx/settings/defaults.py:776 -msgid "Australia Southeast (C)" -msgstr "" - -#: awx/settings/defaults.py:798 -msgid "US East" -msgstr "" - -#: awx/settings/defaults.py:799 -msgid "US East 2" -msgstr "" - -#: awx/settings/defaults.py:800 -msgid "US Central" -msgstr "" - -#: awx/settings/defaults.py:801 -msgid "US North Central" -msgstr "" - -#: awx/settings/defaults.py:802 -msgid "US South Central" -msgstr "" - -#: awx/settings/defaults.py:803 -msgid "US West Central" -msgstr "" - -#: awx/settings/defaults.py:804 -msgid "US West" -msgstr "" - -#: awx/settings/defaults.py:805 -msgid "US West 2" -msgstr "" - -#: awx/settings/defaults.py:806 -msgid "Canada East" -msgstr "" - -#: awx/settings/defaults.py:807 -msgid "Canada Central" -msgstr "" - -#: awx/settings/defaults.py:808 -msgid "Brazil South" -msgstr "" - -#: awx/settings/defaults.py:809 -msgid "Europe North" -msgstr "" - -#: awx/settings/defaults.py:810 -msgid "Europe West" -msgstr "" - -#: awx/settings/defaults.py:811 -msgid "UK West" -msgstr "" - -#: awx/settings/defaults.py:812 -msgid "UK South" -msgstr "" - -#: awx/settings/defaults.py:813 -msgid "Asia East" -msgstr "" - -#: awx/settings/defaults.py:814 -msgid "Asia Southeast" -msgstr "" - -#: awx/settings/defaults.py:815 -msgid "Australia East" -msgstr "" - -#: awx/settings/defaults.py:816 -msgid "Australia Southeast" -msgstr "" - -#: awx/settings/defaults.py:817 -msgid "India West" -msgstr "" - -#: awx/settings/defaults.py:818 -msgid "India South" -msgstr "" - -#: awx/settings/defaults.py:819 -msgid "Japan East" -msgstr "" - -#: awx/settings/defaults.py:820 -msgid "Japan West" -msgstr "" - -#: awx/settings/defaults.py:821 -msgid "Korea Central" -msgstr "" - -#: awx/settings/defaults.py:822 -msgid "Korea South" -msgstr "" - #: awx/sso/apps.py:9 msgid "Single Sign-On" msgstr "" @@ -5536,7 +5300,7 @@ msgid "Hostname of TACACS+ server." msgstr "" #: awx/sso/conf.py:480 awx/sso/conf.py:492 awx/sso/conf.py:504 -#: awx/sso/conf.py:516 awx/sso/conf.py:527 awx/sso/models.py:15 +#: awx/sso/conf.py:516 awx/sso/conf.py:528 awx/sso/models.py:15 msgid "TACACS+" msgstr "" @@ -5564,159 +5328,159 @@ msgstr "" msgid "TACACS+ session timeout value in seconds, 0 disables timeout." msgstr "" -#: awx/sso/conf.py:525 +#: awx/sso/conf.py:526 msgid "TACACS+ Authentication Protocol" msgstr "" -#: awx/sso/conf.py:526 +#: awx/sso/conf.py:527 msgid "Choose the authentication protocol used by TACACS+ client." msgstr "" -#: awx/sso/conf.py:540 +#: awx/sso/conf.py:541 msgid "Google OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:541 awx/sso/conf.py:634 awx/sso/conf.py:699 +#: awx/sso/conf.py:542 awx/sso/conf.py:635 awx/sso/conf.py:700 msgid "" "Provide this URL as the callback URL for your application as part of your " "registration process. Refer to the Ansible Tower documentation for more " "detail." msgstr "" -#: awx/sso/conf.py:544 awx/sso/conf.py:556 awx/sso/conf.py:568 -#: awx/sso/conf.py:581 awx/sso/conf.py:595 awx/sso/conf.py:607 -#: awx/sso/conf.py:619 +#: awx/sso/conf.py:545 awx/sso/conf.py:557 awx/sso/conf.py:569 +#: awx/sso/conf.py:582 awx/sso/conf.py:596 awx/sso/conf.py:608 +#: awx/sso/conf.py:620 msgid "Google OAuth2" msgstr "" -#: awx/sso/conf.py:554 +#: awx/sso/conf.py:555 msgid "Google OAuth2 Key" msgstr "" -#: awx/sso/conf.py:555 +#: awx/sso/conf.py:556 msgid "The OAuth2 key from your web application." msgstr "" -#: awx/sso/conf.py:566 +#: awx/sso/conf.py:567 msgid "Google OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:567 +#: awx/sso/conf.py:568 msgid "The OAuth2 secret from your web application." msgstr "" -#: awx/sso/conf.py:578 -msgid "Google OAuth2 Whitelisted Domains" +#: awx/sso/conf.py:579 +msgid "Google OAuth2 Allowed Domains" msgstr "" -#: awx/sso/conf.py:579 +#: awx/sso/conf.py:580 msgid "" "Update this setting to restrict the domains who are allowed to login using " "Google OAuth2." msgstr "" -#: awx/sso/conf.py:590 +#: awx/sso/conf.py:591 msgid "Google OAuth2 Extra Arguments" msgstr "" -#: awx/sso/conf.py:591 +#: awx/sso/conf.py:592 msgid "" "Extra arguments for Google OAuth2 login. You can restrict it to only allow a " "single domain to authenticate, even if the user is logged in with multple " "Google accounts. Refer to the Ansible Tower documentation for more detail." msgstr "" -#: awx/sso/conf.py:605 +#: awx/sso/conf.py:606 msgid "Google OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:617 +#: awx/sso/conf.py:618 msgid "Google OAuth2 Team Map" msgstr "" -#: awx/sso/conf.py:633 +#: awx/sso/conf.py:634 msgid "GitHub OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:637 awx/sso/conf.py:649 awx/sso/conf.py:660 -#: awx/sso/conf.py:672 awx/sso/conf.py:684 +#: awx/sso/conf.py:638 awx/sso/conf.py:650 awx/sso/conf.py:661 +#: awx/sso/conf.py:673 awx/sso/conf.py:685 msgid "GitHub OAuth2" msgstr "" -#: awx/sso/conf.py:647 +#: awx/sso/conf.py:648 msgid "GitHub OAuth2 Key" msgstr "" -#: awx/sso/conf.py:648 +#: awx/sso/conf.py:649 msgid "The OAuth2 key (Client ID) from your GitHub developer application." msgstr "" -#: awx/sso/conf.py:658 +#: awx/sso/conf.py:659 msgid "GitHub OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:659 +#: awx/sso/conf.py:660 msgid "" "The OAuth2 secret (Client Secret) from your GitHub developer application." msgstr "" -#: awx/sso/conf.py:670 +#: awx/sso/conf.py:671 msgid "GitHub OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:682 +#: awx/sso/conf.py:683 msgid "GitHub OAuth2 Team Map" msgstr "" -#: awx/sso/conf.py:698 +#: awx/sso/conf.py:699 msgid "GitHub Organization OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:702 awx/sso/conf.py:714 awx/sso/conf.py:725 -#: awx/sso/conf.py:738 awx/sso/conf.py:749 awx/sso/conf.py:761 +#: awx/sso/conf.py:703 awx/sso/conf.py:715 awx/sso/conf.py:726 +#: awx/sso/conf.py:739 awx/sso/conf.py:750 awx/sso/conf.py:762 msgid "GitHub Organization OAuth2" msgstr "" -#: awx/sso/conf.py:712 +#: awx/sso/conf.py:713 msgid "GitHub Organization OAuth2 Key" msgstr "" -#: awx/sso/conf.py:713 awx/sso/conf.py:791 +#: awx/sso/conf.py:714 awx/sso/conf.py:792 msgid "The OAuth2 key (Client ID) from your GitHub organization application." msgstr "" -#: awx/sso/conf.py:723 +#: awx/sso/conf.py:724 msgid "GitHub Organization OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:724 awx/sso/conf.py:802 +#: awx/sso/conf.py:725 awx/sso/conf.py:803 msgid "" "The OAuth2 secret (Client Secret) from your GitHub organization application." msgstr "" -#: awx/sso/conf.py:735 +#: awx/sso/conf.py:736 msgid "GitHub Organization Name" msgstr "" -#: awx/sso/conf.py:736 +#: awx/sso/conf.py:737 msgid "" "The name of your GitHub organization, as used in your organization's URL: " "https://github.com//." msgstr "" -#: awx/sso/conf.py:747 +#: awx/sso/conf.py:748 msgid "GitHub Organization OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:759 +#: awx/sso/conf.py:760 msgid "GitHub Organization OAuth2 Team Map" msgstr "" -#: awx/sso/conf.py:775 +#: awx/sso/conf.py:776 msgid "GitHub Team OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:776 +#: awx/sso/conf.py:777 msgid "" "Create an organization-owned application at https://github.com/organizations/" "/settings/applications and obtain an OAuth2 key (Client ID) and " @@ -5724,172 +5488,182 @@ msgid "" "application." msgstr "" -#: awx/sso/conf.py:780 awx/sso/conf.py:792 awx/sso/conf.py:803 -#: awx/sso/conf.py:816 awx/sso/conf.py:827 awx/sso/conf.py:839 +#: awx/sso/conf.py:781 awx/sso/conf.py:793 awx/sso/conf.py:804 +#: awx/sso/conf.py:817 awx/sso/conf.py:828 awx/sso/conf.py:840 msgid "GitHub Team OAuth2" msgstr "" -#: awx/sso/conf.py:790 +#: awx/sso/conf.py:791 msgid "GitHub Team OAuth2 Key" msgstr "" -#: awx/sso/conf.py:801 +#: awx/sso/conf.py:802 msgid "GitHub Team OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:813 +#: awx/sso/conf.py:814 msgid "GitHub Team ID" msgstr "" -#: awx/sso/conf.py:814 +#: awx/sso/conf.py:815 msgid "" "Find the numeric team ID using the Github API: http://fabian-kostadinov." "github.io/2015/01/16/how-to-find-a-github-team-id/." msgstr "" -#: awx/sso/conf.py:825 +#: awx/sso/conf.py:826 msgid "GitHub Team OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:837 +#: awx/sso/conf.py:838 msgid "GitHub Team OAuth2 Team Map" msgstr "" -#: awx/sso/conf.py:853 +#: awx/sso/conf.py:854 msgid "Azure AD OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:854 +#: awx/sso/conf.py:855 msgid "" "Provide this URL as the callback URL for your application as part of your " "registration process. Refer to the Ansible Tower documentation for more " "detail. " msgstr "" -#: awx/sso/conf.py:857 awx/sso/conf.py:869 awx/sso/conf.py:880 -#: awx/sso/conf.py:892 awx/sso/conf.py:904 +#: awx/sso/conf.py:858 awx/sso/conf.py:870 awx/sso/conf.py:881 +#: awx/sso/conf.py:893 awx/sso/conf.py:905 msgid "Azure AD OAuth2" msgstr "" -#: awx/sso/conf.py:867 +#: awx/sso/conf.py:868 msgid "Azure AD OAuth2 Key" msgstr "" -#: awx/sso/conf.py:868 +#: awx/sso/conf.py:869 msgid "The OAuth2 key (Client ID) from your Azure AD application." msgstr "" -#: awx/sso/conf.py:878 +#: awx/sso/conf.py:879 msgid "Azure AD OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:879 +#: awx/sso/conf.py:880 msgid "The OAuth2 secret (Client Secret) from your Azure AD application." msgstr "" -#: awx/sso/conf.py:890 +#: awx/sso/conf.py:891 msgid "Azure AD OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:902 +#: awx/sso/conf.py:903 msgid "Azure AD OAuth2 Team Map" msgstr "" #: awx/sso/conf.py:927 -msgid "SAML Assertion Consumer Service (ACS) URL" +msgid "Automatically Create Organizations and Teams on SAML Login" msgstr "" #: awx/sso/conf.py:928 msgid "" +"When enabled (the default), mapped Organizations and Teams will be created " +"automatically on successful SAML login." +msgstr "" + +#: awx/sso/conf.py:930 awx/sso/conf.py:943 awx/sso/conf.py:956 +#: awx/sso/conf.py:969 awx/sso/conf.py:983 awx/sso/conf.py:996 +#: awx/sso/conf.py:1008 awx/sso/conf.py:1028 awx/sso/conf.py:1045 +#: awx/sso/conf.py:1063 awx/sso/conf.py:1098 awx/sso/conf.py:1129 +#: awx/sso/conf.py:1142 awx/sso/conf.py:1158 awx/sso/conf.py:1170 +#: awx/sso/conf.py:1182 awx/sso/conf.py:1201 awx/sso/models.py:16 +msgid "SAML" +msgstr "" + +#: awx/sso/conf.py:939 +msgid "SAML Assertion Consumer Service (ACS) URL" +msgstr "" + +#: awx/sso/conf.py:940 +msgid "" "Register Tower as a service provider (SP) with each identity provider (IdP) " "you have configured. Provide your SP Entity ID and this ACS URL for your " "application." msgstr "" -#: awx/sso/conf.py:931 awx/sso/conf.py:944 awx/sso/conf.py:957 -#: awx/sso/conf.py:971 awx/sso/conf.py:984 awx/sso/conf.py:996 -#: awx/sso/conf.py:1016 awx/sso/conf.py:1033 awx/sso/conf.py:1051 -#: awx/sso/conf.py:1086 awx/sso/conf.py:1117 awx/sso/conf.py:1130 -#: awx/sso/conf.py:1146 awx/sso/conf.py:1158 awx/sso/conf.py:1170 -#: awx/sso/conf.py:1189 awx/sso/models.py:16 -msgid "SAML" -msgstr "" - -#: awx/sso/conf.py:941 +#: awx/sso/conf.py:953 msgid "SAML Service Provider Metadata URL" msgstr "" -#: awx/sso/conf.py:942 +#: awx/sso/conf.py:954 msgid "" "If your identity provider (IdP) allows uploading an XML metadata file, you " "can download one from this URL." msgstr "" -#: awx/sso/conf.py:953 +#: awx/sso/conf.py:965 msgid "SAML Service Provider Entity ID" msgstr "" -#: awx/sso/conf.py:954 +#: awx/sso/conf.py:966 msgid "" "The application-defined unique identifier used as the audience of the SAML " "service provider (SP) configuration. This is usually the URL for Tower." msgstr "" -#: awx/sso/conf.py:968 +#: awx/sso/conf.py:980 msgid "SAML Service Provider Public Certificate" msgstr "" -#: awx/sso/conf.py:969 +#: awx/sso/conf.py:981 msgid "" "Create a keypair for Tower to use as a service provider (SP) and include the " "certificate content here." msgstr "" -#: awx/sso/conf.py:981 +#: awx/sso/conf.py:993 msgid "SAML Service Provider Private Key" msgstr "" -#: awx/sso/conf.py:982 +#: awx/sso/conf.py:994 msgid "" "Create a keypair for Tower to use as a service provider (SP) and include the " "private key content here." msgstr "" -#: awx/sso/conf.py:993 +#: awx/sso/conf.py:1005 msgid "SAML Service Provider Organization Info" msgstr "" -#: awx/sso/conf.py:994 +#: awx/sso/conf.py:1006 msgid "" "Provide the URL, display name, and the name of your app. Refer to the " "Ansible Tower documentation for example syntax." msgstr "" -#: awx/sso/conf.py:1012 +#: awx/sso/conf.py:1024 msgid "SAML Service Provider Technical Contact" msgstr "" -#: awx/sso/conf.py:1013 +#: awx/sso/conf.py:1025 msgid "" "Provide the name and email address of the technical contact for your service " "provider. Refer to the Ansible Tower documentation for example syntax." msgstr "" -#: awx/sso/conf.py:1029 +#: awx/sso/conf.py:1041 msgid "SAML Service Provider Support Contact" msgstr "" -#: awx/sso/conf.py:1030 +#: awx/sso/conf.py:1042 msgid "" "Provide the name and email address of the support contact for your service " "provider. Refer to the Ansible Tower documentation for example syntax." msgstr "" -#: awx/sso/conf.py:1045 +#: awx/sso/conf.py:1057 msgid "SAML Enabled Identity Providers" msgstr "" -#: awx/sso/conf.py:1046 +#: awx/sso/conf.py:1058 msgid "" "Configure the Entity ID, SSO URL and certificate for each identity provider " "(IdP) in use. Multiple SAML IdPs are supported. Some IdPs may provide user " @@ -5898,57 +5672,57 @@ msgid "" "additional details and syntax." msgstr "" -#: awx/sso/conf.py:1082 +#: awx/sso/conf.py:1094 msgid "SAML Security Config" msgstr "" -#: awx/sso/conf.py:1083 +#: awx/sso/conf.py:1095 msgid "" "A dict of key value pairs that are passed to the underlying python-saml " "security setting https://github.com/onelogin/python-saml#settings" msgstr "" -#: awx/sso/conf.py:1114 +#: awx/sso/conf.py:1126 msgid "SAML Service Provider extra configuration data" msgstr "" -#: awx/sso/conf.py:1115 +#: awx/sso/conf.py:1127 msgid "" "A dict of key value pairs to be passed to the underlying python-saml Service " "Provider configuration setting." msgstr "" -#: awx/sso/conf.py:1127 +#: awx/sso/conf.py:1139 msgid "SAML IDP to extra_data attribute mapping" msgstr "" -#: awx/sso/conf.py:1128 +#: awx/sso/conf.py:1140 msgid "" "A list of tuples that maps IDP attributes to extra_attributes. Each " "attribute will be a list of values, even if only 1 value." msgstr "" -#: awx/sso/conf.py:1144 +#: awx/sso/conf.py:1156 msgid "SAML Organization Map" msgstr "" -#: awx/sso/conf.py:1156 +#: awx/sso/conf.py:1168 msgid "SAML Team Map" msgstr "" -#: awx/sso/conf.py:1168 +#: awx/sso/conf.py:1180 msgid "SAML Organization Attribute Mapping" msgstr "" -#: awx/sso/conf.py:1169 +#: awx/sso/conf.py:1181 msgid "Used to translate user organization membership into Tower." msgstr "" -#: awx/sso/conf.py:1187 +#: awx/sso/conf.py:1199 msgid "SAML Team Attribute Mapping" msgstr "" -#: awx/sso/conf.py:1188 +#: awx/sso/conf.py:1200 msgid "Used to translate user team membership into Tower." msgstr "" @@ -6015,12 +5789,12 @@ msgstr "" msgid "Invalid language code(s) for org info: {invalid_lang_codes}." msgstr "" -#: awx/sso/pipeline.py:27 +#: awx/sso/pipeline.py:28 #, python-brace-format msgid "An account cannot be found for {0}" msgstr "" -#: awx/sso/pipeline.py:33 +#: awx/sso/pipeline.py:34 msgid "Your account is inactive" msgstr "" @@ -6092,8 +5866,8 @@ msgstr "" msgid "" "If needed, you can add specific information (such as a legal notice or a " "disclaimer) to a text box in the login modal using this setting. Any content " -"added must be in plain text, as custom HTML or other markup languages are " -"not supported." +"added must be in plain text or an HTML fragment, as other markup languages " +"are not supported." msgstr "" #: awx/ui/conf.py:45 diff --git a/awx/locale/en-us/LC_MESSAGES/django.po b/awx/locale/en-us/LC_MESSAGES/django.po index dfdd2e72ef..3d2cf41999 100644 --- a/awx/locale/en-us/LC_MESSAGES/django.po +++ b/awx/locale/en-us/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-16 02:52+0000\n" +"POT-Creation-Date: 2020-10-05 19:43+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -27,35 +27,42 @@ msgid "" "again." msgstr "" -#: awx/api/conf.py:17 awx/api/conf.py:26 awx/api/conf.py:34 awx/api/conf.py:50 -#: awx/api/conf.py:62 awx/api/conf.py:74 awx/sso/conf.py:97 awx/sso/conf.py:108 +#: awx/api/conf.py:17 awx/api/conf.py:27 awx/api/conf.py:35 awx/api/conf.py:51 +#: awx/api/conf.py:64 awx/api/conf.py:76 awx/sso/conf.py:97 awx/sso/conf.py:108 #: awx/sso/conf.py:120 awx/sso/conf.py:135 msgid "Authentication" msgstr "" -#: awx/api/conf.py:24 -msgid "Maximum number of simultaneous logged in sessions" +#: awx/api/conf.py:19 awx/api/conf.py:53 awx/main/conf.py:256 +#: awx/main/conf.py:268 awx/main/conf.py:281 awx/main/conf.py:503 +#: awx/main/conf.py:516 awx/main/conf.py:529 awx/main/conf.py:544 +#: awx/main/conf.py:682 awx/main/conf.py:764 awx/sso/conf.py:518 +msgid "seconds" msgstr "" #: awx/api/conf.py:25 +msgid "Maximum number of simultaneous logged in sessions" +msgstr "" + +#: awx/api/conf.py:26 msgid "" "Maximum number of simultaneous logged in sessions a user may have. To " "disable enter -1." msgstr "" -#: awx/api/conf.py:32 +#: awx/api/conf.py:33 msgid "Enable HTTP Basic Auth" msgstr "" -#: awx/api/conf.py:33 +#: awx/api/conf.py:34 msgid "Enable HTTP Basic Auth for the API Browser." msgstr "" -#: awx/api/conf.py:43 +#: awx/api/conf.py:44 msgid "OAuth 2 Timeout Settings" msgstr "" -#: awx/api/conf.py:44 +#: awx/api/conf.py:45 msgid "" "Dictionary for customizing OAuth 2 timeouts, available items are " "`ACCESS_TOKEN_EXPIRE_SECONDS`, the duration of access tokens in the number " @@ -65,11 +72,11 @@ msgid "" "expired access tokens, in the number of seconds." msgstr "" -#: awx/api/conf.py:57 +#: awx/api/conf.py:59 msgid "Allow External Users to Create OAuth2 Tokens" msgstr "" -#: awx/api/conf.py:58 +#: awx/api/conf.py:60 msgid "" "For security reasons, users from external auth providers (LDAP, SAML, SSO, " "Radius, and others) are not allowed to create OAuth2 tokens. To change this " @@ -77,11 +84,11 @@ msgid "" "setting is toggled off." msgstr "" -#: awx/api/conf.py:71 +#: awx/api/conf.py:73 msgid "Login redirect override URL" msgstr "" -#: awx/api/conf.py:72 +#: awx/api/conf.py:74 msgid "" "URL to which unauthorized users will be redirected to log in. If blank, " "users will be sent to the Tower login page." @@ -126,27 +133,27 @@ msgstr "" msgid "Invalid {field_name} id: {field_id}" msgstr "" -#: awx/api/filters.py:333 +#: awx/api/filters.py:338 msgid "" "Cannot apply role_level filter to this list because its model does not use " "roles for access control." msgstr "" -#: awx/api/generics.py:182 +#: awx/api/generics.py:183 msgid "" "You did not use correct Content-Type in your HTTP request. If you are using " "our REST API, the Content-Type must be application/json" msgstr "" -#: awx/api/generics.py:623 awx/api/generics.py:685 +#: awx/api/generics.py:647 awx/api/generics.py:709 msgid "\"id\" field must be an integer." msgstr "" -#: awx/api/generics.py:682 +#: awx/api/generics.py:706 msgid "\"id\" is required to disassociate" msgstr "" -#: awx/api/generics.py:733 +#: awx/api/generics.py:757 msgid "{} 'id' field is missing." msgstr "" @@ -248,1127 +255,1161 @@ msgid "" "saved to the database." msgstr "" -#: awx/api/serializers.py:878 +#: awx/api/serializers.py:880 msgid "Write-only field used to change the password." msgstr "" -#: awx/api/serializers.py:880 +#: awx/api/serializers.py:882 msgid "Set if the account is managed by an external service" msgstr "" -#: awx/api/serializers.py:907 +#: awx/api/serializers.py:909 msgid "Password required for new User." msgstr "" -#: awx/api/serializers.py:992 +#: awx/api/serializers.py:994 #, python-format msgid "Unable to change %s on user managed by LDAP." msgstr "" -#: awx/api/serializers.py:1088 +#: awx/api/serializers.py:1090 msgid "Must be a simple space-separated string with allowed scopes {}." msgstr "" -#: awx/api/serializers.py:1186 +#: awx/api/serializers.py:1188 msgid "Authorization Grant Type" msgstr "" -#: awx/api/serializers.py:1188 awx/main/credential_plugins/azure_kv.py:30 -#: awx/main/models/credential/__init__.py:964 +#: awx/api/serializers.py:1190 awx/main/credential_plugins/azure_kv.py:30 +#: awx/main/models/credential/__init__.py:972 msgid "Client Secret" msgstr "" -#: awx/api/serializers.py:1191 +#: awx/api/serializers.py:1193 msgid "Client Type" msgstr "" -#: awx/api/serializers.py:1194 +#: awx/api/serializers.py:1196 msgid "Redirect URIs" msgstr "" -#: awx/api/serializers.py:1197 +#: awx/api/serializers.py:1199 msgid "Skip Authorization" msgstr "" -#: awx/api/serializers.py:1303 +#: awx/api/serializers.py:1306 msgid "Cannot change max_hosts." msgstr "" -#: awx/api/serializers.py:1336 +#: awx/api/serializers.py:1339 msgid "This path is already being used by another manual project." msgstr "" -#: awx/api/serializers.py:1338 +#: awx/api/serializers.py:1341 +msgid "SCM branch cannot be used with archive projects." +msgstr "" + +#: awx/api/serializers.py:1343 msgid "SCM refspec can only be used with git projects." msgstr "" -#: awx/api/serializers.py:1415 +#: awx/api/serializers.py:1420 msgid "" "One or more job templates depend on branch override behavior for this " "project (ids: {})." msgstr "" -#: awx/api/serializers.py:1422 +#: awx/api/serializers.py:1427 msgid "Update options must be set to false for manual projects." msgstr "" -#: awx/api/serializers.py:1428 +#: awx/api/serializers.py:1433 msgid "Array of playbooks available within this project." msgstr "" -#: awx/api/serializers.py:1447 +#: awx/api/serializers.py:1452 msgid "" "Array of inventory files and directories available within this project, not " "comprehensive." msgstr "" -#: awx/api/serializers.py:1495 awx/api/serializers.py:3034 -#: awx/api/serializers.py:3246 +#: awx/api/serializers.py:1500 awx/api/serializers.py:3089 +#: awx/api/serializers.py:3301 msgid "A count of hosts uniquely assigned to each status." msgstr "" -#: awx/api/serializers.py:1498 awx/api/serializers.py:3037 +#: awx/api/serializers.py:1503 awx/api/serializers.py:3092 msgid "A count of all plays and tasks for the job run." msgstr "" -#: awx/api/serializers.py:1625 +#: awx/api/serializers.py:1630 msgid "Smart inventories must specify host_filter" msgstr "" -#: awx/api/serializers.py:1713 +#: awx/api/serializers.py:1722 #, python-format msgid "Invalid port specification: %s" msgstr "" -#: awx/api/serializers.py:1724 +#: awx/api/serializers.py:1733 msgid "Cannot create Host for Smart Inventory" msgstr "" -#: awx/api/serializers.py:1808 +#: awx/api/serializers.py:1751 +msgid "A Group with that name already exists." +msgstr "" + +#: awx/api/serializers.py:1822 +msgid "A Host with that name already exists." +msgstr "" + +#: awx/api/serializers.py:1827 msgid "Invalid group name." msgstr "" -#: awx/api/serializers.py:1813 +#: awx/api/serializers.py:1832 msgid "Cannot create Group for Smart Inventory" msgstr "" -#: awx/api/serializers.py:1888 +#: awx/api/serializers.py:1907 msgid "" "Script must begin with a hashbang sequence: i.e.... #!/usr/bin/env python" msgstr "" -#: awx/api/serializers.py:1917 +#: awx/api/serializers.py:1936 msgid "Cloud credential to use for inventory updates." msgstr "" -#: awx/api/serializers.py:1938 +#: awx/api/serializers.py:1957 msgid "`{}` is a prohibited environment variable" msgstr "" -#: awx/api/serializers.py:1949 +#: awx/api/serializers.py:1968 msgid "If 'source' is 'custom', 'source_script' must be provided." msgstr "" -#: awx/api/serializers.py:1955 +#: awx/api/serializers.py:1974 msgid "Must provide an inventory." msgstr "" -#: awx/api/serializers.py:1959 +#: awx/api/serializers.py:1978 msgid "" "The 'source_script' does not belong to the same organization as the " "inventory." msgstr "" -#: awx/api/serializers.py:1961 +#: awx/api/serializers.py:1980 msgid "'source_script' doesn't exist." msgstr "" -#: awx/api/serializers.py:2063 +#: awx/api/serializers.py:2082 msgid "Cannot use manual project for SCM-based inventory." msgstr "" -#: awx/api/serializers.py:2068 +#: awx/api/serializers.py:2087 msgid "Setting not compatible with existing schedules." msgstr "" -#: awx/api/serializers.py:2073 +#: awx/api/serializers.py:2092 msgid "Cannot create Inventory Source for Smart Inventory" msgstr "" -#: awx/api/serializers.py:2121 +#: awx/api/serializers.py:2140 msgid "Project required for scm type sources." msgstr "" -#: awx/api/serializers.py:2130 +#: awx/api/serializers.py:2149 #, python-format msgid "Cannot set %s if not SCM type." msgstr "" -#: awx/api/serializers.py:2200 +#: awx/api/serializers.py:2219 msgid "The project used for this job." msgstr "" -#: awx/api/serializers.py:2456 +#: awx/api/serializers.py:2475 msgid "Modifications not allowed for managed credential types" msgstr "" -#: awx/api/serializers.py:2468 +#: awx/api/serializers.py:2487 msgid "" "Modifications to inputs are not allowed for credential types that are in use" msgstr "" -#: awx/api/serializers.py:2473 +#: awx/api/serializers.py:2492 #, python-format msgid "Must be 'cloud' or 'net', not %s" msgstr "" -#: awx/api/serializers.py:2479 +#: awx/api/serializers.py:2498 msgid "'ask_at_runtime' is not supported for custom credentials." msgstr "" -#: awx/api/serializers.py:2527 +#: awx/api/serializers.py:2547 msgid "Credential Type" msgstr "" -#: awx/api/serializers.py:2608 +#: awx/api/serializers.py:2611 +msgid "Modifications not allowed for managed credentials" +msgstr "" + +#: awx/api/serializers.py:2629 awx/api/serializers.py:2703 +msgid "Galaxy credentials must be owned by an Organization." +msgstr "" + +#: awx/api/serializers.py:2646 msgid "" "You cannot change the credential type of the credential, as it may break the " "functionality of the resources using it." msgstr "" -#: awx/api/serializers.py:2620 +#: awx/api/serializers.py:2658 msgid "" "Write-only field used to add user to owner role. If provided, do not give " "either team or organization. Only valid for creation." msgstr "" -#: awx/api/serializers.py:2625 +#: awx/api/serializers.py:2663 msgid "" "Write-only field used to add team to owner role. If provided, do not give " "either user or organization. Only valid for creation." msgstr "" -#: awx/api/serializers.py:2630 +#: awx/api/serializers.py:2668 msgid "" "Inherit permissions from organization roles. If provided on creation, do not " "give either user or team." msgstr "" -#: awx/api/serializers.py:2646 +#: awx/api/serializers.py:2685 msgid "Missing 'user', 'team', or 'organization'." msgstr "" -#: awx/api/serializers.py:2663 +#: awx/api/serializers.py:2690 +msgid "" +"Only one of 'user', 'team', or 'organization' should be provided, received " +"{} fields." +msgstr "" + +#: awx/api/serializers.py:2718 msgid "" "Credential organization must be set and match before assigning to a team" msgstr "" -#: awx/api/serializers.py:2789 +#: awx/api/serializers.py:2844 msgid "This field is required." msgstr "" -#: awx/api/serializers.py:2798 +#: awx/api/serializers.py:2853 msgid "Playbook not found for project." msgstr "" -#: awx/api/serializers.py:2800 +#: awx/api/serializers.py:2855 msgid "Must select playbook for project." msgstr "" -#: awx/api/serializers.py:2802 awx/api/serializers.py:2804 +#: awx/api/serializers.py:2857 awx/api/serializers.py:2859 msgid "Project does not allow overriding branch." msgstr "" -#: awx/api/serializers.py:2841 +#: awx/api/serializers.py:2896 msgid "Must be a Personal Access Token." msgstr "" -#: awx/api/serializers.py:2844 +#: awx/api/serializers.py:2899 msgid "Must match the selected webhook service." msgstr "" -#: awx/api/serializers.py:2915 +#: awx/api/serializers.py:2970 msgid "Cannot enable provisioning callback without an inventory set." msgstr "" -#: awx/api/serializers.py:2918 +#: awx/api/serializers.py:2973 msgid "Must either set a default value or ask to prompt on launch." msgstr "" -#: awx/api/serializers.py:2920 awx/main/models/jobs.py:299 +#: awx/api/serializers.py:2975 awx/main/models/jobs.py:299 msgid "Job Templates must have a project assigned." msgstr "" -#: awx/api/serializers.py:3078 +#: awx/api/serializers.py:3133 msgid "No change to job limit" msgstr "" -#: awx/api/serializers.py:3079 +#: awx/api/serializers.py:3134 msgid "All failed and unreachable hosts" msgstr "" -#: awx/api/serializers.py:3094 +#: awx/api/serializers.py:3149 msgid "Missing passwords needed to start: {}" msgstr "" -#: awx/api/serializers.py:3113 +#: awx/api/serializers.py:3168 msgid "Relaunch by host status not available until job finishes running." msgstr "" -#: awx/api/serializers.py:3127 +#: awx/api/serializers.py:3182 msgid "Job Template Project is missing or undefined." msgstr "" -#: awx/api/serializers.py:3129 +#: awx/api/serializers.py:3184 msgid "Job Template Inventory is missing or undefined." msgstr "" -#: awx/api/serializers.py:3167 +#: awx/api/serializers.py:3222 msgid "Unknown, job may have been ran before launch configurations were saved." msgstr "" -#: awx/api/serializers.py:3238 awx/main/tasks.py:2799 awx/main/tasks.py:2817 +#: awx/api/serializers.py:3293 awx/main/tasks.py:2838 awx/main/tasks.py:2856 msgid "{} are prohibited from use in ad hoc commands." msgstr "" -#: awx/api/serializers.py:3326 awx/api/views/__init__.py:4169 +#: awx/api/serializers.py:3381 awx/api/views/__init__.py:4211 #, python-brace-format msgid "" "Standard Output too large to display ({text_size} bytes), only download " "supported for sizes over {supported_size} bytes." msgstr "" -#: awx/api/serializers.py:3639 +#: awx/api/serializers.py:3694 msgid "Provided variable {} has no database value to replace with." msgstr "" -#: awx/api/serializers.py:3657 +#: awx/api/serializers.py:3712 msgid "\"$encrypted$ is a reserved keyword, may not be used for {}.\"" msgstr "" -#: awx/api/serializers.py:4064 +#: awx/api/serializers.py:4119 msgid "A project is required to run a job." msgstr "" -#: awx/api/serializers.py:4066 +#: awx/api/serializers.py:4121 msgid "Missing a revision to run due to failed project update." msgstr "" -#: awx/api/serializers.py:4070 +#: awx/api/serializers.py:4125 msgid "The inventory associated with this Job Template is being deleted." msgstr "" -#: awx/api/serializers.py:4072 awx/api/serializers.py:4188 +#: awx/api/serializers.py:4127 awx/api/serializers.py:4247 msgid "The provided inventory is being deleted." msgstr "" -#: awx/api/serializers.py:4080 +#: awx/api/serializers.py:4135 msgid "Cannot assign multiple {} credentials." msgstr "" -#: awx/api/serializers.py:4084 +#: awx/api/serializers.py:4140 msgid "Cannot assign a Credential of kind `{}`" msgstr "" -#: awx/api/serializers.py:4097 +#: awx/api/serializers.py:4153 msgid "" "Removing {} credential at launch time without replacement is not supported. " "Provided list lacked credential(s): {}." msgstr "" -#: awx/api/serializers.py:4186 +#: awx/api/serializers.py:4245 msgid "The inventory associated with this Workflow is being deleted." msgstr "" -#: awx/api/serializers.py:4257 +#: awx/api/serializers.py:4316 msgid "Message type '{}' invalid, must be either 'message' or 'body'" msgstr "" -#: awx/api/serializers.py:4263 +#: awx/api/serializers.py:4322 msgid "Expected string for '{}', found {}, " msgstr "" -#: awx/api/serializers.py:4267 +#: awx/api/serializers.py:4326 msgid "Messages cannot contain newlines (found newline in {} event)" msgstr "" -#: awx/api/serializers.py:4273 +#: awx/api/serializers.py:4332 msgid "Expected dict for 'messages' field, found {}" msgstr "" -#: awx/api/serializers.py:4277 +#: awx/api/serializers.py:4336 msgid "" "Event '{}' invalid, must be one of 'started', 'success', 'error', or " "'workflow_approval'" msgstr "" -#: awx/api/serializers.py:4283 +#: awx/api/serializers.py:4342 msgid "Expected dict for event '{}', found {}" msgstr "" -#: awx/api/serializers.py:4288 +#: awx/api/serializers.py:4347 msgid "" "Workflow Approval event '{}' invalid, must be one of 'running', 'approved', " "'timed_out', or 'denied'" msgstr "" -#: awx/api/serializers.py:4295 +#: awx/api/serializers.py:4354 msgid "Expected dict for workflow approval event '{}', found {}" msgstr "" -#: awx/api/serializers.py:4322 +#: awx/api/serializers.py:4381 msgid "Unable to render message '{}': {}" msgstr "" -#: awx/api/serializers.py:4324 +#: awx/api/serializers.py:4383 msgid "Field '{}' unavailable" msgstr "" -#: awx/api/serializers.py:4326 +#: awx/api/serializers.py:4385 msgid "Security error due to field '{}'" msgstr "" -#: awx/api/serializers.py:4346 +#: awx/api/serializers.py:4405 msgid "Webhook body for '{}' should be a json dictionary. Found type '{}'." msgstr "" -#: awx/api/serializers.py:4349 +#: awx/api/serializers.py:4408 msgid "Webhook body for '{}' is not a valid json dictionary ({})." msgstr "" -#: awx/api/serializers.py:4367 +#: awx/api/serializers.py:4426 msgid "" "Missing required fields for Notification Configuration: notification_type" msgstr "" -#: awx/api/serializers.py:4394 +#: awx/api/serializers.py:4453 msgid "No values specified for field '{}'" msgstr "" -#: awx/api/serializers.py:4399 +#: awx/api/serializers.py:4458 msgid "HTTP method must be either 'POST' or 'PUT'." msgstr "" -#: awx/api/serializers.py:4401 +#: awx/api/serializers.py:4460 msgid "Missing required fields for Notification Configuration: {}." msgstr "" -#: awx/api/serializers.py:4404 +#: awx/api/serializers.py:4463 msgid "Configuration field '{}' incorrect type, expected {}." msgstr "" -#: awx/api/serializers.py:4421 +#: awx/api/serializers.py:4480 msgid "Notification body" msgstr "" -#: awx/api/serializers.py:4501 +#: awx/api/serializers.py:4560 msgid "" "Valid DTSTART required in rrule. Value should start with: DTSTART:" "YYYYMMDDTHHMMSSZ" msgstr "" -#: awx/api/serializers.py:4503 +#: awx/api/serializers.py:4562 msgid "" "DTSTART cannot be a naive datetime. Specify ;TZINFO= or YYYYMMDDTHHMMSSZZ." msgstr "" -#: awx/api/serializers.py:4505 +#: awx/api/serializers.py:4564 msgid "Multiple DTSTART is not supported." msgstr "" -#: awx/api/serializers.py:4507 +#: awx/api/serializers.py:4566 msgid "RRULE required in rrule." msgstr "" -#: awx/api/serializers.py:4509 +#: awx/api/serializers.py:4568 msgid "Multiple RRULE is not supported." msgstr "" -#: awx/api/serializers.py:4511 +#: awx/api/serializers.py:4570 msgid "INTERVAL required in rrule." msgstr "" -#: awx/api/serializers.py:4513 +#: awx/api/serializers.py:4572 msgid "SECONDLY is not supported." msgstr "" -#: awx/api/serializers.py:4515 +#: awx/api/serializers.py:4574 msgid "Multiple BYMONTHDAYs not supported." msgstr "" -#: awx/api/serializers.py:4517 +#: awx/api/serializers.py:4576 msgid "Multiple BYMONTHs not supported." msgstr "" -#: awx/api/serializers.py:4519 +#: awx/api/serializers.py:4578 msgid "BYDAY with numeric prefix not supported." msgstr "" -#: awx/api/serializers.py:4521 +#: awx/api/serializers.py:4580 msgid "BYYEARDAY not supported." msgstr "" -#: awx/api/serializers.py:4523 +#: awx/api/serializers.py:4582 msgid "BYWEEKNO not supported." msgstr "" -#: awx/api/serializers.py:4525 +#: awx/api/serializers.py:4584 msgid "RRULE may not contain both COUNT and UNTIL" msgstr "" -#: awx/api/serializers.py:4529 +#: awx/api/serializers.py:4588 msgid "COUNT > 999 is unsupported." msgstr "" -#: awx/api/serializers.py:4535 +#: awx/api/serializers.py:4594 msgid "rrule parsing failed validation: {}" msgstr "" -#: awx/api/serializers.py:4597 +#: awx/api/serializers.py:4656 msgid "Inventory Source must be a cloud resource." msgstr "" -#: awx/api/serializers.py:4599 +#: awx/api/serializers.py:4658 msgid "Manual Project cannot have a schedule set." msgstr "" -#: awx/api/serializers.py:4602 +#: awx/api/serializers.py:4661 msgid "" "Inventory sources with `update_on_project_update` cannot be scheduled. " "Schedule its source project `{}` instead." msgstr "" -#: awx/api/serializers.py:4612 +#: awx/api/serializers.py:4671 msgid "" "Count of jobs in the running or waiting state that are targeted for this " "instance" msgstr "" -#: awx/api/serializers.py:4617 +#: awx/api/serializers.py:4676 msgid "Count of all jobs that target this instance" msgstr "" -#: awx/api/serializers.py:4650 +#: awx/api/serializers.py:4711 msgid "" "Count of jobs in the running or waiting state that are targeted for this " "instance group" msgstr "" -#: awx/api/serializers.py:4655 +#: awx/api/serializers.py:4716 msgid "Count of all jobs that target this instance group" msgstr "" -#: awx/api/serializers.py:4660 +#: awx/api/serializers.py:4721 msgid "Indicates whether instance group controls any other group" msgstr "" -#: awx/api/serializers.py:4664 +#: awx/api/serializers.py:4725 msgid "" "Indicates whether instances in this group are isolated.Isolated groups have " "a designated controller group." msgstr "" -#: awx/api/serializers.py:4669 +#: awx/api/serializers.py:4730 msgid "" "Indicates whether instances in this group are containerized.Containerized " "groups have a designated Openshift or Kubernetes cluster." msgstr "" -#: awx/api/serializers.py:4677 +#: awx/api/serializers.py:4738 msgid "Policy Instance Percentage" msgstr "" -#: awx/api/serializers.py:4678 +#: awx/api/serializers.py:4739 msgid "" "Minimum percentage of all instances that will be automatically assigned to " "this group when new instances come online." msgstr "" -#: awx/api/serializers.py:4683 +#: awx/api/serializers.py:4744 msgid "Policy Instance Minimum" msgstr "" -#: awx/api/serializers.py:4684 +#: awx/api/serializers.py:4745 msgid "" "Static minimum number of Instances that will be automatically assign to this " "group when new instances come online." msgstr "" -#: awx/api/serializers.py:4689 +#: awx/api/serializers.py:4750 msgid "Policy Instance List" msgstr "" -#: awx/api/serializers.py:4690 +#: awx/api/serializers.py:4751 msgid "List of exact-match Instances that will be assigned to this group" msgstr "" -#: awx/api/serializers.py:4716 +#: awx/api/serializers.py:4777 msgid "Duplicate entry {}." msgstr "" -#: awx/api/serializers.py:4718 +#: awx/api/serializers.py:4779 msgid "{} is not a valid hostname of an existing instance." msgstr "" -#: awx/api/serializers.py:4720 awx/api/views/mixin.py:98 +#: awx/api/serializers.py:4781 awx/api/views/mixin.py:98 msgid "" "Isolated instances may not be added or removed from instances groups via the " "API." msgstr "" -#: awx/api/serializers.py:4722 awx/api/views/mixin.py:102 +#: awx/api/serializers.py:4783 awx/api/views/mixin.py:102 msgid "Isolated instance group membership may not be managed via the API." msgstr "" -#: awx/api/serializers.py:4724 awx/api/serializers.py:4729 -#: awx/api/serializers.py:4734 +#: awx/api/serializers.py:4785 awx/api/serializers.py:4790 +#: awx/api/serializers.py:4795 msgid "Containerized instances may not be managed via the API" msgstr "" -#: awx/api/serializers.py:4739 +#: awx/api/serializers.py:4800 msgid "tower instance group name may not be changed." msgstr "" -#: awx/api/serializers.py:4744 +#: awx/api/serializers.py:4805 msgid "Only Kubernetes credentials can be associated with an Instance Group" msgstr "" -#: awx/api/serializers.py:4783 +#: awx/api/serializers.py:4844 msgid "" "When present, shows the field name of the role or relationship that changed." msgstr "" -#: awx/api/serializers.py:4785 +#: awx/api/serializers.py:4846 msgid "" "When present, shows the model on which the role or relationship was defined." msgstr "" -#: awx/api/serializers.py:4818 +#: awx/api/serializers.py:4879 msgid "" "A summary of the new and changed values when an object is created, updated, " "or deleted" msgstr "" -#: awx/api/serializers.py:4820 +#: awx/api/serializers.py:4881 msgid "" "For create, update, and delete events this is the object type that was " "affected. For associate and disassociate events this is the object type " "associated or disassociated with object2." msgstr "" -#: awx/api/serializers.py:4823 +#: awx/api/serializers.py:4884 msgid "" "Unpopulated for create, update, and delete events. For associate and " "disassociate events this is the object type that object1 is being associated " "with." msgstr "" -#: awx/api/serializers.py:4826 +#: awx/api/serializers.py:4887 msgid "The action taken with respect to the given object(s)." msgstr "" -#: awx/api/views/__init__.py:181 +#: awx/api/views/__init__.py:185 +msgid "Not found." +msgstr "" + +#: awx/api/views/__init__.py:193 msgid "Dashboard" msgstr "" -#: awx/api/views/__init__.py:271 +#: awx/api/views/__init__.py:290 msgid "Dashboard Jobs Graphs" msgstr "" -#: awx/api/views/__init__.py:307 +#: awx/api/views/__init__.py:326 #, python-format msgid "Unknown period \"%s\"" msgstr "" -#: awx/api/views/__init__.py:321 +#: awx/api/views/__init__.py:340 msgid "Instances" msgstr "" -#: awx/api/views/__init__.py:329 +#: awx/api/views/__init__.py:348 msgid "Instance Detail" msgstr "" -#: awx/api/views/__init__.py:346 +#: awx/api/views/__init__.py:365 msgid "Instance Jobs" msgstr "" -#: awx/api/views/__init__.py:360 +#: awx/api/views/__init__.py:379 msgid "Instance's Instance Groups" msgstr "" -#: awx/api/views/__init__.py:369 +#: awx/api/views/__init__.py:388 msgid "Instance Groups" msgstr "" -#: awx/api/views/__init__.py:377 +#: awx/api/views/__init__.py:396 msgid "Instance Group Detail" msgstr "" -#: awx/api/views/__init__.py:392 +#: awx/api/views/__init__.py:411 msgid "Isolated Groups can not be removed from the API" msgstr "" -#: awx/api/views/__init__.py:394 +#: awx/api/views/__init__.py:413 msgid "" "Instance Groups acting as a controller for an Isolated Group can not be " "removed from the API" msgstr "" -#: awx/api/views/__init__.py:400 +#: awx/api/views/__init__.py:419 msgid "Instance Group Running Jobs" msgstr "" -#: awx/api/views/__init__.py:409 +#: awx/api/views/__init__.py:428 msgid "Instance Group's Instances" msgstr "" -#: awx/api/views/__init__.py:419 +#: awx/api/views/__init__.py:438 msgid "Schedules" msgstr "" -#: awx/api/views/__init__.py:433 +#: awx/api/views/__init__.py:452 msgid "Schedule Recurrence Rule Preview" msgstr "" -#: awx/api/views/__init__.py:480 +#: awx/api/views/__init__.py:499 msgid "Cannot assign credential when related template is null." msgstr "" -#: awx/api/views/__init__.py:485 +#: awx/api/views/__init__.py:504 msgid "Related template cannot accept {} on launch." msgstr "" -#: awx/api/views/__init__.py:487 +#: awx/api/views/__init__.py:506 msgid "" "Credential that requires user input on launch cannot be used in saved launch " "configuration." msgstr "" -#: awx/api/views/__init__.py:493 +#: awx/api/views/__init__.py:512 msgid "Related template is not configured to accept credentials on launch." msgstr "" -#: awx/api/views/__init__.py:495 +#: awx/api/views/__init__.py:514 #, python-brace-format msgid "" "This launch configuration already provides a {credential_type} credential." msgstr "" -#: awx/api/views/__init__.py:498 +#: awx/api/views/__init__.py:517 #, python-brace-format msgid "Related template already uses {credential_type} credential." msgstr "" -#: awx/api/views/__init__.py:516 +#: awx/api/views/__init__.py:535 msgid "Schedule Jobs List" msgstr "" -#: awx/api/views/__init__.py:600 awx/api/views/__init__.py:4378 +#: awx/api/views/__init__.py:619 awx/api/views/__init__.py:4420 msgid "" "You cannot assign an Organization participation role as a child role for a " "Team." msgstr "" -#: awx/api/views/__init__.py:604 awx/api/views/__init__.py:4392 +#: awx/api/views/__init__.py:623 awx/api/views/__init__.py:4434 msgid "You cannot grant system-level permissions to a team." msgstr "" -#: awx/api/views/__init__.py:611 awx/api/views/__init__.py:4384 +#: awx/api/views/__init__.py:630 awx/api/views/__init__.py:4426 msgid "" "You cannot grant credential access to a team when the Organization field " "isn't set, or belongs to a different organization" msgstr "" -#: awx/api/views/__init__.py:713 +#: awx/api/views/__init__.py:732 msgid "Project Schedules" msgstr "" -#: awx/api/views/__init__.py:724 +#: awx/api/views/__init__.py:743 msgid "Project SCM Inventory Sources" msgstr "" -#: awx/api/views/__init__.py:825 +#: awx/api/views/__init__.py:844 msgid "Project Update Events List" msgstr "" -#: awx/api/views/__init__.py:839 +#: awx/api/views/__init__.py:858 msgid "System Job Events List" msgstr "" -#: awx/api/views/__init__.py:873 +#: awx/api/views/__init__.py:892 msgid "Project Update SCM Inventory Updates" msgstr "" -#: awx/api/views/__init__.py:918 +#: awx/api/views/__init__.py:937 msgid "Me" msgstr "" -#: awx/api/views/__init__.py:927 +#: awx/api/views/__init__.py:946 msgid "OAuth 2 Applications" msgstr "" -#: awx/api/views/__init__.py:936 +#: awx/api/views/__init__.py:955 msgid "OAuth 2 Application Detail" msgstr "" -#: awx/api/views/__init__.py:949 +#: awx/api/views/__init__.py:968 msgid "OAuth 2 Application Tokens" msgstr "" -#: awx/api/views/__init__.py:971 +#: awx/api/views/__init__.py:990 msgid "OAuth2 Tokens" msgstr "" -#: awx/api/views/__init__.py:980 +#: awx/api/views/__init__.py:999 msgid "OAuth2 User Tokens" msgstr "" -#: awx/api/views/__init__.py:992 +#: awx/api/views/__init__.py:1011 msgid "OAuth2 User Authorized Access Tokens" msgstr "" -#: awx/api/views/__init__.py:1007 +#: awx/api/views/__init__.py:1026 msgid "Organization OAuth2 Applications" msgstr "" -#: awx/api/views/__init__.py:1019 +#: awx/api/views/__init__.py:1038 msgid "OAuth2 Personal Access Tokens" msgstr "" -#: awx/api/views/__init__.py:1034 +#: awx/api/views/__init__.py:1053 msgid "OAuth Token Detail" msgstr "" -#: awx/api/views/__init__.py:1096 awx/api/views/__init__.py:4345 +#: awx/api/views/__init__.py:1115 awx/api/views/__init__.py:4387 msgid "" "You cannot grant credential access to a user not in the credentials' " "organization" msgstr "" -#: awx/api/views/__init__.py:1100 awx/api/views/__init__.py:4349 +#: awx/api/views/__init__.py:1119 awx/api/views/__init__.py:4391 msgid "You cannot grant private credential access to another user" msgstr "" -#: awx/api/views/__init__.py:1198 +#: awx/api/views/__init__.py:1217 #, python-format msgid "Cannot change %s." msgstr "" -#: awx/api/views/__init__.py:1204 +#: awx/api/views/__init__.py:1223 msgid "Cannot delete user." msgstr "" -#: awx/api/views/__init__.py:1228 +#: awx/api/views/__init__.py:1247 msgid "Deletion not allowed for managed credential types" msgstr "" -#: awx/api/views/__init__.py:1230 +#: awx/api/views/__init__.py:1249 msgid "Credential types that are in use cannot be deleted" msgstr "" -#: awx/api/views/__init__.py:1381 +#: awx/api/views/__init__.py:1362 +msgid "Deletion not allowed for managed credentials" +msgstr "" + +#: awx/api/views/__init__.py:1407 msgid "External Credential Test" msgstr "" -#: awx/api/views/__init__.py:1408 +#: awx/api/views/__init__.py:1442 msgid "Credential Input Source Detail" msgstr "" -#: awx/api/views/__init__.py:1416 awx/api/views/__init__.py:1424 +#: awx/api/views/__init__.py:1450 awx/api/views/__init__.py:1458 msgid "Credential Input Sources" msgstr "" -#: awx/api/views/__init__.py:1439 +#: awx/api/views/__init__.py:1473 msgid "External Credential Type Test" msgstr "" -#: awx/api/views/__init__.py:1497 +#: awx/api/views/__init__.py:1539 msgid "The inventory for this host is already being deleted." msgstr "" -#: awx/api/views/__init__.py:1614 +#: awx/api/views/__init__.py:1656 msgid "SSLError while trying to connect to {}" msgstr "" -#: awx/api/views/__init__.py:1616 +#: awx/api/views/__init__.py:1658 msgid "Request to {} timed out." msgstr "" -#: awx/api/views/__init__.py:1618 +#: awx/api/views/__init__.py:1660 msgid "Unknown exception {} while trying to GET {}" msgstr "" -#: awx/api/views/__init__.py:1622 +#: awx/api/views/__init__.py:1664 msgid "" "Unauthorized access. Please check your Insights Credential username and " "password." msgstr "" -#: awx/api/views/__init__.py:1626 +#: awx/api/views/__init__.py:1668 msgid "" "Failed to access the Insights API at URL {}. Server responded with {} status " "code and message {}" msgstr "" -#: awx/api/views/__init__.py:1635 +#: awx/api/views/__init__.py:1677 msgid "Expected JSON response from Insights at URL {} but instead got {}" msgstr "" -#: awx/api/views/__init__.py:1653 +#: awx/api/views/__init__.py:1695 msgid "Could not translate Insights system ID {} into an Insights platform ID." msgstr "" -#: awx/api/views/__init__.py:1695 +#: awx/api/views/__init__.py:1737 msgid "This host is not recognized as an Insights host." msgstr "" -#: awx/api/views/__init__.py:1703 +#: awx/api/views/__init__.py:1745 msgid "The Insights Credential for \"{}\" was not found." msgstr "" -#: awx/api/views/__init__.py:1782 +#: awx/api/views/__init__.py:1824 msgid "Cyclical Group association." msgstr "" -#: awx/api/views/__init__.py:1948 +#: awx/api/views/__init__.py:1990 msgid "Inventory subset argument must be a string." msgstr "" -#: awx/api/views/__init__.py:1952 +#: awx/api/views/__init__.py:1994 msgid "Subset does not use any supported syntax." msgstr "" -#: awx/api/views/__init__.py:2002 +#: awx/api/views/__init__.py:2044 msgid "Inventory Source List" msgstr "" -#: awx/api/views/__init__.py:2014 +#: awx/api/views/__init__.py:2056 msgid "Inventory Sources Update" msgstr "" -#: awx/api/views/__init__.py:2047 +#: awx/api/views/__init__.py:2089 msgid "Could not start because `can_update` returned False" msgstr "" -#: awx/api/views/__init__.py:2055 +#: awx/api/views/__init__.py:2097 msgid "No inventory sources to update." msgstr "" -#: awx/api/views/__init__.py:2077 +#: awx/api/views/__init__.py:2119 msgid "Inventory Source Schedules" msgstr "" -#: awx/api/views/__init__.py:2104 +#: awx/api/views/__init__.py:2146 msgid "Notification Templates can only be assigned when source is one of {}." msgstr "" -#: awx/api/views/__init__.py:2202 +#: awx/api/views/__init__.py:2244 msgid "Source already has credential assigned." msgstr "" -#: awx/api/views/__init__.py:2418 +#: awx/api/views/__init__.py:2460 msgid "Job Template Schedules" msgstr "" -#: awx/api/views/__init__.py:2467 +#: awx/api/views/__init__.py:2509 msgid "Field '{}' is missing from survey spec." msgstr "" -#: awx/api/views/__init__.py:2469 +#: awx/api/views/__init__.py:2511 msgid "Expected {} for field '{}', received {} type." msgstr "" -#: awx/api/views/__init__.py:2473 +#: awx/api/views/__init__.py:2515 msgid "'spec' doesn't contain any items." msgstr "" -#: awx/api/views/__init__.py:2487 +#: awx/api/views/__init__.py:2529 #, python-format msgid "Survey question %s is not a json object." msgstr "" -#: awx/api/views/__init__.py:2490 +#: awx/api/views/__init__.py:2532 #, python-brace-format msgid "'{field_name}' missing from survey question {idx}" msgstr "" -#: awx/api/views/__init__.py:2500 +#: awx/api/views/__init__.py:2542 #, python-brace-format msgid "'{field_name}' in survey question {idx} expected to be {type_label}." msgstr "" -#: awx/api/views/__init__.py:2504 +#: awx/api/views/__init__.py:2546 #, python-format msgid "'variable' '%(item)s' duplicated in survey question %(survey)s." msgstr "" -#: awx/api/views/__init__.py:2514 +#: awx/api/views/__init__.py:2556 #, python-brace-format msgid "" "'{survey_item[type]}' in survey question {idx} is not one of " "'{allowed_types}' allowed question types." msgstr "" -#: awx/api/views/__init__.py:2524 +#: awx/api/views/__init__.py:2566 #, python-brace-format msgid "" "Default value {survey_item[default]} in survey question {idx} expected to be " "{type_label}." msgstr "" -#: awx/api/views/__init__.py:2534 +#: awx/api/views/__init__.py:2576 #, python-brace-format msgid "The {min_or_max} limit in survey question {idx} expected to be integer." msgstr "" -#: awx/api/views/__init__.py:2544 +#: awx/api/views/__init__.py:2586 #, python-brace-format msgid "Survey question {idx} of type {survey_item[type]} must specify choices." msgstr "" -#: awx/api/views/__init__.py:2558 +#: awx/api/views/__init__.py:2600 msgid "Multiple Choice (Single Select) can only have one default value." msgstr "" -#: awx/api/views/__init__.py:2562 +#: awx/api/views/__init__.py:2604 msgid "Default choice must be answered from the choices listed." msgstr "" -#: awx/api/views/__init__.py:2571 +#: awx/api/views/__init__.py:2613 #, python-brace-format msgid "" "$encrypted$ is a reserved keyword for password question defaults, survey " "question {idx} is type {survey_item[type]}." msgstr "" -#: awx/api/views/__init__.py:2585 +#: awx/api/views/__init__.py:2627 #, python-brace-format msgid "" "$encrypted$ is a reserved keyword, may not be used for new default in " "position {idx}." msgstr "" -#: awx/api/views/__init__.py:2657 +#: awx/api/views/__init__.py:2699 #, python-brace-format msgid "Cannot assign multiple {credential_type} credentials." msgstr "" -#: awx/api/views/__init__.py:2661 +#: awx/api/views/__init__.py:2703 msgid "Cannot assign a Credential of kind `{}`." msgstr "" -#: awx/api/views/__init__.py:2684 +#: awx/api/views/__init__.py:2726 msgid "Maximum number of labels for {} reached." msgstr "" -#: awx/api/views/__init__.py:2807 +#: awx/api/views/__init__.py:2849 msgid "No matching host could be found!" msgstr "" -#: awx/api/views/__init__.py:2810 +#: awx/api/views/__init__.py:2852 msgid "Multiple hosts matched the request!" msgstr "" -#: awx/api/views/__init__.py:2815 +#: awx/api/views/__init__.py:2857 msgid "Cannot start automatically, user input required!" msgstr "" -#: awx/api/views/__init__.py:2823 +#: awx/api/views/__init__.py:2865 msgid "Host callback job already pending." msgstr "" -#: awx/api/views/__init__.py:2839 awx/api/views/__init__.py:3590 +#: awx/api/views/__init__.py:2881 awx/api/views/__init__.py:3632 msgid "Error starting job!" msgstr "" -#: awx/api/views/__init__.py:2963 awx/api/views/__init__.py:2983 +#: awx/api/views/__init__.py:3005 awx/api/views/__init__.py:3025 msgid "Cycle detected." msgstr "" -#: awx/api/views/__init__.py:2975 +#: awx/api/views/__init__.py:3017 msgid "Relationship not allowed." msgstr "" -#: awx/api/views/__init__.py:3204 +#: awx/api/views/__init__.py:3246 msgid "Cannot relaunch slice workflow job orphaned from job template." msgstr "" -#: awx/api/views/__init__.py:3206 +#: awx/api/views/__init__.py:3248 msgid "Cannot relaunch sliced workflow job after slice count has changed." msgstr "" -#: awx/api/views/__init__.py:3239 +#: awx/api/views/__init__.py:3281 msgid "Workflow Job Template Schedules" msgstr "" -#: awx/api/views/__init__.py:3382 awx/api/views/__init__.py:4013 +#: awx/api/views/__init__.py:3424 awx/api/views/__init__.py:4055 msgid "Superuser privileges needed." msgstr "" -#: awx/api/views/__init__.py:3415 +#: awx/api/views/__init__.py:3457 msgid "System Job Template Schedules" msgstr "" -#: awx/api/views/__init__.py:3573 +#: awx/api/views/__init__.py:3615 #, python-brace-format msgid "Wait until job finishes before retrying on {status_value} hosts." msgstr "" -#: awx/api/views/__init__.py:3578 +#: awx/api/views/__init__.py:3620 #, python-brace-format msgid "Cannot retry on {status_value} hosts, playbook stats not available." msgstr "" -#: awx/api/views/__init__.py:3583 +#: awx/api/views/__init__.py:3625 #, python-brace-format msgid "Cannot relaunch because previous job had 0 {status_value} hosts." msgstr "" -#: awx/api/views/__init__.py:3612 +#: awx/api/views/__init__.py:3654 msgid "Cannot create schedule because job requires credential passwords." msgstr "" -#: awx/api/views/__init__.py:3617 +#: awx/api/views/__init__.py:3659 msgid "Cannot create schedule because job was launched by legacy method." msgstr "" -#: awx/api/views/__init__.py:3619 +#: awx/api/views/__init__.py:3661 msgid "Cannot create schedule because a related resource is missing." msgstr "" -#: awx/api/views/__init__.py:3674 +#: awx/api/views/__init__.py:3716 msgid "Job Host Summaries List" msgstr "" -#: awx/api/views/__init__.py:3728 +#: awx/api/views/__init__.py:3770 msgid "Job Event Children List" msgstr "" -#: awx/api/views/__init__.py:3744 +#: awx/api/views/__init__.py:3786 msgid "Job Event Hosts List" msgstr "" -#: awx/api/views/__init__.py:3759 +#: awx/api/views/__init__.py:3801 msgid "Job Events List" msgstr "" -#: awx/api/views/__init__.py:3970 +#: awx/api/views/__init__.py:4012 msgid "Ad Hoc Command Events List" msgstr "" -#: awx/api/views/__init__.py:4215 +#: awx/api/views/__init__.py:4257 msgid "Delete not allowed while there are pending notifications" msgstr "" -#: awx/api/views/__init__.py:4223 +#: awx/api/views/__init__.py:4265 msgid "Notification Template Test" msgstr "" -#: awx/api/views/__init__.py:4483 awx/api/views/__init__.py:4498 +#: awx/api/views/__init__.py:4525 awx/api/views/__init__.py:4540 msgid "User does not have permission to approve or deny this workflow." msgstr "" -#: awx/api/views/__init__.py:4485 awx/api/views/__init__.py:4500 +#: awx/api/views/__init__.py:4527 awx/api/views/__init__.py:4542 msgid "This workflow step has already been approved or denied." msgstr "" @@ -1380,7 +1421,11 @@ msgstr "" msgid "Cannot delete inventory script." msgstr "" -#: awx/api/views/inventory.py:149 +#: awx/api/views/inventory.py:137 +msgid "You cannot turn a regular inventory into a \"smart\" inventory." +msgstr "" + +#: awx/api/views/inventory.py:150 #, python-brace-format msgid "{0}" msgstr "" @@ -1405,71 +1450,76 @@ msgstr "" msgid "Related job {} is still processing events." msgstr "" -#: awx/api/views/root.py:49 awx/templates/rest_framework/api.html:28 +#: awx/api/views/organization.py:230 +#, python-brace-format +msgid "Credential must be a Galaxy credential, not {sub.credential_type.name}." +msgstr "" + +#: awx/api/views/root.py:50 awx/templates/rest_framework/api.html:28 msgid "REST API" msgstr "" -#: awx/api/views/root.py:59 awx/templates/rest_framework/api.html:4 +#: awx/api/views/root.py:60 awx/templates/rest_framework/api.html:4 msgid "AWX REST API" msgstr "" -#: awx/api/views/root.py:72 +#: awx/api/views/root.py:73 msgid "API OAuth 2 Authorization Root" msgstr "" -#: awx/api/views/root.py:139 +#: awx/api/views/root.py:140 msgid "Version 2" msgstr "" -#: awx/api/views/root.py:148 +#: awx/api/views/root.py:149 msgid "Ping" msgstr "" -#: awx/api/views/root.py:180 awx/api/views/root.py:225 awx/conf/apps.py:10 +#: awx/api/views/root.py:181 awx/api/views/root.py:226 awx/conf/apps.py:10 msgid "Configuration" msgstr "" -#: awx/api/views/root.py:202 awx/api/views/root.py:308 +#: awx/api/views/root.py:203 awx/api/views/root.py:310 msgid "Invalid License" msgstr "" -#: awx/api/views/root.py:207 +#: awx/api/views/root.py:208 msgid "The provided credentials are invalid (HTTP 401)." msgstr "" -#: awx/api/views/root.py:209 +#: awx/api/views/root.py:210 msgid "Unable to connect to proxy server." msgstr "" -#: awx/api/views/root.py:211 +#: awx/api/views/root.py:212 msgid "Could not connect to subscription service." msgstr "" -#: awx/api/views/root.py:284 +#: awx/api/views/root.py:286 msgid "Invalid license data" msgstr "" -#: awx/api/views/root.py:286 +#: awx/api/views/root.py:288 msgid "Missing 'eula_accepted' property" msgstr "" -#: awx/api/views/root.py:290 +#: awx/api/views/root.py:292 msgid "'eula_accepted' value is invalid" msgstr "" -#: awx/api/views/root.py:293 +#: awx/api/views/root.py:295 msgid "'eula_accepted' must be True" msgstr "" -#: awx/api/views/root.py:300 +#: awx/api/views/root.py:302 msgid "Invalid JSON" msgstr "" -#: awx/api/views/root.py:319 +#: awx/api/views/root.py:321 msgid "Invalid license" msgstr "" -#: awx/api/views/root.py:327 +#: awx/api/views/root.py:329 msgid "Failed to remove license." msgstr "" @@ -1660,11 +1710,11 @@ msgstr "" msgid "Expected a list of tuples of max length 2 but got {input_type} instead." msgstr "" -#: awx/conf/registry.py:73 awx/conf/tests/unit/test_registry.py:155 +#: awx/conf/registry.py:73 awx/conf/tests/unit/test_registry.py:156 msgid "All" msgstr "" -#: awx/conf/registry.py:74 awx/conf/tests/unit/test_registry.py:156 +#: awx/conf/registry.py:74 awx/conf/tests/unit/test_registry.py:157 msgid "Changed" msgstr "" @@ -1672,59 +1722,57 @@ msgstr "" msgid "User-Defaults" msgstr "" -#: awx/conf/registry.py:143 +#: awx/conf/registry.py:145 msgid "This value has been set manually in a settings file." msgstr "" -#: awx/conf/tests/unit/test_registry.py:46 -#: awx/conf/tests/unit/test_registry.py:56 -#: awx/conf/tests/unit/test_registry.py:72 -#: awx/conf/tests/unit/test_registry.py:87 -#: awx/conf/tests/unit/test_registry.py:100 -#: awx/conf/tests/unit/test_registry.py:106 -#: awx/conf/tests/unit/test_registry.py:126 -#: awx/conf/tests/unit/test_registry.py:132 -#: awx/conf/tests/unit/test_registry.py:145 -#: awx/conf/tests/unit/test_registry.py:157 -#: awx/conf/tests/unit/test_registry.py:166 -#: awx/conf/tests/unit/test_registry.py:172 -#: awx/conf/tests/unit/test_registry.py:184 -#: awx/conf/tests/unit/test_registry.py:191 -#: awx/conf/tests/unit/test_registry.py:233 -#: awx/conf/tests/unit/test_registry.py:251 -#: awx/conf/tests/unit/test_settings.py:79 -#: awx/conf/tests/unit/test_settings.py:97 -#: awx/conf/tests/unit/test_settings.py:112 -#: awx/conf/tests/unit/test_settings.py:127 -#: awx/conf/tests/unit/test_settings.py:143 -#: awx/conf/tests/unit/test_settings.py:156 -#: awx/conf/tests/unit/test_settings.py:173 -#: awx/conf/tests/unit/test_settings.py:189 -#: awx/conf/tests/unit/test_settings.py:200 -#: awx/conf/tests/unit/test_settings.py:216 -#: awx/conf/tests/unit/test_settings.py:237 -#: awx/conf/tests/unit/test_settings.py:259 -#: awx/conf/tests/unit/test_settings.py:285 -#: awx/conf/tests/unit/test_settings.py:299 -#: awx/conf/tests/unit/test_settings.py:323 +#: awx/conf/tests/unit/test_registry.py:47 +#: awx/conf/tests/unit/test_registry.py:57 +#: awx/conf/tests/unit/test_registry.py:73 +#: awx/conf/tests/unit/test_registry.py:88 +#: awx/conf/tests/unit/test_registry.py:101 +#: awx/conf/tests/unit/test_registry.py:107 +#: awx/conf/tests/unit/test_registry.py:127 +#: awx/conf/tests/unit/test_registry.py:133 +#: awx/conf/tests/unit/test_registry.py:146 +#: awx/conf/tests/unit/test_registry.py:158 +#: awx/conf/tests/unit/test_registry.py:167 +#: awx/conf/tests/unit/test_registry.py:173 +#: awx/conf/tests/unit/test_registry.py:185 +#: awx/conf/tests/unit/test_registry.py:192 +#: awx/conf/tests/unit/test_registry.py:234 +#: awx/conf/tests/unit/test_registry.py:252 +#: awx/conf/tests/unit/test_settings.py:73 +#: awx/conf/tests/unit/test_settings.py:91 +#: awx/conf/tests/unit/test_settings.py:106 +#: awx/conf/tests/unit/test_settings.py:121 +#: awx/conf/tests/unit/test_settings.py:137 +#: awx/conf/tests/unit/test_settings.py:150 +#: awx/conf/tests/unit/test_settings.py:167 +#: awx/conf/tests/unit/test_settings.py:183 +#: awx/conf/tests/unit/test_settings.py:194 +#: awx/conf/tests/unit/test_settings.py:210 +#: awx/conf/tests/unit/test_settings.py:231 +#: awx/conf/tests/unit/test_settings.py:254 +#: awx/conf/tests/unit/test_settings.py:268 +#: awx/conf/tests/unit/test_settings.py:292 +#: awx/conf/tests/unit/test_settings.py:312 +#: awx/conf/tests/unit/test_settings.py:329 #: awx/conf/tests/unit/test_settings.py:343 -#: awx/conf/tests/unit/test_settings.py:360 -#: awx/conf/tests/unit/test_settings.py:374 -#: awx/conf/tests/unit/test_settings.py:398 -#: awx/conf/tests/unit/test_settings.py:411 -#: awx/conf/tests/unit/test_settings.py:430 -#: awx/conf/tests/unit/test_settings.py:466 awx/main/conf.py:24 -#: awx/main/conf.py:33 awx/main/conf.py:43 awx/main/conf.py:53 -#: awx/main/conf.py:65 awx/main/conf.py:78 awx/main/conf.py:91 -#: awx/main/conf.py:116 awx/main/conf.py:129 awx/main/conf.py:142 -#: awx/main/conf.py:154 awx/main/conf.py:162 awx/main/conf.py:173 -#: awx/main/conf.py:405 awx/main/conf.py:830 awx/main/conf.py:840 -#: awx/main/conf.py:852 +#: awx/conf/tests/unit/test_settings.py:367 +#: awx/conf/tests/unit/test_settings.py:380 +#: awx/conf/tests/unit/test_settings.py:399 +#: awx/conf/tests/unit/test_settings.py:435 awx/main/conf.py:23 +#: awx/main/conf.py:32 awx/main/conf.py:42 awx/main/conf.py:52 +#: awx/main/conf.py:64 awx/main/conf.py:77 awx/main/conf.py:90 +#: awx/main/conf.py:115 awx/main/conf.py:128 awx/main/conf.py:141 +#: awx/main/conf.py:153 awx/main/conf.py:161 awx/main/conf.py:172 +#: awx/main/conf.py:395 awx/main/conf.py:750 awx/main/conf.py:762 msgid "System" msgstr "" -#: awx/conf/tests/unit/test_registry.py:151 -#: awx/conf/tests/unit/test_registry.py:158 +#: awx/conf/tests/unit/test_registry.py:152 +#: awx/conf/tests/unit/test_registry.py:159 msgid "OtherSystem" msgstr "" @@ -1791,112 +1839,172 @@ msgstr "" msgid "Unable to change inventory on a group." msgstr "" -#: awx/main/access.py:1264 +#: awx/main/access.py:1261 msgid "Unable to change organization on a team." msgstr "" -#: awx/main/access.py:1280 +#: awx/main/access.py:1277 msgid "The {} role cannot be assigned to a team" msgstr "" -#: awx/main/access.py:1474 +#: awx/main/access.py:1471 msgid "Insufficient access to Job Template credentials." msgstr "" -#: awx/main/access.py:1639 awx/main/access.py:2063 +#: awx/main/access.py:1635 awx/main/access.py:2059 msgid "Job was launched with secret prompts provided by another user." msgstr "" -#: awx/main/access.py:1648 +#: awx/main/access.py:1644 msgid "Job has been orphaned from its job template and organization." msgstr "" -#: awx/main/access.py:1650 +#: awx/main/access.py:1646 msgid "Job was launched with prompted fields you do not have access to." msgstr "" -#: awx/main/access.py:1652 +#: awx/main/access.py:1648 msgid "" "Job was launched with unknown prompted fields. Organization admin " "permissions required." msgstr "" -#: awx/main/access.py:2053 +#: awx/main/access.py:2049 msgid "Workflow Job was launched with unknown prompts." msgstr "" -#: awx/main/access.py:2065 +#: awx/main/access.py:2061 msgid "Job was launched with prompts you lack access to." msgstr "" -#: awx/main/access.py:2067 +#: awx/main/access.py:2063 msgid "Job was launched with prompts no longer accepted." msgstr "" -#: awx/main/access.py:2079 +#: awx/main/access.py:2075 msgid "" "You do not have permission to the workflow job resources required for " "relaunch." msgstr "" +#: awx/main/analytics/collectors.py:36 +msgid "General platform configuration." +msgstr "" + +#: awx/main/analytics/collectors.py:68 +msgid "Counts of objects such as organizations, inventories, and projects" +msgstr "" + +#: awx/main/analytics/collectors.py:103 +msgid "Counts of users and teams by organization" +msgstr "" + +#: awx/main/analytics/collectors.py:115 +msgid "Counts of credentials by credential type" +msgstr "" + +#: awx/main/analytics/collectors.py:127 +msgid "Inventories, their inventory sources, and host counts" +msgstr "" + +#: awx/main/analytics/collectors.py:152 +msgid "Counts of projects by source control type" +msgstr "" + +#: awx/main/analytics/collectors.py:171 +msgid "Cluster topology and capacity" +msgstr "" + +#: awx/main/analytics/collectors.py:197 +msgid "Counts of jobs by status" +msgstr "" + +#: awx/main/analytics/collectors.py:207 +msgid "Counts of jobs by execution node" +msgstr "" + +#: awx/main/analytics/collectors.py:222 +msgid "Metadata about the analytics collected" +msgstr "" + +#: awx/main/analytics/collectors.py:285 +msgid "Automation task records" +msgstr "" + +#: awx/main/analytics/collectors.py:314 +msgid "Data on jobs run" +msgstr "" + +#: awx/main/analytics/collectors.py:351 +msgid "Data on job templates" +msgstr "" + +#: awx/main/analytics/collectors.py:374 +msgid "Data on workflow runs" +msgstr "" + +#: awx/main/analytics/collectors.py:410 +msgid "Data on workflows" +msgstr "" + #: awx/main/apps.py:8 msgid "Main" msgstr "" -#: awx/main/conf.py:22 +#: awx/main/conf.py:21 msgid "Enable Activity Stream" msgstr "" -#: awx/main/conf.py:23 +#: awx/main/conf.py:22 msgid "Enable capturing activity for the activity stream." msgstr "" -#: awx/main/conf.py:31 +#: awx/main/conf.py:30 msgid "Enable Activity Stream for Inventory Sync" msgstr "" -#: awx/main/conf.py:32 +#: awx/main/conf.py:31 msgid "" "Enable capturing activity for the activity stream when running inventory " "sync." msgstr "" -#: awx/main/conf.py:40 +#: awx/main/conf.py:39 msgid "All Users Visible to Organization Admins" msgstr "" -#: awx/main/conf.py:41 +#: awx/main/conf.py:40 msgid "" "Controls whether any Organization Admin can view all users and teams, even " "those not associated with their Organization." msgstr "" -#: awx/main/conf.py:50 +#: awx/main/conf.py:49 msgid "Organization Admins Can Manage Users and Teams" msgstr "" -#: awx/main/conf.py:51 +#: awx/main/conf.py:50 msgid "" "Controls whether any Organization Admin has the privileges to create and " "manage users and teams. You may want to disable this ability if you are " "using an LDAP or SAML integration." msgstr "" -#: awx/main/conf.py:62 +#: awx/main/conf.py:61 msgid "Base URL of the Tower host" msgstr "" -#: awx/main/conf.py:63 +#: awx/main/conf.py:62 msgid "" "This setting is used by services like notifications to render a valid url to " "the Tower host." msgstr "" -#: awx/main/conf.py:72 +#: awx/main/conf.py:71 msgid "Remote Host Headers" msgstr "" -#: awx/main/conf.py:73 +#: awx/main/conf.py:72 msgid "" "HTTP headers and meta keys to search to determine remote host name or IP. " "Add additional items to this list, such as \"HTTP_X_FORWARDED_FOR\", if " @@ -1904,114 +2012,112 @@ msgid "" "Adminstrator guide for more details." msgstr "" -#: awx/main/conf.py:85 -msgid "Proxy IP Whitelist" +#: awx/main/conf.py:84 +msgid "Proxy IP Allowed List" msgstr "" -#: awx/main/conf.py:86 +#: awx/main/conf.py:85 msgid "" "If Tower is behind a reverse proxy/load balancer, use this setting to " -"whitelist the proxy IP addresses from which Tower should trust custom " +"configure the proxy IP addresses from which Tower should trust custom " "REMOTE_HOST_HEADERS header values. If this setting is an empty list (the " "default), the headers specified by REMOTE_HOST_HEADERS will be trusted " "unconditionally')" msgstr "" -#: awx/main/conf.py:112 +#: awx/main/conf.py:111 msgid "License" msgstr "" -#: awx/main/conf.py:113 +#: awx/main/conf.py:112 msgid "" "The license controls which features and functionality are enabled. Use /api/" "v2/config/ to update or change the license." msgstr "" -#: awx/main/conf.py:127 +#: awx/main/conf.py:126 msgid "Red Hat customer username" msgstr "" -#: awx/main/conf.py:128 +#: awx/main/conf.py:127 msgid "" "This username is used to retrieve license information and to send Automation " "Analytics" msgstr "" -#: awx/main/conf.py:140 +#: awx/main/conf.py:139 msgid "Red Hat customer password" msgstr "" -#: awx/main/conf.py:141 +#: awx/main/conf.py:140 msgid "" "This password is used to retrieve license information and to send Automation " "Analytics" msgstr "" -#: awx/main/conf.py:152 -msgid "Automation Analytics upload URL." +#: awx/main/conf.py:151 +msgid "Automation Analytics upload URL" msgstr "" -#: awx/main/conf.py:153 +#: awx/main/conf.py:152 msgid "" "This setting is used to to configure data collection for the Automation " "Analytics dashboard" msgstr "" -#: awx/main/conf.py:161 +#: awx/main/conf.py:160 msgid "Unique identifier for an AWX/Tower installation" msgstr "" -#: awx/main/conf.py:170 +#: awx/main/conf.py:169 msgid "Custom virtual environment paths" msgstr "" -#: awx/main/conf.py:171 +#: awx/main/conf.py:170 msgid "" "Paths where Tower will look for custom virtual environments (in addition to /" "var/lib/awx/venv/). Enter one path per line." msgstr "" -#: awx/main/conf.py:181 +#: awx/main/conf.py:180 msgid "Ansible Modules Allowed for Ad Hoc Jobs" msgstr "" -#: awx/main/conf.py:182 +#: awx/main/conf.py:181 msgid "List of modules allowed to be used by ad-hoc jobs." msgstr "" -#: awx/main/conf.py:183 awx/main/conf.py:205 awx/main/conf.py:214 -#: awx/main/conf.py:225 awx/main/conf.py:235 awx/main/conf.py:245 -#: awx/main/conf.py:256 awx/main/conf.py:267 awx/main/conf.py:278 -#: awx/main/conf.py:290 awx/main/conf.py:299 awx/main/conf.py:312 -#: awx/main/conf.py:325 awx/main/conf.py:337 awx/main/conf.py:348 -#: awx/main/conf.py:359 awx/main/conf.py:371 awx/main/conf.py:383 -#: awx/main/conf.py:394 awx/main/conf.py:414 awx/main/conf.py:424 -#: awx/main/conf.py:434 awx/main/conf.py:450 awx/main/conf.py:463 -#: awx/main/conf.py:477 awx/main/conf.py:491 awx/main/conf.py:503 -#: awx/main/conf.py:513 awx/main/conf.py:524 awx/main/conf.py:534 -#: awx/main/conf.py:545 awx/main/conf.py:555 awx/main/conf.py:565 -#: awx/main/conf.py:577 awx/main/conf.py:589 awx/main/conf.py:601 -#: awx/main/conf.py:615 awx/main/conf.py:627 +#: awx/main/conf.py:182 awx/main/conf.py:204 awx/main/conf.py:213 +#: awx/main/conf.py:224 awx/main/conf.py:234 awx/main/conf.py:244 +#: awx/main/conf.py:254 awx/main/conf.py:266 awx/main/conf.py:279 +#: awx/main/conf.py:289 awx/main/conf.py:302 awx/main/conf.py:315 +#: awx/main/conf.py:327 awx/main/conf.py:338 awx/main/conf.py:349 +#: awx/main/conf.py:361 awx/main/conf.py:373 awx/main/conf.py:384 +#: awx/main/conf.py:404 awx/main/conf.py:414 awx/main/conf.py:424 +#: awx/main/conf.py:437 awx/main/conf.py:448 awx/main/conf.py:458 +#: awx/main/conf.py:469 awx/main/conf.py:479 awx/main/conf.py:489 +#: awx/main/conf.py:501 awx/main/conf.py:514 awx/main/conf.py:527 +#: awx/main/conf.py:542 awx/main/conf.py:555 msgid "Jobs" msgstr "" -#: awx/main/conf.py:192 +#: awx/main/conf.py:191 msgid "Always" msgstr "" -#: awx/main/conf.py:193 +#: awx/main/conf.py:192 msgid "Never" msgstr "" -#: awx/main/conf.py:194 +#: awx/main/conf.py:193 msgid "Only On Job Template Definitions" msgstr "" -#: awx/main/conf.py:197 +#: awx/main/conf.py:196 msgid "When can extra variables contain Jinja templates?" msgstr "" -#: awx/main/conf.py:199 +#: awx/main/conf.py:198 msgid "" "Ansible allows variable substitution via the Jinja2 templating language for " "--extra-vars. This poses a potential security risk where Tower users with " @@ -2020,358 +2126,294 @@ msgid "" "to \"template\" or \"never\"." msgstr "" -#: awx/main/conf.py:212 +#: awx/main/conf.py:211 msgid "Enable job isolation" msgstr "" -#: awx/main/conf.py:213 +#: awx/main/conf.py:212 msgid "" "Isolates an Ansible job from protected parts of the system to prevent " "exposing sensitive information." msgstr "" -#: awx/main/conf.py:221 +#: awx/main/conf.py:220 msgid "Job execution path" msgstr "" -#: awx/main/conf.py:222 +#: awx/main/conf.py:221 msgid "" "The directory in which Tower will create new temporary directories for job " "execution and isolation (such as credential files and custom inventory " "scripts)." msgstr "" -#: awx/main/conf.py:233 +#: awx/main/conf.py:232 msgid "Paths to hide from isolated jobs" msgstr "" -#: awx/main/conf.py:234 +#: awx/main/conf.py:233 msgid "" "Additional paths to hide from isolated processes. Enter one path per line." msgstr "" -#: awx/main/conf.py:243 +#: awx/main/conf.py:242 msgid "Paths to expose to isolated jobs" msgstr "" -#: awx/main/conf.py:244 +#: awx/main/conf.py:243 msgid "" -"Whitelist of paths that would otherwise be hidden to expose to isolated " -"jobs. Enter one path per line." +"List of paths that would otherwise be hidden to expose to isolated jobs. " +"Enter one path per line." msgstr "" -#: awx/main/conf.py:254 -msgid "Verbosity level for isolated node management tasks" -msgstr "" - -#: awx/main/conf.py:255 -msgid "" -"This can be raised to aid in debugging connection issues for isolated task " -"execution" -msgstr "" - -#: awx/main/conf.py:265 +#: awx/main/conf.py:252 msgid "Isolated status check interval" msgstr "" -#: awx/main/conf.py:266 +#: awx/main/conf.py:253 msgid "" "The number of seconds to sleep between status checks for jobs running on " "isolated instances." msgstr "" -#: awx/main/conf.py:275 +#: awx/main/conf.py:263 msgid "Isolated launch timeout" msgstr "" -#: awx/main/conf.py:276 +#: awx/main/conf.py:264 msgid "" "The timeout (in seconds) for launching jobs on isolated instances. This " "includes the time needed to copy source control files (playbooks) to the " "isolated instance." msgstr "" -#: awx/main/conf.py:287 +#: awx/main/conf.py:276 msgid "Isolated connection timeout" msgstr "" -#: awx/main/conf.py:288 +#: awx/main/conf.py:277 msgid "" "Ansible SSH connection timeout (in seconds) to use when communicating with " "isolated instances. Value should be substantially greater than expected " "network latency." msgstr "" -#: awx/main/conf.py:297 +#: awx/main/conf.py:287 msgid "Isolated host key checking" msgstr "" -#: awx/main/conf.py:298 +#: awx/main/conf.py:288 msgid "" "When set to True, AWX will enforce strict host key checking for " "communication with isolated nodes." msgstr "" -#: awx/main/conf.py:308 +#: awx/main/conf.py:298 msgid "Generate RSA keys for isolated instances" msgstr "" -#: awx/main/conf.py:309 +#: awx/main/conf.py:299 msgid "" "If set, a random RSA key will be generated and distributed to isolated " "instances. To disable this behavior and manage authentication for isolated " "instances outside of Tower, disable this setting." msgstr "" -#: awx/main/conf.py:323 awx/main/conf.py:324 +#: awx/main/conf.py:313 awx/main/conf.py:314 msgid "The RSA private key for SSH traffic to isolated instances" msgstr "" -#: awx/main/conf.py:335 awx/main/conf.py:336 +#: awx/main/conf.py:325 awx/main/conf.py:326 msgid "The RSA public key for SSH traffic to isolated instances" msgstr "" -#: awx/main/conf.py:345 +#: awx/main/conf.py:335 msgid "Enable detailed resource profiling on all playbook runs" msgstr "" -#: awx/main/conf.py:346 +#: awx/main/conf.py:336 msgid "" "If set, detailed resource profiling data will be collected on all jobs. This " "data can be gathered with `sosreport`." msgstr "" -#: awx/main/conf.py:356 +#: awx/main/conf.py:346 msgid "Interval (in seconds) between polls for cpu usage." msgstr "" -#: awx/main/conf.py:357 +#: awx/main/conf.py:347 msgid "" "Interval (in seconds) between polls for cpu usage. Setting this lower than " "the default will affect playbook performance." msgstr "" -#: awx/main/conf.py:368 +#: awx/main/conf.py:358 msgid "Interval (in seconds) between polls for memory usage." msgstr "" -#: awx/main/conf.py:369 +#: awx/main/conf.py:359 msgid "" "Interval (in seconds) between polls for memory usage. Setting this lower " "than the default will affect playbook performance." msgstr "" -#: awx/main/conf.py:380 +#: awx/main/conf.py:370 msgid "Interval (in seconds) between polls for PID count." msgstr "" -#: awx/main/conf.py:381 +#: awx/main/conf.py:371 msgid "" "Interval (in seconds) between polls for PID count. Setting this lower than " "the default will affect playbook performance." msgstr "" -#: awx/main/conf.py:392 +#: awx/main/conf.py:382 msgid "Extra Environment Variables" msgstr "" -#: awx/main/conf.py:393 +#: awx/main/conf.py:383 msgid "" "Additional environment variables set for playbook runs, inventory updates, " "project updates, and notification sending." msgstr "" -#: awx/main/conf.py:403 +#: awx/main/conf.py:393 msgid "Gather data for Automation Analytics" msgstr "" -#: awx/main/conf.py:404 +#: awx/main/conf.py:394 msgid "Enables Tower to gather data on automation and send it to Red Hat." msgstr "" -#: awx/main/conf.py:412 +#: awx/main/conf.py:402 msgid "Run Project Updates With Higher Verbosity" msgstr "" -#: awx/main/conf.py:413 +#: awx/main/conf.py:403 msgid "" "Adds the CLI -vvv flag to ansible-playbook runs of project_update.yml used " "for project updates." msgstr "" -#: awx/main/conf.py:422 +#: awx/main/conf.py:412 msgid "Enable Role Download" msgstr "" -#: awx/main/conf.py:423 +#: awx/main/conf.py:413 msgid "" "Allows roles to be dynamically downloaded from a requirements.yml file for " "SCM projects." msgstr "" -#: awx/main/conf.py:432 +#: awx/main/conf.py:422 msgid "Enable Collection(s) Download" msgstr "" -#: awx/main/conf.py:433 +#: awx/main/conf.py:423 msgid "" "Allows collections to be dynamically downloaded from a requirements.yml file " "for SCM projects." msgstr "" -#: awx/main/conf.py:443 -msgid "Primary Galaxy Server URL" +#: awx/main/conf.py:432 +msgid "Follow symlinks" +msgstr "" + +#: awx/main/conf.py:434 +msgid "" +"Follow symbolic links when scanning for playbooks. Be aware that setting " +"this to True can lead to infinite recursion if a link points to a parent " +"directory of itself." msgstr "" #: awx/main/conf.py:445 -msgid "" -"For organizations that run their own Galaxy service, this gives the option " -"to specify a host as the primary galaxy server. Requirements will be " -"downloaded from the primary if the specific role or collection is available " -"there. If the content is not avilable in the primary, or if this field is " -"left blank, it will default to galaxy.ansible.com." -msgstr "" - -#: awx/main/conf.py:459 -msgid "Primary Galaxy Server Username" -msgstr "" - -#: awx/main/conf.py:460 -msgid "" -"For using a galaxy server at higher precedence than the public Ansible " -"Galaxy. The username to use for basic authentication against the Galaxy " -"instance, this is mutually exclusive with PRIMARY_GALAXY_TOKEN." -msgstr "" - -#: awx/main/conf.py:473 -msgid "Primary Galaxy Server Password" -msgstr "" - -#: awx/main/conf.py:474 -msgid "" -"For using a galaxy server at higher precedence than the public Ansible " -"Galaxy. The password to use for basic authentication against the Galaxy " -"instance, this is mutually exclusive with PRIMARY_GALAXY_TOKEN." -msgstr "" - -#: awx/main/conf.py:487 -msgid "Primary Galaxy Server Token" -msgstr "" - -#: awx/main/conf.py:488 -msgid "" -"For using a galaxy server at higher precedence than the public Ansible " -"Galaxy. The token to use for connecting with the Galaxy instance, this is " -"mutually exclusive with corresponding username and password settings." -msgstr "" - -#: awx/main/conf.py:500 -msgid "Primary Galaxy Authentication URL" -msgstr "" - -#: awx/main/conf.py:501 -msgid "" -"For using a galaxy server at higher precedence than the public Ansible " -"Galaxy. The token_endpoint of a Keycloak server." -msgstr "" - -#: awx/main/conf.py:511 -msgid "Allow Access to Public Galaxy" -msgstr "" - -#: awx/main/conf.py:512 -msgid "" -"Allow or deny access to the public Ansible Galaxy during project updates." -msgstr "" - -#: awx/main/conf.py:521 msgid "Ignore Ansible Galaxy SSL Certificate Verification" msgstr "" -#: awx/main/conf.py:522 +#: awx/main/conf.py:446 msgid "" -"If set to true, certificate validation will not be done wheninstalling " +"If set to true, certificate validation will not be done when installing " "content from any Galaxy server." msgstr "" -#: awx/main/conf.py:532 +#: awx/main/conf.py:456 msgid "Standard Output Maximum Display Size" msgstr "" -#: awx/main/conf.py:533 +#: awx/main/conf.py:457 msgid "" "Maximum Size of Standard Output in bytes to display before requiring the " "output be downloaded." msgstr "" -#: awx/main/conf.py:542 +#: awx/main/conf.py:466 msgid "Job Event Standard Output Maximum Display Size" msgstr "" -#: awx/main/conf.py:544 +#: awx/main/conf.py:468 msgid "" "Maximum Size of Standard Output in bytes to display for a single job or ad " "hoc command event. `stdout` will end with `…` when truncated." msgstr "" -#: awx/main/conf.py:553 +#: awx/main/conf.py:477 msgid "Maximum Scheduled Jobs" msgstr "" -#: awx/main/conf.py:554 +#: awx/main/conf.py:478 msgid "" "Maximum number of the same job template that can be waiting to run when " "launching from a schedule before no more are created." msgstr "" -#: awx/main/conf.py:563 +#: awx/main/conf.py:487 msgid "Ansible Callback Plugins" msgstr "" -#: awx/main/conf.py:564 +#: awx/main/conf.py:488 msgid "" "List of paths to search for extra callback plugins to be used when running " "jobs. Enter one path per line." msgstr "" -#: awx/main/conf.py:574 +#: awx/main/conf.py:498 msgid "Default Job Timeout" msgstr "" -#: awx/main/conf.py:575 +#: awx/main/conf.py:499 msgid "" "Maximum time in seconds to allow jobs to run. Use value of 0 to indicate " "that no timeout should be imposed. A timeout set on an individual job " "template will override this." msgstr "" -#: awx/main/conf.py:586 +#: awx/main/conf.py:511 msgid "Default Inventory Update Timeout" msgstr "" -#: awx/main/conf.py:587 +#: awx/main/conf.py:512 msgid "" "Maximum time in seconds to allow inventory updates to run. Use value of 0 to " "indicate that no timeout should be imposed. A timeout set on an individual " "inventory source will override this." msgstr "" -#: awx/main/conf.py:598 +#: awx/main/conf.py:524 msgid "Default Project Update Timeout" msgstr "" -#: awx/main/conf.py:599 +#: awx/main/conf.py:525 msgid "" "Maximum time in seconds to allow project updates to run. Use value of 0 to " "indicate that no timeout should be imposed. A timeout set on an individual " "project will override this." msgstr "" -#: awx/main/conf.py:610 +#: awx/main/conf.py:537 msgid "Per-Host Ansible Fact Cache Timeout" msgstr "" -#: awx/main/conf.py:611 +#: awx/main/conf.py:538 msgid "" "Maximum time, in seconds, that stored Ansible facts are considered valid " "since the last time they were modified. Only valid, non-stale, facts will be " @@ -2380,74 +2422,74 @@ msgid "" "timeout should be imposed." msgstr "" -#: awx/main/conf.py:624 -msgid "Maximum number of forks per job." +#: awx/main/conf.py:552 +msgid "Maximum number of forks per job" msgstr "" -#: awx/main/conf.py:625 +#: awx/main/conf.py:553 msgid "" "Saving a Job Template with more than this number of forks will result in an " "error. When set to 0, no limit is applied." msgstr "" -#: awx/main/conf.py:636 +#: awx/main/conf.py:564 msgid "Logging Aggregator" msgstr "" -#: awx/main/conf.py:637 +#: awx/main/conf.py:565 msgid "Hostname/IP where external logs will be sent to." msgstr "" -#: awx/main/conf.py:638 awx/main/conf.py:649 awx/main/conf.py:661 -#: awx/main/conf.py:671 awx/main/conf.py:683 awx/main/conf.py:698 -#: awx/main/conf.py:710 awx/main/conf.py:719 awx/main/conf.py:729 -#: awx/main/conf.py:741 awx/main/conf.py:752 awx/main/conf.py:764 -#: awx/main/conf.py:777 awx/main/conf.py:787 awx/main/conf.py:799 -#: awx/main/conf.py:810 awx/main/conf.py:820 +#: awx/main/conf.py:566 awx/main/conf.py:577 awx/main/conf.py:589 +#: awx/main/conf.py:599 awx/main/conf.py:611 awx/main/conf.py:626 +#: awx/main/conf.py:638 awx/main/conf.py:647 awx/main/conf.py:657 +#: awx/main/conf.py:669 awx/main/conf.py:680 awx/main/conf.py:693 +#: awx/main/conf.py:706 awx/main/conf.py:718 awx/main/conf.py:729 +#: awx/main/conf.py:739 msgid "Logging" msgstr "" -#: awx/main/conf.py:646 +#: awx/main/conf.py:574 msgid "Logging Aggregator Port" msgstr "" -#: awx/main/conf.py:647 +#: awx/main/conf.py:575 msgid "" "Port on Logging Aggregator to send logs to (if required and not provided in " "Logging Aggregator)." msgstr "" -#: awx/main/conf.py:659 +#: awx/main/conf.py:587 msgid "Logging Aggregator Type" msgstr "" -#: awx/main/conf.py:660 +#: awx/main/conf.py:588 msgid "Format messages for the chosen log aggregator." msgstr "" -#: awx/main/conf.py:669 +#: awx/main/conf.py:597 msgid "Logging Aggregator Username" msgstr "" -#: awx/main/conf.py:670 +#: awx/main/conf.py:598 msgid "Username for external log aggregator (if required; HTTP/s only)." msgstr "" -#: awx/main/conf.py:681 +#: awx/main/conf.py:609 msgid "Logging Aggregator Password/Token" msgstr "" -#: awx/main/conf.py:682 +#: awx/main/conf.py:610 msgid "" "Password or authentication token for external log aggregator (if required; " "HTTP/s only)." msgstr "" -#: awx/main/conf.py:691 +#: awx/main/conf.py:619 msgid "Loggers Sending Data to Log Aggregator Form" msgstr "" -#: awx/main/conf.py:692 +#: awx/main/conf.py:620 msgid "" "List of loggers that will send HTTP logs to the collector, these can include " "any or all of: \n" @@ -2457,11 +2499,11 @@ msgid "" "system_tracking - facts gathered from scan jobs." msgstr "" -#: awx/main/conf.py:705 +#: awx/main/conf.py:633 msgid "Log System Tracking Facts Individually" msgstr "" -#: awx/main/conf.py:706 +#: awx/main/conf.py:634 msgid "" "If set, system tracking facts will be sent for each package, service, or " "other item found in a scan, allowing for greater search query granularity. " @@ -2469,47 +2511,47 @@ msgid "" "efficiency in fact processing." msgstr "" -#: awx/main/conf.py:717 +#: awx/main/conf.py:645 msgid "Enable External Logging" msgstr "" -#: awx/main/conf.py:718 +#: awx/main/conf.py:646 msgid "Enable sending logs to external log aggregator." msgstr "" -#: awx/main/conf.py:727 +#: awx/main/conf.py:655 msgid "Cluster-wide Tower unique identifier." msgstr "" -#: awx/main/conf.py:728 +#: awx/main/conf.py:656 msgid "Useful to uniquely identify Tower instances." msgstr "" -#: awx/main/conf.py:737 +#: awx/main/conf.py:665 msgid "Logging Aggregator Protocol" msgstr "" -#: awx/main/conf.py:738 +#: awx/main/conf.py:666 msgid "" "Protocol used to communicate with log aggregator. HTTPS/HTTP assumes HTTPS " "unless http:// is explicitly used in the Logging Aggregator hostname." msgstr "" -#: awx/main/conf.py:748 +#: awx/main/conf.py:676 msgid "TCP Connection Timeout" msgstr "" -#: awx/main/conf.py:749 +#: awx/main/conf.py:677 msgid "" "Number of seconds for a TCP connection to external log aggregator to " "timeout. Applies to HTTPS and TCP log aggregator protocols." msgstr "" -#: awx/main/conf.py:759 +#: awx/main/conf.py:688 msgid "Enable/disable HTTPS certificate verification" msgstr "" -#: awx/main/conf.py:760 +#: awx/main/conf.py:689 msgid "" "Flag to control enable/disable of certificate verification when " "LOG_AGGREGATOR_PROTOCOL is \"https\". If enabled, Tower's log handler will " @@ -2517,11 +2559,11 @@ msgid "" "connection." msgstr "" -#: awx/main/conf.py:772 +#: awx/main/conf.py:701 msgid "Logging Aggregator Level Threshold" msgstr "" -#: awx/main/conf.py:773 +#: awx/main/conf.py:702 msgid "" "Level threshold used by log handler. Severities from lowest to highest are " "DEBUG, INFO, WARNING, ERROR, CRITICAL. Messages less severe than the " @@ -2529,198 +2571,144 @@ msgid "" "anlytics ignore this setting)" msgstr "" -#: awx/main/conf.py:785 -msgid "Enabled external log aggregation auditing" -msgstr "" - -#: awx/main/conf.py:786 -msgid "" -"When enabled, all external logs emitted by Tower will also be written to /" -"var/log/tower/external.log. This is an experimental setting intended to be " -"used for debugging external log aggregation issues (and may be subject to " -"change in the future)." -msgstr "" - -#: awx/main/conf.py:795 +#: awx/main/conf.py:714 msgid "Maximum disk persistance for external log aggregation (in GB)" msgstr "" -#: awx/main/conf.py:796 +#: awx/main/conf.py:715 msgid "" "Amount of data to store (in gigabytes) during an outage of the external log " "aggregator (defaults to 1). Equivalent to the rsyslogd queue.maxdiskspace " "setting." msgstr "" -#: awx/main/conf.py:806 +#: awx/main/conf.py:725 msgid "File system location for rsyslogd disk persistence" msgstr "" -#: awx/main/conf.py:807 +#: awx/main/conf.py:726 msgid "" "Location to persist logs that should be retried after an outage of the " "external log aggregator (defaults to /var/lib/awx). Equivalent to the " "rsyslogd queue.spoolDirectory setting." msgstr "" -#: awx/main/conf.py:817 +#: awx/main/conf.py:736 msgid "Enable rsyslogd debugging" msgstr "" -#: awx/main/conf.py:818 +#: awx/main/conf.py:737 msgid "" "Enabled high verbosity debugging for rsyslogd. Useful for debugging " "connection issues for external log aggregation." msgstr "" -#: awx/main/conf.py:828 -msgid "Message Durability" -msgstr "" - -#: awx/main/conf.py:829 -msgid "" -"When set (the default), underlying queues will be persisted to disk. " -"Disable this to enable higher message bus throughput." -msgstr "" - -#: awx/main/conf.py:838 +#: awx/main/conf.py:748 msgid "Last gather date for Automation Analytics." msgstr "" -#: awx/main/conf.py:848 +#: awx/main/conf.py:758 msgid "Automation Analytics Gather Interval" msgstr "" -#: awx/main/conf.py:849 +#: awx/main/conf.py:759 msgid "Interval (in seconds) between data gathering." msgstr "" -#: awx/main/conf.py:871 awx/sso/conf.py:1239 +#: awx/main/conf.py:782 awx/sso/conf.py:1251 msgid "\n" msgstr "" -#: awx/main/conf.py:892 -msgid "" -"A URL for Primary Galaxy must be defined before disabling public Galaxy." -msgstr "" - -#: awx/main/conf.py:912 -msgid "Cannot provide field if PRIMARY_GALAXY_URL is not set." -msgstr "" - -#: awx/main/conf.py:925 -#, python-brace-format -msgid "" -"Galaxy server settings are not available until Ansible {min_version}, you " -"are running {current_version}." -msgstr "" - -#: awx/main/conf.py:934 -msgid "" -"Setting Galaxy token and authentication URL is mutually exclusive with " -"username and password." -msgstr "" - -#: awx/main/conf.py:937 -msgid "If authenticating via username and password, both must be provided." -msgstr "" - -#: awx/main/conf.py:943 -msgid "" -"If authenticating via token, both token and authentication URL must be " -"provided." -msgstr "" - -#: awx/main/constants.py:17 +#: awx/main/constants.py:16 msgid "Sudo" msgstr "" -#: awx/main/constants.py:17 +#: awx/main/constants.py:16 msgid "Su" msgstr "" -#: awx/main/constants.py:17 +#: awx/main/constants.py:16 msgid "Pbrun" msgstr "" -#: awx/main/constants.py:17 +#: awx/main/constants.py:16 msgid "Pfexec" msgstr "" -#: awx/main/constants.py:18 +#: awx/main/constants.py:17 msgid "DZDO" msgstr "" -#: awx/main/constants.py:18 +#: awx/main/constants.py:17 msgid "Pmrun" msgstr "" -#: awx/main/constants.py:18 +#: awx/main/constants.py:17 msgid "Runas" msgstr "" -#: awx/main/constants.py:19 +#: awx/main/constants.py:18 msgid "Enable" msgstr "" -#: awx/main/constants.py:19 +#: awx/main/constants.py:18 msgid "Doas" msgstr "" -#: awx/main/constants.py:19 +#: awx/main/constants.py:18 msgid "Ksu" msgstr "" -#: awx/main/constants.py:20 +#: awx/main/constants.py:19 msgid "Machinectl" msgstr "" -#: awx/main/constants.py:20 +#: awx/main/constants.py:19 msgid "Sesu" msgstr "" -#: awx/main/constants.py:22 +#: awx/main/constants.py:21 msgid "None" msgstr "" -#: awx/main/credential_plugins/aim.py:16 +#: awx/main/credential_plugins/aim.py:11 msgid "CyberArk AIM URL" msgstr "" -#: awx/main/credential_plugins/aim.py:21 +#: awx/main/credential_plugins/aim.py:16 msgid "Application ID" msgstr "" -#: awx/main/credential_plugins/aim.py:26 +#: awx/main/credential_plugins/aim.py:21 msgid "Client Key" msgstr "" -#: awx/main/credential_plugins/aim.py:32 +#: awx/main/credential_plugins/aim.py:27 msgid "Client Certificate" msgstr "" -#: awx/main/credential_plugins/aim.py:38 +#: awx/main/credential_plugins/aim.py:33 msgid "Verify SSL Certificates" msgstr "" -#: awx/main/credential_plugins/aim.py:44 +#: awx/main/credential_plugins/aim.py:39 msgid "Object Query" msgstr "" -#: awx/main/credential_plugins/aim.py:46 +#: awx/main/credential_plugins/aim.py:41 msgid "" "Lookup query for the object. Ex: Safe=TestSafe;Object=testAccountName123" msgstr "" -#: awx/main/credential_plugins/aim.py:49 +#: awx/main/credential_plugins/aim.py:44 msgid "Object Query Format" msgstr "" -#: awx/main/credential_plugins/aim.py:55 +#: awx/main/credential_plugins/aim.py:50 msgid "Reason" msgstr "" -#: awx/main/credential_plugins/aim.py:57 +#: awx/main/credential_plugins/aim.py:52 msgid "" "Object request reason. This is only needed if it is required by the object's " "policy." @@ -2731,12 +2719,12 @@ msgid "Vault URL (DNS Name)" msgstr "" #: awx/main/credential_plugins/azure_kv.py:26 -#: awx/main/models/credential/__init__.py:960 +#: awx/main/models/credential/__init__.py:968 msgid "Client ID" msgstr "" #: awx/main/credential_plugins/azure_kv.py:35 -#: awx/main/models/credential/__init__.py:969 +#: awx/main/models/credential/__init__.py:977 msgid "Tenant ID" msgstr "" @@ -2757,166 +2745,177 @@ msgid "The name of the secret to look up." msgstr "" #: awx/main/credential_plugins/azure_kv.py:51 -#: awx/main/credential_plugins/conjur.py:47 +#: awx/main/credential_plugins/conjur.py:42 msgid "Secret Version" msgstr "" #: awx/main/credential_plugins/azure_kv.py:53 -#: awx/main/credential_plugins/conjur.py:49 -#: awx/main/credential_plugins/hashivault.py:86 +#: awx/main/credential_plugins/conjur.py:44 +#: awx/main/credential_plugins/hashivault.py:89 msgid "" "Used to specify a specific secret version (if left empty, the latest version " "will be used)." msgstr "" -#: awx/main/credential_plugins/conjur.py:18 +#: awx/main/credential_plugins/conjur.py:13 msgid "Conjur URL" msgstr "" -#: awx/main/credential_plugins/conjur.py:23 +#: awx/main/credential_plugins/conjur.py:18 msgid "API Key" msgstr "" -#: awx/main/credential_plugins/conjur.py:28 awx/main/models/inventory.py:1018 +#: awx/main/credential_plugins/conjur.py:23 +#: awx/main/migrations/_inventory_source_vars.py:142 msgid "Account" msgstr "" -#: awx/main/credential_plugins/conjur.py:32 -#: awx/main/models/credential/__init__.py:598 -#: awx/main/models/credential/__init__.py:654 -#: awx/main/models/credential/__init__.py:712 -#: awx/main/models/credential/__init__.py:785 -#: awx/main/models/credential/__init__.py:838 -#: awx/main/models/credential/__init__.py:864 -#: awx/main/models/credential/__init__.py:891 -#: awx/main/models/credential/__init__.py:951 -#: awx/main/models/credential/__init__.py:1024 -#: awx/main/models/credential/__init__.py:1055 -#: awx/main/models/credential/__init__.py:1105 +#: awx/main/credential_plugins/conjur.py:27 +#: awx/main/models/credential/__init__.py:606 +#: awx/main/models/credential/__init__.py:662 +#: awx/main/models/credential/__init__.py:720 +#: awx/main/models/credential/__init__.py:793 +#: awx/main/models/credential/__init__.py:846 +#: awx/main/models/credential/__init__.py:872 +#: awx/main/models/credential/__init__.py:899 +#: awx/main/models/credential/__init__.py:959 +#: awx/main/models/credential/__init__.py:1032 +#: awx/main/models/credential/__init__.py:1063 +#: awx/main/models/credential/__init__.py:1113 msgid "Username" msgstr "" -#: awx/main/credential_plugins/conjur.py:36 +#: awx/main/credential_plugins/conjur.py:31 msgid "Public Key Certificate" msgstr "" -#: awx/main/credential_plugins/conjur.py:42 +#: awx/main/credential_plugins/conjur.py:37 msgid "Secret Identifier" msgstr "" -#: awx/main/credential_plugins/conjur.py:44 +#: awx/main/credential_plugins/conjur.py:39 msgid "The identifier for the secret e.g., /some/identifier" msgstr "" -#: awx/main/credential_plugins/hashivault.py:19 +#: awx/main/credential_plugins/hashivault.py:14 msgid "Server URL" msgstr "" -#: awx/main/credential_plugins/hashivault.py:22 +#: awx/main/credential_plugins/hashivault.py:17 msgid "The URL to the HashiCorp Vault" msgstr "" -#: awx/main/credential_plugins/hashivault.py:25 -#: awx/main/models/credential/__init__.py:990 -#: awx/main/models/credential/__init__.py:1007 +#: awx/main/credential_plugins/hashivault.py:20 +#: awx/main/models/credential/__init__.py:998 +#: awx/main/models/credential/__init__.py:1015 msgid "Token" msgstr "" -#: awx/main/credential_plugins/hashivault.py:28 +#: awx/main/credential_plugins/hashivault.py:23 msgid "The access token used to authenticate to the Vault server" msgstr "" -#: awx/main/credential_plugins/hashivault.py:31 +#: awx/main/credential_plugins/hashivault.py:26 msgid "CA Certificate" msgstr "" -#: awx/main/credential_plugins/hashivault.py:34 +#: awx/main/credential_plugins/hashivault.py:29 msgid "" "The CA certificate used to verify the SSL certificate of the Vault server" msgstr "" -#: awx/main/credential_plugins/hashivault.py:37 +#: awx/main/credential_plugins/hashivault.py:32 msgid "AppRole role_id" msgstr "" -#: awx/main/credential_plugins/hashivault.py:40 +#: awx/main/credential_plugins/hashivault.py:35 msgid "The Role ID for AppRole Authentication" msgstr "" -#: awx/main/credential_plugins/hashivault.py:43 +#: awx/main/credential_plugins/hashivault.py:38 msgid "AppRole secret_id" msgstr "" -#: awx/main/credential_plugins/hashivault.py:47 +#: awx/main/credential_plugins/hashivault.py:42 msgid "The Secret ID for AppRole Authentication" msgstr "" -#: awx/main/credential_plugins/hashivault.py:52 -msgid "Path to Secret" +#: awx/main/credential_plugins/hashivault.py:45 +msgid "Path to Approle Auth" +msgstr "" + +#: awx/main/credential_plugins/hashivault.py:49 +msgid "" +"The AppRole Authentication path to use if one isn't provided in the metadata " +"when linking to an input field. Defaults to 'approle'" msgstr "" #: awx/main/credential_plugins/hashivault.py:54 +msgid "Path to Secret" +msgstr "" + +#: awx/main/credential_plugins/hashivault.py:56 msgid "The path to the secret stored in the secret backend e.g, /some/secret/" msgstr "" -#: awx/main/credential_plugins/hashivault.py:57 +#: awx/main/credential_plugins/hashivault.py:59 msgid "Path to Auth" msgstr "" -#: awx/main/credential_plugins/hashivault.py:59 +#: awx/main/credential_plugins/hashivault.py:62 msgid "The path where the Authentication method is mounted e.g, approle" msgstr "" -#: awx/main/credential_plugins/hashivault.py:67 +#: awx/main/credential_plugins/hashivault.py:70 msgid "API Version" msgstr "" -#: awx/main/credential_plugins/hashivault.py:69 +#: awx/main/credential_plugins/hashivault.py:72 msgid "" "API v1 is for static key/value lookups. API v2 is for versioned key/value " "lookups." msgstr "" -#: awx/main/credential_plugins/hashivault.py:74 +#: awx/main/credential_plugins/hashivault.py:77 msgid "Name of Secret Backend" msgstr "" -#: awx/main/credential_plugins/hashivault.py:76 +#: awx/main/credential_plugins/hashivault.py:79 msgid "" "The name of the kv secret backend (if left empty, the first segment of the " "secret path will be used)." msgstr "" -#: awx/main/credential_plugins/hashivault.py:79 -#: awx/main/models/inventory.py:1023 +#: awx/main/credential_plugins/hashivault.py:82 +#: awx/main/migrations/_inventory_source_vars.py:147 msgid "Key Name" msgstr "" -#: awx/main/credential_plugins/hashivault.py:81 +#: awx/main/credential_plugins/hashivault.py:84 msgid "The name of the key to look up in the secret." msgstr "" -#: awx/main/credential_plugins/hashivault.py:84 +#: awx/main/credential_plugins/hashivault.py:87 msgid "Secret Version (v2 only)" msgstr "" -#: awx/main/credential_plugins/hashivault.py:93 +#: awx/main/credential_plugins/hashivault.py:96 msgid "Unsigned Public Key" msgstr "" -#: awx/main/credential_plugins/hashivault.py:98 +#: awx/main/credential_plugins/hashivault.py:101 msgid "Role Name" msgstr "" -#: awx/main/credential_plugins/hashivault.py:100 +#: awx/main/credential_plugins/hashivault.py:103 msgid "The name of the role used to sign." msgstr "" -#: awx/main/credential_plugins/hashivault.py:103 +#: awx/main/credential_plugins/hashivault.py:106 msgid "Valid Principals" msgstr "" -#: awx/main/credential_plugins/hashivault.py:105 +#: awx/main/credential_plugins/hashivault.py:108 msgid "" "Valid principals (either usernames or hostnames) that the certificate should " "be signed for." @@ -2951,70 +2950,74 @@ msgstr "" msgid "secret values must be of type string, not {}" msgstr "" -#: awx/main/fields.py:667 +#: awx/main/fields.py:675 #, python-format msgid "cannot be set unless \"%s\" is set" msgstr "" -#: awx/main/fields.py:702 +#: awx/main/fields.py:710 msgid "must be set when SSH key is encrypted." msgstr "" -#: awx/main/fields.py:710 +#: awx/main/fields.py:718 msgid "should not be set when SSH key is not encrypted." msgstr "" -#: awx/main/fields.py:769 +#: awx/main/fields.py:777 msgid "'dependencies' is not supported for custom credentials." msgstr "" -#: awx/main/fields.py:783 +#: awx/main/fields.py:791 msgid "\"tower\" is a reserved field name" msgstr "" -#: awx/main/fields.py:790 +#: awx/main/fields.py:798 #, python-format msgid "field IDs must be unique (%s)" msgstr "" -#: awx/main/fields.py:805 +#: awx/main/fields.py:813 msgid "{} is not a {}" msgstr "" -#: awx/main/fields.py:811 +#: awx/main/fields.py:819 #, python-brace-format msgid "{sub_key} not allowed for {element_type} type ({element_id})" msgstr "" -#: awx/main/fields.py:869 +#: awx/main/fields.py:877 msgid "" "Environment variable {} may affect Ansible configuration so its use is not " "allowed in credentials." msgstr "" -#: awx/main/fields.py:875 -msgid "Environment variable {} is blacklisted from use in credentials." +#: awx/main/fields.py:883 +msgid "Environment variable {} is not allowed to be used in credentials." msgstr "" -#: awx/main/fields.py:903 +#: awx/main/fields.py:911 msgid "" "Must define unnamed file injector in order to reference `tower.filename`." msgstr "" -#: awx/main/fields.py:910 +#: awx/main/fields.py:918 msgid "Cannot directly reference reserved `tower` namespace container." msgstr "" -#: awx/main/fields.py:920 +#: awx/main/fields.py:928 msgid "Must use multi-file syntax when injecting multiple files" msgstr "" -#: awx/main/fields.py:940 +#: awx/main/fields.py:948 #, python-brace-format msgid "{sub_key} uses an undefined field ({error_msg})" msgstr "" -#: awx/main/fields.py:947 +#: awx/main/fields.py:955 +msgid "Encountered unsafe code execution: {}" +msgstr "" + +#: awx/main/fields.py:959 #, python-brace-format msgid "" "Syntax error rendering template for {sub_key} inside of {type} ({error_msg})" @@ -3044,6 +3047,50 @@ msgid "" "this list to programmatically generate named URLs for resources" msgstr "" +#: awx/main/migrations/_inventory_source_vars.py:140 +msgid "Image ID" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:141 +msgid "Availability Zone" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:143 +msgid "Instance ID" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:144 +msgid "Instance State" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:145 +msgid "Platform" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:146 +msgid "Instance Type" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:148 +msgid "Region" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:149 +msgid "Security Group" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:150 +msgid "Tags" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:151 +msgid "Tag None" +msgstr "" + +#: awx/main/migrations/_inventory_source_vars.py:152 +msgid "VPC ID" +msgstr "" + #: awx/main/models/activity_stream.py:28 msgid "Entity Created" msgstr "" @@ -3090,17 +3137,17 @@ msgstr "" msgid "No argument passed to %s module." msgstr "" -#: awx/main/models/base.py:33 awx/main/models/base.py:39 -#: awx/main/models/base.py:44 awx/main/models/base.py:49 +#: awx/main/models/base.py:34 awx/main/models/base.py:40 +#: awx/main/models/base.py:45 awx/main/models/base.py:50 msgid "Run" msgstr "" -#: awx/main/models/base.py:34 awx/main/models/base.py:40 -#: awx/main/models/base.py:45 awx/main/models/base.py:50 +#: awx/main/models/base.py:35 awx/main/models/base.py:41 +#: awx/main/models/base.py:46 awx/main/models/base.py:51 msgid "Check" msgstr "" -#: awx/main/models/base.py:35 +#: awx/main/models/base.py:36 msgid "Scan" msgstr "" @@ -3110,819 +3157,822 @@ msgid "" "Tower documentation for details on each type." msgstr "" -#: awx/main/models/credential/__init__.py:110 -#: awx/main/models/credential/__init__.py:353 +#: awx/main/models/credential/__init__.py:114 +#: awx/main/models/credential/__init__.py:358 msgid "" "Enter inputs using either JSON or YAML syntax. Refer to the Ansible Tower " "documentation for example syntax." msgstr "" -#: awx/main/models/credential/__init__.py:325 -#: awx/main/models/credential/__init__.py:594 +#: awx/main/models/credential/__init__.py:329 +#: awx/main/models/credential/__init__.py:602 msgid "Machine" msgstr "" -#: awx/main/models/credential/__init__.py:326 -#: awx/main/models/credential/__init__.py:680 +#: awx/main/models/credential/__init__.py:330 +#: awx/main/models/credential/__init__.py:688 msgid "Vault" msgstr "" -#: awx/main/models/credential/__init__.py:327 -#: awx/main/models/credential/__init__.py:707 +#: awx/main/models/credential/__init__.py:331 +#: awx/main/models/credential/__init__.py:715 msgid "Network" msgstr "" -#: awx/main/models/credential/__init__.py:328 -#: awx/main/models/credential/__init__.py:649 +#: awx/main/models/credential/__init__.py:332 +#: awx/main/models/credential/__init__.py:657 msgid "Source Control" msgstr "" -#: awx/main/models/credential/__init__.py:329 +#: awx/main/models/credential/__init__.py:333 msgid "Cloud" msgstr "" -#: awx/main/models/credential/__init__.py:330 +#: awx/main/models/credential/__init__.py:334 msgid "Personal Access Token" msgstr "" -#: awx/main/models/credential/__init__.py:331 -#: awx/main/models/credential/__init__.py:1019 +#: awx/main/models/credential/__init__.py:335 +#: awx/main/models/credential/__init__.py:1027 msgid "Insights" msgstr "" -#: awx/main/models/credential/__init__.py:332 +#: awx/main/models/credential/__init__.py:336 msgid "External" msgstr "" -#: awx/main/models/credential/__init__.py:333 +#: awx/main/models/credential/__init__.py:337 msgid "Kubernetes" msgstr "" -#: awx/main/models/credential/__init__.py:359 +#: awx/main/models/credential/__init__.py:338 +msgid "Galaxy/Automation Hub" +msgstr "" + +#: awx/main/models/credential/__init__.py:364 msgid "" "Enter injectors using either JSON or YAML syntax. Refer to the Ansible Tower " "documentation for example syntax." msgstr "" -#: awx/main/models/credential/__init__.py:428 +#: awx/main/models/credential/__init__.py:433 #, python-format msgid "adding %s credential type" msgstr "" -#: awx/main/models/credential/__init__.py:602 -#: awx/main/models/credential/__init__.py:658 -#: awx/main/models/credential/__init__.py:716 -#: awx/main/models/credential/__init__.py:842 -#: awx/main/models/credential/__init__.py:868 -#: awx/main/models/credential/__init__.py:895 -#: awx/main/models/credential/__init__.py:955 -#: awx/main/models/credential/__init__.py:1028 -#: awx/main/models/credential/__init__.py:1059 -#: awx/main/models/credential/__init__.py:1109 +#: awx/main/models/credential/__init__.py:610 +#: awx/main/models/credential/__init__.py:666 +#: awx/main/models/credential/__init__.py:724 +#: awx/main/models/credential/__init__.py:850 +#: awx/main/models/credential/__init__.py:876 +#: awx/main/models/credential/__init__.py:903 +#: awx/main/models/credential/__init__.py:963 +#: awx/main/models/credential/__init__.py:1036 +#: awx/main/models/credential/__init__.py:1067 +#: awx/main/models/credential/__init__.py:1119 msgid "Password" msgstr "" -#: awx/main/models/credential/__init__.py:608 -#: awx/main/models/credential/__init__.py:721 +#: awx/main/models/credential/__init__.py:616 +#: awx/main/models/credential/__init__.py:729 msgid "SSH Private Key" msgstr "" -#: awx/main/models/credential/__init__.py:615 +#: awx/main/models/credential/__init__.py:623 msgid "Signed SSH Certificate" msgstr "" -#: awx/main/models/credential/__init__.py:621 -#: awx/main/models/credential/__init__.py:670 -#: awx/main/models/credential/__init__.py:728 +#: awx/main/models/credential/__init__.py:629 +#: awx/main/models/credential/__init__.py:678 +#: awx/main/models/credential/__init__.py:736 msgid "Private Key Passphrase" msgstr "" -#: awx/main/models/credential/__init__.py:627 +#: awx/main/models/credential/__init__.py:635 msgid "Privilege Escalation Method" msgstr "" -#: awx/main/models/credential/__init__.py:629 +#: awx/main/models/credential/__init__.py:637 msgid "" "Specify a method for \"become\" operations. This is equivalent to specifying " "the --become-method Ansible parameter." msgstr "" -#: awx/main/models/credential/__init__.py:634 +#: awx/main/models/credential/__init__.py:642 msgid "Privilege Escalation Username" msgstr "" -#: awx/main/models/credential/__init__.py:638 +#: awx/main/models/credential/__init__.py:646 msgid "Privilege Escalation Password" msgstr "" -#: awx/main/models/credential/__init__.py:663 +#: awx/main/models/credential/__init__.py:671 msgid "SCM Private Key" msgstr "" -#: awx/main/models/credential/__init__.py:685 +#: awx/main/models/credential/__init__.py:693 msgid "Vault Password" msgstr "" -#: awx/main/models/credential/__init__.py:691 +#: awx/main/models/credential/__init__.py:699 msgid "Vault Identifier" msgstr "" -#: awx/main/models/credential/__init__.py:694 +#: awx/main/models/credential/__init__.py:702 msgid "" "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+." msgstr "" -#: awx/main/models/credential/__init__.py:733 +#: awx/main/models/credential/__init__.py:741 msgid "Authorize" msgstr "" -#: awx/main/models/credential/__init__.py:737 +#: awx/main/models/credential/__init__.py:745 msgid "Authorize Password" msgstr "" -#: awx/main/models/credential/__init__.py:751 +#: awx/main/models/credential/__init__.py:759 msgid "Amazon Web Services" msgstr "" -#: awx/main/models/credential/__init__.py:756 +#: awx/main/models/credential/__init__.py:764 msgid "Access Key" msgstr "" -#: awx/main/models/credential/__init__.py:760 +#: awx/main/models/credential/__init__.py:768 msgid "Secret Key" msgstr "" -#: awx/main/models/credential/__init__.py:765 +#: awx/main/models/credential/__init__.py:773 msgid "STS Token" msgstr "" -#: awx/main/models/credential/__init__.py:768 +#: awx/main/models/credential/__init__.py:776 msgid "" "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." msgstr "" -#: awx/main/models/credential/__init__.py:780 awx/main/models/inventory.py:833 +#: awx/main/models/credential/__init__.py:788 awx/main/models/inventory.py:826 msgid "OpenStack" msgstr "" -#: awx/main/models/credential/__init__.py:789 +#: awx/main/models/credential/__init__.py:797 msgid "Password (API Key)" msgstr "" -#: awx/main/models/credential/__init__.py:794 -#: awx/main/models/credential/__init__.py:1050 +#: awx/main/models/credential/__init__.py:802 +#: awx/main/models/credential/__init__.py:1058 msgid "Host (Authentication URL)" msgstr "" -#: awx/main/models/credential/__init__.py:796 +#: awx/main/models/credential/__init__.py:804 msgid "" "The host to authenticate with. For example, https://openstack.business.com/" "v2.0/" msgstr "" -#: awx/main/models/credential/__init__.py:800 +#: awx/main/models/credential/__init__.py:808 msgid "Project (Tenant Name)" msgstr "" -#: awx/main/models/credential/__init__.py:804 +#: awx/main/models/credential/__init__.py:812 msgid "Project (Domain Name)" msgstr "" -#: awx/main/models/credential/__init__.py:808 +#: awx/main/models/credential/__init__.py:816 msgid "Domain Name" msgstr "" -#: awx/main/models/credential/__init__.py:810 +#: awx/main/models/credential/__init__.py:818 msgid "" "OpenStack domains define administrative boundaries. It is only needed for " "Keystone v3 authentication URLs. Refer to Ansible Tower documentation for " "common scenarios." msgstr "" -#: awx/main/models/credential/__init__.py:816 -#: awx/main/models/credential/__init__.py:1114 -#: awx/main/models/credential/__init__.py:1148 +#: awx/main/models/credential/__init__.py:824 +#: awx/main/models/credential/__init__.py:1131 +#: awx/main/models/credential/__init__.py:1166 msgid "Verify SSL" msgstr "" -#: awx/main/models/credential/__init__.py:827 awx/main/models/inventory.py:830 +#: awx/main/models/credential/__init__.py:835 awx/main/models/inventory.py:824 msgid "VMware vCenter" msgstr "" -#: awx/main/models/credential/__init__.py:832 +#: awx/main/models/credential/__init__.py:840 msgid "VCenter Host" msgstr "" -#: awx/main/models/credential/__init__.py:834 +#: awx/main/models/credential/__init__.py:842 msgid "" "Enter the hostname or IP address that corresponds to your VMware vCenter." msgstr "" -#: awx/main/models/credential/__init__.py:853 awx/main/models/inventory.py:831 +#: awx/main/models/credential/__init__.py:861 awx/main/models/inventory.py:825 msgid "Red Hat Satellite 6" msgstr "" -#: awx/main/models/credential/__init__.py:858 +#: awx/main/models/credential/__init__.py:866 msgid "Satellite 6 URL" msgstr "" -#: awx/main/models/credential/__init__.py:860 +#: awx/main/models/credential/__init__.py:868 msgid "" "Enter the URL that corresponds to your Red Hat Satellite 6 server. For " "example, https://satellite.example.org" msgstr "" -#: awx/main/models/credential/__init__.py:879 awx/main/models/inventory.py:832 +#: awx/main/models/credential/__init__.py:887 msgid "Red Hat CloudForms" msgstr "" -#: awx/main/models/credential/__init__.py:884 +#: awx/main/models/credential/__init__.py:892 msgid "CloudForms URL" msgstr "" -#: awx/main/models/credential/__init__.py:886 +#: awx/main/models/credential/__init__.py:894 msgid "" "Enter the URL for the virtual machine that corresponds to your CloudForms " "instance. For example, https://cloudforms.example.org" msgstr "" -#: awx/main/models/credential/__init__.py:906 awx/main/models/inventory.py:828 +#: awx/main/models/credential/__init__.py:914 awx/main/models/inventory.py:822 msgid "Google Compute Engine" msgstr "" -#: awx/main/models/credential/__init__.py:911 +#: awx/main/models/credential/__init__.py:919 msgid "Service Account Email Address" msgstr "" -#: awx/main/models/credential/__init__.py:913 +#: awx/main/models/credential/__init__.py:921 msgid "" "The email address assigned to the Google Compute Engine service account." msgstr "" -#: awx/main/models/credential/__init__.py:919 +#: awx/main/models/credential/__init__.py:927 msgid "" "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" msgstr "" -#: awx/main/models/credential/__init__.py:925 +#: awx/main/models/credential/__init__.py:933 msgid "RSA Private Key" msgstr "" -#: awx/main/models/credential/__init__.py:930 +#: awx/main/models/credential/__init__.py:938 msgid "" "Paste the contents of the PEM file associated with the service account email." msgstr "" -#: awx/main/models/credential/__init__.py:940 awx/main/models/inventory.py:829 +#: awx/main/models/credential/__init__.py:948 awx/main/models/inventory.py:823 msgid "Microsoft Azure Resource Manager" msgstr "" -#: awx/main/models/credential/__init__.py:945 +#: awx/main/models/credential/__init__.py:953 msgid "Subscription ID" msgstr "" -#: awx/main/models/credential/__init__.py:947 +#: awx/main/models/credential/__init__.py:955 msgid "Subscription ID is an Azure construct, which is mapped to a username." msgstr "" -#: awx/main/models/credential/__init__.py:973 +#: awx/main/models/credential/__init__.py:981 msgid "Azure Cloud Environment" msgstr "" -#: awx/main/models/credential/__init__.py:975 +#: awx/main/models/credential/__init__.py:983 msgid "" "Environment variable AZURE_CLOUD_ENVIRONMENT when using Azure GovCloud or " "Azure stack." msgstr "" -#: awx/main/models/credential/__init__.py:985 +#: awx/main/models/credential/__init__.py:993 msgid "GitHub Personal Access Token" msgstr "" -#: awx/main/models/credential/__init__.py:993 +#: awx/main/models/credential/__init__.py:1001 msgid "This token needs to come from your profile settings in GitHub" msgstr "" -#: awx/main/models/credential/__init__.py:1002 +#: awx/main/models/credential/__init__.py:1010 msgid "GitLab Personal Access Token" msgstr "" -#: awx/main/models/credential/__init__.py:1010 +#: awx/main/models/credential/__init__.py:1018 msgid "This token needs to come from your profile settings in GitLab" msgstr "" -#: awx/main/models/credential/__init__.py:1045 awx/main/models/inventory.py:834 +#: awx/main/models/credential/__init__.py:1053 awx/main/models/inventory.py:827 msgid "Red Hat Virtualization" msgstr "" -#: awx/main/models/credential/__init__.py:1052 +#: awx/main/models/credential/__init__.py:1060 msgid "The host to authenticate with." msgstr "" -#: awx/main/models/credential/__init__.py:1064 +#: awx/main/models/credential/__init__.py:1072 msgid "CA File" msgstr "" -#: awx/main/models/credential/__init__.py:1066 +#: awx/main/models/credential/__init__.py:1074 msgid "Absolute file path to the CA file to use (optional)" msgstr "" -#: awx/main/models/credential/__init__.py:1095 awx/main/models/inventory.py:835 +#: awx/main/models/credential/__init__.py:1103 awx/main/models/inventory.py:828 msgid "Ansible Tower" msgstr "" -#: awx/main/models/credential/__init__.py:1100 +#: awx/main/models/credential/__init__.py:1108 msgid "Ansible Tower Hostname" msgstr "" -#: awx/main/models/credential/__init__.py:1102 +#: awx/main/models/credential/__init__.py:1110 msgid "The Ansible Tower base URL to authenticate with." msgstr "" -#: awx/main/models/credential/__init__.py:1134 +#: awx/main/models/credential/__init__.py:1115 +msgid "" +"The Ansible Tower user to authenticate as.This should not be set if an OAuth " +"token is being used." +msgstr "" + +#: awx/main/models/credential/__init__.py:1124 +msgid "OAuth Token" +msgstr "" + +#: awx/main/models/credential/__init__.py:1127 +msgid "" +"An OAuth token to use to authenticate to Tower with.This should not be set " +"if username/password are being used." +msgstr "" + +#: awx/main/models/credential/__init__.py:1152 msgid "OpenShift or Kubernetes API Bearer Token" msgstr "" -#: awx/main/models/credential/__init__.py:1138 +#: awx/main/models/credential/__init__.py:1156 msgid "OpenShift or Kubernetes API Endpoint" msgstr "" -#: awx/main/models/credential/__init__.py:1140 +#: awx/main/models/credential/__init__.py:1158 msgid "The OpenShift or Kubernetes API Endpoint to authenticate with." msgstr "" -#: awx/main/models/credential/__init__.py:1143 +#: awx/main/models/credential/__init__.py:1161 msgid "API authentication bearer token" msgstr "" -#: awx/main/models/credential/__init__.py:1153 +#: awx/main/models/credential/__init__.py:1171 msgid "Certificate Authority data" msgstr "" -#: awx/main/models/credential/__init__.py:1194 +#: awx/main/models/credential/__init__.py:1184 +msgid "Ansible Galaxy/Automation Hub API Token" +msgstr "" + +#: awx/main/models/credential/__init__.py:1188 +msgid "Galaxy Server URL" +msgstr "" + +#: awx/main/models/credential/__init__.py:1190 +msgid "The URL of the Galaxy instance to connect to." +msgstr "" + +#: awx/main/models/credential/__init__.py:1193 +msgid "Auth Server URL" +msgstr "" + +#: awx/main/models/credential/__init__.py:1196 +msgid "The URL of a Keycloak server token_endpoint, if using SSO auth." +msgstr "" + +#: awx/main/models/credential/__init__.py:1201 +msgid "API Token" +msgstr "" + +#: awx/main/models/credential/__init__.py:1205 +msgid "A token to use for authentication against the Galaxy instance." +msgstr "" + +#: awx/main/models/credential/__init__.py:1244 msgid "Target must be a non-external credential" msgstr "" -#: awx/main/models/credential/__init__.py:1199 +#: awx/main/models/credential/__init__.py:1249 msgid "Source must be an external credential" msgstr "" -#: awx/main/models/credential/__init__.py:1206 +#: awx/main/models/credential/__init__.py:1256 msgid "Input field must be defined on target credential (options are {})." msgstr "" -#: awx/main/models/events.py:152 awx/main/models/events.py:674 +#: awx/main/models/events.py:165 awx/main/models/events.py:707 msgid "Host Failed" msgstr "" -#: awx/main/models/events.py:153 +#: awx/main/models/events.py:166 msgid "Host Started" msgstr "" -#: awx/main/models/events.py:154 awx/main/models/events.py:675 +#: awx/main/models/events.py:167 awx/main/models/events.py:708 msgid "Host OK" msgstr "" -#: awx/main/models/events.py:155 +#: awx/main/models/events.py:168 msgid "Host Failure" msgstr "" -#: awx/main/models/events.py:156 awx/main/models/events.py:681 +#: awx/main/models/events.py:169 awx/main/models/events.py:714 msgid "Host Skipped" msgstr "" -#: awx/main/models/events.py:157 awx/main/models/events.py:676 +#: awx/main/models/events.py:170 awx/main/models/events.py:709 msgid "Host Unreachable" msgstr "" -#: awx/main/models/events.py:158 awx/main/models/events.py:172 +#: awx/main/models/events.py:171 awx/main/models/events.py:185 msgid "No Hosts Remaining" msgstr "" -#: awx/main/models/events.py:159 +#: awx/main/models/events.py:172 msgid "Host Polling" msgstr "" -#: awx/main/models/events.py:160 +#: awx/main/models/events.py:173 msgid "Host Async OK" msgstr "" -#: awx/main/models/events.py:161 +#: awx/main/models/events.py:174 msgid "Host Async Failure" msgstr "" -#: awx/main/models/events.py:162 +#: awx/main/models/events.py:175 msgid "Item OK" msgstr "" -#: awx/main/models/events.py:163 +#: awx/main/models/events.py:176 msgid "Item Failed" msgstr "" -#: awx/main/models/events.py:164 +#: awx/main/models/events.py:177 msgid "Item Skipped" msgstr "" -#: awx/main/models/events.py:165 +#: awx/main/models/events.py:178 msgid "Host Retry" msgstr "" -#: awx/main/models/events.py:167 +#: awx/main/models/events.py:180 msgid "File Difference" msgstr "" -#: awx/main/models/events.py:168 +#: awx/main/models/events.py:181 msgid "Playbook Started" msgstr "" -#: awx/main/models/events.py:169 +#: awx/main/models/events.py:182 msgid "Running Handlers" msgstr "" -#: awx/main/models/events.py:170 +#: awx/main/models/events.py:183 msgid "Including File" msgstr "" -#: awx/main/models/events.py:171 +#: awx/main/models/events.py:184 msgid "No Hosts Matched" msgstr "" -#: awx/main/models/events.py:173 +#: awx/main/models/events.py:186 msgid "Task Started" msgstr "" -#: awx/main/models/events.py:175 +#: awx/main/models/events.py:188 msgid "Variables Prompted" msgstr "" -#: awx/main/models/events.py:176 +#: awx/main/models/events.py:189 msgid "Gathering Facts" msgstr "" -#: awx/main/models/events.py:177 +#: awx/main/models/events.py:190 msgid "internal: on Import for Host" msgstr "" -#: awx/main/models/events.py:178 +#: awx/main/models/events.py:191 msgid "internal: on Not Import for Host" msgstr "" -#: awx/main/models/events.py:179 +#: awx/main/models/events.py:192 msgid "Play Started" msgstr "" -#: awx/main/models/events.py:180 +#: awx/main/models/events.py:193 msgid "Playbook Complete" msgstr "" -#: awx/main/models/events.py:184 awx/main/models/events.py:691 +#: awx/main/models/events.py:197 awx/main/models/events.py:724 msgid "Debug" msgstr "" -#: awx/main/models/events.py:185 awx/main/models/events.py:692 +#: awx/main/models/events.py:198 awx/main/models/events.py:725 msgid "Verbose" msgstr "" -#: awx/main/models/events.py:186 awx/main/models/events.py:693 +#: awx/main/models/events.py:199 awx/main/models/events.py:726 msgid "Deprecated" msgstr "" -#: awx/main/models/events.py:187 awx/main/models/events.py:694 +#: awx/main/models/events.py:200 awx/main/models/events.py:727 msgid "Warning" msgstr "" -#: awx/main/models/events.py:188 awx/main/models/events.py:695 +#: awx/main/models/events.py:201 awx/main/models/events.py:728 msgid "System Warning" msgstr "" -#: awx/main/models/events.py:189 awx/main/models/events.py:696 +#: awx/main/models/events.py:202 awx/main/models/events.py:729 #: awx/main/models/unified_jobs.py:75 msgid "Error" msgstr "" -#: awx/main/models/ha.py:175 +#: awx/main/models/ha.py:184 msgid "Instances that are members of this InstanceGroup" msgstr "" -#: awx/main/models/ha.py:180 +#: awx/main/models/ha.py:189 msgid "Instance Group to remotely control this group." msgstr "" -#: awx/main/models/ha.py:200 +#: awx/main/models/ha.py:209 msgid "Percentage of Instances to automatically assign to this group" msgstr "" -#: awx/main/models/ha.py:204 +#: awx/main/models/ha.py:213 msgid "" "Static minimum number of Instances to automatically assign to this group" msgstr "" -#: awx/main/models/ha.py:209 +#: awx/main/models/ha.py:218 msgid "" "List of exact-match Instances that will always be automatically assigned to " "this group" msgstr "" -#: awx/main/models/inventory.py:80 +#: awx/main/models/inventory.py:74 msgid "Hosts have a direct link to this inventory." msgstr "" -#: awx/main/models/inventory.py:81 +#: awx/main/models/inventory.py:75 msgid "Hosts for inventory generated using the host_filter property." msgstr "" -#: awx/main/models/inventory.py:86 +#: awx/main/models/inventory.py:80 msgid "inventories" msgstr "" -#: awx/main/models/inventory.py:93 +#: awx/main/models/inventory.py:87 msgid "Organization containing this inventory." msgstr "" -#: awx/main/models/inventory.py:100 +#: awx/main/models/inventory.py:94 msgid "Inventory variables in JSON or YAML format." msgstr "" -#: awx/main/models/inventory.py:105 +#: awx/main/models/inventory.py:99 msgid "" "This field is deprecated and will be removed in a future release. Flag " "indicating whether any hosts in this inventory have failed." msgstr "" -#: awx/main/models/inventory.py:111 +#: awx/main/models/inventory.py:105 msgid "" "This field is deprecated and will be removed in a future release. Total " "number of hosts in this inventory." msgstr "" -#: awx/main/models/inventory.py:117 +#: awx/main/models/inventory.py:111 msgid "" "This field is deprecated and will be removed in a future release. Number of " "hosts in this inventory with active failures." msgstr "" -#: awx/main/models/inventory.py:123 +#: awx/main/models/inventory.py:117 msgid "" "This field is deprecated and will be removed in a future release. Total " "number of groups in this inventory." msgstr "" -#: awx/main/models/inventory.py:129 +#: awx/main/models/inventory.py:123 msgid "" "This field is deprecated and will be removed in a future release. Flag " "indicating whether this inventory has any external inventory sources." msgstr "" -#: awx/main/models/inventory.py:135 +#: awx/main/models/inventory.py:129 msgid "" "Total number of external inventory sources configured within this inventory." msgstr "" -#: awx/main/models/inventory.py:140 +#: awx/main/models/inventory.py:134 msgid "Number of external inventory sources in this inventory with failures." msgstr "" -#: awx/main/models/inventory.py:147 +#: awx/main/models/inventory.py:141 msgid "Kind of inventory being represented." msgstr "" -#: awx/main/models/inventory.py:153 +#: awx/main/models/inventory.py:147 msgid "Filter that will be applied to the hosts of this inventory." msgstr "" -#: awx/main/models/inventory.py:181 +#: awx/main/models/inventory.py:175 msgid "" "Credentials to be used by hosts belonging to this inventory when accessing " "Red Hat Insights API." msgstr "" -#: awx/main/models/inventory.py:190 +#: awx/main/models/inventory.py:184 msgid "Flag indicating the inventory is being deleted." msgstr "" -#: awx/main/models/inventory.py:245 +#: awx/main/models/inventory.py:239 msgid "Could not parse subset as slice specification." msgstr "" -#: awx/main/models/inventory.py:249 +#: awx/main/models/inventory.py:243 msgid "Slice number must be less than total number of slices." msgstr "" -#: awx/main/models/inventory.py:251 +#: awx/main/models/inventory.py:245 msgid "Slice number must be 1 or higher." msgstr "" -#: awx/main/models/inventory.py:388 +#: awx/main/models/inventory.py:382 msgid "Assignment not allowed for Smart Inventory" msgstr "" -#: awx/main/models/inventory.py:390 awx/main/models/projects.py:166 +#: awx/main/models/inventory.py:384 awx/main/models/projects.py:167 msgid "Credential kind must be 'insights'." msgstr "" -#: awx/main/models/inventory.py:475 +#: awx/main/models/inventory.py:469 msgid "Is this host online and available for running jobs?" msgstr "" -#: awx/main/models/inventory.py:481 +#: awx/main/models/inventory.py:475 msgid "" "The value used by the remote inventory source to uniquely identify the host" msgstr "" -#: awx/main/models/inventory.py:486 +#: awx/main/models/inventory.py:480 msgid "Host variables in JSON or YAML format." msgstr "" -#: awx/main/models/inventory.py:509 +#: awx/main/models/inventory.py:503 msgid "Inventory source(s) that created or modified this host." msgstr "" -#: awx/main/models/inventory.py:514 +#: awx/main/models/inventory.py:508 msgid "Arbitrary JSON structure of most recent ansible_facts, per-host." msgstr "" -#: awx/main/models/inventory.py:520 +#: awx/main/models/inventory.py:514 msgid "The date and time ansible_facts was last modified." msgstr "" -#: awx/main/models/inventory.py:527 +#: awx/main/models/inventory.py:521 msgid "Red Hat Insights host unique identifier." msgstr "" -#: awx/main/models/inventory.py:641 +#: awx/main/models/inventory.py:635 msgid "Group variables in JSON or YAML format." msgstr "" -#: awx/main/models/inventory.py:647 +#: awx/main/models/inventory.py:641 msgid "Hosts associated directly with this group." msgstr "" -#: awx/main/models/inventory.py:653 +#: awx/main/models/inventory.py:647 msgid "Inventory source(s) that created or modified this group." msgstr "" -#: awx/main/models/inventory.py:825 +#: awx/main/models/inventory.py:819 msgid "File, Directory or Script" msgstr "" -#: awx/main/models/inventory.py:826 +#: awx/main/models/inventory.py:820 msgid "Sourced from a Project" msgstr "" -#: awx/main/models/inventory.py:827 +#: awx/main/models/inventory.py:821 msgid "Amazon EC2" msgstr "" -#: awx/main/models/inventory.py:836 +#: awx/main/models/inventory.py:829 msgid "Custom Script" msgstr "" -#: awx/main/models/inventory.py:953 +#: awx/main/models/inventory.py:863 msgid "Inventory source variables in YAML or JSON format." msgstr "" -#: awx/main/models/inventory.py:964 +#: awx/main/models/inventory.py:868 msgid "" -"Comma-separated list of filter expressions (EC2 only). Hosts are imported " -"when ANY of the filters match." +"Retrieve the enabled state from the given dict of host variables. The " +"enabled variable may be specified as \"foo.bar\", in which case the lookup " +"will traverse into nested dicts, equivalent to: from_dict.get(\"foo\", {})." +"get(\"bar\", default)" msgstr "" -#: awx/main/models/inventory.py:970 -msgid "Limit groups automatically created from inventory source (EC2 only)." +#: awx/main/models/inventory.py:876 +msgid "" +"Only used when enabled_var is set. Value when the host is considered " +"enabled. For example if enabled_var=\"status.power_state\"and enabled_value=" +"\"powered_on\" with host variables:{ \"status\": { \"power_state\": " +"\"powered_on\", \"created\": \"2020-08-04T18:13:04+00:00\", \"healthy" +"\": true }, \"name\": \"foobar\", \"ip_address\": \"192.168.2.1\"}" +"The host would be marked enabled. If power_state where any value other than " +"powered_on then the host would be disabled when imported into Tower. If the " +"key is not found then the host will be enabled" msgstr "" -#: awx/main/models/inventory.py:974 +#: awx/main/models/inventory.py:896 +msgid "Regex where only matching hosts will be imported into Tower." +msgstr "" + +#: awx/main/models/inventory.py:900 msgid "Overwrite local groups and hosts from remote inventory source." msgstr "" -#: awx/main/models/inventory.py:978 +#: awx/main/models/inventory.py:904 msgid "Overwrite local variables from remote inventory source." msgstr "" -#: awx/main/models/inventory.py:983 awx/main/models/jobs.py:154 -#: awx/main/models/projects.py:135 +#: awx/main/models/inventory.py:909 awx/main/models/jobs.py:154 +#: awx/main/models/projects.py:136 msgid "The amount of time (in seconds) to run before the task is canceled." msgstr "" -#: awx/main/models/inventory.py:1016 -msgid "Image ID" -msgstr "" - -#: awx/main/models/inventory.py:1017 -msgid "Availability Zone" -msgstr "" - -#: awx/main/models/inventory.py:1019 -msgid "Instance ID" -msgstr "" - -#: awx/main/models/inventory.py:1020 -msgid "Instance State" -msgstr "" - -#: awx/main/models/inventory.py:1021 -msgid "Platform" -msgstr "" - -#: awx/main/models/inventory.py:1022 -msgid "Instance Type" -msgstr "" - -#: awx/main/models/inventory.py:1024 -msgid "Region" -msgstr "" - -#: awx/main/models/inventory.py:1025 -msgid "Security Group" -msgstr "" - -#: awx/main/models/inventory.py:1026 -msgid "Tags" -msgstr "" - -#: awx/main/models/inventory.py:1027 -msgid "Tag None" -msgstr "" - -#: awx/main/models/inventory.py:1028 -msgid "VPC ID" -msgstr "" - -#: awx/main/models/inventory.py:1096 +#: awx/main/models/inventory.py:926 #, python-format msgid "" "Cloud-based inventory sources (such as %s) require credentials for the " "matching cloud service." msgstr "" -#: awx/main/models/inventory.py:1102 +#: awx/main/models/inventory.py:932 msgid "Credential is required for a cloud source." msgstr "" -#: awx/main/models/inventory.py:1105 +#: awx/main/models/inventory.py:935 msgid "" "Credentials of type machine, source control, insights and vault are " "disallowed for custom inventory sources." msgstr "" -#: awx/main/models/inventory.py:1110 +#: awx/main/models/inventory.py:940 msgid "" "Credentials of type insights and vault are disallowed for scm inventory " "sources." msgstr "" -#: awx/main/models/inventory.py:1170 -#, python-format -msgid "Invalid %(source)s region: %(region)s" -msgstr "" - -#: awx/main/models/inventory.py:1194 -#, python-format -msgid "Invalid filter expression: %(filter)s" -msgstr "" - -#: awx/main/models/inventory.py:1215 -#, python-format -msgid "Invalid group by choice: %(choice)s" -msgstr "" - -#: awx/main/models/inventory.py:1243 +#: awx/main/models/inventory.py:1004 msgid "Project containing inventory file used as source." msgstr "" -#: awx/main/models/inventory.py:1416 +#: awx/main/models/inventory.py:1177 msgid "" "More than one SCM-based inventory source with update on project update per-" "inventory not allowed." msgstr "" -#: awx/main/models/inventory.py:1423 +#: awx/main/models/inventory.py:1184 msgid "" "Cannot update SCM-based inventory source on launch if set to update on " "project update. Instead, configure the corresponding source project to " "update on launch." msgstr "" -#: awx/main/models/inventory.py:1429 +#: awx/main/models/inventory.py:1190 msgid "Cannot set source_path if not SCM type." msgstr "" -#: awx/main/models/inventory.py:1472 +#: awx/main/models/inventory.py:1233 msgid "" "Inventory files from this Project Update were used for the inventory update." msgstr "" -#: awx/main/models/inventory.py:1583 +#: awx/main/models/inventory.py:1344 msgid "Inventory script contents" msgstr "" -#: awx/main/models/inventory.py:1588 +#: awx/main/models/inventory.py:1349 msgid "Organization owning this inventory script" msgstr "" @@ -3980,8 +4030,8 @@ msgstr "" msgid "Job Template {} is missing or undefined." msgstr "" -#: awx/main/models/jobs.py:570 awx/main/models/projects.py:278 -#: awx/main/models/projects.py:497 +#: awx/main/models/jobs.py:570 awx/main/models/projects.py:284 +#: awx/main/models/projects.py:508 msgid "SCM Revision" msgstr "" @@ -4081,63 +4131,59 @@ msgstr "" msgid "Unique identifier of the event that triggered this webhook" msgstr "" -#: awx/main/models/notifications.py:42 +#: awx/main/models/notifications.py:41 msgid "Email" msgstr "" -#: awx/main/models/notifications.py:43 +#: awx/main/models/notifications.py:42 msgid "Slack" msgstr "" -#: awx/main/models/notifications.py:44 +#: awx/main/models/notifications.py:43 msgid "Twilio" msgstr "" -#: awx/main/models/notifications.py:45 +#: awx/main/models/notifications.py:44 msgid "Pagerduty" msgstr "" -#: awx/main/models/notifications.py:46 +#: awx/main/models/notifications.py:45 msgid "Grafana" msgstr "" -#: awx/main/models/notifications.py:47 -msgid "HipChat" -msgstr "" - -#: awx/main/models/notifications.py:48 awx/main/models/unified_jobs.py:544 +#: awx/main/models/notifications.py:46 awx/main/models/unified_jobs.py:544 msgid "Webhook" msgstr "" -#: awx/main/models/notifications.py:49 +#: awx/main/models/notifications.py:47 msgid "Mattermost" msgstr "" -#: awx/main/models/notifications.py:50 +#: awx/main/models/notifications.py:48 msgid "Rocket.Chat" msgstr "" -#: awx/main/models/notifications.py:51 +#: awx/main/models/notifications.py:49 msgid "IRC" msgstr "" -#: awx/main/models/notifications.py:82 +#: awx/main/models/notifications.py:80 msgid "Optional custom messages for notification template." msgstr "" -#: awx/main/models/notifications.py:212 awx/main/models/unified_jobs.py:70 +#: awx/main/models/notifications.py:210 awx/main/models/unified_jobs.py:70 msgid "Pending" msgstr "" -#: awx/main/models/notifications.py:213 awx/main/models/unified_jobs.py:73 +#: awx/main/models/notifications.py:211 awx/main/models/unified_jobs.py:73 msgid "Successful" msgstr "" -#: awx/main/models/notifications.py:214 awx/main/models/unified_jobs.py:74 +#: awx/main/models/notifications.py:212 awx/main/models/unified_jobs.py:74 msgid "Failed" msgstr "" -#: awx/main/models/notifications.py:467 +#: awx/main/models/notifications.py:470 msgid "status must be either running, succeeded or failed" msgstr "" @@ -4206,7 +4252,7 @@ msgid "" "authentication provider ({})" msgstr "" -#: awx/main/models/organization.py:51 +#: awx/main/models/organization.py:57 msgid "Maximum number of hosts allowed to be managed by this organization." msgstr "" @@ -4230,118 +4276,122 @@ msgstr "" msgid "Red Hat Insights" msgstr "" -#: awx/main/models/projects.py:83 +#: awx/main/models/projects.py:58 +msgid "Remote Archive" +msgstr "" + +#: awx/main/models/projects.py:84 msgid "" "Local path (relative to PROJECTS_ROOT) containing playbooks and related " "files for this project." msgstr "" -#: awx/main/models/projects.py:92 +#: awx/main/models/projects.py:93 msgid "SCM Type" msgstr "" -#: awx/main/models/projects.py:93 +#: awx/main/models/projects.py:94 msgid "Specifies the source control system used to store the project." msgstr "" -#: awx/main/models/projects.py:99 +#: awx/main/models/projects.py:100 msgid "SCM URL" msgstr "" -#: awx/main/models/projects.py:100 +#: awx/main/models/projects.py:101 msgid "The location where the project is stored." msgstr "" -#: awx/main/models/projects.py:106 +#: awx/main/models/projects.py:107 msgid "SCM Branch" msgstr "" -#: awx/main/models/projects.py:107 +#: awx/main/models/projects.py:108 msgid "Specific branch, tag or commit to checkout." msgstr "" -#: awx/main/models/projects.py:113 +#: awx/main/models/projects.py:114 msgid "SCM refspec" msgstr "" -#: awx/main/models/projects.py:114 +#: awx/main/models/projects.py:115 msgid "For git projects, an additional refspec to fetch." msgstr "" -#: awx/main/models/projects.py:118 +#: awx/main/models/projects.py:119 msgid "Discard any local changes before syncing the project." msgstr "" -#: awx/main/models/projects.py:122 +#: awx/main/models/projects.py:123 msgid "Delete the project before syncing." msgstr "" -#: awx/main/models/projects.py:151 +#: awx/main/models/projects.py:152 msgid "Invalid SCM URL." msgstr "" -#: awx/main/models/projects.py:154 +#: awx/main/models/projects.py:155 msgid "SCM URL is required." msgstr "" -#: awx/main/models/projects.py:162 +#: awx/main/models/projects.py:163 msgid "Insights Credential is required for an Insights Project." msgstr "" -#: awx/main/models/projects.py:168 +#: awx/main/models/projects.py:169 msgid "Credential kind must be 'scm'." msgstr "" -#: awx/main/models/projects.py:185 +#: awx/main/models/projects.py:186 msgid "Invalid credential." msgstr "" -#: awx/main/models/projects.py:259 +#: awx/main/models/projects.py:265 msgid "Update the project when a job is launched that uses the project." msgstr "" -#: awx/main/models/projects.py:264 +#: awx/main/models/projects.py:270 msgid "" "The number of seconds after the last project update ran that a new project " "update will be launched as a job dependency." msgstr "" -#: awx/main/models/projects.py:269 +#: awx/main/models/projects.py:275 msgid "" "Allow changing the SCM branch or revision in a job template that uses this " "project." msgstr "" -#: awx/main/models/projects.py:279 +#: awx/main/models/projects.py:285 msgid "The last revision fetched by a project update" msgstr "" -#: awx/main/models/projects.py:286 +#: awx/main/models/projects.py:292 msgid "Playbook Files" msgstr "" -#: awx/main/models/projects.py:287 +#: awx/main/models/projects.py:293 msgid "List of playbooks found in the project" msgstr "" -#: awx/main/models/projects.py:294 +#: awx/main/models/projects.py:300 msgid "Inventory Files" msgstr "" -#: awx/main/models/projects.py:295 +#: awx/main/models/projects.py:301 msgid "" "Suggested list of content that could be Ansible inventory in the project" msgstr "" -#: awx/main/models/projects.py:332 +#: awx/main/models/projects.py:338 msgid "Organization cannot be changed when in use by job templates." msgstr "" -#: awx/main/models/projects.py:490 +#: awx/main/models/projects.py:501 msgid "Parts of the project update playbook that will be run." msgstr "" -#: awx/main/models/projects.py:498 +#: awx/main/models/projects.py:509 msgid "" "The SCM Revision discovered by this update for the given project and branch." msgstr "" @@ -4697,41 +4747,33 @@ msgid "" "Shows when an approval node (with a timeout assigned to it) has timed out." msgstr "" -#: awx/main/notifications/grafana_backend.py:86 +#: awx/main/notifications/grafana_backend.py:81 msgid "Error converting time {} or timeEnd {} to int." msgstr "" -#: awx/main/notifications/grafana_backend.py:88 +#: awx/main/notifications/grafana_backend.py:83 msgid "Error converting time {} and/or timeEnd {} to int." msgstr "" -#: awx/main/notifications/grafana_backend.py:102 -#: awx/main/notifications/grafana_backend.py:104 +#: awx/main/notifications/grafana_backend.py:97 +#: awx/main/notifications/grafana_backend.py:99 msgid "Error sending notification grafana: {}" msgstr "" -#: awx/main/notifications/hipchat_backend.py:50 -msgid "Error sending messages: {}" -msgstr "" - -#: awx/main/notifications/hipchat_backend.py:52 -msgid "Error sending message to hipchat: {}" -msgstr "" - #: awx/main/notifications/irc_backend.py:56 msgid "Exception connecting to irc server: {}" msgstr "" -#: awx/main/notifications/mattermost_backend.py:50 -#: awx/main/notifications/mattermost_backend.py:52 +#: awx/main/notifications/mattermost_backend.py:49 +#: awx/main/notifications/mattermost_backend.py:51 msgid "Error sending notification mattermost: {}" msgstr "" -#: awx/main/notifications/pagerduty_backend.py:64 +#: awx/main/notifications/pagerduty_backend.py:75 msgid "Exception connecting to PagerDuty: {}" msgstr "" -#: awx/main/notifications/pagerduty_backend.py:73 +#: awx/main/notifications/pagerduty_backend.py:84 #: awx/main/notifications/slack_backend.py:58 #: awx/main/notifications/twilio_backend.py:48 msgid "Exception sending messages: {}" @@ -4758,46 +4800,52 @@ msgid "" "job node(s) missing unified job template and error handling path [{no_ufjt}]." msgstr "" -#: awx/main/scheduler/task_manager.py:118 +#: awx/main/scheduler/task_manager.py:127 msgid "" "Workflow Job spawned from workflow could not start because it would result " "in recursion (spawn order, most recent first: {})" msgstr "" -#: awx/main/scheduler/task_manager.py:126 +#: awx/main/scheduler/task_manager.py:135 msgid "" "Job spawned from workflow could not start because it was missing a related " "resource such as project or inventory" msgstr "" -#: awx/main/scheduler/task_manager.py:135 +#: awx/main/scheduler/task_manager.py:144 msgid "" "Job spawned from workflow could not start because it was not in the right " "state or required manual credentials" msgstr "" -#: awx/main/scheduler/task_manager.py:176 +#: awx/main/scheduler/task_manager.py:185 msgid "No error handling paths found, marking workflow as failed" msgstr "" -#: awx/main/scheduler/task_manager.py:508 +#: awx/main/scheduler/task_manager.py:523 #, python-brace-format msgid "The approval node {name} ({pk}) has expired after {timeout} seconds." msgstr "" -#: awx/main/tasks.py:1046 +#: awx/main/tasks.py:599 +msgid "" +"Scheduled job could not start because it was not in the " +"right state or required manual credentials" +msgstr "" + +#: awx/main/tasks.py:1070 msgid "Invalid virtual environment selected: {}" msgstr "" -#: awx/main/tasks.py:1849 +#: awx/main/tasks.py:1857 msgid "Job could not start because it does not have a valid inventory." msgstr "" -#: awx/main/tasks.py:1853 +#: awx/main/tasks.py:1861 msgid "Job could not start because it does not have a valid project." msgstr "" -#: awx/main/tasks.py:1858 +#: awx/main/tasks.py:1866 msgid "" "The project revision for this job template is unknown due to a failed update." msgstr "" @@ -4822,53 +4870,53 @@ msgstr "" msgid "Unable to convert \"%s\" to boolean" msgstr "" -#: awx/main/utils/common.py:261 +#: awx/main/utils/common.py:248 #, python-format msgid "Unsupported SCM type \"%s\"" msgstr "" -#: awx/main/utils/common.py:268 awx/main/utils/common.py:280 -#: awx/main/utils/common.py:299 +#: awx/main/utils/common.py:255 awx/main/utils/common.py:267 +#: awx/main/utils/common.py:286 #, python-format msgid "Invalid %s URL" msgstr "" -#: awx/main/utils/common.py:270 awx/main/utils/common.py:309 +#: awx/main/utils/common.py:257 awx/main/utils/common.py:297 #, python-format msgid "Unsupported %s URL" msgstr "" -#: awx/main/utils/common.py:311 +#: awx/main/utils/common.py:299 #, python-format msgid "Unsupported host \"%s\" for file:// URL" msgstr "" -#: awx/main/utils/common.py:313 +#: awx/main/utils/common.py:301 #, python-format msgid "Host is required for %s URL" msgstr "" -#: awx/main/utils/common.py:331 +#: awx/main/utils/common.py:319 #, python-format msgid "Username must be \"git\" for SSH access to %s." msgstr "" -#: awx/main/utils/common.py:337 +#: awx/main/utils/common.py:325 #, python-format msgid "Username must be \"hg\" for SSH access to %s." msgstr "" -#: awx/main/utils/common.py:668 +#: awx/main/utils/common.py:656 #, python-brace-format msgid "Input type `{data_type}` is not a dictionary" msgstr "" -#: awx/main/utils/common.py:701 +#: awx/main/utils/common.py:689 #, python-brace-format msgid "Variables not compatible with JSON standard (error: {json_error})" msgstr "" -#: awx/main/utils/common.py:707 +#: awx/main/utils/common.py:695 #, python-brace-format msgid "" "Cannot parse as JSON (error: {json_error}) or YAML (error: {yaml_error})." @@ -4980,290 +5028,6 @@ msgstr "" msgid "A server error has occurred." msgstr "" -#: awx/settings/defaults.py:683 -msgid "US East (Northern Virginia)" -msgstr "" - -#: awx/settings/defaults.py:684 -msgid "US East (Ohio)" -msgstr "" - -#: awx/settings/defaults.py:685 -msgid "US West (Oregon)" -msgstr "" - -#: awx/settings/defaults.py:686 -msgid "US West (Northern California)" -msgstr "" - -#: awx/settings/defaults.py:687 -msgid "Canada (Central)" -msgstr "" - -#: awx/settings/defaults.py:688 -msgid "EU (Frankfurt)" -msgstr "" - -#: awx/settings/defaults.py:689 -msgid "EU (Ireland)" -msgstr "" - -#: awx/settings/defaults.py:690 -msgid "EU (London)" -msgstr "" - -#: awx/settings/defaults.py:691 -msgid "Asia Pacific (Singapore)" -msgstr "" - -#: awx/settings/defaults.py:692 -msgid "Asia Pacific (Sydney)" -msgstr "" - -#: awx/settings/defaults.py:693 -msgid "Asia Pacific (Tokyo)" -msgstr "" - -#: awx/settings/defaults.py:694 -msgid "Asia Pacific (Seoul)" -msgstr "" - -#: awx/settings/defaults.py:695 -msgid "Asia Pacific (Mumbai)" -msgstr "" - -#: awx/settings/defaults.py:696 -msgid "South America (Sao Paulo)" -msgstr "" - -#: awx/settings/defaults.py:697 -msgid "US West (GovCloud)" -msgstr "" - -#: awx/settings/defaults.py:698 -msgid "China (Beijing)" -msgstr "" - -#: awx/settings/defaults.py:747 -msgid "US East 1 (B)" -msgstr "" - -#: awx/settings/defaults.py:748 -msgid "US East 1 (C)" -msgstr "" - -#: awx/settings/defaults.py:749 -msgid "US East 1 (D)" -msgstr "" - -#: awx/settings/defaults.py:750 -msgid "US East 4 (A)" -msgstr "" - -#: awx/settings/defaults.py:751 -msgid "US East 4 (B)" -msgstr "" - -#: awx/settings/defaults.py:752 -msgid "US East 4 (C)" -msgstr "" - -#: awx/settings/defaults.py:753 -msgid "US Central (A)" -msgstr "" - -#: awx/settings/defaults.py:754 -msgid "US Central (B)" -msgstr "" - -#: awx/settings/defaults.py:755 -msgid "US Central (C)" -msgstr "" - -#: awx/settings/defaults.py:756 -msgid "US Central (F)" -msgstr "" - -#: awx/settings/defaults.py:757 -msgid "US West (A)" -msgstr "" - -#: awx/settings/defaults.py:758 -msgid "US West (B)" -msgstr "" - -#: awx/settings/defaults.py:759 -msgid "US West (C)" -msgstr "" - -#: awx/settings/defaults.py:760 -msgid "Europe West 1 (B)" -msgstr "" - -#: awx/settings/defaults.py:761 -msgid "Europe West 1 (C)" -msgstr "" - -#: awx/settings/defaults.py:762 -msgid "Europe West 1 (D)" -msgstr "" - -#: awx/settings/defaults.py:763 -msgid "Europe West 2 (A)" -msgstr "" - -#: awx/settings/defaults.py:764 -msgid "Europe West 2 (B)" -msgstr "" - -#: awx/settings/defaults.py:765 -msgid "Europe West 2 (C)" -msgstr "" - -#: awx/settings/defaults.py:766 -msgid "Asia East (A)" -msgstr "" - -#: awx/settings/defaults.py:767 -msgid "Asia East (B)" -msgstr "" - -#: awx/settings/defaults.py:768 -msgid "Asia East (C)" -msgstr "" - -#: awx/settings/defaults.py:769 -msgid "Asia Southeast (A)" -msgstr "" - -#: awx/settings/defaults.py:770 -msgid "Asia Southeast (B)" -msgstr "" - -#: awx/settings/defaults.py:771 -msgid "Asia Northeast (A)" -msgstr "" - -#: awx/settings/defaults.py:772 -msgid "Asia Northeast (B)" -msgstr "" - -#: awx/settings/defaults.py:773 -msgid "Asia Northeast (C)" -msgstr "" - -#: awx/settings/defaults.py:774 -msgid "Australia Southeast (A)" -msgstr "" - -#: awx/settings/defaults.py:775 -msgid "Australia Southeast (B)" -msgstr "" - -#: awx/settings/defaults.py:776 -msgid "Australia Southeast (C)" -msgstr "" - -#: awx/settings/defaults.py:798 -msgid "US East" -msgstr "" - -#: awx/settings/defaults.py:799 -msgid "US East 2" -msgstr "" - -#: awx/settings/defaults.py:800 -msgid "US Central" -msgstr "" - -#: awx/settings/defaults.py:801 -msgid "US North Central" -msgstr "" - -#: awx/settings/defaults.py:802 -msgid "US South Central" -msgstr "" - -#: awx/settings/defaults.py:803 -msgid "US West Central" -msgstr "" - -#: awx/settings/defaults.py:804 -msgid "US West" -msgstr "" - -#: awx/settings/defaults.py:805 -msgid "US West 2" -msgstr "" - -#: awx/settings/defaults.py:806 -msgid "Canada East" -msgstr "" - -#: awx/settings/defaults.py:807 -msgid "Canada Central" -msgstr "" - -#: awx/settings/defaults.py:808 -msgid "Brazil South" -msgstr "" - -#: awx/settings/defaults.py:809 -msgid "Europe North" -msgstr "" - -#: awx/settings/defaults.py:810 -msgid "Europe West" -msgstr "" - -#: awx/settings/defaults.py:811 -msgid "UK West" -msgstr "" - -#: awx/settings/defaults.py:812 -msgid "UK South" -msgstr "" - -#: awx/settings/defaults.py:813 -msgid "Asia East" -msgstr "" - -#: awx/settings/defaults.py:814 -msgid "Asia Southeast" -msgstr "" - -#: awx/settings/defaults.py:815 -msgid "Australia East" -msgstr "" - -#: awx/settings/defaults.py:816 -msgid "Australia Southeast" -msgstr "" - -#: awx/settings/defaults.py:817 -msgid "India West" -msgstr "" - -#: awx/settings/defaults.py:818 -msgid "India South" -msgstr "" - -#: awx/settings/defaults.py:819 -msgid "Japan East" -msgstr "" - -#: awx/settings/defaults.py:820 -msgid "Japan West" -msgstr "" - -#: awx/settings/defaults.py:821 -msgid "Korea Central" -msgstr "" - -#: awx/settings/defaults.py:822 -msgid "Korea South" -msgstr "" - #: awx/sso/apps.py:9 msgid "Single Sign-On" msgstr "" @@ -5536,7 +5300,7 @@ msgid "Hostname of TACACS+ server." msgstr "" #: awx/sso/conf.py:480 awx/sso/conf.py:492 awx/sso/conf.py:504 -#: awx/sso/conf.py:516 awx/sso/conf.py:527 awx/sso/models.py:15 +#: awx/sso/conf.py:516 awx/sso/conf.py:528 awx/sso/models.py:15 msgid "TACACS+" msgstr "" @@ -5564,159 +5328,159 @@ msgstr "" msgid "TACACS+ session timeout value in seconds, 0 disables timeout." msgstr "" -#: awx/sso/conf.py:525 +#: awx/sso/conf.py:526 msgid "TACACS+ Authentication Protocol" msgstr "" -#: awx/sso/conf.py:526 +#: awx/sso/conf.py:527 msgid "Choose the authentication protocol used by TACACS+ client." msgstr "" -#: awx/sso/conf.py:540 +#: awx/sso/conf.py:541 msgid "Google OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:541 awx/sso/conf.py:634 awx/sso/conf.py:699 +#: awx/sso/conf.py:542 awx/sso/conf.py:635 awx/sso/conf.py:700 msgid "" "Provide this URL as the callback URL for your application as part of your " "registration process. Refer to the Ansible Tower documentation for more " "detail." msgstr "" -#: awx/sso/conf.py:544 awx/sso/conf.py:556 awx/sso/conf.py:568 -#: awx/sso/conf.py:581 awx/sso/conf.py:595 awx/sso/conf.py:607 -#: awx/sso/conf.py:619 +#: awx/sso/conf.py:545 awx/sso/conf.py:557 awx/sso/conf.py:569 +#: awx/sso/conf.py:582 awx/sso/conf.py:596 awx/sso/conf.py:608 +#: awx/sso/conf.py:620 msgid "Google OAuth2" msgstr "" -#: awx/sso/conf.py:554 +#: awx/sso/conf.py:555 msgid "Google OAuth2 Key" msgstr "" -#: awx/sso/conf.py:555 +#: awx/sso/conf.py:556 msgid "The OAuth2 key from your web application." msgstr "" -#: awx/sso/conf.py:566 +#: awx/sso/conf.py:567 msgid "Google OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:567 +#: awx/sso/conf.py:568 msgid "The OAuth2 secret from your web application." msgstr "" -#: awx/sso/conf.py:578 -msgid "Google OAuth2 Whitelisted Domains" +#: awx/sso/conf.py:579 +msgid "Google OAuth2 Allowed Domains" msgstr "" -#: awx/sso/conf.py:579 +#: awx/sso/conf.py:580 msgid "" "Update this setting to restrict the domains who are allowed to login using " "Google OAuth2." msgstr "" -#: awx/sso/conf.py:590 +#: awx/sso/conf.py:591 msgid "Google OAuth2 Extra Arguments" msgstr "" -#: awx/sso/conf.py:591 +#: awx/sso/conf.py:592 msgid "" "Extra arguments for Google OAuth2 login. You can restrict it to only allow a " "single domain to authenticate, even if the user is logged in with multple " "Google accounts. Refer to the Ansible Tower documentation for more detail." msgstr "" -#: awx/sso/conf.py:605 +#: awx/sso/conf.py:606 msgid "Google OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:617 +#: awx/sso/conf.py:618 msgid "Google OAuth2 Team Map" msgstr "" -#: awx/sso/conf.py:633 +#: awx/sso/conf.py:634 msgid "GitHub OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:637 awx/sso/conf.py:649 awx/sso/conf.py:660 -#: awx/sso/conf.py:672 awx/sso/conf.py:684 +#: awx/sso/conf.py:638 awx/sso/conf.py:650 awx/sso/conf.py:661 +#: awx/sso/conf.py:673 awx/sso/conf.py:685 msgid "GitHub OAuth2" msgstr "" -#: awx/sso/conf.py:647 +#: awx/sso/conf.py:648 msgid "GitHub OAuth2 Key" msgstr "" -#: awx/sso/conf.py:648 +#: awx/sso/conf.py:649 msgid "The OAuth2 key (Client ID) from your GitHub developer application." msgstr "" -#: awx/sso/conf.py:658 +#: awx/sso/conf.py:659 msgid "GitHub OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:659 +#: awx/sso/conf.py:660 msgid "" "The OAuth2 secret (Client Secret) from your GitHub developer application." msgstr "" -#: awx/sso/conf.py:670 +#: awx/sso/conf.py:671 msgid "GitHub OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:682 +#: awx/sso/conf.py:683 msgid "GitHub OAuth2 Team Map" msgstr "" -#: awx/sso/conf.py:698 +#: awx/sso/conf.py:699 msgid "GitHub Organization OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:702 awx/sso/conf.py:714 awx/sso/conf.py:725 -#: awx/sso/conf.py:738 awx/sso/conf.py:749 awx/sso/conf.py:761 +#: awx/sso/conf.py:703 awx/sso/conf.py:715 awx/sso/conf.py:726 +#: awx/sso/conf.py:739 awx/sso/conf.py:750 awx/sso/conf.py:762 msgid "GitHub Organization OAuth2" msgstr "" -#: awx/sso/conf.py:712 +#: awx/sso/conf.py:713 msgid "GitHub Organization OAuth2 Key" msgstr "" -#: awx/sso/conf.py:713 awx/sso/conf.py:791 +#: awx/sso/conf.py:714 awx/sso/conf.py:792 msgid "The OAuth2 key (Client ID) from your GitHub organization application." msgstr "" -#: awx/sso/conf.py:723 +#: awx/sso/conf.py:724 msgid "GitHub Organization OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:724 awx/sso/conf.py:802 +#: awx/sso/conf.py:725 awx/sso/conf.py:803 msgid "" "The OAuth2 secret (Client Secret) from your GitHub organization application." msgstr "" -#: awx/sso/conf.py:735 +#: awx/sso/conf.py:736 msgid "GitHub Organization Name" msgstr "" -#: awx/sso/conf.py:736 +#: awx/sso/conf.py:737 msgid "" "The name of your GitHub organization, as used in your organization's URL: " "https://github.com//." msgstr "" -#: awx/sso/conf.py:747 +#: awx/sso/conf.py:748 msgid "GitHub Organization OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:759 +#: awx/sso/conf.py:760 msgid "GitHub Organization OAuth2 Team Map" msgstr "" -#: awx/sso/conf.py:775 +#: awx/sso/conf.py:776 msgid "GitHub Team OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:776 +#: awx/sso/conf.py:777 msgid "" "Create an organization-owned application at https://github.com/organizations/" "/settings/applications and obtain an OAuth2 key (Client ID) and " @@ -5724,172 +5488,182 @@ msgid "" "application." msgstr "" -#: awx/sso/conf.py:780 awx/sso/conf.py:792 awx/sso/conf.py:803 -#: awx/sso/conf.py:816 awx/sso/conf.py:827 awx/sso/conf.py:839 +#: awx/sso/conf.py:781 awx/sso/conf.py:793 awx/sso/conf.py:804 +#: awx/sso/conf.py:817 awx/sso/conf.py:828 awx/sso/conf.py:840 msgid "GitHub Team OAuth2" msgstr "" -#: awx/sso/conf.py:790 +#: awx/sso/conf.py:791 msgid "GitHub Team OAuth2 Key" msgstr "" -#: awx/sso/conf.py:801 +#: awx/sso/conf.py:802 msgid "GitHub Team OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:813 +#: awx/sso/conf.py:814 msgid "GitHub Team ID" msgstr "" -#: awx/sso/conf.py:814 +#: awx/sso/conf.py:815 msgid "" "Find the numeric team ID using the Github API: http://fabian-kostadinov." "github.io/2015/01/16/how-to-find-a-github-team-id/." msgstr "" -#: awx/sso/conf.py:825 +#: awx/sso/conf.py:826 msgid "GitHub Team OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:837 +#: awx/sso/conf.py:838 msgid "GitHub Team OAuth2 Team Map" msgstr "" -#: awx/sso/conf.py:853 +#: awx/sso/conf.py:854 msgid "Azure AD OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:854 +#: awx/sso/conf.py:855 msgid "" "Provide this URL as the callback URL for your application as part of your " "registration process. Refer to the Ansible Tower documentation for more " "detail. " msgstr "" -#: awx/sso/conf.py:857 awx/sso/conf.py:869 awx/sso/conf.py:880 -#: awx/sso/conf.py:892 awx/sso/conf.py:904 +#: awx/sso/conf.py:858 awx/sso/conf.py:870 awx/sso/conf.py:881 +#: awx/sso/conf.py:893 awx/sso/conf.py:905 msgid "Azure AD OAuth2" msgstr "" -#: awx/sso/conf.py:867 +#: awx/sso/conf.py:868 msgid "Azure AD OAuth2 Key" msgstr "" -#: awx/sso/conf.py:868 +#: awx/sso/conf.py:869 msgid "The OAuth2 key (Client ID) from your Azure AD application." msgstr "" -#: awx/sso/conf.py:878 +#: awx/sso/conf.py:879 msgid "Azure AD OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:879 +#: awx/sso/conf.py:880 msgid "The OAuth2 secret (Client Secret) from your Azure AD application." msgstr "" -#: awx/sso/conf.py:890 +#: awx/sso/conf.py:891 msgid "Azure AD OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:902 +#: awx/sso/conf.py:903 msgid "Azure AD OAuth2 Team Map" msgstr "" #: awx/sso/conf.py:927 -msgid "SAML Assertion Consumer Service (ACS) URL" +msgid "Automatically Create Organizations and Teams on SAML Login" msgstr "" #: awx/sso/conf.py:928 msgid "" +"When enabled (the default), mapped Organizations and Teams will be created " +"automatically on successful SAML login." +msgstr "" + +#: awx/sso/conf.py:930 awx/sso/conf.py:943 awx/sso/conf.py:956 +#: awx/sso/conf.py:969 awx/sso/conf.py:983 awx/sso/conf.py:996 +#: awx/sso/conf.py:1008 awx/sso/conf.py:1028 awx/sso/conf.py:1045 +#: awx/sso/conf.py:1063 awx/sso/conf.py:1098 awx/sso/conf.py:1129 +#: awx/sso/conf.py:1142 awx/sso/conf.py:1158 awx/sso/conf.py:1170 +#: awx/sso/conf.py:1182 awx/sso/conf.py:1201 awx/sso/models.py:16 +msgid "SAML" +msgstr "" + +#: awx/sso/conf.py:939 +msgid "SAML Assertion Consumer Service (ACS) URL" +msgstr "" + +#: awx/sso/conf.py:940 +msgid "" "Register Tower as a service provider (SP) with each identity provider (IdP) " "you have configured. Provide your SP Entity ID and this ACS URL for your " "application." msgstr "" -#: awx/sso/conf.py:931 awx/sso/conf.py:944 awx/sso/conf.py:957 -#: awx/sso/conf.py:971 awx/sso/conf.py:984 awx/sso/conf.py:996 -#: awx/sso/conf.py:1016 awx/sso/conf.py:1033 awx/sso/conf.py:1051 -#: awx/sso/conf.py:1086 awx/sso/conf.py:1117 awx/sso/conf.py:1130 -#: awx/sso/conf.py:1146 awx/sso/conf.py:1158 awx/sso/conf.py:1170 -#: awx/sso/conf.py:1189 awx/sso/models.py:16 -msgid "SAML" -msgstr "" - -#: awx/sso/conf.py:941 +#: awx/sso/conf.py:953 msgid "SAML Service Provider Metadata URL" msgstr "" -#: awx/sso/conf.py:942 +#: awx/sso/conf.py:954 msgid "" "If your identity provider (IdP) allows uploading an XML metadata file, you " "can download one from this URL." msgstr "" -#: awx/sso/conf.py:953 +#: awx/sso/conf.py:965 msgid "SAML Service Provider Entity ID" msgstr "" -#: awx/sso/conf.py:954 +#: awx/sso/conf.py:966 msgid "" "The application-defined unique identifier used as the audience of the SAML " "service provider (SP) configuration. This is usually the URL for Tower." msgstr "" -#: awx/sso/conf.py:968 +#: awx/sso/conf.py:980 msgid "SAML Service Provider Public Certificate" msgstr "" -#: awx/sso/conf.py:969 +#: awx/sso/conf.py:981 msgid "" "Create a keypair for Tower to use as a service provider (SP) and include the " "certificate content here." msgstr "" -#: awx/sso/conf.py:981 +#: awx/sso/conf.py:993 msgid "SAML Service Provider Private Key" msgstr "" -#: awx/sso/conf.py:982 +#: awx/sso/conf.py:994 msgid "" "Create a keypair for Tower to use as a service provider (SP) and include the " "private key content here." msgstr "" -#: awx/sso/conf.py:993 +#: awx/sso/conf.py:1005 msgid "SAML Service Provider Organization Info" msgstr "" -#: awx/sso/conf.py:994 +#: awx/sso/conf.py:1006 msgid "" "Provide the URL, display name, and the name of your app. Refer to the " "Ansible Tower documentation for example syntax." msgstr "" -#: awx/sso/conf.py:1012 +#: awx/sso/conf.py:1024 msgid "SAML Service Provider Technical Contact" msgstr "" -#: awx/sso/conf.py:1013 +#: awx/sso/conf.py:1025 msgid "" "Provide the name and email address of the technical contact for your service " "provider. Refer to the Ansible Tower documentation for example syntax." msgstr "" -#: awx/sso/conf.py:1029 +#: awx/sso/conf.py:1041 msgid "SAML Service Provider Support Contact" msgstr "" -#: awx/sso/conf.py:1030 +#: awx/sso/conf.py:1042 msgid "" "Provide the name and email address of the support contact for your service " "provider. Refer to the Ansible Tower documentation for example syntax." msgstr "" -#: awx/sso/conf.py:1045 +#: awx/sso/conf.py:1057 msgid "SAML Enabled Identity Providers" msgstr "" -#: awx/sso/conf.py:1046 +#: awx/sso/conf.py:1058 msgid "" "Configure the Entity ID, SSO URL and certificate for each identity provider " "(IdP) in use. Multiple SAML IdPs are supported. Some IdPs may provide user " @@ -5898,57 +5672,57 @@ msgid "" "additional details and syntax." msgstr "" -#: awx/sso/conf.py:1082 +#: awx/sso/conf.py:1094 msgid "SAML Security Config" msgstr "" -#: awx/sso/conf.py:1083 +#: awx/sso/conf.py:1095 msgid "" "A dict of key value pairs that are passed to the underlying python-saml " "security setting https://github.com/onelogin/python-saml#settings" msgstr "" -#: awx/sso/conf.py:1114 +#: awx/sso/conf.py:1126 msgid "SAML Service Provider extra configuration data" msgstr "" -#: awx/sso/conf.py:1115 +#: awx/sso/conf.py:1127 msgid "" "A dict of key value pairs to be passed to the underlying python-saml Service " "Provider configuration setting." msgstr "" -#: awx/sso/conf.py:1127 +#: awx/sso/conf.py:1139 msgid "SAML IDP to extra_data attribute mapping" msgstr "" -#: awx/sso/conf.py:1128 +#: awx/sso/conf.py:1140 msgid "" "A list of tuples that maps IDP attributes to extra_attributes. Each " "attribute will be a list of values, even if only 1 value." msgstr "" -#: awx/sso/conf.py:1144 +#: awx/sso/conf.py:1156 msgid "SAML Organization Map" msgstr "" -#: awx/sso/conf.py:1156 +#: awx/sso/conf.py:1168 msgid "SAML Team Map" msgstr "" -#: awx/sso/conf.py:1168 +#: awx/sso/conf.py:1180 msgid "SAML Organization Attribute Mapping" msgstr "" -#: awx/sso/conf.py:1169 +#: awx/sso/conf.py:1181 msgid "Used to translate user organization membership into Tower." msgstr "" -#: awx/sso/conf.py:1187 +#: awx/sso/conf.py:1199 msgid "SAML Team Attribute Mapping" msgstr "" -#: awx/sso/conf.py:1188 +#: awx/sso/conf.py:1200 msgid "Used to translate user team membership into Tower." msgstr "" @@ -6015,12 +5789,12 @@ msgstr "" msgid "Invalid language code(s) for org info: {invalid_lang_codes}." msgstr "" -#: awx/sso/pipeline.py:27 +#: awx/sso/pipeline.py:28 #, python-brace-format msgid "An account cannot be found for {0}" msgstr "" -#: awx/sso/pipeline.py:33 +#: awx/sso/pipeline.py:34 msgid "Your account is inactive" msgstr "" @@ -6092,8 +5866,8 @@ msgstr "" msgid "" "If needed, you can add specific information (such as a legal notice or a " "disclaimer) to a text box in the login modal using this setting. Any content " -"added must be in plain text, as custom HTML or other markup languages are " -"not supported." +"added must be in plain text or an HTML fragment, as other markup languages " +"are not supported." msgstr "" #: awx/ui/conf.py:45 diff --git a/awx/locale/ja/LC_MESSAGES/django.po b/awx/locale/ja/LC_MESSAGES/django.po index a878a3d1ad..abc2ad15aa 100644 --- a/awx/locale/ja/LC_MESSAGES/django.po +++ b/awx/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-28 21:45+0000\n" +"POT-Creation-Date: 2020-09-29 15:20+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -126,27 +126,27 @@ msgstr "クエリー文字列フィールド名は指定されていません。 msgid "Invalid {field_name} id: {field_id}" msgstr "無効な {field_name} id: {field_id}" -#: awx/api/filters.py:333 +#: awx/api/filters.py:338 msgid "" "Cannot apply role_level filter to this list because its model does not use " "roles for access control." msgstr "モデルがアクセスコントロールにロールを使用していないので、このリストに role_level フィルターを適用できません。" -#: awx/api/generics.py:182 +#: awx/api/generics.py:183 msgid "" "You did not use correct Content-Type in your HTTP request. If you are using " "our REST API, the Content-Type must be application/json" msgstr "HTTP 要求で正しい Content-Type (コンテンツタイプ) が使用されていません。REST API を使用している場合、Content-Type (コンテンツタイプ) は「application/json」でなければなりません。" -#: awx/api/generics.py:623 awx/api/generics.py:685 +#: awx/api/generics.py:647 awx/api/generics.py:709 msgid "\"id\" field must be an integer." msgstr "「id」フィールドは整数でなければなりません。" -#: awx/api/generics.py:682 +#: awx/api/generics.py:706 msgid "\"id\" is required to disassociate" msgstr "関連付けを解除するには 「id」が必要です" -#: awx/api/generics.py:733 +#: awx/api/generics.py:757 msgid "{} 'id' field is missing." msgstr "{} 「id」フィールドがありません。" @@ -219,7 +219,7 @@ msgstr "Playbook 実行" msgid "Command" msgstr "コマンド" -#: awx/api/serializers.py:318 awx/main/models/unified_jobs.py:547 +#: awx/api/serializers.py:318 awx/main/models/unified_jobs.py:546 msgid "SCM Update" msgstr "SCM 更新" @@ -249,1139 +249,1161 @@ msgid "" "saved to the database." msgstr "この統一されたジョブで生成されるイベントすべてがデータベースに保存されているかどうかを示します。" -#: awx/api/serializers.py:878 +#: awx/api/serializers.py:880 msgid "Write-only field used to change the password." msgstr "パスワードを変更するために使用される書き込み専用フィールド。" -#: awx/api/serializers.py:880 +#: awx/api/serializers.py:882 msgid "Set if the account is managed by an external service" msgstr "アカウントが外部サービスで管理される場合に設定されます" -#: awx/api/serializers.py:907 +#: awx/api/serializers.py:909 msgid "Password required for new User." msgstr "新規ユーザーのパスワードを入力してください。" -#: awx/api/serializers.py:992 +#: awx/api/serializers.py:994 #, python-format msgid "Unable to change %s on user managed by LDAP." msgstr "LDAP で管理されたユーザーの %s を変更できません。" -#: awx/api/serializers.py:1088 +#: awx/api/serializers.py:1090 msgid "Must be a simple space-separated string with allowed scopes {}." msgstr "許可されたスコープ {} のある単純なスペースで区切られた文字列でなければなりません。" -#: awx/api/serializers.py:1186 +#: awx/api/serializers.py:1188 msgid "Authorization Grant Type" msgstr "認証付与タイプ" -#: awx/api/serializers.py:1188 awx/main/credential_plugins/azure_kv.py:30 -#: awx/main/models/credential/__init__.py:960 +#: awx/api/serializers.py:1190 awx/main/credential_plugins/azure_kv.py:30 +#: awx/main/models/credential/__init__.py:972 msgid "Client Secret" msgstr "クライアントシークレット" -#: awx/api/serializers.py:1191 +#: awx/api/serializers.py:1193 msgid "Client Type" msgstr "クライアントタイプ" -#: awx/api/serializers.py:1194 +#: awx/api/serializers.py:1196 msgid "Redirect URIs" msgstr "リダイレクト URI" -#: awx/api/serializers.py:1197 +#: awx/api/serializers.py:1199 msgid "Skip Authorization" msgstr "認証のスキップ" -#: awx/api/serializers.py:1303 +#: awx/api/serializers.py:1306 msgid "Cannot change max_hosts." msgstr "max_hosts を変更できません。" -#: awx/api/serializers.py:1336 +#: awx/api/serializers.py:1339 msgid "This path is already being used by another manual project." msgstr "このパスは別の手動プロジェクトですでに使用されています。" -#: awx/api/serializers.py:1338 +#: awx/api/serializers.py:1341 +msgid "SCM branch cannot be used with archive projects." +msgstr "SCM ブランチはアーカイブプロジェクトでは使用できません。" + +#: awx/api/serializers.py:1343 msgid "SCM refspec can only be used with git projects." msgstr "SCM refspec は、git プロジェクトでのみ使用できます。" -#: awx/api/serializers.py:1415 +#: awx/api/serializers.py:1420 msgid "" "One or more job templates depend on branch override behavior for this " "project (ids: {})." msgstr "1 つまたは複数のジョブテンプレートは、このプロジェクトのブランチオーバーライドに依存しています (ids: {})。" -#: awx/api/serializers.py:1422 +#: awx/api/serializers.py:1427 msgid "Update options must be set to false for manual projects." msgstr "手動プロジェクトについては更新オプションを false に設定する必要があります。" -#: awx/api/serializers.py:1428 +#: awx/api/serializers.py:1433 msgid "Array of playbooks available within this project." msgstr "このプロジェクト内で利用可能な一連の Playbook。" -#: awx/api/serializers.py:1447 +#: awx/api/serializers.py:1452 msgid "" "Array of inventory files and directories available within this project, not " "comprehensive." msgstr "このプロジェクト内で利用可能な一連のインベントリーファイルおよびディレクトリー (包括的な一覧ではありません)。" -#: awx/api/serializers.py:1495 awx/api/serializers.py:3048 -#: awx/api/serializers.py:3260 +#: awx/api/serializers.py:1500 awx/api/serializers.py:3089 +#: awx/api/serializers.py:3301 msgid "A count of hosts uniquely assigned to each status." msgstr "各ステータスに一意に割り当てられたホスト数です。" -#: awx/api/serializers.py:1498 awx/api/serializers.py:3051 +#: awx/api/serializers.py:1503 awx/api/serializers.py:3092 msgid "A count of all plays and tasks for the job run." msgstr "ジョブ実行用のすべてのプレイおよびタスクの数です。" -#: awx/api/serializers.py:1625 +#: awx/api/serializers.py:1630 msgid "Smart inventories must specify host_filter" msgstr "スマートインベントリーは host_filter を指定する必要があります" -#: awx/api/serializers.py:1713 +#: awx/api/serializers.py:1722 #, python-format msgid "Invalid port specification: %s" msgstr "無効なポート指定: %s" -#: awx/api/serializers.py:1724 +#: awx/api/serializers.py:1733 msgid "Cannot create Host for Smart Inventory" msgstr "スマートインベントリーのホストを作成できません" -#: awx/api/serializers.py:1808 +#: awx/api/serializers.py:1751 +msgid "A Group with that name already exists." +msgstr "その名前のグループはすでに存在します。" + +#: awx/api/serializers.py:1822 +msgid "A Host with that name already exists." +msgstr "その名前のホストはすでに存在します。" + +#: awx/api/serializers.py:1827 msgid "Invalid group name." msgstr "無効なグループ名。" -#: awx/api/serializers.py:1813 +#: awx/api/serializers.py:1832 msgid "Cannot create Group for Smart Inventory" msgstr "スマートインベントリーのグループを作成できません" -#: awx/api/serializers.py:1888 +#: awx/api/serializers.py:1907 msgid "" "Script must begin with a hashbang sequence: i.e.... #!/usr/bin/env python" msgstr "スクリプトは hashbang シーケンスで開始する必要があります (例: .... #!/usr/bin/env python)" -#: awx/api/serializers.py:1917 +#: awx/api/serializers.py:1936 msgid "Cloud credential to use for inventory updates." msgstr "インベントリー更新に使用するクラウド認証情報" -#: awx/api/serializers.py:1938 +#: awx/api/serializers.py:1957 msgid "`{}` is a prohibited environment variable" msgstr "`{}` は禁止されている環境変数です" -#: awx/api/serializers.py:1949 +#: awx/api/serializers.py:1968 msgid "If 'source' is 'custom', 'source_script' must be provided." msgstr "「source」が「custom」である場合、「source_script」を指定する必要があります。" -#: awx/api/serializers.py:1955 +#: awx/api/serializers.py:1974 msgid "Must provide an inventory." msgstr "インベントリーを指定する必要があります。" -#: awx/api/serializers.py:1959 +#: awx/api/serializers.py:1978 msgid "" "The 'source_script' does not belong to the same organization as the " "inventory." msgstr "「source_script」はインベントリーと同じ組織に属しません。" -#: awx/api/serializers.py:1961 +#: awx/api/serializers.py:1980 msgid "'source_script' doesn't exist." msgstr "「source_script」は存在しません。" -#: awx/api/serializers.py:2063 +#: awx/api/serializers.py:2082 msgid "Cannot use manual project for SCM-based inventory." msgstr "SCM ベースのインベントリーの手動プロジェクトを使用できません。" -#: awx/api/serializers.py:2068 +#: awx/api/serializers.py:2087 msgid "Setting not compatible with existing schedules." msgstr "設定は既存スケジュールとの互換性がありません。" -#: awx/api/serializers.py:2073 +#: awx/api/serializers.py:2092 msgid "Cannot create Inventory Source for Smart Inventory" msgstr "スマートインベントリーのインベントリーソースを作成できません" -#: awx/api/serializers.py:2121 +#: awx/api/serializers.py:2140 msgid "Project required for scm type sources." msgstr "SCM タイプのソースに必要なプロジェクト。" -#: awx/api/serializers.py:2130 +#: awx/api/serializers.py:2149 #, python-format msgid "Cannot set %s if not SCM type." msgstr "SCM タイプでない場合 %s を設定できません。" -#: awx/api/serializers.py:2200 +#: awx/api/serializers.py:2219 msgid "The project used for this job." msgstr "このジョブに使用するプロジェクト" -#: awx/api/serializers.py:2455 +#: awx/api/serializers.py:2475 msgid "Modifications not allowed for managed credential types" msgstr "管理されている認証情報タイプで変更は許可されません" -#: awx/api/serializers.py:2467 +#: awx/api/serializers.py:2487 msgid "" "Modifications to inputs are not allowed for credential types that are in use" msgstr "入力への変更は使用中の認証情報タイプで許可されません" -#: awx/api/serializers.py:2472 +#: awx/api/serializers.py:2492 #, python-format msgid "Must be 'cloud' or 'net', not %s" msgstr "「cloud」または「net」にする必要があります (%s ではない)" -#: awx/api/serializers.py:2478 +#: awx/api/serializers.py:2498 msgid "'ask_at_runtime' is not supported for custom credentials." msgstr "「ask_at_runtime」はカスタム認証情報ではサポートされません。" -#: awx/api/serializers.py:2526 +#: awx/api/serializers.py:2547 msgid "Credential Type" msgstr "認証情報タイプ" -#: awx/api/serializers.py:2607 +#: awx/api/serializers.py:2611 +msgid "Modifications not allowed for managed credentials" +msgstr "管理されている認証情報では変更が許可されません" + +#: awx/api/serializers.py:2629 awx/api/serializers.py:2703 +msgid "Galaxy credentials must be owned by an Organization." +msgstr "Galaxy 認証情報は組織が所有している必要があります。" + +#: awx/api/serializers.py:2646 msgid "" "You cannot change the credential type of the credential, as it may break the " "functionality of the resources using it." msgstr "認証情報の認証情報タイプを変更することはできません。これにより、認証情報を使用するリソースの機能が中断する可能性があるためです。" -#: awx/api/serializers.py:2619 +#: awx/api/serializers.py:2658 msgid "" "Write-only field used to add user to owner role. If provided, do not give " "either team or organization. Only valid for creation." msgstr "ユーザーを所有者ロールに追加するために使用される書き込み専用フィールドです。提供されている場合は、チームまたは組織のいずれも指定しないでください。作成時にのみ有効です。" -#: awx/api/serializers.py:2624 +#: awx/api/serializers.py:2663 msgid "" "Write-only field used to add team to owner role. If provided, do not give " "either user or organization. Only valid for creation." msgstr "チームを所有者ロールに追加するために使用される書き込み専用フィールドです。提供されている場合は、ユーザーまたは組織のいずれも指定しないでください。作成時にのみ有効です。" -#: awx/api/serializers.py:2629 +#: awx/api/serializers.py:2668 msgid "" "Inherit permissions from organization roles. If provided on creation, do not " "give either user or team." msgstr "組織ロールからパーミッションを継承します。作成時に提供される場合は、ユーザーまたはチームのいずれも指定しないでください。" -#: awx/api/serializers.py:2645 +#: awx/api/serializers.py:2685 msgid "Missing 'user', 'team', or 'organization'." msgstr "「user」、「team」、または「organization」がありません。" -#: awx/api/serializers.py:2662 +#: awx/api/serializers.py:2690 +msgid "" +"Only one of 'user', 'team', or 'organization' should be provided, received " +"{} fields." +msgstr "「user」、「team」、または「organization」のいずれか 1 つのみを指定し、{} フィールドを受け取る必要があります。" + +#: awx/api/serializers.py:2718 msgid "" "Credential organization must be set and match before assigning to a team" msgstr "認証情報の組織が設定され、一致している状態でチームに割り当てる必要があります。" -#: awx/api/serializers.py:2793 +#: awx/api/serializers.py:2844 msgid "This field is required." msgstr "このフィールドは必須です。" -#: awx/api/serializers.py:2802 +#: awx/api/serializers.py:2853 msgid "Playbook not found for project." msgstr "プロジェクトの Playbook が見つかりません。" -#: awx/api/serializers.py:2804 +#: awx/api/serializers.py:2855 msgid "Must select playbook for project." msgstr "プロジェクトの Playbook を選択してください。" -#: awx/api/serializers.py:2806 awx/api/serializers.py:2808 +#: awx/api/serializers.py:2857 awx/api/serializers.py:2859 msgid "Project does not allow overriding branch." msgstr "プロジェクトは、ブランチをオーバーライドできません。" -#: awx/api/serializers.py:2845 +#: awx/api/serializers.py:2896 msgid "Must be a Personal Access Token." msgstr "パーソナルアクセストークンである必要があります。" -#: awx/api/serializers.py:2848 +#: awx/api/serializers.py:2899 msgid "Must match the selected webhook service." msgstr "選択した Webhook サービスと一致する必要があります。" -#: awx/api/serializers.py:2919 +#: awx/api/serializers.py:2970 msgid "Cannot enable provisioning callback without an inventory set." msgstr "インベントリー設定なしにプロビジョニングコールバックを有効にすることはできません。" -#: awx/api/serializers.py:2922 +#: awx/api/serializers.py:2973 msgid "Must either set a default value or ask to prompt on launch." msgstr "起動時にプロントを出すには、デフォルト値を設定するか、またはプロンプトを出すよう指定する必要があります。" -#: awx/api/serializers.py:2924 awx/main/models/jobs.py:299 +#: awx/api/serializers.py:2975 awx/main/models/jobs.py:299 msgid "Job Templates must have a project assigned." msgstr "ジョブテンプレートにはプロジェクトを割り当てる必要があります。" -#: awx/api/serializers.py:3092 +#: awx/api/serializers.py:3133 msgid "No change to job limit" msgstr "ジョブ制限に変更はありません" -#: awx/api/serializers.py:3093 +#: awx/api/serializers.py:3134 msgid "All failed and unreachable hosts" msgstr "失敗している、到達できないすべてのホスト" -#: awx/api/serializers.py:3108 +#: awx/api/serializers.py:3149 msgid "Missing passwords needed to start: {}" msgstr "起動に必要なパスワードが見つかりません: {}" -#: awx/api/serializers.py:3127 +#: awx/api/serializers.py:3168 msgid "Relaunch by host status not available until job finishes running." msgstr "ホストのステータス別の再起動はジョブが実行を終了するまで利用できません。" -#: awx/api/serializers.py:3141 +#: awx/api/serializers.py:3182 msgid "Job Template Project is missing or undefined." msgstr "ジョブテンプレートプロジェクトが見つからないか、または定義されていません。" -#: awx/api/serializers.py:3143 +#: awx/api/serializers.py:3184 msgid "Job Template Inventory is missing or undefined." msgstr "ジョブテンプレートインベントリーが見つからないか、または定義されていません。" -#: awx/api/serializers.py:3181 +#: awx/api/serializers.py:3222 msgid "Unknown, job may have been ran before launch configurations were saved." msgstr "不明です。ジョブは起動設定が保存される前に実行された可能性があります。" -#: awx/api/serializers.py:3252 awx/main/tasks.py:2800 awx/main/tasks.py:2818 +#: awx/api/serializers.py:3293 awx/main/tasks.py:2838 awx/main/tasks.py:2856 msgid "{} are prohibited from use in ad hoc commands." msgstr "{} の使用はアドホックコマンドで禁止されています。" -#: awx/api/serializers.py:3340 awx/api/views/__init__.py:4243 +#: awx/api/serializers.py:3381 awx/api/views/__init__.py:4211 #, python-brace-format msgid "" "Standard Output too large to display ({text_size} bytes), only download " "supported for sizes over {supported_size} bytes." msgstr "標準出力が大きすぎて表示できません ({text_size} バイト)。サイズが {supported_size} バイトを超える場合はダウンロードのみがサポートされます。" -#: awx/api/serializers.py:3653 +#: awx/api/serializers.py:3694 msgid "Provided variable {} has no database value to replace with." msgstr "指定された変数 {} には置き換えるデータベースの値がありません。" -#: awx/api/serializers.py:3671 +#: awx/api/serializers.py:3712 msgid "\"$encrypted$ is a reserved keyword, may not be used for {}.\"" msgstr "\"$encrypted$ は予約されたキーワードで、{} には使用できません。\"" -#: awx/api/serializers.py:4078 +#: awx/api/serializers.py:4119 msgid "A project is required to run a job." msgstr "ジョブを実行するにはプロジェクトが必要です。" -#: awx/api/serializers.py:4080 +#: awx/api/serializers.py:4121 msgid "Missing a revision to run due to failed project update." msgstr "プロジェクトの更新に失敗したため、実行するリビジョンがありません。" -#: awx/api/serializers.py:4084 +#: awx/api/serializers.py:4125 msgid "The inventory associated with this Job Template is being deleted." msgstr "このジョブテンプレートに関連付けられているインベントリーが削除されています。" -#: awx/api/serializers.py:4086 awx/api/serializers.py:4202 +#: awx/api/serializers.py:4127 awx/api/serializers.py:4244 msgid "The provided inventory is being deleted." msgstr "指定されたインベントリーが削除されています。" -#: awx/api/serializers.py:4094 +#: awx/api/serializers.py:4135 msgid "Cannot assign multiple {} credentials." msgstr "複数の {} 認証情報を割り当てることができません。" -#: awx/api/serializers.py:4098 +#: awx/api/serializers.py:4140 msgid "Cannot assign a Credential of kind `{}`" msgstr "`{}`の種類の認証情報を割り当てることができません。" -#: awx/api/serializers.py:4111 +#: awx/api/serializers.py:4153 msgid "" "Removing {} credential at launch time without replacement is not supported. " "Provided list lacked credential(s): {}." msgstr "置き換えなしで起動時に {} 認証情報を削除することはサポートされていません。指定された一覧には認証情報がありません: {}" -#: awx/api/serializers.py:4200 +#: awx/api/serializers.py:4242 msgid "The inventory associated with this Workflow is being deleted." msgstr "このワークフローに関連付けられているインベントリーが削除されています。" -#: awx/api/serializers.py:4271 +#: awx/api/serializers.py:4313 msgid "Message type '{}' invalid, must be either 'message' or 'body'" msgstr "メッセージタイプ '{}' が無効です。'メッセージ' または 'ボディー' のいずれかに指定する必要があります。" -#: awx/api/serializers.py:4277 +#: awx/api/serializers.py:4319 msgid "Expected string for '{}', found {}, " msgstr "'{}' の文字列が必要ですが、{} が見つかりました。 " -#: awx/api/serializers.py:4281 +#: awx/api/serializers.py:4323 msgid "Messages cannot contain newlines (found newline in {} event)" msgstr "メッセージでは改行を追加できません ({} イベントに改行が含まれます)" -#: awx/api/serializers.py:4287 +#: awx/api/serializers.py:4329 msgid "Expected dict for 'messages' field, found {}" msgstr "'messages' フィールドには辞書が必要ですが、{} が見つかりました。" -#: awx/api/serializers.py:4291 +#: awx/api/serializers.py:4333 msgid "" "Event '{}' invalid, must be one of 'started', 'success', 'error', or " "'workflow_approval'" msgstr "イベント '{}' は無効です。'started'、'success'、'error' または 'workflow_approval' のいずれかでなければなりません。" -#: awx/api/serializers.py:4297 +#: awx/api/serializers.py:4339 msgid "Expected dict for event '{}', found {}" msgstr "イベント '{}' には辞書が必要ですが、{} が見つかりました。" -#: awx/api/serializers.py:4302 +#: awx/api/serializers.py:4344 msgid "" "Workflow Approval event '{}' invalid, must be one of 'running', 'approved', " "'timed_out', or 'denied'" msgstr "ワークフロー承認イベント '{}' が無効です。'running'、'approved'、'timed_out' または 'denied' のいずれかでなければなりません。" -#: awx/api/serializers.py:4309 +#: awx/api/serializers.py:4351 msgid "Expected dict for workflow approval event '{}', found {}" msgstr "ワークフロー承認イベント '{}' には辞書が必要ですが、{} が見つかりました。" -#: awx/api/serializers.py:4336 +#: awx/api/serializers.py:4378 msgid "Unable to render message '{}': {}" msgstr "メッセージ '{}' のレンダリングができません: {}" -#: awx/api/serializers.py:4338 +#: awx/api/serializers.py:4380 msgid "Field '{}' unavailable" msgstr "フィールド '{}' が利用できません" -#: awx/api/serializers.py:4340 +#: awx/api/serializers.py:4382 msgid "Security error due to field '{}'" msgstr "フィールド '{}' が原因のセキュリティーエラー" -#: awx/api/serializers.py:4360 +#: awx/api/serializers.py:4402 msgid "Webhook body for '{}' should be a json dictionary. Found type '{}'." msgstr "'{}' の Webhook のボディーは json 辞書でなければなりません。'{}' のタイプが見つかりました。" -#: awx/api/serializers.py:4363 +#: awx/api/serializers.py:4405 msgid "Webhook body for '{}' is not a valid json dictionary ({})." msgstr "'{}' の Webhook ボディーは有効な json 辞書ではありません ({})。" -#: awx/api/serializers.py:4381 +#: awx/api/serializers.py:4423 msgid "" "Missing required fields for Notification Configuration: notification_type" msgstr "通知設定の必須フィールドがありません: notification_type" -#: awx/api/serializers.py:4408 +#: awx/api/serializers.py:4450 msgid "No values specified for field '{}'" msgstr "フィールド '{}' に値が指定されていません" -#: awx/api/serializers.py:4413 +#: awx/api/serializers.py:4455 msgid "HTTP method must be either 'POST' or 'PUT'." msgstr "HTTP メソッドは 'POST' または 'PUT' のいずれかでなければなりません。" -#: awx/api/serializers.py:4415 +#: awx/api/serializers.py:4457 msgid "Missing required fields for Notification Configuration: {}." msgstr "通知設定の必須フィールドがありません: {}。" -#: awx/api/serializers.py:4418 +#: awx/api/serializers.py:4460 msgid "Configuration field '{}' incorrect type, expected {}." msgstr "設定フィールド '{}' のタイプが正しくありません。{} が予期されました。" -#: awx/api/serializers.py:4435 +#: awx/api/serializers.py:4477 msgid "Notification body" msgstr "通知ボディー" -#: awx/api/serializers.py:4515 +#: awx/api/serializers.py:4557 msgid "" "Valid DTSTART required in rrule. Value should start with: DTSTART:" "YYYYMMDDTHHMMSSZ" msgstr "有効な DTSTART が rrule で必要です。値は DTSTART:YYYYMMDDTHHMMSSZ で開始する必要があります。" -#: awx/api/serializers.py:4517 +#: awx/api/serializers.py:4559 msgid "" "DTSTART cannot be a naive datetime. Specify ;TZINFO= or YYYYMMDDTHHMMSSZZ." msgstr "DTSTART をネイティブの日時にすることができません。;TZINFO= or YYYYMMDDTHHMMSSZZ を指定します。" -#: awx/api/serializers.py:4519 +#: awx/api/serializers.py:4561 msgid "Multiple DTSTART is not supported." msgstr "複数の DTSTART はサポートされません。" -#: awx/api/serializers.py:4521 +#: awx/api/serializers.py:4563 msgid "RRULE required in rrule." msgstr "RRULE が rrule で必要です。" -#: awx/api/serializers.py:4523 +#: awx/api/serializers.py:4565 msgid "Multiple RRULE is not supported." msgstr "複数の RRULE はサポートされません。" -#: awx/api/serializers.py:4525 +#: awx/api/serializers.py:4567 msgid "INTERVAL required in rrule." msgstr "INTERVAL が rrule で必要です。" -#: awx/api/serializers.py:4527 +#: awx/api/serializers.py:4569 msgid "SECONDLY is not supported." msgstr "SECONDLY はサポートされません。" -#: awx/api/serializers.py:4529 +#: awx/api/serializers.py:4571 msgid "Multiple BYMONTHDAYs not supported." msgstr "複数の BYMONTHDAY はサポートされません。" -#: awx/api/serializers.py:4531 +#: awx/api/serializers.py:4573 msgid "Multiple BYMONTHs not supported." msgstr "複数の BYMONTH はサポートされません。" -#: awx/api/serializers.py:4533 +#: awx/api/serializers.py:4575 msgid "BYDAY with numeric prefix not supported." msgstr "数字の接頭辞のある BYDAY はサポートされません。" -#: awx/api/serializers.py:4535 +#: awx/api/serializers.py:4577 msgid "BYYEARDAY not supported." msgstr "BYYEARDAY はサポートされません。" -#: awx/api/serializers.py:4537 +#: awx/api/serializers.py:4579 msgid "BYWEEKNO not supported." msgstr "BYWEEKNO はサポートされません。" -#: awx/api/serializers.py:4539 +#: awx/api/serializers.py:4581 msgid "RRULE may not contain both COUNT and UNTIL" msgstr "RRULE には COUNT と UNTIL の両方を含めることができません" -#: awx/api/serializers.py:4543 +#: awx/api/serializers.py:4585 msgid "COUNT > 999 is unsupported." msgstr "COUNT > 999 はサポートされません。" -#: awx/api/serializers.py:4549 +#: awx/api/serializers.py:4591 msgid "rrule parsing failed validation: {}" msgstr "rrule の構文解析で検証に失敗しました: {}" -#: awx/api/serializers.py:4611 +#: awx/api/serializers.py:4653 msgid "Inventory Source must be a cloud resource." msgstr "インベントリーソースはクラウドリソースでなければなりません。" -#: awx/api/serializers.py:4613 +#: awx/api/serializers.py:4655 msgid "Manual Project cannot have a schedule set." msgstr "手動プロジェクトにはスケジュールを設定できません。" -#: awx/api/serializers.py:4616 +#: awx/api/serializers.py:4658 msgid "" "Inventory sources with `update_on_project_update` cannot be scheduled. " "Schedule its source project `{}` instead." msgstr "「update_on_project_update」が設定されたインベントリーソースはスケジュールできません。代わりのそのソースプロジェクト「{}」 をスケジュールします。" -#: awx/api/serializers.py:4626 +#: awx/api/serializers.py:4668 msgid "" "Count of jobs in the running or waiting state that are targeted for this " "instance" msgstr "このインスタンスにターゲット設定されている実行中または待機状態のジョブの数" -#: awx/api/serializers.py:4631 +#: awx/api/serializers.py:4673 msgid "Count of all jobs that target this instance" msgstr "このインスタンスをターゲットに設定するすべてのジョブの数" -#: awx/api/serializers.py:4664 +#: awx/api/serializers.py:4708 msgid "" "Count of jobs in the running or waiting state that are targeted for this " "instance group" msgstr "このインスタンスグループにターゲット設定されている実行中または待機状態のジョブの数" -#: awx/api/serializers.py:4669 +#: awx/api/serializers.py:4713 msgid "Count of all jobs that target this instance group" msgstr "このインスタンスグループをターゲットに設定するすべてのジョブの数" -#: awx/api/serializers.py:4674 +#: awx/api/serializers.py:4718 msgid "Indicates whether instance group controls any other group" msgstr "インスタンスグループが他のグループを制御するかどうかを指定します。" -#: awx/api/serializers.py:4678 +#: awx/api/serializers.py:4722 msgid "" "Indicates whether instances in this group are isolated.Isolated groups have " "a designated controller group." msgstr "このグループ内でインスタンスを分離させるかを指定します。分離されたグループには指定したコントローラーグループがあります。" -#: awx/api/serializers.py:4683 +#: awx/api/serializers.py:4727 msgid "" "Indicates whether instances in this group are containerized.Containerized " "groups have a designated Openshift or Kubernetes cluster." msgstr "このグループ内でインスタンスをコンテナー化するかを指定します。コンテナー化したグループには、指定の OpenShift または Kubernetes クラスターが含まれます。" -#: awx/api/serializers.py:4691 +#: awx/api/serializers.py:4735 msgid "Policy Instance Percentage" msgstr "ポリシーインスタンスの割合" -#: awx/api/serializers.py:4692 +#: awx/api/serializers.py:4736 msgid "" "Minimum percentage of all instances that will be automatically assigned to " "this group when new instances come online." msgstr "新規インスタンスがオンラインになると、このグループに自動的に最小限割り当てられるインスタンスの割合を選択します。" -#: awx/api/serializers.py:4697 +#: awx/api/serializers.py:4741 msgid "Policy Instance Minimum" msgstr "ポリシーインスタンスの最小値" -#: awx/api/serializers.py:4698 +#: awx/api/serializers.py:4742 msgid "" "Static minimum number of Instances that will be automatically assign to this " "group when new instances come online." msgstr "新規インスタンスがオンラインになると、このグループに自動的に最小限割り当てられるインスタンス数を入力します。" -#: awx/api/serializers.py:4703 +#: awx/api/serializers.py:4747 msgid "Policy Instance List" msgstr "ポリシーインスタンスの一覧" -#: awx/api/serializers.py:4704 +#: awx/api/serializers.py:4748 msgid "List of exact-match Instances that will be assigned to this group" msgstr "このグループに割り当てられる完全一致のインスタンスの一覧" -#: awx/api/serializers.py:4730 +#: awx/api/serializers.py:4774 msgid "Duplicate entry {}." msgstr "重複するエントリー {}。" -#: awx/api/serializers.py:4732 +#: awx/api/serializers.py:4776 msgid "{} is not a valid hostname of an existing instance." msgstr "{} は既存インスタンスの有効なホスト名ではありません。" -#: awx/api/serializers.py:4734 awx/api/views/mixin.py:98 +#: awx/api/serializers.py:4778 awx/api/views/mixin.py:98 msgid "" "Isolated instances may not be added or removed from instances groups via the " "API." msgstr "分離されたインスタンスは、API 経由でインスタンスグループから追加したり、削除したりすることができません。" -#: awx/api/serializers.py:4736 awx/api/views/mixin.py:102 +#: awx/api/serializers.py:4780 awx/api/views/mixin.py:102 msgid "Isolated instance group membership may not be managed via the API." msgstr "分離されたインスタンスグループのメンバーシップは API で管理できません。" -#: awx/api/serializers.py:4738 awx/api/serializers.py:4743 -#: awx/api/serializers.py:4748 +#: awx/api/serializers.py:4782 awx/api/serializers.py:4787 +#: awx/api/serializers.py:4792 msgid "Containerized instances may not be managed via the API" msgstr "コンテナー化されたインスタンスは API で管理されないことがあります" -#: awx/api/serializers.py:4753 +#: awx/api/serializers.py:4797 msgid "tower instance group name may not be changed." msgstr "Tower のインスタンスグループ名は変更できません。" -#: awx/api/serializers.py:4758 +#: awx/api/serializers.py:4802 msgid "Only Kubernetes credentials can be associated with an Instance Group" msgstr "インスタンスグループに関連付けることができる Kubernetes 認証情報のみです" -#: awx/api/serializers.py:4797 +#: awx/api/serializers.py:4841 msgid "" "When present, shows the field name of the role or relationship that changed." msgstr "これがある場合には、変更された関係またはロールのフィールド名を表示します。" -#: awx/api/serializers.py:4799 +#: awx/api/serializers.py:4843 msgid "" "When present, shows the model on which the role or relationship was defined." msgstr "これがある場合には、ロールまたは関係が定義されているモデルを表示します。" -#: awx/api/serializers.py:4832 +#: awx/api/serializers.py:4876 msgid "" "A summary of the new and changed values when an object is created, updated, " "or deleted" msgstr "オブジェクトの作成、更新または削除時の新規値および変更された値の概要" -#: awx/api/serializers.py:4834 +#: awx/api/serializers.py:4878 msgid "" "For create, update, and delete events this is the object type that was " "affected. For associate and disassociate events this is the object type " "associated or disassociated with object2." msgstr "作成、更新、および削除イベントの場合、これは影響を受けたオブジェクトタイプになります。関連付けおよび関連付け解除イベントの場合、これは object2 に関連付けられたか、またはその関連付けが解除されたオブジェクトタイプになります。" -#: awx/api/serializers.py:4837 +#: awx/api/serializers.py:4881 msgid "" "Unpopulated for create, update, and delete events. For associate and " "disassociate events this is the object type that object1 is being associated " "with." msgstr "作成、更新、および削除イベントの場合は設定されません。関連付けおよび関連付け解除イベントの場合、これは object1 が関連付けられるオブジェクトタイプになります。" -#: awx/api/serializers.py:4840 +#: awx/api/serializers.py:4884 msgid "The action taken with respect to the given object(s)." msgstr "指定されたオブジェクトについて実行されたアクション。" -#: awx/api/views/__init__.py:181 +#: awx/api/views/__init__.py:185 +msgid "Not found." +msgstr "見つかりません" + +#: awx/api/views/__init__.py:193 msgid "Dashboard" msgstr "ダッシュボード" -#: awx/api/views/__init__.py:271 +#: awx/api/views/__init__.py:290 msgid "Dashboard Jobs Graphs" msgstr "ダッシュボードのジョブグラフ" -#: awx/api/views/__init__.py:307 +#: awx/api/views/__init__.py:326 #, python-format msgid "Unknown period \"%s\"" msgstr "不明な期間 \"%s\"" -#: awx/api/views/__init__.py:321 +#: awx/api/views/__init__.py:340 msgid "Instances" msgstr "インスタンス" -#: awx/api/views/__init__.py:329 +#: awx/api/views/__init__.py:348 msgid "Instance Detail" msgstr "インスタンスの詳細" -#: awx/api/views/__init__.py:346 +#: awx/api/views/__init__.py:365 msgid "Instance Jobs" msgstr "インスタンスジョブ" -#: awx/api/views/__init__.py:360 +#: awx/api/views/__init__.py:379 msgid "Instance's Instance Groups" msgstr "インスタンスのインスタンスグループ" -#: awx/api/views/__init__.py:369 +#: awx/api/views/__init__.py:388 msgid "Instance Groups" msgstr "インスタンスグループ" -#: awx/api/views/__init__.py:377 +#: awx/api/views/__init__.py:396 msgid "Instance Group Detail" msgstr "インスタンスグループの詳細" -#: awx/api/views/__init__.py:392 +#: awx/api/views/__init__.py:411 msgid "Isolated Groups can not be removed from the API" msgstr "分離されたグループは API から削除できません" -#: awx/api/views/__init__.py:394 +#: awx/api/views/__init__.py:413 msgid "" "Instance Groups acting as a controller for an Isolated Group can not be " "removed from the API" msgstr "分離されたグループのコントローラーとして動作するインスタンスグループは API から削除できません" -#: awx/api/views/__init__.py:400 +#: awx/api/views/__init__.py:419 msgid "Instance Group Running Jobs" msgstr "ジョブを実行しているインスタンスグループ" -#: awx/api/views/__init__.py:409 +#: awx/api/views/__init__.py:428 msgid "Instance Group's Instances" msgstr "インスタンスグループのインスタンス" -#: awx/api/views/__init__.py:419 +#: awx/api/views/__init__.py:438 msgid "Schedules" msgstr "スケジュール" -#: awx/api/views/__init__.py:433 +#: awx/api/views/__init__.py:452 msgid "Schedule Recurrence Rule Preview" msgstr "繰り返しルールプレビューのスケジュール" -#: awx/api/views/__init__.py:480 +#: awx/api/views/__init__.py:499 msgid "Cannot assign credential when related template is null." msgstr "関連するテンプレートが null の場合は認証情報を割り当てることができません。" -#: awx/api/views/__init__.py:485 +#: awx/api/views/__init__.py:504 msgid "Related template cannot accept {} on launch." msgstr "関連するテンプレートは起動時に {} を受け入れません。" -#: awx/api/views/__init__.py:487 +#: awx/api/views/__init__.py:506 msgid "" "Credential that requires user input on launch cannot be used in saved launch " "configuration." msgstr "起動時にユーザー入力を必要とする認証情報は保存された起動設定で使用できません。" -#: awx/api/views/__init__.py:493 +#: awx/api/views/__init__.py:512 msgid "Related template is not configured to accept credentials on launch." msgstr "関連するテンプレートは起動時に認証情報を受け入れるよう設定されていません。" -#: awx/api/views/__init__.py:495 +#: awx/api/views/__init__.py:514 #, python-brace-format msgid "" "This launch configuration already provides a {credential_type} credential." msgstr "この起動設定は {credential_type} 認証情報をすでに指定しています。" -#: awx/api/views/__init__.py:498 +#: awx/api/views/__init__.py:517 #, python-brace-format msgid "Related template already uses {credential_type} credential." msgstr "関連するテンプレートは {credential_type} 認証情報をすでに使用しています。" -#: awx/api/views/__init__.py:516 +#: awx/api/views/__init__.py:535 msgid "Schedule Jobs List" msgstr "スケジュールジョブの一覧" -#: awx/api/views/__init__.py:600 awx/api/views/__init__.py:4452 +#: awx/api/views/__init__.py:619 awx/api/views/__init__.py:4420 msgid "" "You cannot assign an Organization participation role as a child role for a " "Team." msgstr "組織の参加ロールをチームの子ロールとして割り当てることができません。" -#: awx/api/views/__init__.py:604 awx/api/views/__init__.py:4466 +#: awx/api/views/__init__.py:623 awx/api/views/__init__.py:4434 msgid "You cannot grant system-level permissions to a team." msgstr "システムレベルのパーミッションをチームに付与できません。" -#: awx/api/views/__init__.py:611 awx/api/views/__init__.py:4458 +#: awx/api/views/__init__.py:630 awx/api/views/__init__.py:4426 msgid "" "You cannot grant credential access to a team when the Organization field " "isn't set, or belongs to a different organization" msgstr "組織フィールドが設定されていないか、または別の組織に属する場合に認証情報のアクセス権をチームに付与できません" -#: awx/api/views/__init__.py:713 +#: awx/api/views/__init__.py:732 msgid "Project Schedules" msgstr "プロジェクトのスケジュール" -#: awx/api/views/__init__.py:724 +#: awx/api/views/__init__.py:743 msgid "Project SCM Inventory Sources" msgstr "プロジェクト SCM のインベントリーソース" -#: awx/api/views/__init__.py:825 +#: awx/api/views/__init__.py:844 msgid "Project Update Events List" msgstr "プロジェクト更新イベント一覧" -#: awx/api/views/__init__.py:839 +#: awx/api/views/__init__.py:858 msgid "System Job Events List" msgstr "システムジョブイベント一覧" -#: awx/api/views/__init__.py:873 +#: awx/api/views/__init__.py:892 msgid "Project Update SCM Inventory Updates" msgstr "プロジェクト更新 SCM のインベントリー更新" -#: awx/api/views/__init__.py:918 +#: awx/api/views/__init__.py:937 msgid "Me" msgstr "自分" -#: awx/api/views/__init__.py:927 +#: awx/api/views/__init__.py:946 msgid "OAuth 2 Applications" msgstr "OAuth 2 アプリケーション" -#: awx/api/views/__init__.py:936 +#: awx/api/views/__init__.py:955 msgid "OAuth 2 Application Detail" msgstr "OAuth 2 アプリケーションの詳細" -#: awx/api/views/__init__.py:949 +#: awx/api/views/__init__.py:968 msgid "OAuth 2 Application Tokens" msgstr "OAuth 2 アプリケーショントークン" -#: awx/api/views/__init__.py:971 +#: awx/api/views/__init__.py:990 msgid "OAuth2 Tokens" msgstr "OAuth2 トークン" -#: awx/api/views/__init__.py:980 +#: awx/api/views/__init__.py:999 msgid "OAuth2 User Tokens" msgstr "OAuth2 ユーザートークン" -#: awx/api/views/__init__.py:992 +#: awx/api/views/__init__.py:1011 msgid "OAuth2 User Authorized Access Tokens" msgstr "OAuth2 ユーザー認可アクセストークン" -#: awx/api/views/__init__.py:1007 +#: awx/api/views/__init__.py:1026 msgid "Organization OAuth2 Applications" msgstr "組織 OAuth2 アプリケーション" -#: awx/api/views/__init__.py:1019 +#: awx/api/views/__init__.py:1038 msgid "OAuth2 Personal Access Tokens" msgstr "OAuth2 パーソナルアクセストークン" -#: awx/api/views/__init__.py:1034 +#: awx/api/views/__init__.py:1053 msgid "OAuth Token Detail" msgstr "OAuth トークンの詳細" -#: awx/api/views/__init__.py:1096 awx/api/views/__init__.py:4419 +#: awx/api/views/__init__.py:1115 awx/api/views/__init__.py:4387 msgid "" "You cannot grant credential access to a user not in the credentials' " "organization" msgstr "認証情報の組織に属さないユーザーに認証情報のアクセス権を付与することはできません" -#: awx/api/views/__init__.py:1100 awx/api/views/__init__.py:4423 +#: awx/api/views/__init__.py:1119 awx/api/views/__init__.py:4391 msgid "You cannot grant private credential access to another user" msgstr "非公開の認証情報のアクセス権を別のユーザーに付与することはできません" -#: awx/api/views/__init__.py:1198 +#: awx/api/views/__init__.py:1217 #, python-format msgid "Cannot change %s." msgstr "%s を変更できません。" -#: awx/api/views/__init__.py:1204 +#: awx/api/views/__init__.py:1223 msgid "Cannot delete user." msgstr "ユーザーを削除できません。" -#: awx/api/views/__init__.py:1228 +#: awx/api/views/__init__.py:1247 msgid "Deletion not allowed for managed credential types" msgstr "管理されている認証情報タイプで削除は許可されません" -#: awx/api/views/__init__.py:1230 +#: awx/api/views/__init__.py:1249 msgid "Credential types that are in use cannot be deleted" msgstr "使用中の認証情報タイプを削除できません" -#: awx/api/views/__init__.py:1381 +#: awx/api/views/__init__.py:1362 +msgid "Deletion not allowed for managed credentials" +msgstr "管理されている認証情報では削除が許可されません" + +#: awx/api/views/__init__.py:1407 msgid "External Credential Test" msgstr "外部認証情報のテスト" -#: awx/api/views/__init__.py:1408 +#: awx/api/views/__init__.py:1442 msgid "Credential Input Source Detail" msgstr "認証情報の入力ソース詳細" -#: awx/api/views/__init__.py:1416 awx/api/views/__init__.py:1424 +#: awx/api/views/__init__.py:1450 awx/api/views/__init__.py:1458 msgid "Credential Input Sources" msgstr "認証情報の入力ソース" -#: awx/api/views/__init__.py:1439 +#: awx/api/views/__init__.py:1473 msgid "External Credential Type Test" msgstr "外部認証情報の種類テスト" -#: awx/api/views/__init__.py:1497 +#: awx/api/views/__init__.py:1539 msgid "The inventory for this host is already being deleted." msgstr "このホストのインベントリーはすでに削除されています。" -#: awx/api/views/__init__.py:1614 +#: awx/api/views/__init__.py:1656 msgid "SSLError while trying to connect to {}" msgstr "{} への接続試行中に SSL エラーが発生しました" -#: awx/api/views/__init__.py:1616 +#: awx/api/views/__init__.py:1658 msgid "Request to {} timed out." msgstr "{} の要求がタイムアウトになりました。" -#: awx/api/views/__init__.py:1618 +#: awx/api/views/__init__.py:1660 msgid "Unknown exception {} while trying to GET {}" msgstr "GET {} の試行中に不明の例外 {} が発生しました" -#: awx/api/views/__init__.py:1622 +#: awx/api/views/__init__.py:1664 msgid "" "Unauthorized access. Please check your Insights Credential username and " "password." msgstr "不正アクセスです。Insights 認証情報のユーザー名およびパスワードを確認してください。" -#: awx/api/views/__init__.py:1626 +#: awx/api/views/__init__.py:1668 msgid "" "Failed to access the Insights API at URL {}. Server responded with {} status " "code and message {}" msgstr "URL {} で Insights API にアクセスできませんでした。サーバーが {} ステータスコードおよびメッセージ {} を出して応答しました。" -#: awx/api/views/__init__.py:1635 +#: awx/api/views/__init__.py:1677 msgid "Expected JSON response from Insights at URL {} but instead got {}" msgstr "Insights からの JSON 応答を想定していましたが、URL {} の代わりに {} を取得しました。" -#: awx/api/views/__init__.py:1653 +#: awx/api/views/__init__.py:1695 msgid "Could not translate Insights system ID {} into an Insights platform ID." msgstr "Insights システム ID {} から Insights プラットフォーム ID に変換できませんでした。" -#: awx/api/views/__init__.py:1695 +#: awx/api/views/__init__.py:1737 msgid "This host is not recognized as an Insights host." msgstr "このホストは Insights ホストとして認識されていません。" -#: awx/api/views/__init__.py:1703 +#: awx/api/views/__init__.py:1745 msgid "The Insights Credential for \"{}\" was not found." msgstr "\"{}\" の Insights 認証情報が見つかりませんでした。" -#: awx/api/views/__init__.py:1782 +#: awx/api/views/__init__.py:1824 msgid "Cyclical Group association." msgstr "循環的なグループの関連付け" -#: awx/api/views/__init__.py:1948 +#: awx/api/views/__init__.py:1990 msgid "Inventory subset argument must be a string." msgstr "インベントリーサブセットの引数は文字列でなければなりません。" -#: awx/api/views/__init__.py:1952 +#: awx/api/views/__init__.py:1994 msgid "Subset does not use any supported syntax." msgstr "サポートされている構文がサブセットで使用されていません。" -#: awx/api/views/__init__.py:2002 +#: awx/api/views/__init__.py:2044 msgid "Inventory Source List" msgstr "インベントリーソース一覧" -#: awx/api/views/__init__.py:2014 +#: awx/api/views/__init__.py:2056 msgid "Inventory Sources Update" msgstr "インベントリーソースの更新" -#: awx/api/views/__init__.py:2047 +#: awx/api/views/__init__.py:2089 msgid "Could not start because `can_update` returned False" msgstr "`can_update` が False を返したので開始できませんでした" -#: awx/api/views/__init__.py:2055 +#: awx/api/views/__init__.py:2097 msgid "No inventory sources to update." msgstr "更新するインベントリーソースがありません。" -#: awx/api/views/__init__.py:2077 +#: awx/api/views/__init__.py:2119 msgid "Inventory Source Schedules" msgstr "インベントリーソースのスケジュール" -#: awx/api/views/__init__.py:2104 +#: awx/api/views/__init__.py:2146 msgid "Notification Templates can only be assigned when source is one of {}." msgstr "ソースが {} のいずれかである場合、通知テンプレートのみを割り当てることができます。" -#: awx/api/views/__init__.py:2202 +#: awx/api/views/__init__.py:2244 msgid "Source already has credential assigned." msgstr "ソースには認証情報がすでに割り当てられています。" -#: awx/api/views/__init__.py:2350 -msgid "'credentials' cannot be used in combination with 'extra_credentials'." -msgstr "'credentials' は、'extra_credentials' と組み合わせて使用することはできません。" - -#: awx/api/views/__init__.py:2368 -msgid "Incorrect type. Expected a list received {}." -msgstr "タイプが正しくありません。リストが必要でしたが、{} が受信されました。" - -#: awx/api/views/__init__.py:2466 +#: awx/api/views/__init__.py:2460 msgid "Job Template Schedules" msgstr "ジョブテンプレートスケジュール" -#: awx/api/views/__init__.py:2515 +#: awx/api/views/__init__.py:2509 msgid "Field '{}' is missing from survey spec." msgstr "Survey の指定にフィールド '{}' がありません。" -#: awx/api/views/__init__.py:2517 +#: awx/api/views/__init__.py:2511 msgid "Expected {} for field '{}', received {} type." msgstr "フィールド '{}' の予期される {}。{} タイプを受信しました。" -#: awx/api/views/__init__.py:2521 +#: awx/api/views/__init__.py:2515 msgid "'spec' doesn't contain any items." msgstr "「spec」には項目が含まれません。" -#: awx/api/views/__init__.py:2535 +#: awx/api/views/__init__.py:2529 #, python-format msgid "Survey question %s is not a json object." msgstr "Survey の質問 %s は json オブジェクトではありません。" -#: awx/api/views/__init__.py:2538 +#: awx/api/views/__init__.py:2532 #, python-brace-format msgid "'{field_name}' missing from survey question {idx}" msgstr "Survey の質問 {idx} に '{field_name}' がありません。" -#: awx/api/views/__init__.py:2548 +#: awx/api/views/__init__.py:2542 #, python-brace-format msgid "'{field_name}' in survey question {idx} expected to be {type_label}." msgstr "Survey の質問 {idx} の '{field_name}' は {type_label} である必要があります。" -#: awx/api/views/__init__.py:2552 +#: awx/api/views/__init__.py:2546 #, python-format msgid "'variable' '%(item)s' duplicated in survey question %(survey)s." msgstr "Survey の質問 %(survey)s で '変数' '%(item)s' が重複しています。" -#: awx/api/views/__init__.py:2562 +#: awx/api/views/__init__.py:2556 #, python-brace-format msgid "" "'{survey_item[type]}' in survey question {idx} is not one of " "'{allowed_types}' allowed question types." msgstr "Survey の質問 {idx} の '{survey_item[type]}' は、'{allowed_types}' で許可されている質問タイプではありません。" -#: awx/api/views/__init__.py:2572 +#: awx/api/views/__init__.py:2566 #, python-brace-format msgid "" "Default value {survey_item[default]} in survey question {idx} expected to be " "{type_label}." msgstr "Survey の質問 {idx} のデフォルト値 {survey_item[default]} は、{type_label} である必要があります。" -#: awx/api/views/__init__.py:2582 +#: awx/api/views/__init__.py:2576 #, python-brace-format msgid "The {min_or_max} limit in survey question {idx} expected to be integer." msgstr "Survey の質問 {idx} の {min_or_max} の制限は整数である必要があります。" -#: awx/api/views/__init__.py:2592 +#: awx/api/views/__init__.py:2586 #, python-brace-format msgid "Survey question {idx} of type {survey_item[type]} must specify choices." msgstr "タイプ {survey_item[type]} の Survey の質問 {idx} には選択肢を指定する必要があります。" -#: awx/api/views/__init__.py:2606 +#: awx/api/views/__init__.py:2600 msgid "Multiple Choice (Single Select) can only have one default value." msgstr "選択肢方式 (単一の選択) では、デフォルト値を 1 つだけ使用できます。" -#: awx/api/views/__init__.py:2610 +#: awx/api/views/__init__.py:2604 msgid "Default choice must be answered from the choices listed." msgstr "デフォルトで指定されている選択項目は、一覧から回答する必要があります。" -#: awx/api/views/__init__.py:2619 +#: awx/api/views/__init__.py:2613 #, python-brace-format msgid "" "$encrypted$ is a reserved keyword for password question defaults, survey " "question {idx} is type {survey_item[type]}." msgstr "$encrypted$ は、デフォルト設定されているパスワードの質問に予約されたキーワードで、Survey の質問 {idx} は {survey_item[type]} タイプです。" -#: awx/api/views/__init__.py:2633 +#: awx/api/views/__init__.py:2627 #, python-brace-format msgid "" "$encrypted$ is a reserved keyword, may not be used for new default in " "position {idx}." msgstr "$encrypted$ は予約されたキーワードで、位置 {idx} の新規デフォルトに使用できません。" -#: awx/api/views/__init__.py:2705 +#: awx/api/views/__init__.py:2699 #, python-brace-format msgid "Cannot assign multiple {credential_type} credentials." msgstr "複数の {credential_type} 認証情報を割り当てることができません。" -#: awx/api/views/__init__.py:2709 +#: awx/api/views/__init__.py:2703 msgid "Cannot assign a Credential of kind `{}`." msgstr "`{}`の種類の認証情報を割り当てることができません。" #: awx/api/views/__init__.py:2726 -msgid "Extra credentials must be network or cloud." -msgstr "追加の認証情報はネットワークまたはクラウドにする必要があります。" - -#: awx/api/views/__init__.py:2748 msgid "Maximum number of labels for {} reached." msgstr "{} のラベルの最大数に達しました。" -#: awx/api/views/__init__.py:2871 +#: awx/api/views/__init__.py:2849 msgid "No matching host could be found!" msgstr "一致するホストが見つかりませんでした!" -#: awx/api/views/__init__.py:2874 +#: awx/api/views/__init__.py:2852 msgid "Multiple hosts matched the request!" msgstr "複数のホストが要求に一致しました!" -#: awx/api/views/__init__.py:2879 +#: awx/api/views/__init__.py:2857 msgid "Cannot start automatically, user input required!" msgstr "自動的に開始できません。ユーザー入力が必要です!" -#: awx/api/views/__init__.py:2887 +#: awx/api/views/__init__.py:2865 msgid "Host callback job already pending." msgstr "ホストのコールバックジョブがすでに保留中です。" -#: awx/api/views/__init__.py:2903 awx/api/views/__init__.py:3664 +#: awx/api/views/__init__.py:2881 awx/api/views/__init__.py:3632 msgid "Error starting job!" msgstr "ジョブの開始時にエラーが発生しました!" -#: awx/api/views/__init__.py:3027 awx/api/views/__init__.py:3047 +#: awx/api/views/__init__.py:3005 awx/api/views/__init__.py:3025 msgid "Cycle detected." msgstr "サイクルが検出されました。" -#: awx/api/views/__init__.py:3039 +#: awx/api/views/__init__.py:3017 msgid "Relationship not allowed." msgstr "リレーションシップは許可されていません。" -#: awx/api/views/__init__.py:3268 +#: awx/api/views/__init__.py:3246 msgid "Cannot relaunch slice workflow job orphaned from job template." msgstr "ジョブテンプレートから孤立しているスライスされたワークフロージョブを再起動することはできません。" -#: awx/api/views/__init__.py:3270 +#: awx/api/views/__init__.py:3248 msgid "Cannot relaunch sliced workflow job after slice count has changed." msgstr "スライス数を変更した後は、スライスされたワークフロージョブを再起動することはできません。" -#: awx/api/views/__init__.py:3303 +#: awx/api/views/__init__.py:3281 msgid "Workflow Job Template Schedules" msgstr "ワークフロージョブテンプレートのスケジュール" -#: awx/api/views/__init__.py:3446 awx/api/views/__init__.py:4087 +#: awx/api/views/__init__.py:3424 awx/api/views/__init__.py:4055 msgid "Superuser privileges needed." msgstr "スーパーユーザー権限が必要です。" -#: awx/api/views/__init__.py:3479 +#: awx/api/views/__init__.py:3457 msgid "System Job Template Schedules" msgstr "システムジョブテンプレートのスケジュール" -#: awx/api/views/__init__.py:3647 +#: awx/api/views/__init__.py:3615 #, python-brace-format msgid "Wait until job finishes before retrying on {status_value} hosts." msgstr "ジョブの終了を待機してから {status_value} ホストで再試行します。" -#: awx/api/views/__init__.py:3652 +#: awx/api/views/__init__.py:3620 #, python-brace-format msgid "Cannot retry on {status_value} hosts, playbook stats not available." msgstr "Playbook 統計を利用できないため、{status_value} ホストで再試行できません。" -#: awx/api/views/__init__.py:3657 +#: awx/api/views/__init__.py:3625 #, python-brace-format msgid "Cannot relaunch because previous job had 0 {status_value} hosts." msgstr "直前のジョブにあるのが 0 {status_value} ホストがあるため、再起動できません。" -#: awx/api/views/__init__.py:3686 +#: awx/api/views/__init__.py:3654 msgid "Cannot create schedule because job requires credential passwords." msgstr "ジョブには認証情報パスワードが必要なため、スケジュールを削除できません。" -#: awx/api/views/__init__.py:3691 +#: awx/api/views/__init__.py:3659 msgid "Cannot create schedule because job was launched by legacy method." msgstr "ジョブがレガシー方式で起動したため、スケジュールを作成できません。" -#: awx/api/views/__init__.py:3693 +#: awx/api/views/__init__.py:3661 msgid "Cannot create schedule because a related resource is missing." msgstr "関連するリソースがないため、スケジュールを作成できません。" -#: awx/api/views/__init__.py:3748 +#: awx/api/views/__init__.py:3716 msgid "Job Host Summaries List" msgstr "ジョブホスト概要一覧" -#: awx/api/views/__init__.py:3802 +#: awx/api/views/__init__.py:3770 msgid "Job Event Children List" msgstr "ジョブイベント子一覧" -#: awx/api/views/__init__.py:3818 +#: awx/api/views/__init__.py:3786 msgid "Job Event Hosts List" msgstr "ジョブイベントホスト一覧" -#: awx/api/views/__init__.py:3833 +#: awx/api/views/__init__.py:3801 msgid "Job Events List" msgstr "ジョブイベント一覧" -#: awx/api/views/__init__.py:4044 +#: awx/api/views/__init__.py:4012 msgid "Ad Hoc Command Events List" msgstr "アドホックコマンドイベント一覧" -#: awx/api/views/__init__.py:4289 +#: awx/api/views/__init__.py:4257 msgid "Delete not allowed while there are pending notifications" msgstr "保留中の通知がある場合に削除は許可されません" -#: awx/api/views/__init__.py:4297 +#: awx/api/views/__init__.py:4265 msgid "Notification Template Test" msgstr "通知テンプレートテスト" -#: awx/api/views/__init__.py:4557 awx/api/views/__init__.py:4572 +#: awx/api/views/__init__.py:4525 awx/api/views/__init__.py:4540 msgid "User does not have permission to approve or deny this workflow." msgstr "このワークフローを承認または拒否するパーミッションはありません。" -#: awx/api/views/__init__.py:4559 awx/api/views/__init__.py:4574 +#: awx/api/views/__init__.py:4527 awx/api/views/__init__.py:4542 msgid "This workflow step has already been approved or denied." msgstr "このワークフローの手順はすでに承認または拒否されています。" @@ -1393,7 +1415,11 @@ msgstr "インベントリー更新イベント一覧" msgid "Cannot delete inventory script." msgstr "インベントリースクリプトを削除できません。" -#: awx/api/views/inventory.py:149 +#: awx/api/views/inventory.py:137 +msgid "You cannot turn a regular inventory into a \"smart\" inventory." +msgstr "通常の在庫を「スマート」在庫に変えることはできません。" + +#: awx/api/views/inventory.py:150 #, python-brace-format msgid "{0}" msgstr "{0}" @@ -1418,71 +1444,76 @@ msgstr "ジョブはイベント処理を終了していません。" msgid "Related job {} is still processing events." msgstr "関連するジョブ {} は依然としてイベントを処理しています。" -#: awx/api/views/root.py:49 awx/templates/rest_framework/api.html:28 +#: awx/api/views/organization.py:230 +#, python-brace-format +msgid "Credential must be a Galaxy credential, not {sub.credential_type.name}." +msgstr "認証情報は、{sub.credential_type.name} ではなく、Galaxy 認証情報にする必要があります。" + +#: awx/api/views/root.py:50 awx/templates/rest_framework/api.html:28 msgid "REST API" msgstr "REST API" -#: awx/api/views/root.py:59 awx/templates/rest_framework/api.html:4 +#: awx/api/views/root.py:60 awx/templates/rest_framework/api.html:4 msgid "AWX REST API" msgstr "AWX REST API" -#: awx/api/views/root.py:72 +#: awx/api/views/root.py:73 msgid "API OAuth 2 Authorization Root" msgstr "API OAuth 2 認証ルート" -#: awx/api/views/root.py:139 +#: awx/api/views/root.py:140 msgid "Version 2" msgstr "バージョン 2" -#: awx/api/views/root.py:148 +#: awx/api/views/root.py:149 msgid "Ping" msgstr "Ping" -#: awx/api/views/root.py:180 awx/api/views/root.py:225 awx/conf/apps.py:10 +#: awx/api/views/root.py:181 awx/api/views/root.py:226 awx/conf/apps.py:10 msgid "Configuration" msgstr "Configuration (構成)" -#: awx/api/views/root.py:202 awx/api/views/root.py:308 +#: awx/api/views/root.py:203 awx/api/views/root.py:310 msgid "Invalid License" msgstr "無効なライセンス" -#: awx/api/views/root.py:207 +#: awx/api/views/root.py:208 msgid "The provided credentials are invalid (HTTP 401)." msgstr "指定した認証情報は無効 (HTTP 401) です。" -#: awx/api/views/root.py:209 +#: awx/api/views/root.py:210 msgid "Unable to connect to proxy server." msgstr "プロキシサーバーに接続できません。" -#: awx/api/views/root.py:211 +#: awx/api/views/root.py:212 msgid "Could not connect to subscription service." msgstr "サブスクリプションサービスに接続できませんでした。" -#: awx/api/views/root.py:284 +#: awx/api/views/root.py:286 msgid "Invalid license data" msgstr "無効なライセンスデータ" -#: awx/api/views/root.py:286 +#: awx/api/views/root.py:288 msgid "Missing 'eula_accepted' property" msgstr "'eula_accepted' プロパティーがありません" -#: awx/api/views/root.py:290 +#: awx/api/views/root.py:292 msgid "'eula_accepted' value is invalid" msgstr "'eula_accepted' 値は無効です。" -#: awx/api/views/root.py:293 +#: awx/api/views/root.py:295 msgid "'eula_accepted' must be True" msgstr "'eula_accepted' は True でなければなりません" -#: awx/api/views/root.py:300 +#: awx/api/views/root.py:302 msgid "Invalid JSON" msgstr "無効な JSON" -#: awx/api/views/root.py:319 +#: awx/api/views/root.py:321 msgid "Invalid license" msgstr "無効なライセンス" -#: awx/api/views/root.py:327 +#: awx/api/views/root.py:329 msgid "Failed to remove license." msgstr "ライセンスを削除できませんでした。" @@ -1673,11 +1704,11 @@ msgstr "\"{input}\" は有効な文字列ではありません。" msgid "Expected a list of tuples of max length 2 but got {input_type} instead." msgstr "最大の長さが 2 のタプルの一覧が予想されましたが、代わりに {input_type} を取得しました。" -#: awx/conf/registry.py:73 awx/conf/tests/unit/test_registry.py:155 +#: awx/conf/registry.py:73 awx/conf/tests/unit/test_registry.py:156 msgid "All" msgstr "すべて" -#: awx/conf/registry.py:74 awx/conf/tests/unit/test_registry.py:156 +#: awx/conf/registry.py:74 awx/conf/tests/unit/test_registry.py:157 msgid "Changed" msgstr "変更済み" @@ -1689,55 +1720,53 @@ msgstr "ユーザー設定" msgid "This value has been set manually in a settings file." msgstr "この値は設定ファイルに手動で設定されました。" -#: awx/conf/tests/unit/test_registry.py:46 -#: awx/conf/tests/unit/test_registry.py:56 -#: awx/conf/tests/unit/test_registry.py:72 -#: awx/conf/tests/unit/test_registry.py:87 -#: awx/conf/tests/unit/test_registry.py:100 -#: awx/conf/tests/unit/test_registry.py:106 -#: awx/conf/tests/unit/test_registry.py:126 -#: awx/conf/tests/unit/test_registry.py:132 -#: awx/conf/tests/unit/test_registry.py:145 -#: awx/conf/tests/unit/test_registry.py:157 -#: awx/conf/tests/unit/test_registry.py:166 -#: awx/conf/tests/unit/test_registry.py:172 -#: awx/conf/tests/unit/test_registry.py:184 -#: awx/conf/tests/unit/test_registry.py:191 -#: awx/conf/tests/unit/test_registry.py:233 -#: awx/conf/tests/unit/test_registry.py:251 -#: awx/conf/tests/unit/test_settings.py:79 -#: awx/conf/tests/unit/test_settings.py:97 -#: awx/conf/tests/unit/test_settings.py:112 -#: awx/conf/tests/unit/test_settings.py:127 -#: awx/conf/tests/unit/test_settings.py:143 -#: awx/conf/tests/unit/test_settings.py:156 -#: awx/conf/tests/unit/test_settings.py:173 -#: awx/conf/tests/unit/test_settings.py:189 -#: awx/conf/tests/unit/test_settings.py:200 -#: awx/conf/tests/unit/test_settings.py:216 -#: awx/conf/tests/unit/test_settings.py:237 -#: awx/conf/tests/unit/test_settings.py:259 -#: awx/conf/tests/unit/test_settings.py:285 -#: awx/conf/tests/unit/test_settings.py:299 -#: awx/conf/tests/unit/test_settings.py:323 +#: awx/conf/tests/unit/test_registry.py:47 +#: awx/conf/tests/unit/test_registry.py:57 +#: awx/conf/tests/unit/test_registry.py:73 +#: awx/conf/tests/unit/test_registry.py:88 +#: awx/conf/tests/unit/test_registry.py:101 +#: awx/conf/tests/unit/test_registry.py:107 +#: awx/conf/tests/unit/test_registry.py:127 +#: awx/conf/tests/unit/test_registry.py:133 +#: awx/conf/tests/unit/test_registry.py:146 +#: awx/conf/tests/unit/test_registry.py:158 +#: awx/conf/tests/unit/test_registry.py:167 +#: awx/conf/tests/unit/test_registry.py:173 +#: awx/conf/tests/unit/test_registry.py:185 +#: awx/conf/tests/unit/test_registry.py:192 +#: awx/conf/tests/unit/test_registry.py:234 +#: awx/conf/tests/unit/test_registry.py:252 +#: awx/conf/tests/unit/test_settings.py:73 +#: awx/conf/tests/unit/test_settings.py:91 +#: awx/conf/tests/unit/test_settings.py:106 +#: awx/conf/tests/unit/test_settings.py:121 +#: awx/conf/tests/unit/test_settings.py:137 +#: awx/conf/tests/unit/test_settings.py:150 +#: awx/conf/tests/unit/test_settings.py:167 +#: awx/conf/tests/unit/test_settings.py:183 +#: awx/conf/tests/unit/test_settings.py:194 +#: awx/conf/tests/unit/test_settings.py:210 +#: awx/conf/tests/unit/test_settings.py:231 +#: awx/conf/tests/unit/test_settings.py:254 +#: awx/conf/tests/unit/test_settings.py:268 +#: awx/conf/tests/unit/test_settings.py:292 +#: awx/conf/tests/unit/test_settings.py:312 +#: awx/conf/tests/unit/test_settings.py:329 #: awx/conf/tests/unit/test_settings.py:343 -#: awx/conf/tests/unit/test_settings.py:360 -#: awx/conf/tests/unit/test_settings.py:374 -#: awx/conf/tests/unit/test_settings.py:398 -#: awx/conf/tests/unit/test_settings.py:411 -#: awx/conf/tests/unit/test_settings.py:430 -#: awx/conf/tests/unit/test_settings.py:466 awx/main/conf.py:24 -#: awx/main/conf.py:33 awx/main/conf.py:43 awx/main/conf.py:53 -#: awx/main/conf.py:65 awx/main/conf.py:78 awx/main/conf.py:91 -#: awx/main/conf.py:116 awx/main/conf.py:129 awx/main/conf.py:142 -#: awx/main/conf.py:154 awx/main/conf.py:162 awx/main/conf.py:173 -#: awx/main/conf.py:405 awx/main/conf.py:830 awx/main/conf.py:840 -#: awx/main/conf.py:852 +#: awx/conf/tests/unit/test_settings.py:367 +#: awx/conf/tests/unit/test_settings.py:380 +#: awx/conf/tests/unit/test_settings.py:399 +#: awx/conf/tests/unit/test_settings.py:435 awx/main/conf.py:23 +#: awx/main/conf.py:32 awx/main/conf.py:42 awx/main/conf.py:52 +#: awx/main/conf.py:64 awx/main/conf.py:77 awx/main/conf.py:90 +#: awx/main/conf.py:115 awx/main/conf.py:128 awx/main/conf.py:141 +#: awx/main/conf.py:153 awx/main/conf.py:161 awx/main/conf.py:172 +#: awx/main/conf.py:392 awx/main/conf.py:742 awx/main/conf.py:754 msgid "System" msgstr "システム" -#: awx/conf/tests/unit/test_registry.py:151 -#: awx/conf/tests/unit/test_registry.py:158 +#: awx/conf/tests/unit/test_registry.py:152 +#: awx/conf/tests/unit/test_registry.py:159 msgid "OtherSystem" msgstr "他のシステム" @@ -1804,112 +1833,172 @@ msgstr "異なるインベントリーの 2 つの項目を関連付けること msgid "Unable to change inventory on a group." msgstr "グループのインベントリーを変更できません。" -#: awx/main/access.py:1264 +#: awx/main/access.py:1261 msgid "Unable to change organization on a team." msgstr "チームの組織を変更できません。" -#: awx/main/access.py:1280 +#: awx/main/access.py:1277 msgid "The {} role cannot be assigned to a team" msgstr "{} ロールをチームに割り当てることができません" -#: awx/main/access.py:1474 +#: awx/main/access.py:1471 msgid "Insufficient access to Job Template credentials." msgstr "ジョブテンプレート認証情報へのアクセス権がありません。" -#: awx/main/access.py:1639 awx/main/access.py:2063 +#: awx/main/access.py:1635 awx/main/access.py:2059 msgid "Job was launched with secret prompts provided by another user." msgstr "別のユーザーのシークレットプロンプトで、ジョブが起動しました。" -#: awx/main/access.py:1648 +#: awx/main/access.py:1644 msgid "Job has been orphaned from its job template and organization." msgstr "ジョブはジョブテンプレートおよび組織から孤立しています。" -#: awx/main/access.py:1650 +#: awx/main/access.py:1646 msgid "Job was launched with prompted fields you do not have access to." msgstr "アクセス権のないプロンプトフィールドでジョブが起動されました。" -#: awx/main/access.py:1652 +#: awx/main/access.py:1648 msgid "" "Job was launched with unknown prompted fields. Organization admin " "permissions required." msgstr "不明なプロンプトフィールドでジョブが起動されました。組織の管理者権限が必要です。" -#: awx/main/access.py:2053 +#: awx/main/access.py:2049 msgid "Workflow Job was launched with unknown prompts." msgstr "ワークフロージョブは不明なプロンプトで起動されています。" -#: awx/main/access.py:2065 +#: awx/main/access.py:2061 msgid "Job was launched with prompts you lack access to." msgstr "ジョブはアクセスできないプロンプトで起動されています。" -#: awx/main/access.py:2067 +#: awx/main/access.py:2063 msgid "Job was launched with prompts no longer accepted." msgstr "ジョブは受け入れられなくなったプロンプトで起動されています。" -#: awx/main/access.py:2079 +#: awx/main/access.py:2075 msgid "" "You do not have permission to the workflow job resources required for " "relaunch." msgstr "再起動に必要なワークフロージョブリソースへのパーミッションがありません。" +#: awx/main/analytics/collectors.py:36 +msgid "General platform configuration." +msgstr "一般的なプラットフォーム構成。" + +#: awx/main/analytics/collectors.py:68 +msgid "Counts of objects such as organizations, inventories, and projects" +msgstr "組織、在庫、プロジェクトなどのオブジェクトの数" + +#: awx/main/analytics/collectors.py:103 +msgid "Counts of users and teams by organization" +msgstr "組織別のユーザーとチームの数" + +#: awx/main/analytics/collectors.py:115 +msgid "Counts of credentials by credential type" +msgstr "認証情報タイプ別の認証情報の数" + +#: awx/main/analytics/collectors.py:127 +msgid "Inventories, their inventory sources, and host counts" +msgstr "インベントリー、そのインベントリソース、およびホスト数" + +#: awx/main/analytics/collectors.py:152 +msgid "Counts of projects by source control type" +msgstr "ソース管理タイプ別のプロジェクト数" + +#: awx/main/analytics/collectors.py:171 +msgid "Cluster topology and capacity" +msgstr "クラスターのトポロジーと容量" + +#: awx/main/analytics/collectors.py:197 +msgid "Counts of jobs by status" +msgstr "ステータス別のジョブ数" + +#: awx/main/analytics/collectors.py:207 +msgid "Counts of jobs by execution node" +msgstr "実行ノードごとのジョブ数" + +#: awx/main/analytics/collectors.py:222 +msgid "Metadata about the analytics collected" +msgstr "収集された分析に関するメタデータ" + +#: awx/main/analytics/collectors.py:285 +msgid "Automation task records" +msgstr "自動化タスクレコード" + +#: awx/main/analytics/collectors.py:314 +msgid "Data on jobs run" +msgstr "実行されたジョブに関するデータ" + +#: awx/main/analytics/collectors.py:351 +msgid "Data on job templates" +msgstr "ジョブテンプレートに関するデータ" + +#: awx/main/analytics/collectors.py:374 +msgid "Data on workflow runs" +msgstr "ワークフロー実行に関するデータ" + +#: awx/main/analytics/collectors.py:410 +msgid "Data on workflows" +msgstr "ワークフローに関するデータ" + #: awx/main/apps.py:8 msgid "Main" msgstr "メイン" -#: awx/main/conf.py:22 +#: awx/main/conf.py:21 msgid "Enable Activity Stream" msgstr "アクティビティーストリームの有効化" -#: awx/main/conf.py:23 +#: awx/main/conf.py:22 msgid "Enable capturing activity for the activity stream." msgstr "アクティビティーストリームのアクティビティーのキャプチャーを有効にします。" -#: awx/main/conf.py:31 +#: awx/main/conf.py:30 msgid "Enable Activity Stream for Inventory Sync" msgstr "インベントリー同期のアクティビティティーストリームの有効化" -#: awx/main/conf.py:32 +#: awx/main/conf.py:31 msgid "" "Enable capturing activity for the activity stream when running inventory " "sync." msgstr "インベントリー同期の実行時にアクティビティーストリームのアクティビティーのキャプチャーを有効にします。" -#: awx/main/conf.py:40 +#: awx/main/conf.py:39 msgid "All Users Visible to Organization Admins" msgstr "組織管理者に表示されるすべてのユーザー" -#: awx/main/conf.py:41 +#: awx/main/conf.py:40 msgid "" "Controls whether any Organization Admin can view all users and teams, even " "those not associated with their Organization." msgstr "組織管理者が、それぞれの組織に関連付けられていないすべてのユーザーおよびチームを閲覧できるかどうかを制御します。" -#: awx/main/conf.py:50 +#: awx/main/conf.py:49 msgid "Organization Admins Can Manage Users and Teams" msgstr "組織管理者はユーザーおよびチームを管理できる" -#: awx/main/conf.py:51 +#: awx/main/conf.py:50 msgid "" "Controls whether any Organization Admin has the privileges to create and " "manage users and teams. You may want to disable this ability if you are " "using an LDAP or SAML integration." msgstr "組織管理者がユーザーおよびチームを作成し、管理する権限を持つようにするかどうかを制御します。LDAP または SAML 統合を使用している場合はこの機能を無効にする必要がある場合があります。" -#: awx/main/conf.py:62 +#: awx/main/conf.py:61 msgid "Base URL of the Tower host" msgstr "Tower ホストのベース URL" -#: awx/main/conf.py:63 +#: awx/main/conf.py:62 msgid "" "This setting is used by services like notifications to render a valid url to " "the Tower host." msgstr "この設定は、有効な URL を Tower ホストにレンダリングする通知などのサービスで使用されます。" -#: awx/main/conf.py:72 +#: awx/main/conf.py:71 msgid "Remote Host Headers" msgstr "リモートホストヘッダー" -#: awx/main/conf.py:73 +#: awx/main/conf.py:72 msgid "" "HTTP headers and meta keys to search to determine remote host name or IP. " "Add additional items to this list, such as \"HTTP_X_FORWARDED_FOR\", if " @@ -1917,114 +2006,112 @@ msgid "" "Adminstrator guide for more details." msgstr "リモートホスト名または IP を判別するために検索する HTTP ヘッダーおよびメタキーです。リバースプロキシーの後ろの場合は、\"HTTP_X_FORWARDED_FOR\" のように項目をこの一覧に追加します。詳細は、Administrator Guide の「Proxy Support」セクションを参照してください。" -#: awx/main/conf.py:85 -msgid "Proxy IP Whitelist" -msgstr "プロキシー IP ホワイトリスト" +#: awx/main/conf.py:84 +msgid "Proxy IP Allowed List" +msgstr "プロキシ IP 許可リスト" -#: awx/main/conf.py:86 +#: awx/main/conf.py:85 msgid "" "If Tower is behind a reverse proxy/load balancer, use this setting to " -"whitelist the proxy IP addresses from which Tower should trust custom " +"configure the proxy IP addresses from which Tower should trust custom " "REMOTE_HOST_HEADERS header values. If this setting is an empty list (the " "default), the headers specified by REMOTE_HOST_HEADERS will be trusted " "unconditionally')" -msgstr "Tower がリバースプロキシー/ロードバランサーの背後にある場合、この設定を使用し、Tower がカスタム REMOTE_HOST_HEADERS ヘッダーの値を信頼するのに使用するプロキシー IP アドレスをホワイトリスト化します。この設定が空のリスト (デフォルト) の場合、REMOTE_HOST_HEADERS で指定されるヘッダーは条件なしに信頼されます')" +msgstr "Tower がリバースプロキシー/ロードバランサーの背後にある場合は、この設定を使用して、Tower がカスタム REMOTE_HOST_HEADERS ヘッダーの値を信頼するのに使用するプロキシー IP アドレスを設定します。この設定が空のリスト (デフォルト) の場合は、REMOTE_HOST_HEADERS で指定されるヘッダーは条件なしに信頼されます')" -#: awx/main/conf.py:112 +#: awx/main/conf.py:111 msgid "License" msgstr "ライセンス" -#: awx/main/conf.py:113 +#: awx/main/conf.py:112 msgid "" "The license controls which features and functionality are enabled. Use /api/" "v2/config/ to update or change the license." msgstr "ライセンスで、どの特徴および機能を有効にするかを管理します。/api/v2/config/ を使用してライセンスを更新または変更してください。" -#: awx/main/conf.py:127 +#: awx/main/conf.py:126 msgid "Red Hat customer username" msgstr "Red Hat のお客様ユーザー名" -#: awx/main/conf.py:128 +#: awx/main/conf.py:127 msgid "" "This username is used to retrieve license information and to send Automation " "Analytics" msgstr "このユーザー名を使用して、ライセンス情報の受信、自動化アナリティクスの送信を行います。" -#: awx/main/conf.py:140 +#: awx/main/conf.py:139 msgid "Red Hat customer password" msgstr "Red Hat のお客様パスワード" -#: awx/main/conf.py:141 +#: awx/main/conf.py:140 msgid "" "This password is used to retrieve license information and to send Automation " "Analytics" msgstr "このパスワードを使用して、ライセンス情報の受信、自動化アナリティクスの送信を行います。" -#: awx/main/conf.py:152 +#: awx/main/conf.py:151 msgid "Automation Analytics upload URL." msgstr "自動化アナリティクスのアップロード用 URL" -#: awx/main/conf.py:153 +#: awx/main/conf.py:152 msgid "" "This setting is used to to configure data collection for the Automation " "Analytics dashboard" msgstr "この設定は、自動化アナリティクスダッシュボードのデータ収集を設定するのに使用します。" -#: awx/main/conf.py:161 +#: awx/main/conf.py:160 msgid "Unique identifier for an AWX/Tower installation" msgstr "AWX/Tower インストールの一意識別子" -#: awx/main/conf.py:170 +#: awx/main/conf.py:169 msgid "Custom virtual environment paths" msgstr "カスタムの仮想環境パス" -#: awx/main/conf.py:171 +#: awx/main/conf.py:170 msgid "" "Paths where Tower will look for custom virtual environments (in addition to /" "var/lib/awx/venv/). Enter one path per line." msgstr "Tower が (/var/lib/awx/venv/ 以外に) カスタムの仮想環境を検索するパス。1 行にパスを 1 つ入力してください。" -#: awx/main/conf.py:181 +#: awx/main/conf.py:180 msgid "Ansible Modules Allowed for Ad Hoc Jobs" msgstr "アドホックジョブで許可される Ansible モジュール" -#: awx/main/conf.py:182 +#: awx/main/conf.py:181 msgid "List of modules allowed to be used by ad-hoc jobs." msgstr "アドホックジョブで使用できるモジュール一覧。" -#: awx/main/conf.py:183 awx/main/conf.py:205 awx/main/conf.py:214 -#: awx/main/conf.py:225 awx/main/conf.py:235 awx/main/conf.py:245 -#: awx/main/conf.py:256 awx/main/conf.py:267 awx/main/conf.py:278 -#: awx/main/conf.py:290 awx/main/conf.py:299 awx/main/conf.py:312 -#: awx/main/conf.py:325 awx/main/conf.py:337 awx/main/conf.py:348 -#: awx/main/conf.py:359 awx/main/conf.py:371 awx/main/conf.py:383 -#: awx/main/conf.py:394 awx/main/conf.py:414 awx/main/conf.py:424 -#: awx/main/conf.py:434 awx/main/conf.py:450 awx/main/conf.py:463 -#: awx/main/conf.py:477 awx/main/conf.py:491 awx/main/conf.py:503 -#: awx/main/conf.py:513 awx/main/conf.py:524 awx/main/conf.py:534 -#: awx/main/conf.py:545 awx/main/conf.py:555 awx/main/conf.py:565 -#: awx/main/conf.py:577 awx/main/conf.py:589 awx/main/conf.py:601 -#: awx/main/conf.py:615 awx/main/conf.py:627 +#: awx/main/conf.py:182 awx/main/conf.py:204 awx/main/conf.py:213 +#: awx/main/conf.py:224 awx/main/conf.py:234 awx/main/conf.py:244 +#: awx/main/conf.py:254 awx/main/conf.py:265 awx/main/conf.py:277 +#: awx/main/conf.py:286 awx/main/conf.py:299 awx/main/conf.py:312 +#: awx/main/conf.py:324 awx/main/conf.py:335 awx/main/conf.py:346 +#: awx/main/conf.py:358 awx/main/conf.py:370 awx/main/conf.py:381 +#: awx/main/conf.py:401 awx/main/conf.py:411 awx/main/conf.py:421 +#: awx/main/conf.py:434 awx/main/conf.py:445 awx/main/conf.py:455 +#: awx/main/conf.py:466 awx/main/conf.py:476 awx/main/conf.py:486 +#: awx/main/conf.py:498 awx/main/conf.py:510 awx/main/conf.py:522 +#: awx/main/conf.py:536 awx/main/conf.py:548 msgid "Jobs" msgstr "ジョブ" -#: awx/main/conf.py:192 +#: awx/main/conf.py:191 msgid "Always" msgstr "常時" -#: awx/main/conf.py:193 +#: awx/main/conf.py:192 msgid "Never" msgstr "なし" -#: awx/main/conf.py:194 +#: awx/main/conf.py:193 msgid "Only On Job Template Definitions" msgstr "ジョブテンプレートの定義のみ" -#: awx/main/conf.py:197 +#: awx/main/conf.py:196 msgid "When can extra variables contain Jinja templates?" msgstr "いつ追加変数に Jinja テンプレートが含まれるか?" -#: awx/main/conf.py:199 +#: awx/main/conf.py:198 msgid "" "Ansible allows variable substitution via the Jinja2 templating language for " "--extra-vars. This poses a potential security risk where Tower users with " @@ -2033,358 +2120,294 @@ msgid "" "to \"template\" or \"never\"." msgstr "Ansible は Jinja2 テンプレート言語を使用した --extra-vars の変数置き換えを許可します。これにより、ジョブの起動時に追加変数を指定できる Tower ユーザーが Jinja2 テンプレートを使用して任意の Python を実行できるようになるためセキュリティー上のリスクが生じます。この値は「template」または「never」に設定することをお勧めします。" -#: awx/main/conf.py:212 +#: awx/main/conf.py:211 msgid "Enable job isolation" msgstr "ジョブの分離の有効化" -#: awx/main/conf.py:213 +#: awx/main/conf.py:212 msgid "" "Isolates an Ansible job from protected parts of the system to prevent " "exposing sensitive information." msgstr "機密情報の公開を防ぐためにシステムの保護された部分から Ansible ジョブを分離します。" -#: awx/main/conf.py:221 +#: awx/main/conf.py:220 msgid "Job execution path" msgstr "ジョブの実行パス" -#: awx/main/conf.py:222 +#: awx/main/conf.py:221 msgid "" "The directory in which Tower will create new temporary directories for job " "execution and isolation (such as credential files and custom inventory " "scripts)." msgstr "Tower がジョブの実行および分離用に新規の一時ディレクトリーを作成するディレクトリーです (認証情報ファイルおよびカスタムインベントリースクリプトなど)。" -#: awx/main/conf.py:233 +#: awx/main/conf.py:232 msgid "Paths to hide from isolated jobs" msgstr "分離されたジョブから非表示にするパス" -#: awx/main/conf.py:234 +#: awx/main/conf.py:233 msgid "" "Additional paths to hide from isolated processes. Enter one path per line." msgstr "分離されたプロセスには公開しないその他のパスです。1 行に 1 つのパスを入力します。" -#: awx/main/conf.py:243 +#: awx/main/conf.py:242 msgid "Paths to expose to isolated jobs" msgstr "分離されたジョブに公開するパス" -#: awx/main/conf.py:244 +#: awx/main/conf.py:243 msgid "" -"Whitelist of paths that would otherwise be hidden to expose to isolated " -"jobs. Enter one path per line." -msgstr "分離されたジョブに公開するために非表示にされるパスのホワイトリストです。1 行に 1 つのパスを入力します。" +"List of paths that would otherwise be hidden to expose to isolated jobs. " +"Enter one path per line." +msgstr "分離されたジョブに公開するために非表示にされるパスの一覧。1 行に 1 つのパスを入力します。" -#: awx/main/conf.py:254 -msgid "Verbosity level for isolated node management tasks" -msgstr "分離ノード管理タスクの詳細レベル" - -#: awx/main/conf.py:255 -msgid "" -"This can be raised to aid in debugging connection issues for isolated task " -"execution" -msgstr "このレベルを引き上げると、分離されたタスクの実行に接続の問題をデバッグするのに役立ちます。" - -#: awx/main/conf.py:265 +#: awx/main/conf.py:252 msgid "Isolated status check interval" msgstr "分離されたステータスチェックの間隔" -#: awx/main/conf.py:266 +#: awx/main/conf.py:253 msgid "" "The number of seconds to sleep between status checks for jobs running on " "isolated instances." msgstr "分離されたインスタンスで実行されるジョブに対するステータスチェック間にスリープ状態になる期間の秒数。" -#: awx/main/conf.py:275 +#: awx/main/conf.py:262 msgid "Isolated launch timeout" msgstr "分離された起動のタイムアウト" -#: awx/main/conf.py:276 +#: awx/main/conf.py:263 msgid "" "The timeout (in seconds) for launching jobs on isolated instances. This " "includes the time needed to copy source control files (playbooks) to the " "isolated instance." msgstr "分離されたインスタンスでジョブを起動する際のタイムアウト (秒数)。これにはソースコントロールファイル (Playbook) を分離されたインスタンスにコピーするために必要な時間が含まれます。" -#: awx/main/conf.py:287 +#: awx/main/conf.py:274 msgid "Isolated connection timeout" msgstr "分離された接続のタイムアウト" -#: awx/main/conf.py:288 +#: awx/main/conf.py:275 msgid "" "Ansible SSH connection timeout (in seconds) to use when communicating with " "isolated instances. Value should be substantially greater than expected " "network latency." msgstr "分離されたインスタンスと通信する際に使用される Ansible SSH 接続のタイムアウト (秒数) です。値は予想されるネットワークの待ち時間よりも大幅に大きな値になるはずです。" -#: awx/main/conf.py:297 +#: awx/main/conf.py:284 msgid "Isolated host key checking" msgstr "分離されたホストキーチェック" -#: awx/main/conf.py:298 +#: awx/main/conf.py:285 msgid "" "When set to True, AWX will enforce strict host key checking for " "communication with isolated nodes." msgstr "True に設定すると、AWX は、分離したノードとの通信に対するホストキーの厳密なチェックを有効にします。" -#: awx/main/conf.py:308 +#: awx/main/conf.py:295 msgid "Generate RSA keys for isolated instances" msgstr "分離されたインスタンスの RSA 鍵の生成" -#: awx/main/conf.py:309 +#: awx/main/conf.py:296 msgid "" "If set, a random RSA key will be generated and distributed to isolated " "instances. To disable this behavior and manage authentication for isolated " "instances outside of Tower, disable this setting." msgstr "設定されている場合、RSA 鍵が生成され、分離されたインスタンスに配布されます。この動作を無効にし、Tower の外部にある分離されたインスタンスの認証を管理するには、この設定を無効にします。" -#: awx/main/conf.py:323 awx/main/conf.py:324 +#: awx/main/conf.py:310 awx/main/conf.py:311 msgid "The RSA private key for SSH traffic to isolated instances" msgstr "分離されたインスタンスへの SSH トラフィック用の RSA 秘密鍵" -#: awx/main/conf.py:335 awx/main/conf.py:336 +#: awx/main/conf.py:322 awx/main/conf.py:323 msgid "The RSA public key for SSH traffic to isolated instances" msgstr "分離されたインスタンスへの SSH トラフィック用の RSA 公開鍵" -#: awx/main/conf.py:345 +#: awx/main/conf.py:332 msgid "Enable detailed resource profiling on all playbook runs" msgstr "全 Playbook の実行に対する詳細なリソースプロファイリングを有効にする" -#: awx/main/conf.py:346 +#: awx/main/conf.py:333 msgid "" "If set, detailed resource profiling data will be collected on all jobs. This " "data can be gathered with `sosreport`." msgstr "設定されている場合には、すべてのジョブに対するリソースプロファイリングデータが収集されます。このデータは、`sosreport` で収集できます。" -#: awx/main/conf.py:356 +#: awx/main/conf.py:343 msgid "Interval (in seconds) between polls for cpu usage." msgstr "CPU 使用率のポーリングの間隔 (秒単位)。" -#: awx/main/conf.py:357 +#: awx/main/conf.py:344 msgid "" "Interval (in seconds) between polls for cpu usage. Setting this lower than " "the default will affect playbook performance." msgstr "CPU 使用率のポーリングの間隔 (秒単位)。これをデフォルトよりも小さい値に設定すると Playbook のパフォーマンスに影響があります。" -#: awx/main/conf.py:368 +#: awx/main/conf.py:355 msgid "Interval (in seconds) between polls for memory usage." msgstr "メモリー使用率のポーリングの間隔 (秒単位)。" -#: awx/main/conf.py:369 +#: awx/main/conf.py:356 msgid "" "Interval (in seconds) between polls for memory usage. Setting this lower " "than the default will affect playbook performance." msgstr "メモリー使用率のポーリングの間隔 (秒単位)。これをデフォルトよりも小さい値に設定すると Playbook のパフォーマンスに影響があります。" -#: awx/main/conf.py:380 +#: awx/main/conf.py:367 msgid "Interval (in seconds) between polls for PID count." msgstr "PID 数のポーリングの間隔 (秒単位)。" -#: awx/main/conf.py:381 +#: awx/main/conf.py:368 msgid "" "Interval (in seconds) between polls for PID count. Setting this lower than " "the default will affect playbook performance." msgstr "PID 数のポーリングの間隔 (秒単位)。これをデフォルトよりも小さい値に設定すると Playbook のパフォーマンスに影響があります。" -#: awx/main/conf.py:392 +#: awx/main/conf.py:379 msgid "Extra Environment Variables" msgstr "追加の環境変数" -#: awx/main/conf.py:393 +#: awx/main/conf.py:380 msgid "" "Additional environment variables set for playbook runs, inventory updates, " "project updates, and notification sending." msgstr "Playbook 実行、インベントリー更新、プロジェクト更新および通知の送信に設定される追加の環境変数。" -#: awx/main/conf.py:403 +#: awx/main/conf.py:390 msgid "Gather data for Automation Analytics" msgstr "自動化アナリティクス向けにデータを収集する" -#: awx/main/conf.py:404 +#: awx/main/conf.py:391 msgid "Enables Tower to gather data on automation and send it to Red Hat." msgstr "Tower が自動化のデータを収集して Red Hat に送信できるようにします。" -#: awx/main/conf.py:412 +#: awx/main/conf.py:399 msgid "Run Project Updates With Higher Verbosity" msgstr "より詳細なプロジェクト更新を実行する" -#: awx/main/conf.py:413 +#: awx/main/conf.py:400 msgid "" "Adds the CLI -vvv flag to ansible-playbook runs of project_update.yml used " "for project updates." msgstr "プロジェクトの更新に使用する project_update.yml の ansible-playbook に CLI -vvv フラグを追加します。" -#: awx/main/conf.py:422 +#: awx/main/conf.py:409 msgid "Enable Role Download" msgstr "ロールのダウンロードを有効にする" -#: awx/main/conf.py:423 +#: awx/main/conf.py:410 msgid "" "Allows roles to be dynamically downloaded from a requirements.yml file for " "SCM projects." msgstr "ロールが SCM プロジェクトの requirements.yml ファイルから動的にダウンロードされるのを許可します。" -#: awx/main/conf.py:432 +#: awx/main/conf.py:419 msgid "Enable Collection(s) Download" msgstr "コレクションのダウンロードを有効にする" -#: awx/main/conf.py:433 +#: awx/main/conf.py:420 msgid "" "Allows collections to be dynamically downloaded from a requirements.yml file " "for SCM projects." msgstr "コレクションが SCM プロジェクトの requirements.yml ファイルから動的にダウンロードされるのを許可します。" -#: awx/main/conf.py:443 -msgid "Primary Galaxy Server URL" -msgstr "プライマリー Galaxy Server URL" +#: awx/main/conf.py:429 +msgid "Follow symlinks" +msgstr "シンボリックリンクをたどる" -#: awx/main/conf.py:445 +#: awx/main/conf.py:431 msgid "" -"For organizations that run their own Galaxy service, this gives the option " -"to specify a host as the primary galaxy server. Requirements will be " -"downloaded from the primary if the specific role or collection is available " -"there. If the content is not avilable in the primary, or if this field is " -"left blank, it will default to galaxy.ansible.com." -msgstr "Galaxy サービスを実行する組織では、プライマリーの Galaxy Server としてホストを指定するオプションが追加されます。特定のロールやコレクションが利用できる場合には、このプライマリーのサーバーから要件がダウンロードされます。または、フィールドが空白の場合は、galaxy.ansible.com のデフォルト設定を使用します。" +"Follow symbolic links when scanning for playbooks. Be aware that setting " +"this to True can lead to infinite recursion if a link points to a parent " +"directory of itself." +msgstr "Playbook をスキャンするときは、シンボリックリンクをたどってください。リンクがそれ自体の親ディレクトリーを指している場合は、これを True に設定すると、無限再帰が発生する可能性があることに注意してください。" -#: awx/main/conf.py:459 -msgid "Primary Galaxy Server Username" -msgstr "プライマリー Galaxy Server のユーザー名" - -#: awx/main/conf.py:460 -msgid "" -"For using a galaxy server at higher precedence than the public Ansible " -"Galaxy. The username to use for basic authentication against the Galaxy " -"instance, this is mutually exclusive with PRIMARY_GALAXY_TOKEN." -msgstr "パブリックの Ansible Galaxy より優先順位の高い Galaxy Server を使用する場合に、Galaxy インスタンスへの Basic 認証に使用するユーザー名です。これは、PRIMARY_GALAXY_TOKEN と同時に使用できません。" - -#: awx/main/conf.py:473 -msgid "Primary Galaxy Server Password" -msgstr "プライマリー Galaxy Server のパスワード" - -#: awx/main/conf.py:474 -msgid "" -"For using a galaxy server at higher precedence than the public Ansible " -"Galaxy. The password to use for basic authentication against the Galaxy " -"instance, this is mutually exclusive with PRIMARY_GALAXY_TOKEN." -msgstr "パブリックの Ansible Galaxy より優先順位の高い Galaxy Server を使用する場合に、Galaxy インスタンスへの Basic 認証に使用するパスワードです。これは、PRIMARY_GALAXY_TOKEN と同時に使用できません。" - -#: awx/main/conf.py:487 -msgid "Primary Galaxy Server Token" -msgstr "プライマリー Galaxy Server トークン" - -#: awx/main/conf.py:488 -msgid "" -"For using a galaxy server at higher precedence than the public Ansible " -"Galaxy. The token to use for connecting with the Galaxy instance, this is " -"mutually exclusive with corresponding username and password settings." -msgstr "パブリックの Ansible Galaxy より優先順位の高い Galaxy Server を使用する場合に、Galaxy インスタンスへの接続に使用するトークンです。これは、対応のユーザー名およびパスワード設定と同時に使用できません。" - -#: awx/main/conf.py:500 -msgid "Primary Galaxy Authentication URL" -msgstr "プライマリー Galaxy 認証 URL" - -#: awx/main/conf.py:501 -msgid "" -"For using a galaxy server at higher precedence than the public Ansible " -"Galaxy. The token_endpoint of a Keycloak server." -msgstr "パブリックの Ansible Galaxy より優先順位の高い Galaxy Server を使用する場合に、使用する Keycloak Server の token_endpoint です。" - -#: awx/main/conf.py:511 -msgid "Allow Access to Public Galaxy" -msgstr "Public Galaxy へのアクセスを許可" - -#: awx/main/conf.py:512 -msgid "" -"Allow or deny access to the public Ansible Galaxy during project updates." -msgstr "プロジェクト更新中にパブリックの Ansible Galaxy へのアクセスを許可または拒否します。" - -#: awx/main/conf.py:521 +#: awx/main/conf.py:442 msgid "Ignore Ansible Galaxy SSL Certificate Verification" msgstr "Ansible Galaxy SSL 証明書の検証を無視する" -#: awx/main/conf.py:522 +#: awx/main/conf.py:443 msgid "" -"If set to true, certificate validation will not be done wheninstalling " +"If set to true, certificate validation will not be done when installing " "content from any Galaxy server." msgstr "True に設定すると、任意の Galaxy サーバーからコンテンツをインストールする時に証明書は検証されません。" -#: awx/main/conf.py:532 +#: awx/main/conf.py:453 msgid "Standard Output Maximum Display Size" msgstr "標準出力の最大表示サイズ" -#: awx/main/conf.py:533 +#: awx/main/conf.py:454 msgid "" "Maximum Size of Standard Output in bytes to display before requiring the " "output be downloaded." msgstr "出力のダウンロードを要求する前に表示される標準出力の最大サイズ (バイト単位)。" -#: awx/main/conf.py:542 +#: awx/main/conf.py:463 msgid "Job Event Standard Output Maximum Display Size" msgstr "ジョブイベントの標準出力の最大表示サイズ" -#: awx/main/conf.py:544 +#: awx/main/conf.py:465 msgid "" "Maximum Size of Standard Output in bytes to display for a single job or ad " "hoc command event. `stdout` will end with `…` when truncated." msgstr "単一ジョブまたはアドホックコマンドイベントについて表示される標準出力の最大サイズ (バイト単位)。`stdout` は切り捨てが実行されると `…` で終了します。" -#: awx/main/conf.py:553 +#: awx/main/conf.py:474 msgid "Maximum Scheduled Jobs" msgstr "スケジュール済みジョブの最大数" -#: awx/main/conf.py:554 +#: awx/main/conf.py:475 msgid "" "Maximum number of the same job template that can be waiting to run when " "launching from a schedule before no more are created." msgstr "スケジュールからの起動時に実行を待機している同じジョブテンプレートの最大数です (これ以上作成されることはありません)。" -#: awx/main/conf.py:563 +#: awx/main/conf.py:484 msgid "Ansible Callback Plugins" msgstr "Ansible コールバックプラグイン" -#: awx/main/conf.py:564 +#: awx/main/conf.py:485 msgid "" "List of paths to search for extra callback plugins to be used when running " "jobs. Enter one path per line." msgstr "ジョブの実行時に使用される追加のコールバックプラグインを検索する際のパスの一覧です。1 行に 1 つのパスを入力します。" -#: awx/main/conf.py:574 +#: awx/main/conf.py:495 msgid "Default Job Timeout" msgstr "デフォルトのジョブタイムアウト" -#: awx/main/conf.py:575 +#: awx/main/conf.py:496 msgid "" "Maximum time in seconds to allow jobs to run. Use value of 0 to indicate " "that no timeout should be imposed. A timeout set on an individual job " "template will override this." msgstr "ジョブの実行可能な最大時間 (秒数) です。値 0 が使用されている場合はタイムアウトを設定できないことを示します。個別のジョブテンプレートに設定されるタイムアウトはこれを上書きします。" -#: awx/main/conf.py:586 +#: awx/main/conf.py:507 msgid "Default Inventory Update Timeout" msgstr "デフォルトのインベントリー更新タイムアウト" -#: awx/main/conf.py:587 +#: awx/main/conf.py:508 msgid "" "Maximum time in seconds to allow inventory updates to run. Use value of 0 to " "indicate that no timeout should be imposed. A timeout set on an individual " "inventory source will override this." msgstr "インベントリー更新の実行可能な最大時間 (秒数)。値 0 が設定されている場合はタイムアウトを設定できないことを示します。個別のインベントリーソースに設定されるタイムアウトはこれを上書きします。" -#: awx/main/conf.py:598 +#: awx/main/conf.py:519 msgid "Default Project Update Timeout" msgstr "デフォルトのプロジェクト更新タイムアウト" -#: awx/main/conf.py:599 +#: awx/main/conf.py:520 msgid "" "Maximum time in seconds to allow project updates to run. Use value of 0 to " "indicate that no timeout should be imposed. A timeout set on an individual " "project will override this." msgstr "プロジェクト更新の実行可能な最大時間 (秒数)。値 0 が設定されている場合はタイムアウトを設定できないことを示します。個別のプロジェクトに設定されるタイムアウトはこれを上書きします。" -#: awx/main/conf.py:610 +#: awx/main/conf.py:531 msgid "Per-Host Ansible Fact Cache Timeout" msgstr "ホストあたりの Ansible ファクトキャッシュのタイムアウト" -#: awx/main/conf.py:611 +#: awx/main/conf.py:532 msgid "" "Maximum time, in seconds, that stored Ansible facts are considered valid " "since the last time they were modified. Only valid, non-stale, facts will be " @@ -2393,74 +2416,74 @@ msgid "" "timeout should be imposed." msgstr "保存される Ansible ファクトが最後に変更されてから有効とみなされる最大時間 (秒数) です。有効な新規のファクトのみが Playbook でアクセスできます。ansible_facts のデータベースからの削除はこれによる影響を受けません。タイムアウトが設定されないことを示すには 0 の値を使用します。" -#: awx/main/conf.py:624 +#: awx/main/conf.py:545 msgid "Maximum number of forks per job." msgstr "ジョブ別のフォークの最大数。" -#: awx/main/conf.py:625 +#: awx/main/conf.py:546 msgid "" "Saving a Job Template with more than this number of forks will result in an " "error. When set to 0, no limit is applied." msgstr "この数を超えるフォークを指定してジョブテンプレートを保存すると、エラーが発生します。 0 に設定すると、制限は適用されません。" -#: awx/main/conf.py:636 +#: awx/main/conf.py:557 msgid "Logging Aggregator" msgstr "ログアグリゲーター" -#: awx/main/conf.py:637 +#: awx/main/conf.py:558 msgid "Hostname/IP where external logs will be sent to." msgstr "外部ログの送信先のホスト名/IP" -#: awx/main/conf.py:638 awx/main/conf.py:649 awx/main/conf.py:661 -#: awx/main/conf.py:671 awx/main/conf.py:683 awx/main/conf.py:698 -#: awx/main/conf.py:710 awx/main/conf.py:719 awx/main/conf.py:729 -#: awx/main/conf.py:741 awx/main/conf.py:752 awx/main/conf.py:764 -#: awx/main/conf.py:777 awx/main/conf.py:787 awx/main/conf.py:799 -#: awx/main/conf.py:810 awx/main/conf.py:820 +#: awx/main/conf.py:559 awx/main/conf.py:570 awx/main/conf.py:582 +#: awx/main/conf.py:592 awx/main/conf.py:604 awx/main/conf.py:619 +#: awx/main/conf.py:631 awx/main/conf.py:640 awx/main/conf.py:650 +#: awx/main/conf.py:662 awx/main/conf.py:673 awx/main/conf.py:685 +#: awx/main/conf.py:698 awx/main/conf.py:710 awx/main/conf.py:721 +#: awx/main/conf.py:731 msgid "Logging" msgstr "ロギング" -#: awx/main/conf.py:646 +#: awx/main/conf.py:567 msgid "Logging Aggregator Port" msgstr "ログアグリゲーターポート" -#: awx/main/conf.py:647 +#: awx/main/conf.py:568 msgid "" "Port on Logging Aggregator to send logs to (if required and not provided in " "Logging Aggregator)." msgstr "ログの送信先のログアグリゲーターのポート (必要な場合。ログアグリゲーターでは指定されません)。" -#: awx/main/conf.py:659 +#: awx/main/conf.py:580 msgid "Logging Aggregator Type" msgstr "ログアグリゲーターのタイプ" -#: awx/main/conf.py:660 +#: awx/main/conf.py:581 msgid "Format messages for the chosen log aggregator." msgstr "選択されたログアグリゲーターのメッセージのフォーマット。" -#: awx/main/conf.py:669 +#: awx/main/conf.py:590 msgid "Logging Aggregator Username" msgstr "ログアグリゲーターのユーザー名" -#: awx/main/conf.py:670 +#: awx/main/conf.py:591 msgid "Username for external log aggregator (if required; HTTP/s only)." msgstr "外部ログアグリゲーターのユーザー名 (必要な場合、HTTP/s のみ)。" -#: awx/main/conf.py:681 +#: awx/main/conf.py:602 msgid "Logging Aggregator Password/Token" msgstr "ログアグリゲーターのパスワード/トークン" -#: awx/main/conf.py:682 +#: awx/main/conf.py:603 msgid "" "Password or authentication token for external log aggregator (if required; " "HTTP/s only)." msgstr "外部ログアグリゲーターのパスワードまたは認証トークン (必要な場合、HTTP/s のみ)。" -#: awx/main/conf.py:691 +#: awx/main/conf.py:612 msgid "Loggers Sending Data to Log Aggregator Form" msgstr "ログアグリゲーターフォームにデータを送信するロガー" -#: awx/main/conf.py:692 +#: awx/main/conf.py:613 msgid "" "List of loggers that will send HTTP logs to the collector, these can include " "any or all of: \n" @@ -2474,11 +2497,11 @@ msgstr "HTTP ログをコレクターに送信するロガーの一覧です。 "job_events - Ansible ジョブイベントからのコールバックデータ\n" "system_tracking - スキャンジョブから生成されるファクト" -#: awx/main/conf.py:705 +#: awx/main/conf.py:626 msgid "Log System Tracking Facts Individually" msgstr "ログシステムトラッキングの個別ファクト" -#: awx/main/conf.py:706 +#: awx/main/conf.py:627 msgid "" "If set, system tracking facts will be sent for each package, service, or " "other item found in a scan, allowing for greater search query granularity. " @@ -2486,47 +2509,47 @@ msgid "" "efficiency in fact processing." msgstr "設定されている場合、スキャンで見つかる各パッケージ、サービスその他の項目についてのシステムトラッキングのファクトが送信され、検索クエリーの詳細度が上がります。設定されていない場合、ファクトは単一辞書として送信され、ファクトの処理の効率が上がります。" -#: awx/main/conf.py:717 +#: awx/main/conf.py:638 msgid "Enable External Logging" msgstr "外部ログの有効化" -#: awx/main/conf.py:718 +#: awx/main/conf.py:639 msgid "Enable sending logs to external log aggregator." msgstr "外部ログアグリゲーターへのログ送信の有効化" -#: awx/main/conf.py:727 +#: awx/main/conf.py:648 msgid "Cluster-wide Tower unique identifier." msgstr "クラスター全体での Tower 固有識別子。" -#: awx/main/conf.py:728 +#: awx/main/conf.py:649 msgid "Useful to uniquely identify Tower instances." msgstr "Tower インスタンスを一意に識別するのに役立ちます。" -#: awx/main/conf.py:737 +#: awx/main/conf.py:658 msgid "Logging Aggregator Protocol" msgstr "ログアグリゲーターのプロトコル" -#: awx/main/conf.py:738 +#: awx/main/conf.py:659 msgid "" "Protocol used to communicate with log aggregator. HTTPS/HTTP assumes HTTPS " "unless http:// is explicitly used in the Logging Aggregator hostname." msgstr "ログアグリゲーターとの通信に使用されるプロトコルです。HTTPS/HTTP については、 http:// がログアグリゲーターのホスト名で明示的に使用されていない限り HTTPS の使用が前提となります。" -#: awx/main/conf.py:748 +#: awx/main/conf.py:669 msgid "TCP Connection Timeout" msgstr "TCP 接続のタイムアウト" -#: awx/main/conf.py:749 +#: awx/main/conf.py:670 msgid "" "Number of seconds for a TCP connection to external log aggregator to " "timeout. Applies to HTTPS and TCP log aggregator protocols." msgstr "外部ログアグリゲーターへの TCP 接続がタイムアウトする秒数です。HTTPS および TCP ログアグリゲータープロトコルに適用されます。" -#: awx/main/conf.py:759 +#: awx/main/conf.py:680 msgid "Enable/disable HTTPS certificate verification" msgstr "HTTPS 証明書の検証を有効化/無効化" -#: awx/main/conf.py:760 +#: awx/main/conf.py:681 msgid "" "Flag to control enable/disable of certificate verification when " "LOG_AGGREGATOR_PROTOCOL is \"https\". If enabled, Tower's log handler will " @@ -2534,11 +2557,11 @@ msgid "" "connection." msgstr "LOG_AGGREGATOR_PROTOCOL が「https」の場合の証明書の検証の有効化/無効化を制御するフラグです。有効にされている場合、Tower のログハンドラーは接続を確立する前に外部ログアグリゲーターによって送信される証明書を検証します。" -#: awx/main/conf.py:772 +#: awx/main/conf.py:693 msgid "Logging Aggregator Level Threshold" msgstr "ログアグリゲーターレベルのしきい値" -#: awx/main/conf.py:773 +#: awx/main/conf.py:694 msgid "" "Level threshold used by log handler. Severities from lowest to highest are " "DEBUG, INFO, WARNING, ERROR, CRITICAL. Messages less severe than the " @@ -2546,198 +2569,144 @@ msgid "" "anlytics ignore this setting)" msgstr "ログハンドラーによって使用されるレベルのしきい値です。重大度は低い順から高い順に DEBUG、INFO、WARNING、ERROR、CRITICAL になります。しきい値より重大度の低いメッセージはログハンドラーによって無視されます (カテゴリー awx.anlytics の下にあるメッセージはこの設定を無視します)。" -#: awx/main/conf.py:785 -msgid "Enabled external log aggregation auditing" -msgstr "有効な外部ログ集計監査" - -#: awx/main/conf.py:786 -msgid "" -"When enabled, all external logs emitted by Tower will also be written to /" -"var/log/tower/external.log. This is an experimental setting intended to be " -"used for debugging external log aggregation issues (and may be subject to " -"change in the future)." -msgstr "有効な場合には、Tower が出力した外部ログはすべて、/var/log/tower/external.log に書き込まれます。この試用的な設定は、外部ログを累積する問題をデバッグするのに使用します (今後、この設定は変更される可能性があります)。" - -#: awx/main/conf.py:795 +#: awx/main/conf.py:706 msgid "Maximum disk persistance for external log aggregation (in GB)" msgstr "外部ログ集計の最大ディスク永続性 (GB)" -#: awx/main/conf.py:796 +#: awx/main/conf.py:707 msgid "" "Amount of data to store (in gigabytes) during an outage of the external log " "aggregator (defaults to 1). Equivalent to the rsyslogd queue.maxdiskspace " "setting." msgstr "外部ログアグリゲーターの停止中に保存するデータ容量 (GB、デフォルトは 1)。rsyslogd queue.maxdiskspace 設定と同じです。" -#: awx/main/conf.py:806 +#: awx/main/conf.py:717 msgid "File system location for rsyslogd disk persistence" msgstr "rsyslogd ディスク永続性のファイルシステムの場所" -#: awx/main/conf.py:807 +#: awx/main/conf.py:718 msgid "" "Location to persist logs that should be retried after an outage of the " "external log aggregator (defaults to /var/lib/awx). Equivalent to the " "rsyslogd queue.spoolDirectory setting." msgstr "外部ログアグリゲーターが停止した後に再試行する必要のあるログを永続化する場所 (デフォルト: /var/lib/awx)。rsyslogd queue.spoolDirectory の設定と同じです。" -#: awx/main/conf.py:817 +#: awx/main/conf.py:728 msgid "Enable rsyslogd debugging" msgstr "rsyslogd デバッグを有効にする" -#: awx/main/conf.py:818 +#: awx/main/conf.py:729 msgid "" "Enabled high verbosity debugging for rsyslogd. Useful for debugging " "connection issues for external log aggregation." msgstr "rsyslogd の詳細デバッグを有効にしました。外部ログ集計の接続問題のデバッグに役立ちます。" -#: awx/main/conf.py:828 -msgid "Message Durability" -msgstr "メッセージの永続性" - -#: awx/main/conf.py:829 -msgid "" -"When set (the default), underlying queues will be persisted to disk. " -"Disable this to enable higher message bus throughput." -msgstr "(デフォルト値) が設定されている場合には、下層のキューはディスクに永続化されます。これを無効すると、メッセージバスのスループットを増加できます。" - -#: awx/main/conf.py:838 +#: awx/main/conf.py:740 msgid "Last gather date for Automation Analytics." msgstr "自動化アナリティクス向けにデータを最後に収集した日" -#: awx/main/conf.py:848 +#: awx/main/conf.py:750 msgid "Automation Analytics Gather Interval" msgstr "自動化アナリティクスの収集間隔" -#: awx/main/conf.py:849 +#: awx/main/conf.py:751 msgid "Interval (in seconds) between data gathering." msgstr "データ収集の間隔 (秒単位)" -#: awx/main/conf.py:871 awx/sso/conf.py:1239 +#: awx/main/conf.py:773 awx/sso/conf.py:1250 msgid "\n" msgstr "\n" -#: awx/main/conf.py:892 -msgid "" -"A URL for Primary Galaxy must be defined before disabling public Galaxy." -msgstr "パブリック Galaxy を無効にする前に、Primary Galaxy の URL を定義する必要があります。" - -#: awx/main/conf.py:912 -msgid "Cannot provide field if PRIMARY_GALAXY_URL is not set." -msgstr "PRIMARY_Galaxy_URL が設定されていない場合にはフィールドは指定できません。" - -#: awx/main/conf.py:925 -#, python-brace-format -msgid "" -"Galaxy server settings are not available until Ansible {min_version}, you " -"are running {current_version}." -msgstr "Galaxy Server の設定は Ansible {min_version} するまで利用できません。{current_version} を実行しています。" - -#: awx/main/conf.py:934 -msgid "" -"Setting Galaxy token and authentication URL is mutually exclusive with " -"username and password." -msgstr "Galaxy トークンと認証 URL を設定すると、ユーザー名とパスワードの認証は使用できません。" - -#: awx/main/conf.py:937 -msgid "If authenticating via username and password, both must be provided." -msgstr "ユーザー名とパスワードで認証する場合には、ユーザー名とパスワード両方を指定する必要があります。" - -#: awx/main/conf.py:943 -msgid "" -"If authenticating via token, both token and authentication URL must be " -"provided." -msgstr "トークンで認証する場合には、トークンと認証 URL の両方を指定する必要があります。" - -#: awx/main/constants.py:17 +#: awx/main/constants.py:16 msgid "Sudo" msgstr "Sudo" -#: awx/main/constants.py:17 +#: awx/main/constants.py:16 msgid "Su" msgstr "Su" -#: awx/main/constants.py:17 +#: awx/main/constants.py:16 msgid "Pbrun" msgstr "Pbrun" -#: awx/main/constants.py:17 +#: awx/main/constants.py:16 msgid "Pfexec" msgstr "Pfexec" -#: awx/main/constants.py:18 +#: awx/main/constants.py:17 msgid "DZDO" msgstr "DZDO" -#: awx/main/constants.py:18 +#: awx/main/constants.py:17 msgid "Pmrun" msgstr "Pmrun" -#: awx/main/constants.py:18 +#: awx/main/constants.py:17 msgid "Runas" msgstr "Runas" -#: awx/main/constants.py:19 +#: awx/main/constants.py:18 msgid "Enable" msgstr "有効化" -#: awx/main/constants.py:19 +#: awx/main/constants.py:18 msgid "Doas" msgstr "Doas" -#: awx/main/constants.py:19 +#: awx/main/constants.py:18 msgid "Ksu" msgstr "Ksu" -#: awx/main/constants.py:20 +#: awx/main/constants.py:19 msgid "Machinectl" msgstr "Machinectl" -#: awx/main/constants.py:20 +#: awx/main/constants.py:19 msgid "Sesu" msgstr "Sesu" -#: awx/main/constants.py:22 +#: awx/main/constants.py:21 msgid "None" msgstr "なし" -#: awx/main/credential_plugins/aim.py:16 +#: awx/main/credential_plugins/aim.py:11 msgid "CyberArk AIM URL" msgstr "CyberArk AIM URL" -#: awx/main/credential_plugins/aim.py:21 +#: awx/main/credential_plugins/aim.py:16 msgid "Application ID" msgstr "アプリケーション ID" -#: awx/main/credential_plugins/aim.py:26 +#: awx/main/credential_plugins/aim.py:21 msgid "Client Key" msgstr "クライアントキー" -#: awx/main/credential_plugins/aim.py:32 +#: awx/main/credential_plugins/aim.py:27 msgid "Client Certificate" msgstr "クライアント証明書" -#: awx/main/credential_plugins/aim.py:38 +#: awx/main/credential_plugins/aim.py:33 msgid "Verify SSL Certificates" msgstr "SSL 証明書の検証" -#: awx/main/credential_plugins/aim.py:44 +#: awx/main/credential_plugins/aim.py:39 msgid "Object Query" msgstr "オブジェクトのクエリー" -#: awx/main/credential_plugins/aim.py:46 +#: awx/main/credential_plugins/aim.py:41 msgid "" "Lookup query for the object. Ex: Safe=TestSafe;Object=testAccountName123" msgstr "オブジェクトの検索クエリー。例: Safe=TestSafe;Object=testAccountName123" -#: awx/main/credential_plugins/aim.py:49 +#: awx/main/credential_plugins/aim.py:44 msgid "Object Query Format" msgstr "オブジェクトクエリーフォーマット (必須)" -#: awx/main/credential_plugins/aim.py:55 +#: awx/main/credential_plugins/aim.py:50 msgid "Reason" msgstr "理由" -#: awx/main/credential_plugins/aim.py:57 +#: awx/main/credential_plugins/aim.py:52 msgid "" "Object request reason. This is only needed if it is required by the object's " "policy." @@ -2748,12 +2717,12 @@ msgid "Vault URL (DNS Name)" msgstr "Vault URL (DNS 名)" #: awx/main/credential_plugins/azure_kv.py:26 -#: awx/main/models/credential/__init__.py:956 +#: awx/main/models/credential/__init__.py:968 msgid "Client ID" msgstr "クライアント ID" #: awx/main/credential_plugins/azure_kv.py:35 -#: awx/main/models/credential/__init__.py:965 +#: awx/main/models/credential/__init__.py:977 msgid "Tenant ID" msgstr "テナント ID" @@ -2774,142 +2743,177 @@ msgid "The name of the secret to look up." msgstr "検索するシークレット名" #: awx/main/credential_plugins/azure_kv.py:51 -#: awx/main/credential_plugins/conjur.py:47 +#: awx/main/credential_plugins/conjur.py:42 msgid "Secret Version" msgstr "シークレットのバージョン" #: awx/main/credential_plugins/azure_kv.py:53 -#: awx/main/credential_plugins/conjur.py:49 -#: awx/main/credential_plugins/hashivault.py:67 +#: awx/main/credential_plugins/conjur.py:44 +#: awx/main/credential_plugins/hashivault.py:89 msgid "" "Used to specify a specific secret version (if left empty, the latest version " "will be used)." msgstr "シークレットバージョンの指定に使用します (空白の場合は、最新のバージョンが使用されます)。" -#: awx/main/credential_plugins/conjur.py:18 +#: awx/main/credential_plugins/conjur.py:13 msgid "Conjur URL" msgstr "Conjur URL" -#: awx/main/credential_plugins/conjur.py:23 +#: awx/main/credential_plugins/conjur.py:18 msgid "API Key" msgstr "API キー" -#: awx/main/credential_plugins/conjur.py:28 awx/main/models/inventory.py:1018 +#: awx/main/credential_plugins/conjur.py:23 +#: awx/main/migrations/_inventory_source_vars.py:142 msgid "Account" msgstr "アカウント" -#: awx/main/credential_plugins/conjur.py:32 -#: awx/main/models/credential/__init__.py:598 -#: awx/main/models/credential/__init__.py:654 -#: awx/main/models/credential/__init__.py:712 -#: awx/main/models/credential/__init__.py:785 -#: awx/main/models/credential/__init__.py:834 -#: awx/main/models/credential/__init__.py:860 -#: awx/main/models/credential/__init__.py:887 -#: awx/main/models/credential/__init__.py:947 -#: awx/main/models/credential/__init__.py:1020 -#: awx/main/models/credential/__init__.py:1051 -#: awx/main/models/credential/__init__.py:1101 +#: awx/main/credential_plugins/conjur.py:27 +#: awx/main/models/credential/__init__.py:606 +#: awx/main/models/credential/__init__.py:662 +#: awx/main/models/credential/__init__.py:720 +#: awx/main/models/credential/__init__.py:793 +#: awx/main/models/credential/__init__.py:846 +#: awx/main/models/credential/__init__.py:872 +#: awx/main/models/credential/__init__.py:899 +#: awx/main/models/credential/__init__.py:959 +#: awx/main/models/credential/__init__.py:1032 +#: awx/main/models/credential/__init__.py:1063 +#: awx/main/models/credential/__init__.py:1113 msgid "Username" msgstr "ユーザー名" -#: awx/main/credential_plugins/conjur.py:36 +#: awx/main/credential_plugins/conjur.py:31 msgid "Public Key Certificate" msgstr "公開鍵の証明書" -#: awx/main/credential_plugins/conjur.py:42 +#: awx/main/credential_plugins/conjur.py:37 msgid "Secret Identifier" msgstr "シークレット識別子" -#: awx/main/credential_plugins/conjur.py:44 +#: awx/main/credential_plugins/conjur.py:39 msgid "The identifier for the secret e.g., /some/identifier" msgstr "シークレットの識別子。例: /some/identifier" -#: awx/main/credential_plugins/hashivault.py:19 +#: awx/main/credential_plugins/hashivault.py:14 msgid "Server URL" msgstr "サーバー URL" -#: awx/main/credential_plugins/hashivault.py:22 +#: awx/main/credential_plugins/hashivault.py:17 msgid "The URL to the HashiCorp Vault" msgstr "HashiCorp Vault の URL" -#: awx/main/credential_plugins/hashivault.py:25 -#: awx/main/models/credential/__init__.py:986 -#: awx/main/models/credential/__init__.py:1003 +#: awx/main/credential_plugins/hashivault.py:20 +#: awx/main/models/credential/__init__.py:998 +#: awx/main/models/credential/__init__.py:1015 msgid "Token" msgstr "トークン" -#: awx/main/credential_plugins/hashivault.py:28 +#: awx/main/credential_plugins/hashivault.py:23 msgid "The access token used to authenticate to the Vault server" msgstr "Vault サーバーへの認証に使用するアクセストークン" -#: awx/main/credential_plugins/hashivault.py:31 +#: awx/main/credential_plugins/hashivault.py:26 msgid "CA Certificate" msgstr "CA 証明書" -#: awx/main/credential_plugins/hashivault.py:34 +#: awx/main/credential_plugins/hashivault.py:29 msgid "" "The CA certificate used to verify the SSL certificate of the Vault server" msgstr "Vault サーバーの SSL 証明書の検証に使用する CA 証明書" +#: awx/main/credential_plugins/hashivault.py:32 +msgid "AppRole role_id" +msgstr "AppRole role_id" + +#: awx/main/credential_plugins/hashivault.py:35 +msgid "The Role ID for AppRole Authentication" +msgstr "AppRole 認証のロール ID" + #: awx/main/credential_plugins/hashivault.py:38 +msgid "AppRole secret_id" +msgstr "AppRole secret_id" + +#: awx/main/credential_plugins/hashivault.py:42 +msgid "The Secret ID for AppRole Authentication" +msgstr "AppRole 認証のシークレット ID" + +#: awx/main/credential_plugins/hashivault.py:45 +msgid "Path to Approle Auth" +msgstr "ApproleAuth へのパス" + +#: awx/main/credential_plugins/hashivault.py:49 +msgid "" +"The AppRole Authentication path to use if one isn't provided in the metadata " +"when linking to an input field. Defaults to 'approle'" +msgstr "入力フィールドにリンクするときにメタデータで指定されていない場合に使用する AppRole 認証パス (デフォルトは「approle」)" + +#: awx/main/credential_plugins/hashivault.py:54 msgid "Path to Secret" msgstr "シークレットへのパス" -#: awx/main/credential_plugins/hashivault.py:40 +#: awx/main/credential_plugins/hashivault.py:56 msgid "The path to the secret stored in the secret backend e.g, /some/secret/" -msgstr "シークレットのバックエンドに保存されているシークレットへのパス。例: /some/secret/" +msgstr "シークレットのバックエンドに保存されているシークレットへのパス。例: /some/secret/" -#: awx/main/credential_plugins/hashivault.py:48 +#: awx/main/credential_plugins/hashivault.py:59 +msgid "Path to Auth" +msgstr "認証へのパス" + +#: awx/main/credential_plugins/hashivault.py:62 +msgid "The path where the Authentication method is mounted e.g, approle" +msgstr "認証方法がマウントされるパス (例: approle)" + +#: awx/main/credential_plugins/hashivault.py:70 msgid "API Version" msgstr "API バージョン" -#: awx/main/credential_plugins/hashivault.py:50 +#: awx/main/credential_plugins/hashivault.py:72 msgid "" "API v1 is for static key/value lookups. API v2 is for versioned key/value " "lookups." msgstr "API v1 は静的なキー/値のルックアップ用で、API v2 はバージョン管理されたキー/値のルックアップ用です。" -#: awx/main/credential_plugins/hashivault.py:55 +#: awx/main/credential_plugins/hashivault.py:77 msgid "Name of Secret Backend" msgstr "シークレットバックエンドの名前" -#: awx/main/credential_plugins/hashivault.py:57 +#: awx/main/credential_plugins/hashivault.py:79 msgid "" "The name of the kv secret backend (if left empty, the first segment of the " "secret path will be used)." msgstr "KV シークレットバックエンド名 (空白の場合は、シークレットパスの最初のセグメントが使用されます)。" -#: awx/main/credential_plugins/hashivault.py:60 -#: awx/main/models/inventory.py:1023 +#: awx/main/credential_plugins/hashivault.py:82 +#: awx/main/migrations/_inventory_source_vars.py:147 msgid "Key Name" msgstr "キー名" -#: awx/main/credential_plugins/hashivault.py:62 +#: awx/main/credential_plugins/hashivault.py:84 msgid "The name of the key to look up in the secret." msgstr "シークレットで検索するキーの名前" -#: awx/main/credential_plugins/hashivault.py:65 +#: awx/main/credential_plugins/hashivault.py:87 msgid "Secret Version (v2 only)" msgstr "シークレットバージョン (v2 のみ)" -#: awx/main/credential_plugins/hashivault.py:74 +#: awx/main/credential_plugins/hashivault.py:96 msgid "Unsigned Public Key" msgstr "未署名の公開鍵" -#: awx/main/credential_plugins/hashivault.py:79 +#: awx/main/credential_plugins/hashivault.py:101 msgid "Role Name" msgstr "ロール名" -#: awx/main/credential_plugins/hashivault.py:81 +#: awx/main/credential_plugins/hashivault.py:103 msgid "The name of the role used to sign." msgstr "署名に使用するロール名" -#: awx/main/credential_plugins/hashivault.py:84 +#: awx/main/credential_plugins/hashivault.py:106 msgid "Valid Principals" msgstr "有効なプリンシパル" -#: awx/main/credential_plugins/hashivault.py:86 +#: awx/main/credential_plugins/hashivault.py:108 msgid "" "Valid principals (either usernames or hostnames) that the certificate should " "be signed for." @@ -2944,70 +2948,74 @@ msgstr "%s に必須です" msgid "secret values must be of type string, not {}" msgstr "シークレットの値は文字列型にする必要があります ({}ではない)" -#: awx/main/fields.py:667 +#: awx/main/fields.py:675 #, python-format msgid "cannot be set unless \"%s\" is set" msgstr "\"%s\" が設定されていない場合は設定できません" -#: awx/main/fields.py:702 +#: awx/main/fields.py:710 msgid "must be set when SSH key is encrypted." msgstr "SSH キーが暗号化されている場合に設定する必要があります。" -#: awx/main/fields.py:710 +#: awx/main/fields.py:718 msgid "should not be set when SSH key is not encrypted." msgstr "SSH キーが暗号化されていない場合は設定できません。" -#: awx/main/fields.py:769 +#: awx/main/fields.py:777 msgid "'dependencies' is not supported for custom credentials." msgstr "「dependencies (依存関係)」はカスタム認証情報の場合にはサポートされません。" -#: awx/main/fields.py:783 +#: awx/main/fields.py:791 msgid "\"tower\" is a reserved field name" msgstr "「tower」は予約されたフィールド名です" -#: awx/main/fields.py:790 +#: awx/main/fields.py:798 #, python-format msgid "field IDs must be unique (%s)" msgstr "フィールド ID は固有でなければなりません (%s)" -#: awx/main/fields.py:805 +#: awx/main/fields.py:813 msgid "{} is not a {}" msgstr "{} は {} ではありません" -#: awx/main/fields.py:811 +#: awx/main/fields.py:819 #, python-brace-format msgid "{sub_key} not allowed for {element_type} type ({element_id})" msgstr "{element_type} タイプには {sub_key} を使用できません ({element_id})" -#: awx/main/fields.py:869 +#: awx/main/fields.py:877 msgid "" "Environment variable {} may affect Ansible configuration so its use is not " "allowed in credentials." msgstr "環境変数 {} は Ansible の設定に影響を及ぼす可能性があります。したがって認証情報で使用することはできません。" -#: awx/main/fields.py:875 -msgid "Environment variable {} is blacklisted from use in credentials." -msgstr "環境変数 {} を認証情報で使用することは禁止されています。" +#: awx/main/fields.py:883 +msgid "Environment variable {} is not allowed to be used in credentials." +msgstr "環境変数 {} を認証情報で使用することは許可されていません。" -#: awx/main/fields.py:903 +#: awx/main/fields.py:911 msgid "" "Must define unnamed file injector in order to reference `tower.filename`." msgstr "`tower.filename`を参照するために名前が設定されていないファイルインジェクターを定義する必要があります。" -#: awx/main/fields.py:910 +#: awx/main/fields.py:918 msgid "Cannot directly reference reserved `tower` namespace container." msgstr "予約された `tower` 名前空間コンテナーを直接参照することができません。" -#: awx/main/fields.py:920 +#: awx/main/fields.py:928 msgid "Must use multi-file syntax when injecting multiple files" msgstr "複数ファイルの挿入時に複数ファイル構文を使用する必要があります。" -#: awx/main/fields.py:940 +#: awx/main/fields.py:948 #, python-brace-format msgid "{sub_key} uses an undefined field ({error_msg})" msgstr "{sub_key} は未定義のフィールドを使用します ({error_msg})" -#: awx/main/fields.py:947 +#: awx/main/fields.py:955 +msgid "Encountered unsafe code execution: {}" +msgstr "unsafe コードの実行が発生しました: {}" + +#: awx/main/fields.py:959 #, python-brace-format msgid "" "Syntax error rendering template for {sub_key} inside of {type} ({error_msg})" @@ -3037,6 +3045,50 @@ msgid "" "this list to programmatically generate named URLs for resources" msgstr "名前付き URL グラフトポロジーを公開するキーと値のペアの読み取り専用一覧です。この一覧を使用してリソースの名前付き URL をプログラムで生成します。" +#: awx/main/migrations/_inventory_source_vars.py:140 +msgid "Image ID" +msgstr "イメージ ID" + +#: awx/main/migrations/_inventory_source_vars.py:141 +msgid "Availability Zone" +msgstr "アベイラビリティーゾーン" + +#: awx/main/migrations/_inventory_source_vars.py:143 +msgid "Instance ID" +msgstr "インスタンス ID" + +#: awx/main/migrations/_inventory_source_vars.py:144 +msgid "Instance State" +msgstr "インスタンスの状態" + +#: awx/main/migrations/_inventory_source_vars.py:145 +msgid "Platform" +msgstr "プラットフォーム" + +#: awx/main/migrations/_inventory_source_vars.py:146 +msgid "Instance Type" +msgstr "インスタンスタイプ" + +#: awx/main/migrations/_inventory_source_vars.py:148 +msgid "Region" +msgstr "リージョン" + +#: awx/main/migrations/_inventory_source_vars.py:149 +msgid "Security Group" +msgstr "セキュリティーグループ" + +#: awx/main/migrations/_inventory_source_vars.py:150 +msgid "Tags" +msgstr "タグ" + +#: awx/main/migrations/_inventory_source_vars.py:151 +msgid "Tag None" +msgstr "タグ None" + +#: awx/main/migrations/_inventory_source_vars.py:152 +msgid "VPC ID" +msgstr "VPC ID" + #: awx/main/models/activity_stream.py:28 msgid "Entity Created" msgstr "エンティティーの作成" @@ -3083,17 +3135,17 @@ msgstr "アドホックコマンドのサポートされていないモジュー msgid "No argument passed to %s module." msgstr "%s モジュールに渡される引数はありません。" -#: awx/main/models/base.py:33 awx/main/models/base.py:39 -#: awx/main/models/base.py:44 awx/main/models/base.py:49 +#: awx/main/models/base.py:34 awx/main/models/base.py:40 +#: awx/main/models/base.py:45 awx/main/models/base.py:50 msgid "Run" msgstr "実行" -#: awx/main/models/base.py:34 awx/main/models/base.py:40 -#: awx/main/models/base.py:45 awx/main/models/base.py:50 +#: awx/main/models/base.py:35 awx/main/models/base.py:41 +#: awx/main/models/base.py:46 awx/main/models/base.py:51 msgid "Check" msgstr "チェック" -#: awx/main/models/base.py:35 +#: awx/main/models/base.py:36 msgid "Scan" msgstr "スキャン" @@ -3103,815 +3155,822 @@ msgid "" "Tower documentation for details on each type." msgstr "作成する必要のある証明書のタイプを指定します。それぞれのタイプの詳細については、Ansible Tower ドキュメントを参照してください。" -#: awx/main/models/credential/__init__.py:110 -#: awx/main/models/credential/__init__.py:353 +#: awx/main/models/credential/__init__.py:114 +#: awx/main/models/credential/__init__.py:358 msgid "" "Enter inputs using either JSON or YAML syntax. Refer to the Ansible Tower " "documentation for example syntax." msgstr "JSON または YAML 構文のいずれかを使用して入力を行います。構文のサンプルについては Ansible Tower ドキュメントを参照してください。" -#: awx/main/models/credential/__init__.py:325 -#: awx/main/models/credential/__init__.py:594 +#: awx/main/models/credential/__init__.py:329 +#: awx/main/models/credential/__init__.py:602 msgid "Machine" msgstr "マシン" -#: awx/main/models/credential/__init__.py:326 -#: awx/main/models/credential/__init__.py:680 +#: awx/main/models/credential/__init__.py:330 +#: awx/main/models/credential/__init__.py:688 msgid "Vault" msgstr "Vault" -#: awx/main/models/credential/__init__.py:327 -#: awx/main/models/credential/__init__.py:707 +#: awx/main/models/credential/__init__.py:331 +#: awx/main/models/credential/__init__.py:715 msgid "Network" msgstr "ネットワーク" -#: awx/main/models/credential/__init__.py:328 -#: awx/main/models/credential/__init__.py:649 +#: awx/main/models/credential/__init__.py:332 +#: awx/main/models/credential/__init__.py:657 msgid "Source Control" msgstr "ソースコントロール" -#: awx/main/models/credential/__init__.py:329 +#: awx/main/models/credential/__init__.py:333 msgid "Cloud" msgstr "クラウド" -#: awx/main/models/credential/__init__.py:330 +#: awx/main/models/credential/__init__.py:334 msgid "Personal Access Token" msgstr "パーソナルアクセストークン" -#: awx/main/models/credential/__init__.py:331 -#: awx/main/models/credential/__init__.py:1015 +#: awx/main/models/credential/__init__.py:335 +#: awx/main/models/credential/__init__.py:1027 msgid "Insights" msgstr "Insights" -#: awx/main/models/credential/__init__.py:332 +#: awx/main/models/credential/__init__.py:336 msgid "External" msgstr "外部" -#: awx/main/models/credential/__init__.py:333 +#: awx/main/models/credential/__init__.py:337 msgid "Kubernetes" msgstr "Kubernetes" -#: awx/main/models/credential/__init__.py:359 +#: awx/main/models/credential/__init__.py:338 +msgid "Galaxy/Automation Hub" +msgstr "Galaxy / Automation Hub" + +#: awx/main/models/credential/__init__.py:364 msgid "" "Enter injectors using either JSON or YAML syntax. Refer to the Ansible Tower " "documentation for example syntax." msgstr "JSON または YAML 構文のいずれかを使用してインジェクターを入力します。構文のサンプルについては Ansible Tower ドキュメントを参照してください。" -#: awx/main/models/credential/__init__.py:428 +#: awx/main/models/credential/__init__.py:433 #, python-format msgid "adding %s credential type" msgstr "%s 認証情報タイプの追加" -#: awx/main/models/credential/__init__.py:602 -#: awx/main/models/credential/__init__.py:658 -#: awx/main/models/credential/__init__.py:716 -#: awx/main/models/credential/__init__.py:838 -#: awx/main/models/credential/__init__.py:864 -#: awx/main/models/credential/__init__.py:891 -#: awx/main/models/credential/__init__.py:951 -#: awx/main/models/credential/__init__.py:1024 -#: awx/main/models/credential/__init__.py:1055 -#: awx/main/models/credential/__init__.py:1105 +#: awx/main/models/credential/__init__.py:610 +#: awx/main/models/credential/__init__.py:666 +#: awx/main/models/credential/__init__.py:724 +#: awx/main/models/credential/__init__.py:850 +#: awx/main/models/credential/__init__.py:876 +#: awx/main/models/credential/__init__.py:903 +#: awx/main/models/credential/__init__.py:963 +#: awx/main/models/credential/__init__.py:1036 +#: awx/main/models/credential/__init__.py:1067 +#: awx/main/models/credential/__init__.py:1119 msgid "Password" msgstr "パスワード" -#: awx/main/models/credential/__init__.py:608 -#: awx/main/models/credential/__init__.py:721 +#: awx/main/models/credential/__init__.py:616 +#: awx/main/models/credential/__init__.py:729 msgid "SSH Private Key" msgstr "SSH 秘密鍵" -#: awx/main/models/credential/__init__.py:615 +#: awx/main/models/credential/__init__.py:623 msgid "Signed SSH Certificate" msgstr "署名済みの SSH 証明書" -#: awx/main/models/credential/__init__.py:621 -#: awx/main/models/credential/__init__.py:670 -#: awx/main/models/credential/__init__.py:728 +#: awx/main/models/credential/__init__.py:629 +#: awx/main/models/credential/__init__.py:678 +#: awx/main/models/credential/__init__.py:736 msgid "Private Key Passphrase" msgstr "秘密鍵のパスフレーズ" -#: awx/main/models/credential/__init__.py:627 +#: awx/main/models/credential/__init__.py:635 msgid "Privilege Escalation Method" msgstr "権限昇格方法" -#: awx/main/models/credential/__init__.py:629 +#: awx/main/models/credential/__init__.py:637 msgid "" "Specify a method for \"become\" operations. This is equivalent to specifying " "the --become-method Ansible parameter." msgstr "「become」操作の方式を指定します。これは --become-method Ansible パラメーターを指定することに相当します。" -#: awx/main/models/credential/__init__.py:634 +#: awx/main/models/credential/__init__.py:642 msgid "Privilege Escalation Username" msgstr "権限昇格のユーザー名" -#: awx/main/models/credential/__init__.py:638 +#: awx/main/models/credential/__init__.py:646 msgid "Privilege Escalation Password" msgstr "権限昇格のパスワード" -#: awx/main/models/credential/__init__.py:663 +#: awx/main/models/credential/__init__.py:671 msgid "SCM Private Key" msgstr "SCM 秘密鍵" -#: awx/main/models/credential/__init__.py:685 +#: awx/main/models/credential/__init__.py:693 msgid "Vault Password" msgstr "Vault パスワード" -#: awx/main/models/credential/__init__.py:691 +#: awx/main/models/credential/__init__.py:699 msgid "Vault Identifier" msgstr "Vault ID" -#: awx/main/models/credential/__init__.py:694 +#: awx/main/models/credential/__init__.py:702 msgid "" "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+." msgstr "(オプションの) Vault ID を指定します。これは、複数の Vault パスワードを指定するために --vault-id Ansible パラメーターを指定することに相当します。注: この機能は Ansible 2.4+ でのみ機能します。" -#: awx/main/models/credential/__init__.py:733 +#: awx/main/models/credential/__init__.py:741 msgid "Authorize" msgstr "承認" -#: awx/main/models/credential/__init__.py:737 +#: awx/main/models/credential/__init__.py:745 msgid "Authorize Password" msgstr "パスワードの承認" -#: awx/main/models/credential/__init__.py:751 +#: awx/main/models/credential/__init__.py:759 msgid "Amazon Web Services" msgstr "Amazon Web Services" -#: awx/main/models/credential/__init__.py:756 +#: awx/main/models/credential/__init__.py:764 msgid "Access Key" msgstr "アクセスキー" -#: awx/main/models/credential/__init__.py:760 +#: awx/main/models/credential/__init__.py:768 msgid "Secret Key" msgstr "シークレットキー" -#: awx/main/models/credential/__init__.py:765 +#: awx/main/models/credential/__init__.py:773 msgid "STS Token" msgstr "STS トークン" -#: awx/main/models/credential/__init__.py:768 +#: awx/main/models/credential/__init__.py:776 msgid "" "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." msgstr "セキュリティートークンサービス (STS) は、AWS Identity and Access Management (IAM) ユーザーの一時的な、権限の制限された認証情報を要求できる web サービスです。" -#: awx/main/models/credential/__init__.py:780 awx/main/models/inventory.py:833 +#: awx/main/models/credential/__init__.py:788 awx/main/models/inventory.py:826 msgid "OpenStack" msgstr "OpenStack" -#: awx/main/models/credential/__init__.py:789 +#: awx/main/models/credential/__init__.py:797 msgid "Password (API Key)" msgstr "パスワード (API キー)" -#: awx/main/models/credential/__init__.py:794 -#: awx/main/models/credential/__init__.py:1046 +#: awx/main/models/credential/__init__.py:802 +#: awx/main/models/credential/__init__.py:1058 msgid "Host (Authentication URL)" msgstr "ホスト (認証 URL)" -#: awx/main/models/credential/__init__.py:796 +#: awx/main/models/credential/__init__.py:804 msgid "" "The host to authenticate with. For example, https://openstack.business.com/" "v2.0/" msgstr "認証に使用するホスト。例: https://openstack.business.com/v2.0/" -#: awx/main/models/credential/__init__.py:800 +#: awx/main/models/credential/__init__.py:808 msgid "Project (Tenant Name)" msgstr "プロジェクト (テナント名)" -#: awx/main/models/credential/__init__.py:804 +#: awx/main/models/credential/__init__.py:812 +msgid "Project (Domain Name)" +msgstr "プロジェクト (ドメイン名)" + +#: awx/main/models/credential/__init__.py:816 msgid "Domain Name" msgstr "ドメイン名" -#: awx/main/models/credential/__init__.py:806 +#: awx/main/models/credential/__init__.py:818 msgid "" "OpenStack domains define administrative boundaries. It is only needed for " "Keystone v3 authentication URLs. Refer to Ansible Tower documentation for " "common scenarios." msgstr "OpenStack ドメインは管理上の境界を定義します。これは Keystone v3 認証 URL にのみ必要です。共通するシナリオについては Ansible Tower ドキュメントを参照してください。" -#: awx/main/models/credential/__init__.py:812 -#: awx/main/models/credential/__init__.py:1110 -#: awx/main/models/credential/__init__.py:1144 +#: awx/main/models/credential/__init__.py:824 +#: awx/main/models/credential/__init__.py:1131 +#: awx/main/models/credential/__init__.py:1166 msgid "Verify SSL" msgstr "SSL の検証" -#: awx/main/models/credential/__init__.py:823 awx/main/models/inventory.py:830 +#: awx/main/models/credential/__init__.py:835 awx/main/models/inventory.py:824 msgid "VMware vCenter" msgstr "VMware vCenter" -#: awx/main/models/credential/__init__.py:828 +#: awx/main/models/credential/__init__.py:840 msgid "VCenter Host" msgstr "vCenter ホスト" -#: awx/main/models/credential/__init__.py:830 +#: awx/main/models/credential/__init__.py:842 msgid "" "Enter the hostname or IP address that corresponds to your VMware vCenter." msgstr "VMware vCenter に対応するホスト名または IP アドレスを入力します。" -#: awx/main/models/credential/__init__.py:849 awx/main/models/inventory.py:831 +#: awx/main/models/credential/__init__.py:861 awx/main/models/inventory.py:825 msgid "Red Hat Satellite 6" msgstr "Red Hat Satellite 6" -#: awx/main/models/credential/__init__.py:854 +#: awx/main/models/credential/__init__.py:866 msgid "Satellite 6 URL" msgstr "Satellite 6 URL" -#: awx/main/models/credential/__init__.py:856 +#: awx/main/models/credential/__init__.py:868 msgid "" "Enter the URL that corresponds to your Red Hat Satellite 6 server. For " "example, https://satellite.example.org" msgstr "Red Hat Satellite 6 Server に対応する URL を入力します (例: https://satellite.example.org)。" -#: awx/main/models/credential/__init__.py:875 awx/main/models/inventory.py:832 +#: awx/main/models/credential/__init__.py:887 msgid "Red Hat CloudForms" msgstr "Red Hat CloudForms" -#: awx/main/models/credential/__init__.py:880 +#: awx/main/models/credential/__init__.py:892 msgid "CloudForms URL" msgstr "CloudForms URL" -#: awx/main/models/credential/__init__.py:882 +#: awx/main/models/credential/__init__.py:894 msgid "" "Enter the URL for the virtual machine that corresponds to your CloudForms " "instance. For example, https://cloudforms.example.org" msgstr "CloudForms インスタンスに対応する仮想マシンの URL を入力します (例: https://cloudforms.example.org)。" -#: awx/main/models/credential/__init__.py:902 awx/main/models/inventory.py:828 +#: awx/main/models/credential/__init__.py:914 awx/main/models/inventory.py:822 msgid "Google Compute Engine" msgstr "Google Compute Engine" -#: awx/main/models/credential/__init__.py:907 +#: awx/main/models/credential/__init__.py:919 msgid "Service Account Email Address" msgstr "サービスアカウントのメールアドレス" -#: awx/main/models/credential/__init__.py:909 +#: awx/main/models/credential/__init__.py:921 msgid "" "The email address assigned to the Google Compute Engine service account." msgstr "Google Compute Engine サービスアカウントに割り当てられたメールアドレス。" -#: awx/main/models/credential/__init__.py:915 +#: awx/main/models/credential/__init__.py:927 msgid "" "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" msgstr "プロジェクト ID は GCE によって割り当てられる識別情報です。これは 3 語か、または 2 語とそれに続く 3 桁の数字のいずれかで構成されます。例: project-id-000、another-project-id" -#: awx/main/models/credential/__init__.py:921 +#: awx/main/models/credential/__init__.py:933 msgid "RSA Private Key" msgstr "RSA 秘密鍵" -#: awx/main/models/credential/__init__.py:926 +#: awx/main/models/credential/__init__.py:938 msgid "" "Paste the contents of the PEM file associated with the service account email." msgstr "サービスアカウントメールに関連付けられた PEM ファイルの内容を貼り付けます。" -#: awx/main/models/credential/__init__.py:936 awx/main/models/inventory.py:829 +#: awx/main/models/credential/__init__.py:948 awx/main/models/inventory.py:823 msgid "Microsoft Azure Resource Manager" msgstr "Microsoft Azure Resource Manager" -#: awx/main/models/credential/__init__.py:941 +#: awx/main/models/credential/__init__.py:953 msgid "Subscription ID" msgstr "サブスクリプション ID" -#: awx/main/models/credential/__init__.py:943 +#: awx/main/models/credential/__init__.py:955 msgid "Subscription ID is an Azure construct, which is mapped to a username." msgstr "サブスクリプション ID は、ユーザー名にマップされる Azure コンストラクトです。" -#: awx/main/models/credential/__init__.py:969 +#: awx/main/models/credential/__init__.py:981 msgid "Azure Cloud Environment" msgstr "Azure クラウド環境" -#: awx/main/models/credential/__init__.py:971 +#: awx/main/models/credential/__init__.py:983 msgid "" "Environment variable AZURE_CLOUD_ENVIRONMENT when using Azure GovCloud or " "Azure stack." msgstr "Azure GovCloud または Azure スタック使用時の環境変数 AZURE_CLOUD_ENVIRONMENT。" -#: awx/main/models/credential/__init__.py:981 +#: awx/main/models/credential/__init__.py:993 msgid "GitHub Personal Access Token" msgstr "GitHub パーソナルアクセストークン" -#: awx/main/models/credential/__init__.py:989 +#: awx/main/models/credential/__init__.py:1001 msgid "This token needs to come from your profile settings in GitHub" msgstr "このトークンは GitHub のプロファイル設定から取得する必要があります。" -#: awx/main/models/credential/__init__.py:998 +#: awx/main/models/credential/__init__.py:1010 msgid "GitLab Personal Access Token" msgstr "GitLab パーソナルアクセストークン" -#: awx/main/models/credential/__init__.py:1006 +#: awx/main/models/credential/__init__.py:1018 msgid "This token needs to come from your profile settings in GitLab" msgstr "このトークンは GitLab のプロファイル設定から取得する必要があります。" -#: awx/main/models/credential/__init__.py:1041 awx/main/models/inventory.py:834 +#: awx/main/models/credential/__init__.py:1053 awx/main/models/inventory.py:827 msgid "Red Hat Virtualization" msgstr "Red Hat Virtualization" -#: awx/main/models/credential/__init__.py:1048 +#: awx/main/models/credential/__init__.py:1060 msgid "The host to authenticate with." msgstr "認証するホスト。" -#: awx/main/models/credential/__init__.py:1060 +#: awx/main/models/credential/__init__.py:1072 msgid "CA File" msgstr "CA ファイル" -#: awx/main/models/credential/__init__.py:1062 +#: awx/main/models/credential/__init__.py:1074 msgid "Absolute file path to the CA file to use (optional)" msgstr "使用する CA ファイルへの絶対ファイルパス (オプション)" -#: awx/main/models/credential/__init__.py:1091 awx/main/models/inventory.py:835 +#: awx/main/models/credential/__init__.py:1103 awx/main/models/inventory.py:828 msgid "Ansible Tower" msgstr "Ansible Tower" -#: awx/main/models/credential/__init__.py:1096 +#: awx/main/models/credential/__init__.py:1108 msgid "Ansible Tower Hostname" msgstr "Ansible Tower ホスト名" -#: awx/main/models/credential/__init__.py:1098 +#: awx/main/models/credential/__init__.py:1110 msgid "The Ansible Tower base URL to authenticate with." msgstr "認証で使用する Ansible Tower ベース URL。" -#: awx/main/models/credential/__init__.py:1130 +#: awx/main/models/credential/__init__.py:1115 +msgid "" +"The Ansible Tower user to authenticate as.This should not be set if an OAuth " +"token is being used." +msgstr "認証する AnsibleTower ユーザー。OAuth トークンが使用されている場合は、これを設定しないでください。" + +#: awx/main/models/credential/__init__.py:1124 +msgid "OAuth Token" +msgstr "OAuth トークン" + +#: awx/main/models/credential/__init__.py:1127 +msgid "" +"An OAuth token to use to authenticate to Tower with.This should not be set " +"if username/password are being used." +msgstr "Tower への認証に使用する OAuth トークン。ユーザー名/パスワードが使用されている場合は設定しないでください。" + +#: awx/main/models/credential/__init__.py:1152 msgid "OpenShift or Kubernetes API Bearer Token" msgstr "OpenShift または Kubernetes API Bearer トークン" -#: awx/main/models/credential/__init__.py:1134 +#: awx/main/models/credential/__init__.py:1156 msgid "OpenShift or Kubernetes API Endpoint" msgstr "OpenShift または Kubernetes API エンドポイント" -#: awx/main/models/credential/__init__.py:1136 +#: awx/main/models/credential/__init__.py:1158 msgid "The OpenShift or Kubernetes API Endpoint to authenticate with." msgstr "認証する OpenShift または Kubernetes API エンドポイント。" -#: awx/main/models/credential/__init__.py:1139 +#: awx/main/models/credential/__init__.py:1161 msgid "API authentication bearer token" msgstr "API 認証ベアラートークン" -#: awx/main/models/credential/__init__.py:1149 +#: awx/main/models/credential/__init__.py:1171 msgid "Certificate Authority data" msgstr "認証局データ" +#: awx/main/models/credential/__init__.py:1184 +msgid "Ansible Galaxy/Automation Hub API Token" +msgstr "Ansible Galaxy/Automation Hub API トークン" + +#: awx/main/models/credential/__init__.py:1188 +msgid "Galaxy Server URL" +msgstr "Galaxy Server URL" + #: awx/main/models/credential/__init__.py:1190 +msgid "The URL of the Galaxy instance to connect to." +msgstr "接続する Galaxy インスタンスの URL。" + +#: awx/main/models/credential/__init__.py:1193 +msgid "Auth Server URL" +msgstr "認証サーバー URL" + +#: awx/main/models/credential/__init__.py:1196 +msgid "The URL of a Keycloak server token_endpoint, if using SSO auth." +msgstr "SSO 認証を使用している場合は、Keycloak サーバー token_endpoint の URL。" + +#: awx/main/models/credential/__init__.py:1201 +msgid "API Token" +msgstr "API トークン" + +#: awx/main/models/credential/__init__.py:1205 +msgid "A token to use for authentication against the Galaxy instance." +msgstr "Galaxy インスタンスに対する認証に使用するトークン。" + +#: awx/main/models/credential/__init__.py:1244 msgid "Target must be a non-external credential" msgstr "ターゲットには、外部の認証情報以外を使用してください。" -#: awx/main/models/credential/__init__.py:1195 +#: awx/main/models/credential/__init__.py:1249 msgid "Source must be an external credential" msgstr "ソースは、外部の認証情報でなければなりません。" -#: awx/main/models/credential/__init__.py:1202 +#: awx/main/models/credential/__init__.py:1256 msgid "Input field must be defined on target credential (options are {})." msgstr "入力フィールドは、ターゲットの認証情報 (オプションは {}) で定義する必要があります。" -#: awx/main/models/events.py:152 awx/main/models/events.py:674 +#: awx/main/models/events.py:165 awx/main/models/events.py:707 msgid "Host Failed" msgstr "ホストの失敗" -#: awx/main/models/events.py:153 +#: awx/main/models/events.py:166 msgid "Host Started" msgstr "ホストの開始" -#: awx/main/models/events.py:154 awx/main/models/events.py:675 +#: awx/main/models/events.py:167 awx/main/models/events.py:708 msgid "Host OK" msgstr "ホスト OK" -#: awx/main/models/events.py:155 +#: awx/main/models/events.py:168 msgid "Host Failure" msgstr "ホストの失敗" -#: awx/main/models/events.py:156 awx/main/models/events.py:681 +#: awx/main/models/events.py:169 awx/main/models/events.py:714 msgid "Host Skipped" msgstr "ホストがスキップされました" -#: awx/main/models/events.py:157 awx/main/models/events.py:676 +#: awx/main/models/events.py:170 awx/main/models/events.py:709 msgid "Host Unreachable" msgstr "ホストに到達できません" -#: awx/main/models/events.py:158 awx/main/models/events.py:172 +#: awx/main/models/events.py:171 awx/main/models/events.py:185 msgid "No Hosts Remaining" msgstr "残りのホストがありません" -#: awx/main/models/events.py:159 +#: awx/main/models/events.py:172 msgid "Host Polling" msgstr "ホストのポーリング" -#: awx/main/models/events.py:160 +#: awx/main/models/events.py:173 msgid "Host Async OK" msgstr "ホストの非同期 OK" -#: awx/main/models/events.py:161 +#: awx/main/models/events.py:174 msgid "Host Async Failure" msgstr "ホストの非同期失敗" -#: awx/main/models/events.py:162 +#: awx/main/models/events.py:175 msgid "Item OK" msgstr "項目 OK" -#: awx/main/models/events.py:163 +#: awx/main/models/events.py:176 msgid "Item Failed" msgstr "項目の失敗" -#: awx/main/models/events.py:164 +#: awx/main/models/events.py:177 msgid "Item Skipped" msgstr "項目のスキップ" -#: awx/main/models/events.py:165 +#: awx/main/models/events.py:178 msgid "Host Retry" msgstr "ホストの再試行" -#: awx/main/models/events.py:167 +#: awx/main/models/events.py:180 msgid "File Difference" msgstr "ファイルの相違点" -#: awx/main/models/events.py:168 +#: awx/main/models/events.py:181 msgid "Playbook Started" msgstr "Playbook の開始" -#: awx/main/models/events.py:169 +#: awx/main/models/events.py:182 msgid "Running Handlers" msgstr "実行中のハンドラー" -#: awx/main/models/events.py:170 +#: awx/main/models/events.py:183 msgid "Including File" msgstr "組み込みファイル" -#: awx/main/models/events.py:171 +#: awx/main/models/events.py:184 msgid "No Hosts Matched" msgstr "一致するホストがありません" -#: awx/main/models/events.py:173 +#: awx/main/models/events.py:186 msgid "Task Started" msgstr "タスクの開始" -#: awx/main/models/events.py:175 +#: awx/main/models/events.py:188 msgid "Variables Prompted" msgstr "変数のプロモート" -#: awx/main/models/events.py:176 +#: awx/main/models/events.py:189 msgid "Gathering Facts" msgstr "ファクトの収集" -#: awx/main/models/events.py:177 +#: awx/main/models/events.py:190 msgid "internal: on Import for Host" msgstr "内部: ホストのインポート時" -#: awx/main/models/events.py:178 +#: awx/main/models/events.py:191 msgid "internal: on Not Import for Host" msgstr "内部: ホストの非インポート時" -#: awx/main/models/events.py:179 +#: awx/main/models/events.py:192 msgid "Play Started" msgstr "プレイの開始" -#: awx/main/models/events.py:180 +#: awx/main/models/events.py:193 msgid "Playbook Complete" msgstr "Playbook の完了" -#: awx/main/models/events.py:184 awx/main/models/events.py:691 +#: awx/main/models/events.py:197 awx/main/models/events.py:724 msgid "Debug" msgstr "デバッグ" -#: awx/main/models/events.py:185 awx/main/models/events.py:692 +#: awx/main/models/events.py:198 awx/main/models/events.py:725 msgid "Verbose" msgstr "詳細" -#: awx/main/models/events.py:186 awx/main/models/events.py:693 +#: awx/main/models/events.py:199 awx/main/models/events.py:726 msgid "Deprecated" msgstr "非推奨" -#: awx/main/models/events.py:187 awx/main/models/events.py:694 +#: awx/main/models/events.py:200 awx/main/models/events.py:727 msgid "Warning" msgstr "警告" -#: awx/main/models/events.py:188 awx/main/models/events.py:695 +#: awx/main/models/events.py:201 awx/main/models/events.py:728 msgid "System Warning" msgstr "システム警告" -#: awx/main/models/events.py:189 awx/main/models/events.py:696 +#: awx/main/models/events.py:202 awx/main/models/events.py:729 #: awx/main/models/unified_jobs.py:75 msgid "Error" msgstr "エラー" -#: awx/main/models/ha.py:175 +#: awx/main/models/ha.py:184 msgid "Instances that are members of this InstanceGroup" msgstr "このインスタンスグループのメンバーであるインスタンス" -#: awx/main/models/ha.py:180 +#: awx/main/models/ha.py:189 msgid "Instance Group to remotely control this group." msgstr "このグループをリモートで制御するためのインスタンスグループ" -#: awx/main/models/ha.py:200 +#: awx/main/models/ha.py:209 msgid "Percentage of Instances to automatically assign to this group" msgstr "このグループに自動的に割り当てるインスタンスの割合" -#: awx/main/models/ha.py:204 +#: awx/main/models/ha.py:213 msgid "" "Static minimum number of Instances to automatically assign to this group" msgstr "このグループに自動的に割り当てるインスタンスの静的な最小数。" -#: awx/main/models/ha.py:209 +#: awx/main/models/ha.py:218 msgid "" "List of exact-match Instances that will always be automatically assigned to " "this group" msgstr "このグループに常に自動的に割り当てられる完全一致のインスタンスの一覧" -#: awx/main/models/inventory.py:80 +#: awx/main/models/inventory.py:74 msgid "Hosts have a direct link to this inventory." msgstr "ホストにはこのインベントリーへの直接のリンクがあります。" -#: awx/main/models/inventory.py:81 +#: awx/main/models/inventory.py:75 msgid "Hosts for inventory generated using the host_filter property." msgstr "host_filter プロパティーを使用して生成されたインベントリーのホスト。" -#: awx/main/models/inventory.py:86 +#: awx/main/models/inventory.py:80 msgid "inventories" msgstr "インベントリー" -#: awx/main/models/inventory.py:93 +#: awx/main/models/inventory.py:87 msgid "Organization containing this inventory." msgstr "このインベントリーを含む組織。" -#: awx/main/models/inventory.py:100 +#: awx/main/models/inventory.py:94 msgid "Inventory variables in JSON or YAML format." msgstr "JSON または YAML 形式のインベントリー変数。" -#: awx/main/models/inventory.py:105 +#: awx/main/models/inventory.py:99 msgid "" "This field is deprecated and will be removed in a future release. Flag " "indicating whether any hosts in this inventory have failed." msgstr "このフィールドは非推奨で、今後のリリースで削除予定です。このインベントリーのホストが失敗したかどうかを示すフラグ。" -#: awx/main/models/inventory.py:111 +#: awx/main/models/inventory.py:105 msgid "" "This field is deprecated and will be removed in a future release. Total " "number of hosts in this inventory." msgstr "このフィールドは非推奨で、今後のリリースで削除予定です。このインベントリーでの合計ホスト数。" -#: awx/main/models/inventory.py:117 +#: awx/main/models/inventory.py:111 msgid "" "This field is deprecated and will be removed in a future release. Number of " "hosts in this inventory with active failures." msgstr "このフィールドは非推奨で、今後のリリースで削除予定です。このインベントリーで障害が発生中のホスト数。" -#: awx/main/models/inventory.py:123 +#: awx/main/models/inventory.py:117 msgid "" "This field is deprecated and will be removed in a future release. Total " "number of groups in this inventory." msgstr "このフィールドは非推奨で、今後のリリースで削除予定です。このインベントリーでの合計グループ数。" -#: awx/main/models/inventory.py:129 +#: awx/main/models/inventory.py:123 msgid "" "This field is deprecated and will be removed in a future release. Flag " "indicating whether this inventory has any external inventory sources." msgstr "このフィールドは非推奨で、今後のリリースで削除予定です。このインベントリーに外部のインベントリーソースがあるかどうかを示すフラグ。" -#: awx/main/models/inventory.py:135 +#: awx/main/models/inventory.py:129 msgid "" "Total number of external inventory sources configured within this inventory." msgstr "このインベントリー内で設定される外部インベントリーソースの合計数。" -#: awx/main/models/inventory.py:140 +#: awx/main/models/inventory.py:134 msgid "Number of external inventory sources in this inventory with failures." msgstr "エラーのあるこのインベントリー内の外部インベントリーソースの数。" -#: awx/main/models/inventory.py:147 +#: awx/main/models/inventory.py:141 msgid "Kind of inventory being represented." msgstr "表示されているインベントリーの種類。" -#: awx/main/models/inventory.py:153 +#: awx/main/models/inventory.py:147 msgid "Filter that will be applied to the hosts of this inventory." msgstr "このインべントリーのホストに適用されるフィルター。" -#: awx/main/models/inventory.py:181 +#: awx/main/models/inventory.py:175 msgid "" "Credentials to be used by hosts belonging to this inventory when accessing " "Red Hat Insights API." msgstr "Red Hat Insights API へのアクセス時にこのインベントリーに属するホストによって使用される認証情報。" -#: awx/main/models/inventory.py:190 +#: awx/main/models/inventory.py:184 msgid "Flag indicating the inventory is being deleted." msgstr "このインベントリーが削除されていることを示すフラグ。" -#: awx/main/models/inventory.py:245 +#: awx/main/models/inventory.py:239 msgid "Could not parse subset as slice specification." msgstr "サブセットをスライスの詳細として解析できませんでした。" -#: awx/main/models/inventory.py:249 +#: awx/main/models/inventory.py:243 msgid "Slice number must be less than total number of slices." msgstr "スライス番号はスライスの合計数より小さくなければなりません。" -#: awx/main/models/inventory.py:251 +#: awx/main/models/inventory.py:245 msgid "Slice number must be 1 or higher." msgstr "スライス番号は 1 以上でなければなりません。" -#: awx/main/models/inventory.py:388 +#: awx/main/models/inventory.py:382 msgid "Assignment not allowed for Smart Inventory" msgstr "割り当てはスマートインベントリーでは許可されません" -#: awx/main/models/inventory.py:390 awx/main/models/projects.py:166 +#: awx/main/models/inventory.py:384 awx/main/models/projects.py:167 msgid "Credential kind must be 'insights'." msgstr "認証情報の種類は「insights」である必要があります。" -#: awx/main/models/inventory.py:475 +#: awx/main/models/inventory.py:469 msgid "Is this host online and available for running jobs?" msgstr "このホストはオンラインで、ジョブを実行するために利用できますか?" -#: awx/main/models/inventory.py:481 +#: awx/main/models/inventory.py:475 msgid "" "The value used by the remote inventory source to uniquely identify the host" msgstr "ホストを一意に識別するためにリモートインベントリーソースで使用される値" -#: awx/main/models/inventory.py:486 +#: awx/main/models/inventory.py:480 msgid "Host variables in JSON or YAML format." msgstr "JSON または YAML 形式のホスト変数。" -#: awx/main/models/inventory.py:509 +#: awx/main/models/inventory.py:503 msgid "Inventory source(s) that created or modified this host." msgstr "このホストを作成または変更したインベントリーソース。" -#: awx/main/models/inventory.py:514 +#: awx/main/models/inventory.py:508 msgid "Arbitrary JSON structure of most recent ansible_facts, per-host." msgstr "ホスト別の最新 ansible_facts の任意の JSON 構造。" -#: awx/main/models/inventory.py:520 +#: awx/main/models/inventory.py:514 msgid "The date and time ansible_facts was last modified." msgstr "ansible_facts の最終変更日時。" -#: awx/main/models/inventory.py:527 +#: awx/main/models/inventory.py:521 msgid "Red Hat Insights host unique identifier." msgstr "Red Hat Insights ホスト固有 ID。" -#: awx/main/models/inventory.py:641 +#: awx/main/models/inventory.py:635 msgid "Group variables in JSON or YAML format." msgstr "JSON または YAML 形式のグループ変数。" -#: awx/main/models/inventory.py:647 +#: awx/main/models/inventory.py:641 msgid "Hosts associated directly with this group." msgstr "このグループに直接関連付けられたホスト。" -#: awx/main/models/inventory.py:653 +#: awx/main/models/inventory.py:647 msgid "Inventory source(s) that created or modified this group." msgstr "このグループを作成または変更したインベントリーソース。" -#: awx/main/models/inventory.py:825 +#: awx/main/models/inventory.py:819 msgid "File, Directory or Script" msgstr "ファイル、ディレクトリーまたはスクリプト" -#: awx/main/models/inventory.py:826 +#: awx/main/models/inventory.py:820 msgid "Sourced from a Project" msgstr "ソース: プロジェクト" -#: awx/main/models/inventory.py:827 +#: awx/main/models/inventory.py:821 msgid "Amazon EC2" msgstr "Amazon EC2" -#: awx/main/models/inventory.py:836 +#: awx/main/models/inventory.py:829 msgid "Custom Script" msgstr "カスタムスクリプト" -#: awx/main/models/inventory.py:953 +#: awx/main/models/inventory.py:863 msgid "Inventory source variables in YAML or JSON format." msgstr "YAML または JSON 形式のインベントリーソース変数。" -#: awx/main/models/inventory.py:964 +#: awx/main/models/inventory.py:868 msgid "" -"Comma-separated list of filter expressions (EC2 only). Hosts are imported " -"when ANY of the filters match." -msgstr "カンマ区切りのフィルター式の一覧 (EC2 のみ) です。ホストは、フィルターのいずれかが一致する場合にインポートされます。" +"Retrieve the enabled state from the given dict of host variables. The " +"enabled variable may be specified as \"foo.bar\", in which case the lookup " +"will traverse into nested dicts, equivalent to: from_dict.get(\"foo\", {})." +"get(\"bar\", default)" +msgstr "ホスト変数の指定された辞書から有効な状態を取得します。有効な変数は「foo.bar」として指定できます。その場合、ルックアップはネストされた辞書に移動します。これは、from_dict.get(\"foo\", {}).get(\"bar\", default) と同等です。" -#: awx/main/models/inventory.py:970 -msgid "Limit groups automatically created from inventory source (EC2 only)." -msgstr "インベントリーソースから自動的に作成されるグループを制限します (EC2 のみ)。" +#: awx/main/models/inventory.py:876 +msgid "" +"Only used when enabled_var is set. Value when the host is considered " +"enabled. For example if enabled_var=\"status.power_state\"and enabled_value=" +"\"powered_on\" with host variables:{ \"status\": { \"power_state\": " +"\"powered_on\", \"created\": \"2020-08-04T18:13:04+00:00\", \"healthy" +"\": true }, \"name\": \"foobar\", \"ip_address\": \"192.168.2.1\"}" +"The host would be marked enabled. If power_state where any value other than " +"powered_on then the host would be disabled when imported into Tower. If the " +"key is not found then the host will be enabled" +msgstr "enabled_var が設定されている場合にのみ使用されます。ホストが有効と見なされる場合の値です。たとえば、ホスト変数 { \"status\": { \"power_state\": \"powered_on\", \"created\": \"2020-08-04T18:13:04+00:00\", \"healthy\": true }, \"name\": \"foobar\", \"ip_address\": \"192.168.2.1\"} を使用した enabled_var=\"status.power_state\" および enabled_value=\"powered_on\" の場合は、ホストは有効とマークされます。powered_on 以外の値が power_state の場合は、Tower にインポートするとホストは無効になります。キーが見つからない場合は、ホストが有効になります" -#: awx/main/models/inventory.py:974 +#: awx/main/models/inventory.py:896 +msgid "Regex where only matching hosts will be imported into Tower." +msgstr "一致するホストのみが Tower にインポートされる正規表現。" + +#: awx/main/models/inventory.py:900 msgid "Overwrite local groups and hosts from remote inventory source." msgstr "リモートインベントリーソースからのローカルグループおよびホストを上書きします。" -#: awx/main/models/inventory.py:978 +#: awx/main/models/inventory.py:904 msgid "Overwrite local variables from remote inventory source." msgstr "リモートインベントリーソースからのローカル変数を上書きします。" -#: awx/main/models/inventory.py:983 awx/main/models/jobs.py:154 -#: awx/main/models/projects.py:135 +#: awx/main/models/inventory.py:909 awx/main/models/jobs.py:154 +#: awx/main/models/projects.py:136 msgid "The amount of time (in seconds) to run before the task is canceled." msgstr "タスクが取り消される前の実行時間 (秒数)。" -#: awx/main/models/inventory.py:1016 -msgid "Image ID" -msgstr "イメージ ID" - -#: awx/main/models/inventory.py:1017 -msgid "Availability Zone" -msgstr "アベイラビリティーゾーン" - -#: awx/main/models/inventory.py:1019 -msgid "Instance ID" -msgstr "インスタンス ID" - -#: awx/main/models/inventory.py:1020 -msgid "Instance State" -msgstr "インスタンスの状態" - -#: awx/main/models/inventory.py:1021 -msgid "Platform" -msgstr "プラットフォーム" - -#: awx/main/models/inventory.py:1022 -msgid "Instance Type" -msgstr "インスタンスタイプ" - -#: awx/main/models/inventory.py:1024 -msgid "Region" -msgstr "リージョン" - -#: awx/main/models/inventory.py:1025 -msgid "Security Group" -msgstr "セキュリティーグループ" - -#: awx/main/models/inventory.py:1026 -msgid "Tags" -msgstr "タグ" - -#: awx/main/models/inventory.py:1027 -msgid "Tag None" -msgstr "タグ None" - -#: awx/main/models/inventory.py:1028 -msgid "VPC ID" -msgstr "VPC ID" - -#: awx/main/models/inventory.py:1096 +#: awx/main/models/inventory.py:926 #, python-format msgid "" "Cloud-based inventory sources (such as %s) require credentials for the " "matching cloud service." msgstr "クラウドベースのインベントリーソース (%s など) には一致するクラウドサービスの認証情報が必要です。" -#: awx/main/models/inventory.py:1102 +#: awx/main/models/inventory.py:932 msgid "Credential is required for a cloud source." msgstr "認証情報がクラウドソースに必要です。" -#: awx/main/models/inventory.py:1105 +#: awx/main/models/inventory.py:935 msgid "" "Credentials of type machine, source control, insights and vault are " "disallowed for custom inventory sources." msgstr "タイプがマシン、ソースコントロール、Insights および Vault の認証情報はカスタムインベントリーソースには許可されません。" -#: awx/main/models/inventory.py:1110 +#: awx/main/models/inventory.py:940 msgid "" "Credentials of type insights and vault are disallowed for scm inventory " "sources." msgstr "タイプが Insights および Vault の認証情報は SCM のインベントリーソースには許可されません。" -#: awx/main/models/inventory.py:1170 -#, python-format -msgid "Invalid %(source)s region: %(region)s" -msgstr "無効な %(source)s リージョン: %(region)s" - -#: awx/main/models/inventory.py:1194 -#, python-format -msgid "Invalid filter expression: %(filter)s" -msgstr "無効なフィルター式: %(filter)s" - -#: awx/main/models/inventory.py:1215 -#, python-format -msgid "Invalid group by choice: %(choice)s" -msgstr "無効なグループ (選択による): %(choice)s" - -#: awx/main/models/inventory.py:1243 +#: awx/main/models/inventory.py:1004 msgid "Project containing inventory file used as source." msgstr "ソースとして使用されるインベントリーファイルが含まれるプロジェクト。" -#: awx/main/models/inventory.py:1416 +#: awx/main/models/inventory.py:1177 msgid "" "More than one SCM-based inventory source with update on project update per-" "inventory not allowed." msgstr "複数の SCM ベースのインベントリーソースについて、インベントリー別のプロジェクト更新時の更新は許可されません。" -#: awx/main/models/inventory.py:1423 +#: awx/main/models/inventory.py:1184 msgid "" "Cannot update SCM-based inventory source on launch if set to update on " "project update. Instead, configure the corresponding source project to " "update on launch." msgstr "プロジェクト更新時の更新に設定している場合、SCM ベースのインベントリーソースを更新できません。その代わりに起動時に更新するように対応するソースプロジェクトを設定します。" -#: awx/main/models/inventory.py:1429 +#: awx/main/models/inventory.py:1190 msgid "Cannot set source_path if not SCM type." msgstr "SCM タイプでない場合 source_path を設定できません。" -#: awx/main/models/inventory.py:1472 +#: awx/main/models/inventory.py:1233 msgid "" "Inventory files from this Project Update were used for the inventory update." msgstr "このプロジェクト更新のインベントリーファイルがインベントリー更新に使用されました。" -#: awx/main/models/inventory.py:1583 +#: awx/main/models/inventory.py:1344 msgid "Inventory script contents" msgstr "インベントリースクリプトの内容" -#: awx/main/models/inventory.py:1588 +#: awx/main/models/inventory.py:1349 msgid "Organization owning this inventory script" msgstr "このインベントリースクリプトを所有する組織" @@ -3949,89 +4008,89 @@ msgstr "ジョブテンプレートは「inventory」を指定するか、この msgid "Maximum number of forks ({settings.MAX_FORKS}) exceeded." msgstr "フォームの最大数 ({settings.MAX_FORKS}) を超えました。" -#: awx/main/models/jobs.py:463 +#: awx/main/models/jobs.py:459 msgid "Project is missing." msgstr "プロジェクトがありません。" -#: awx/main/models/jobs.py:467 +#: awx/main/models/jobs.py:463 msgid "Project does not allow override of branch." msgstr "プロジェクトではブランチの上書きはできません。" -#: awx/main/models/jobs.py:477 awx/main/models/workflow.py:545 +#: awx/main/models/jobs.py:473 awx/main/models/workflow.py:545 msgid "Field is not configured to prompt on launch." msgstr "フィールドは起動時にプロンプトを出すよう設定されていません。" -#: awx/main/models/jobs.py:483 +#: awx/main/models/jobs.py:479 msgid "Saved launch configurations cannot provide passwords needed to start." msgstr "保存された起動設定は、開始に必要なパスワードを提供しません。" -#: awx/main/models/jobs.py:491 +#: awx/main/models/jobs.py:487 msgid "Job Template {} is missing or undefined." msgstr "ジョブテンプレート {} が見つからないか、または定義されていません。" -#: awx/main/models/jobs.py:574 awx/main/models/projects.py:278 -#: awx/main/models/projects.py:497 +#: awx/main/models/jobs.py:570 awx/main/models/projects.py:284 +#: awx/main/models/projects.py:508 msgid "SCM Revision" msgstr "SCM リビジョン" -#: awx/main/models/jobs.py:575 +#: awx/main/models/jobs.py:571 msgid "The SCM Revision from the Project used for this job, if available" msgstr "このジョブに使用されるプロジェクトからの SCM リビジョン (ある場合)" -#: awx/main/models/jobs.py:583 +#: awx/main/models/jobs.py:579 msgid "" "The SCM Refresh task used to make sure the playbooks were available for the " "job run" msgstr "SCM 更新タスクは、Playbook がジョブの実行で利用可能であったことを確認するために使用されます" -#: awx/main/models/jobs.py:588 +#: awx/main/models/jobs.py:584 msgid "" "If part of a sliced job, the ID of the inventory slice operated on. If not " "part of sliced job, parameter is not used." msgstr "スライスされたジョブの一部の場合には、スライス処理が行われたインベントリーの ID です。スライスされたジョブの一部でなければ、パラメーターは使用されません。" -#: awx/main/models/jobs.py:594 +#: awx/main/models/jobs.py:590 msgid "" "If ran as part of sliced jobs, the total number of slices. If 1, job is not " "part of a sliced job." msgstr "スライスされたジョブの一部として実行された場合には、スライスの合計数です。1 であればジョブはスライスされたジョブの一部ではありません。" -#: awx/main/models/jobs.py:676 +#: awx/main/models/jobs.py:672 #, python-brace-format msgid "{status_value} is not a valid status option." msgstr "{status_value} は、有効なステータスオプションではありません。" -#: awx/main/models/jobs.py:926 +#: awx/main/models/jobs.py:922 msgid "" "Inventory applied as a prompt, assuming job template prompts for inventory" msgstr "インベントリーがプロンプトとして適用されると、ジョブテンプレートでインベントリーをプロンプトで要求することが前提となります。" -#: awx/main/models/jobs.py:1085 +#: awx/main/models/jobs.py:1081 msgid "job host summaries" msgstr "ジョブホストの概要" -#: awx/main/models/jobs.py:1144 +#: awx/main/models/jobs.py:1140 msgid "Remove jobs older than a certain number of days" msgstr "特定の日数より前のジョブを削除" -#: awx/main/models/jobs.py:1145 +#: awx/main/models/jobs.py:1141 msgid "Remove activity stream entries older than a certain number of days" msgstr "特定の日数より前のアクティビティーストリームのエントリーを削除" -#: awx/main/models/jobs.py:1146 +#: awx/main/models/jobs.py:1142 msgid "Removes expired browser sessions from the database" msgstr "期限切れブラウザーセッションをデータベースから削除" -#: awx/main/models/jobs.py:1147 +#: awx/main/models/jobs.py:1143 msgid "Removes expired OAuth 2 access tokens and refresh tokens" msgstr "期限切れの OAuth 2 アクセストークンを削除し、トークンを更新" -#: awx/main/models/jobs.py:1217 +#: awx/main/models/jobs.py:1213 #, python-brace-format msgid "Variables {list_of_keys} are not allowed for system jobs." msgstr "システムジョブでは変数 {list_of_keys} を使用できません。" -#: awx/main/models/jobs.py:1233 +#: awx/main/models/jobs.py:1229 msgid "days must be a positive integer." msgstr "日数は正の整数である必要があります。" @@ -4070,63 +4129,59 @@ msgstr "サービス API のステータスに戻すためのパーソナルア msgid "Unique identifier of the event that triggered this webhook" msgstr "この Webhook をトリガーしたイベントの一意識別子" -#: awx/main/models/notifications.py:42 +#: awx/main/models/notifications.py:41 msgid "Email" msgstr "メール" -#: awx/main/models/notifications.py:43 +#: awx/main/models/notifications.py:42 msgid "Slack" msgstr "Slack" -#: awx/main/models/notifications.py:44 +#: awx/main/models/notifications.py:43 msgid "Twilio" msgstr "Twilio" -#: awx/main/models/notifications.py:45 +#: awx/main/models/notifications.py:44 msgid "Pagerduty" msgstr "Pagerduty" -#: awx/main/models/notifications.py:46 +#: awx/main/models/notifications.py:45 msgid "Grafana" msgstr "Grafana" -#: awx/main/models/notifications.py:47 -msgid "HipChat" -msgstr "HipChat" - -#: awx/main/models/notifications.py:48 awx/main/models/unified_jobs.py:545 +#: awx/main/models/notifications.py:46 awx/main/models/unified_jobs.py:544 msgid "Webhook" msgstr "Webhook" -#: awx/main/models/notifications.py:49 +#: awx/main/models/notifications.py:47 msgid "Mattermost" msgstr "Mattermost" -#: awx/main/models/notifications.py:50 +#: awx/main/models/notifications.py:48 msgid "Rocket.Chat" msgstr "Rocket.Chat" -#: awx/main/models/notifications.py:51 +#: awx/main/models/notifications.py:49 msgid "IRC" msgstr "IRC" -#: awx/main/models/notifications.py:82 +#: awx/main/models/notifications.py:80 msgid "Optional custom messages for notification template." msgstr "通知テンプレートのオプションのカスタムメッセージ" -#: awx/main/models/notifications.py:212 awx/main/models/unified_jobs.py:70 +#: awx/main/models/notifications.py:210 awx/main/models/unified_jobs.py:70 msgid "Pending" msgstr "保留中" -#: awx/main/models/notifications.py:213 awx/main/models/unified_jobs.py:73 +#: awx/main/models/notifications.py:211 awx/main/models/unified_jobs.py:73 msgid "Successful" msgstr "成功" -#: awx/main/models/notifications.py:214 awx/main/models/unified_jobs.py:74 +#: awx/main/models/notifications.py:212 awx/main/models/unified_jobs.py:74 msgid "Failed" msgstr "失敗" -#: awx/main/models/notifications.py:467 +#: awx/main/models/notifications.py:466 msgid "status must be either running, succeeded or failed" msgstr "ステータスは、実行中、成功、失敗のいずれかでなければなりません。" @@ -4195,11 +4250,11 @@ msgid "" "authentication provider ({})" msgstr "OAuth2 トークンは、外部の認証プロバイダー ({}) に関連付けられたユーザーが作成することはできません。" -#: awx/main/models/organization.py:51 +#: awx/main/models/organization.py:57 msgid "Maximum number of hosts allowed to be managed by this organization." msgstr "この組織が管理可能な最大ホスト数" -#: awx/main/models/projects.py:53 awx/main/models/unified_jobs.py:539 +#: awx/main/models/projects.py:53 awx/main/models/unified_jobs.py:538 msgid "Manual" msgstr "手動" @@ -4219,118 +4274,122 @@ msgstr "Subversion" msgid "Red Hat Insights" msgstr "Red Hat Insights" -#: awx/main/models/projects.py:83 +#: awx/main/models/projects.py:58 +msgid "Remote Archive" +msgstr "リモートアーカイブ" + +#: awx/main/models/projects.py:84 msgid "" "Local path (relative to PROJECTS_ROOT) containing playbooks and related " "files for this project." msgstr "このプロジェクトの Playbook および関連するファイルを含むローカルパス (PROJECTS_ROOT との相対)。" -#: awx/main/models/projects.py:92 +#: awx/main/models/projects.py:93 msgid "SCM Type" msgstr "SCM タイプ" -#: awx/main/models/projects.py:93 +#: awx/main/models/projects.py:94 msgid "Specifies the source control system used to store the project." msgstr "プロジェクトを保存するために使用されるソースコントロールシステムを指定します。" -#: awx/main/models/projects.py:99 +#: awx/main/models/projects.py:100 msgid "SCM URL" msgstr "SCM URL" -#: awx/main/models/projects.py:100 +#: awx/main/models/projects.py:101 msgid "The location where the project is stored." msgstr "プロジェクトが保存される場所。" -#: awx/main/models/projects.py:106 +#: awx/main/models/projects.py:107 msgid "SCM Branch" msgstr "SCM ブランチ" -#: awx/main/models/projects.py:107 +#: awx/main/models/projects.py:108 msgid "Specific branch, tag or commit to checkout." msgstr "チェックアウトする特定のブランチ、タグまたはコミット。" -#: awx/main/models/projects.py:113 +#: awx/main/models/projects.py:114 msgid "SCM refspec" msgstr "SCM refspec" -#: awx/main/models/projects.py:114 +#: awx/main/models/projects.py:115 msgid "For git projects, an additional refspec to fetch." msgstr "git プロジェクトについては、追加の refspec を使用して取得します。" -#: awx/main/models/projects.py:118 +#: awx/main/models/projects.py:119 msgid "Discard any local changes before syncing the project." msgstr "ローカル変更を破棄してからプロジェクトを同期します。" -#: awx/main/models/projects.py:122 +#: awx/main/models/projects.py:123 msgid "Delete the project before syncing." msgstr "プロジェクトを削除してから同期します。" -#: awx/main/models/projects.py:151 +#: awx/main/models/projects.py:152 msgid "Invalid SCM URL." msgstr "無効な SCM URL。" -#: awx/main/models/projects.py:154 +#: awx/main/models/projects.py:155 msgid "SCM URL is required." msgstr "SCM URL が必要です。" -#: awx/main/models/projects.py:162 +#: awx/main/models/projects.py:163 msgid "Insights Credential is required for an Insights Project." msgstr "Insights 認証情報が Insights プロジェクトに必要です。" -#: awx/main/models/projects.py:168 +#: awx/main/models/projects.py:169 msgid "Credential kind must be 'scm'." msgstr "認証情報の種類は 'scm' にする必要があります。" -#: awx/main/models/projects.py:185 +#: awx/main/models/projects.py:186 msgid "Invalid credential." msgstr "無効な認証情報。" -#: awx/main/models/projects.py:259 +#: awx/main/models/projects.py:265 msgid "Update the project when a job is launched that uses the project." msgstr "プロジェクトを使用するジョブの起動時にプロジェクトを更新します。" -#: awx/main/models/projects.py:264 +#: awx/main/models/projects.py:270 msgid "" "The number of seconds after the last project update ran that a new project " "update will be launched as a job dependency." msgstr "最終プロジェクト更新を実行して新規プロジェクトの更新をジョブの依存関係として起動した後の秒数。" -#: awx/main/models/projects.py:269 +#: awx/main/models/projects.py:275 msgid "" "Allow changing the SCM branch or revision in a job template that uses this " "project." msgstr "このプロジェクトを使用するジョブテンプレートで SCM ブランチまたはリビジョンを変更できるようにします。" -#: awx/main/models/projects.py:279 +#: awx/main/models/projects.py:285 msgid "The last revision fetched by a project update" msgstr "プロジェクト更新で取得される最新リビジョン" -#: awx/main/models/projects.py:286 +#: awx/main/models/projects.py:292 msgid "Playbook Files" msgstr "Playbook ファイル" -#: awx/main/models/projects.py:287 +#: awx/main/models/projects.py:293 msgid "List of playbooks found in the project" msgstr "プロジェクトにある Playbook の一覧" -#: awx/main/models/projects.py:294 +#: awx/main/models/projects.py:300 msgid "Inventory Files" msgstr "インベントリーファイル" -#: awx/main/models/projects.py:295 +#: awx/main/models/projects.py:301 msgid "" "Suggested list of content that could be Ansible inventory in the project" msgstr "プロジェクト内の Ansible インベントリーの可能性のあるコンテンツの一覧" -#: awx/main/models/projects.py:332 +#: awx/main/models/projects.py:338 msgid "Organization cannot be changed when in use by job templates." msgstr "組織は、ジョブテンプレートで使用中の場合には変更できません。" -#: awx/main/models/projects.py:490 +#: awx/main/models/projects.py:501 msgid "Parts of the project update playbook that will be run." msgstr "実行予定のプロジェクト更新 Playbook の一部。" -#: awx/main/models/projects.py:498 +#: awx/main/models/projects.py:509 msgid "" "The SCM Revision discovered by this update for the given project and branch." msgstr "指定のプロジェクトおよびブランチ用にこの更新が検出した SCM リビジョン" @@ -4559,81 +4618,81 @@ msgstr "更新中" msgid "The organization used to determine access to this template." msgstr "このテンプレートにアクセスできるかを決定する時に使用する組織" -#: awx/main/models/unified_jobs.py:466 +#: awx/main/models/unified_jobs.py:465 msgid "Field is not allowed on launch." msgstr "フィールドは起動時に許可されません。" -#: awx/main/models/unified_jobs.py:494 +#: awx/main/models/unified_jobs.py:493 #, python-brace-format msgid "" "Variables {list_of_keys} provided, but this template cannot accept variables." msgstr "変数 {list_of_keys} が指定されましたが、このテンプレートは変数に対応していません。" -#: awx/main/models/unified_jobs.py:540 +#: awx/main/models/unified_jobs.py:539 msgid "Relaunch" msgstr "再起動" -#: awx/main/models/unified_jobs.py:541 +#: awx/main/models/unified_jobs.py:540 msgid "Callback" msgstr "コールバック" -#: awx/main/models/unified_jobs.py:542 +#: awx/main/models/unified_jobs.py:541 msgid "Scheduled" msgstr "スケジュール済み" -#: awx/main/models/unified_jobs.py:543 +#: awx/main/models/unified_jobs.py:542 msgid "Dependency" msgstr "依存関係" -#: awx/main/models/unified_jobs.py:544 +#: awx/main/models/unified_jobs.py:543 msgid "Workflow" msgstr "ワークフロー" -#: awx/main/models/unified_jobs.py:546 +#: awx/main/models/unified_jobs.py:545 msgid "Sync" msgstr "同期" -#: awx/main/models/unified_jobs.py:601 +#: awx/main/models/unified_jobs.py:600 msgid "The node the job executed on." msgstr "ジョブが実行されるノード。" -#: awx/main/models/unified_jobs.py:607 +#: awx/main/models/unified_jobs.py:606 msgid "The instance that managed the isolated execution environment." msgstr "分離された実行環境を管理したインスタンス。" -#: awx/main/models/unified_jobs.py:634 +#: awx/main/models/unified_jobs.py:633 msgid "The date and time the job was queued for starting." msgstr "ジョブが開始のために待機した日時。" -#: awx/main/models/unified_jobs.py:639 +#: awx/main/models/unified_jobs.py:638 msgid "" "If True, the task manager has already processed potential dependencies for " "this job." msgstr "True の場合には、タスクマネージャーは、このジョブの潜在的な依存関係を処理済みです。" -#: awx/main/models/unified_jobs.py:645 +#: awx/main/models/unified_jobs.py:644 msgid "The date and time the job finished execution." msgstr "ジョブが実行を完了した日時。" -#: awx/main/models/unified_jobs.py:652 +#: awx/main/models/unified_jobs.py:651 msgid "The date and time when the cancel request was sent." msgstr "取り消しリクエストが送信された日時。" -#: awx/main/models/unified_jobs.py:659 +#: awx/main/models/unified_jobs.py:658 msgid "Elapsed time in seconds that the job ran." msgstr "ジョブ実行の経過時間 (秒単位)" -#: awx/main/models/unified_jobs.py:681 +#: awx/main/models/unified_jobs.py:680 msgid "" "A status field to indicate the state of the job if it wasn't able to run and " "capture stdout" msgstr "stdout の実行およびキャプチャーを実行できない場合のジョブの状態を示すための状態フィールド" -#: awx/main/models/unified_jobs.py:710 +#: awx/main/models/unified_jobs.py:709 msgid "The Instance group the job was run under" msgstr "ジョブが実行されたインスタンスグループ" -#: awx/main/models/unified_jobs.py:718 +#: awx/main/models/unified_jobs.py:717 msgid "The organization used to determine access to this unified job." msgstr "この統一されたジョブにアクセスできるかを決定する時に使用する組織" @@ -4687,41 +4746,33 @@ msgid "" "Shows when an approval node (with a timeout assigned to it) has timed out." msgstr "承認ノード (タイムアウトが割り当てられている場合) がタイムアウトになると表示されます。" -#: awx/main/notifications/grafana_backend.py:86 +#: awx/main/notifications/grafana_backend.py:81 msgid "Error converting time {} or timeEnd {} to int." msgstr "time {} または timeEnd {} を int に変換中のエラー" -#: awx/main/notifications/grafana_backend.py:88 +#: awx/main/notifications/grafana_backend.py:83 msgid "Error converting time {} and/or timeEnd {} to int." msgstr "time {} および/または timeEnd {} を int に変換中のエラー" -#: awx/main/notifications/grafana_backend.py:102 -#: awx/main/notifications/grafana_backend.py:104 +#: awx/main/notifications/grafana_backend.py:97 +#: awx/main/notifications/grafana_backend.py:99 msgid "Error sending notification grafana: {}" msgstr "通知 grafana の送信時のエラー: {}" -#: awx/main/notifications/hipchat_backend.py:50 -msgid "Error sending messages: {}" -msgstr "メッセージの送信時のエラー: {}" - -#: awx/main/notifications/hipchat_backend.py:52 -msgid "Error sending message to hipchat: {}" -msgstr "メッセージの hipchat への送信時のエラー: {}" - #: awx/main/notifications/irc_backend.py:56 msgid "Exception connecting to irc server: {}" msgstr "irc サーバーへの接続時の例外: {}" -#: awx/main/notifications/mattermost_backend.py:50 -#: awx/main/notifications/mattermost_backend.py:52 +#: awx/main/notifications/mattermost_backend.py:49 +#: awx/main/notifications/mattermost_backend.py:51 msgid "Error sending notification mattermost: {}" msgstr "通知 mattermost の送信時のエラー: {}" -#: awx/main/notifications/pagerduty_backend.py:64 +#: awx/main/notifications/pagerduty_backend.py:75 msgid "Exception connecting to PagerDuty: {}" msgstr "PagerDuty への接続時の例外: {}" -#: awx/main/notifications/pagerduty_backend.py:73 +#: awx/main/notifications/pagerduty_backend.py:84 #: awx/main/notifications/slack_backend.py:58 #: awx/main/notifications/twilio_backend.py:48 msgid "Exception sending messages: {}" @@ -4748,46 +4799,52 @@ msgid "" "job node(s) missing unified job template and error handling path [{no_ufjt}]." msgstr "ワークフロージョブのノードにエラー処理パスがありません [{node_status}] ワークフロージョブのノードに統一されたジョブテンプレートおよびエラー処理パスがありません [{no_ufjt}]。" -#: awx/main/scheduler/task_manager.py:118 +#: awx/main/scheduler/task_manager.py:127 msgid "" "Workflow Job spawned from workflow could not start because it would result " "in recursion (spawn order, most recent first: {})" msgstr "ワークフローから起動されるワークフロージョブは、再帰が生じるために開始できませんでした (起動順、もっとも新しいものから: {})" -#: awx/main/scheduler/task_manager.py:126 +#: awx/main/scheduler/task_manager.py:135 msgid "" "Job spawned from workflow could not start because it was missing a related " "resource such as project or inventory" msgstr "ワークフローから起動されるジョブは、プロジェクトまたはインベントリーなどの関連するリソースがないために開始できませんでした" -#: awx/main/scheduler/task_manager.py:135 +#: awx/main/scheduler/task_manager.py:144 msgid "" "Job spawned from workflow could not start because it was not in the right " "state or required manual credentials" msgstr "ワークフローから起動されるジョブは、正常な状態にないか、または手動の認証が必要であるために開始できませんでした" -#: awx/main/scheduler/task_manager.py:176 +#: awx/main/scheduler/task_manager.py:185 msgid "No error handling paths found, marking workflow as failed" msgstr "エラーの処理パスが見つかりません。ワークフローを失敗としてマークしました" -#: awx/main/scheduler/task_manager.py:508 +#: awx/main/scheduler/task_manager.py:523 #, python-brace-format msgid "The approval node {name} ({pk}) has expired after {timeout} seconds." msgstr "承認ノード {name} ({pk}) は {timeout} 秒後に失効しました。" -#: awx/main/tasks.py:1049 +#: awx/main/tasks.py:599 +msgid "" +"Scheduled job could not start because it was not in the " +"right state or required manual credentials" +msgstr "スケジュール済みのジョブは、正常な状態にないか、手動の認証が必要であるために開始できませんでした" + +#: awx/main/tasks.py:1070 msgid "Invalid virtual environment selected: {}" msgstr "無効な仮想環境が選択されました: {}" -#: awx/main/tasks.py:1853 +#: awx/main/tasks.py:1857 msgid "Job could not start because it does not have a valid inventory." msgstr "ジョブは有効なインベントリーがないために開始できませんでした。" -#: awx/main/tasks.py:1857 +#: awx/main/tasks.py:1861 msgid "Job could not start because it does not have a valid project." msgstr "ジョブは有効なプロジェクトがないために開始できませんでした。" -#: awx/main/tasks.py:1862 +#: awx/main/tasks.py:1866 msgid "" "The project revision for this job template is unknown due to a failed update." msgstr "更新に失敗したため、このジョブテンプレートのプロジェクトリビジョンは不明です。" @@ -4812,53 +4869,53 @@ msgstr "ワークフロージョブのノードにエラーハンドルパスが msgid "Unable to convert \"%s\" to boolean" msgstr "\"%s\" をブール値に変換できません" -#: awx/main/utils/common.py:261 +#: awx/main/utils/common.py:248 #, python-format msgid "Unsupported SCM type \"%s\"" msgstr "サポートされない SCM タイプ \"%s\"" -#: awx/main/utils/common.py:268 awx/main/utils/common.py:280 -#: awx/main/utils/common.py:299 +#: awx/main/utils/common.py:255 awx/main/utils/common.py:267 +#: awx/main/utils/common.py:286 #, python-format msgid "Invalid %s URL" msgstr "無効な %s URL" -#: awx/main/utils/common.py:270 awx/main/utils/common.py:309 +#: awx/main/utils/common.py:257 awx/main/utils/common.py:297 #, python-format msgid "Unsupported %s URL" msgstr "サポートされない %s URL" -#: awx/main/utils/common.py:311 +#: awx/main/utils/common.py:299 #, python-format msgid "Unsupported host \"%s\" for file:// URL" msgstr "file:// URL でサポートされないホスト \"%s\"" -#: awx/main/utils/common.py:313 +#: awx/main/utils/common.py:301 #, python-format msgid "Host is required for %s URL" msgstr "%s URL にはホストが必要です" -#: awx/main/utils/common.py:331 +#: awx/main/utils/common.py:319 #, python-format msgid "Username must be \"git\" for SSH access to %s." msgstr "%s への SSH アクセスではユーザー名を \"git\" にする必要があります。" -#: awx/main/utils/common.py:337 +#: awx/main/utils/common.py:325 #, python-format msgid "Username must be \"hg\" for SSH access to %s." msgstr "%s への SSH アクセスではユーザー名を \"hg\" にする必要があります。" -#: awx/main/utils/common.py:668 +#: awx/main/utils/common.py:656 #, python-brace-format msgid "Input type `{data_type}` is not a dictionary" msgstr "入力タイプ `{data_type}` は辞書ではありません" -#: awx/main/utils/common.py:701 +#: awx/main/utils/common.py:689 #, python-brace-format msgid "Variables not compatible with JSON standard (error: {json_error})" msgstr "変数には JSON 標準との互換性がありません (エラー: {json_error})" -#: awx/main/utils/common.py:707 +#: awx/main/utils/common.py:695 #, python-brace-format msgid "" "Cannot parse as JSON (error: {json_error}) or YAML (error: {yaml_error})." @@ -4970,290 +5027,6 @@ msgstr "サーバーエラー" msgid "A server error has occurred." msgstr "サーバーエラーが発生しました。" -#: awx/settings/defaults.py:683 -msgid "US East (Northern Virginia)" -msgstr "米国東部 (バージニア北部)" - -#: awx/settings/defaults.py:684 -msgid "US East (Ohio)" -msgstr "米国東部 (オハイオ)" - -#: awx/settings/defaults.py:685 -msgid "US West (Oregon)" -msgstr "米国西部 (オレゴン)" - -#: awx/settings/defaults.py:686 -msgid "US West (Northern California)" -msgstr "米国西部 (北カリフォルニア)" - -#: awx/settings/defaults.py:687 -msgid "Canada (Central)" -msgstr "カナダ (中部)" - -#: awx/settings/defaults.py:688 -msgid "EU (Frankfurt)" -msgstr "EU (フランクフルト)" - -#: awx/settings/defaults.py:689 -msgid "EU (Ireland)" -msgstr "EU (アイルランド)" - -#: awx/settings/defaults.py:690 -msgid "EU (London)" -msgstr "EU (ロンドン)" - -#: awx/settings/defaults.py:691 -msgid "Asia Pacific (Singapore)" -msgstr "アジア太平洋 (シンガポール)" - -#: awx/settings/defaults.py:692 -msgid "Asia Pacific (Sydney)" -msgstr "アジア太平洋 (シドニー)" - -#: awx/settings/defaults.py:693 -msgid "Asia Pacific (Tokyo)" -msgstr "アジア太平洋 (東京)" - -#: awx/settings/defaults.py:694 -msgid "Asia Pacific (Seoul)" -msgstr "アジア太平洋 (ソウル)" - -#: awx/settings/defaults.py:695 -msgid "Asia Pacific (Mumbai)" -msgstr "アジア太平洋 (ムンバイ)" - -#: awx/settings/defaults.py:696 -msgid "South America (Sao Paulo)" -msgstr "南アメリカ (サンパウロ)" - -#: awx/settings/defaults.py:697 -msgid "US West (GovCloud)" -msgstr "米国西部 (GovCloud)" - -#: awx/settings/defaults.py:698 -msgid "China (Beijing)" -msgstr "中国 (北京)" - -#: awx/settings/defaults.py:747 -msgid "US East 1 (B)" -msgstr "米国東部 1 (B)" - -#: awx/settings/defaults.py:748 -msgid "US East 1 (C)" -msgstr "米国東部 1 (C)" - -#: awx/settings/defaults.py:749 -msgid "US East 1 (D)" -msgstr "米国東部 1 (D)" - -#: awx/settings/defaults.py:750 -msgid "US East 4 (A)" -msgstr "米国東部 4 (A)" - -#: awx/settings/defaults.py:751 -msgid "US East 4 (B)" -msgstr "米国東部 4 (B)" - -#: awx/settings/defaults.py:752 -msgid "US East 4 (C)" -msgstr "米国東部 4 (C)" - -#: awx/settings/defaults.py:753 -msgid "US Central (A)" -msgstr "米国中部 (A)" - -#: awx/settings/defaults.py:754 -msgid "US Central (B)" -msgstr "米国中部 (B)" - -#: awx/settings/defaults.py:755 -msgid "US Central (C)" -msgstr "米国中部 (C)" - -#: awx/settings/defaults.py:756 -msgid "US Central (F)" -msgstr "米国中部 (F)" - -#: awx/settings/defaults.py:757 -msgid "US West (A)" -msgstr "米国西部 (A)" - -#: awx/settings/defaults.py:758 -msgid "US West (B)" -msgstr "米国西部 (B)" - -#: awx/settings/defaults.py:759 -msgid "US West (C)" -msgstr "米国西部 (C)" - -#: awx/settings/defaults.py:760 -msgid "Europe West 1 (B)" -msgstr "欧州西部 1 (B)" - -#: awx/settings/defaults.py:761 -msgid "Europe West 1 (C)" -msgstr "欧州西部 1 (C)" - -#: awx/settings/defaults.py:762 -msgid "Europe West 1 (D)" -msgstr "欧州西部 1 (D)" - -#: awx/settings/defaults.py:763 -msgid "Europe West 2 (A)" -msgstr "欧州西部 2 (A)" - -#: awx/settings/defaults.py:764 -msgid "Europe West 2 (B)" -msgstr "欧州西部 2 (B)" - -#: awx/settings/defaults.py:765 -msgid "Europe West 2 (C)" -msgstr "欧州西部 2 (C)" - -#: awx/settings/defaults.py:766 -msgid "Asia East (A)" -msgstr "アジア東部 (A)" - -#: awx/settings/defaults.py:767 -msgid "Asia East (B)" -msgstr "アジア東部 (B)" - -#: awx/settings/defaults.py:768 -msgid "Asia East (C)" -msgstr "アジア東部 (C)" - -#: awx/settings/defaults.py:769 -msgid "Asia Southeast (A)" -msgstr "アジア南東部 (A)" - -#: awx/settings/defaults.py:770 -msgid "Asia Southeast (B)" -msgstr "アジア南東部 (B)" - -#: awx/settings/defaults.py:771 -msgid "Asia Northeast (A)" -msgstr "アジア北東部 (A)" - -#: awx/settings/defaults.py:772 -msgid "Asia Northeast (B)" -msgstr "アジア北東部 (B)" - -#: awx/settings/defaults.py:773 -msgid "Asia Northeast (C)" -msgstr "アジア北東部 (C)" - -#: awx/settings/defaults.py:774 -msgid "Australia Southeast (A)" -msgstr "オーストラリア南東部 (A)" - -#: awx/settings/defaults.py:775 -msgid "Australia Southeast (B)" -msgstr "オーストラリア南東部 (B)" - -#: awx/settings/defaults.py:776 -msgid "Australia Southeast (C)" -msgstr "オーストラリア南東部 (C)" - -#: awx/settings/defaults.py:798 -msgid "US East" -msgstr "米国東部" - -#: awx/settings/defaults.py:799 -msgid "US East 2" -msgstr "米国東部 2" - -#: awx/settings/defaults.py:800 -msgid "US Central" -msgstr "米国中部" - -#: awx/settings/defaults.py:801 -msgid "US North Central" -msgstr "米国中北部" - -#: awx/settings/defaults.py:802 -msgid "US South Central" -msgstr "米国中南部" - -#: awx/settings/defaults.py:803 -msgid "US West Central" -msgstr "米国中西部" - -#: awx/settings/defaults.py:804 -msgid "US West" -msgstr "米国西部" - -#: awx/settings/defaults.py:805 -msgid "US West 2" -msgstr "米国西部 2" - -#: awx/settings/defaults.py:806 -msgid "Canada East" -msgstr "カナダ東部" - -#: awx/settings/defaults.py:807 -msgid "Canada Central" -msgstr "カナダ中部" - -#: awx/settings/defaults.py:808 -msgid "Brazil South" -msgstr "ブラジル南部" - -#: awx/settings/defaults.py:809 -msgid "Europe North" -msgstr "欧州北部" - -#: awx/settings/defaults.py:810 -msgid "Europe West" -msgstr "欧州西部" - -#: awx/settings/defaults.py:811 -msgid "UK West" -msgstr "英国西部" - -#: awx/settings/defaults.py:812 -msgid "UK South" -msgstr "英国南部" - -#: awx/settings/defaults.py:813 -msgid "Asia East" -msgstr "アジア東部" - -#: awx/settings/defaults.py:814 -msgid "Asia Southeast" -msgstr "アジア南東部" - -#: awx/settings/defaults.py:815 -msgid "Australia East" -msgstr "オーストラリア東部" - -#: awx/settings/defaults.py:816 -msgid "Australia Southeast" -msgstr "オーストラリア南東部 " - -#: awx/settings/defaults.py:817 -msgid "India West" -msgstr "インド西部" - -#: awx/settings/defaults.py:818 -msgid "India South" -msgstr "インド南部" - -#: awx/settings/defaults.py:819 -msgid "Japan East" -msgstr "日本東部" - -#: awx/settings/defaults.py:820 -msgid "Japan West" -msgstr "日本西部" - -#: awx/settings/defaults.py:821 -msgid "Korea Central" -msgstr "韓国中部" - -#: awx/settings/defaults.py:822 -msgid "Korea South" -msgstr "韓国南部" - #: awx/sso/apps.py:9 msgid "Single Sign-On" msgstr "シングルサインオン" @@ -5596,8 +5369,8 @@ msgid "The OAuth2 secret from your web application." msgstr "web アプリケーションの OAuth2 シークレット" #: awx/sso/conf.py:578 -msgid "Google OAuth2 Whitelisted Domains" -msgstr "Google OAuth2 ホワイトリストドメイン" +msgid "Google OAuth2 Allowed Domains" +msgstr "GoogleOAuth2 で許可されているドメイン" #: awx/sso/conf.py:579 msgid "" @@ -5785,101 +5558,111 @@ msgstr "Azure AD OAuth2 組織マップ" msgid "Azure AD OAuth2 Team Map" msgstr "Azure AD OAuth2 チームマップ" +#: awx/sso/conf.py:926 +msgid "Automatically Create Organizations and Teams on SAML Login" +msgstr "SAML ログインで組織とチームを自動的に作成" + #: awx/sso/conf.py:927 +msgid "" +"When enabled (the default), mapped Organizations and Teams will be created " +"automatically on successful SAML login." +msgstr "有効にすると (デフォルト)、マップされた組織とチームは、SAML ログインが成功すると自動的に作成されます。" + +#: awx/sso/conf.py:929 awx/sso/conf.py:942 awx/sso/conf.py:955 +#: awx/sso/conf.py:968 awx/sso/conf.py:982 awx/sso/conf.py:995 +#: awx/sso/conf.py:1007 awx/sso/conf.py:1027 awx/sso/conf.py:1044 +#: awx/sso/conf.py:1062 awx/sso/conf.py:1097 awx/sso/conf.py:1128 +#: awx/sso/conf.py:1141 awx/sso/conf.py:1157 awx/sso/conf.py:1169 +#: awx/sso/conf.py:1181 awx/sso/conf.py:1200 awx/sso/models.py:16 +msgid "SAML" +msgstr "SAML" + +#: awx/sso/conf.py:938 msgid "SAML Assertion Consumer Service (ACS) URL" msgstr "SAML アサーションコンシューマー サービス (ACS) URL" -#: awx/sso/conf.py:928 +#: awx/sso/conf.py:939 msgid "" "Register Tower as a service provider (SP) with each identity provider (IdP) " "you have configured. Provide your SP Entity ID and this ACS URL for your " "application." msgstr "設定済みの各アイデンティティープロバイダー (IdP) で Tower をサービスプロバイダー (SP) として登録します。SP エンティティー ID およびアプリケーションのこの ACS URL を指定します。" -#: awx/sso/conf.py:931 awx/sso/conf.py:944 awx/sso/conf.py:957 -#: awx/sso/conf.py:971 awx/sso/conf.py:984 awx/sso/conf.py:996 -#: awx/sso/conf.py:1016 awx/sso/conf.py:1033 awx/sso/conf.py:1051 -#: awx/sso/conf.py:1086 awx/sso/conf.py:1117 awx/sso/conf.py:1130 -#: awx/sso/conf.py:1146 awx/sso/conf.py:1158 awx/sso/conf.py:1170 -#: awx/sso/conf.py:1189 awx/sso/models.py:16 -msgid "SAML" -msgstr "SAML" - -#: awx/sso/conf.py:941 +#: awx/sso/conf.py:952 msgid "SAML Service Provider Metadata URL" msgstr "SAML サービスプロバイダーメタデータ URL" -#: awx/sso/conf.py:942 +#: awx/sso/conf.py:953 msgid "" "If your identity provider (IdP) allows uploading an XML metadata file, you " "can download one from this URL." msgstr "アイデンティティープロバイダー (IdP) が XML メタデータファイルのアップロードを許可する場合、この URL からダウンロードできます。" -#: awx/sso/conf.py:953 +#: awx/sso/conf.py:964 msgid "SAML Service Provider Entity ID" msgstr "SAML サービスプロバイダーエンティティー ID" -#: awx/sso/conf.py:954 +#: awx/sso/conf.py:965 msgid "" "The application-defined unique identifier used as the audience of the SAML " "service provider (SP) configuration. This is usually the URL for Tower." msgstr "SAML サービスプロバイダー (SP) 設定の対象として使用されるアプリケーションで定義される固有識別子です。通常これは Tower の URL になります。" -#: awx/sso/conf.py:968 +#: awx/sso/conf.py:979 msgid "SAML Service Provider Public Certificate" msgstr "SAML サービスプロバイダーの公開証明書" -#: awx/sso/conf.py:969 +#: awx/sso/conf.py:980 msgid "" "Create a keypair for Tower to use as a service provider (SP) and include the " "certificate content here." msgstr "サービスプロバイダー (SP) として使用するための Tower のキーペアを作成し、ここに証明書の内容を組み込みます。" -#: awx/sso/conf.py:981 +#: awx/sso/conf.py:992 msgid "SAML Service Provider Private Key" msgstr "SAML サービスプロバイダーの秘密鍵|" -#: awx/sso/conf.py:982 +#: awx/sso/conf.py:993 msgid "" "Create a keypair for Tower to use as a service provider (SP) and include the " "private key content here." msgstr "サービスプロバイダー (SP) として使用するための Tower のキーペアを作成し、ここに秘密鍵の内容を組み込みます。" -#: awx/sso/conf.py:993 +#: awx/sso/conf.py:1004 msgid "SAML Service Provider Organization Info" msgstr "SAML サービスプロバイダーの組織情報" -#: awx/sso/conf.py:994 +#: awx/sso/conf.py:1005 msgid "" "Provide the URL, display name, and the name of your app. Refer to the " "Ansible Tower documentation for example syntax." msgstr "アプリケーションの URL、表示名、および名前を指定します。構文のサンプルについては Ansible Tower ドキュメントを参照してください。" -#: awx/sso/conf.py:1012 +#: awx/sso/conf.py:1023 msgid "SAML Service Provider Technical Contact" msgstr "SAML サービスプロバイダーテクニカルサポートの問い合わせ先" -#: awx/sso/conf.py:1013 +#: awx/sso/conf.py:1024 msgid "" "Provide the name and email address of the technical contact for your service " "provider. Refer to the Ansible Tower documentation for example syntax." msgstr "サービスプロバイダーのテクニカルサポート担当の名前およびメールアドレスを指定します。構文のサンプルについては Ansible Tower ドキュメントを参照してください。" -#: awx/sso/conf.py:1029 +#: awx/sso/conf.py:1040 msgid "SAML Service Provider Support Contact" msgstr "SAML サービスプロバイダーサポートの問い合わせ先" -#: awx/sso/conf.py:1030 +#: awx/sso/conf.py:1041 msgid "" "Provide the name and email address of the support contact for your service " "provider. Refer to the Ansible Tower documentation for example syntax." msgstr "サービスプロバイダーのサポート担当の名前およびメールアドレスを指定します。構文のサンプルについては Ansible Tower ドキュメントを参照してください。" -#: awx/sso/conf.py:1045 +#: awx/sso/conf.py:1056 msgid "SAML Enabled Identity Providers" msgstr "SAML で有効にされたアイデンティティープロバイダー" -#: awx/sso/conf.py:1046 +#: awx/sso/conf.py:1057 msgid "" "Configure the Entity ID, SSO URL and certificate for each identity provider " "(IdP) in use. Multiple SAML IdPs are supported. Some IdPs may provide user " @@ -5888,57 +5671,57 @@ msgid "" "additional details and syntax." msgstr "使用中のそれぞれのアイデンティティープロバイダー (IdP) についてのエンティティー ID、SSO URL および証明書を設定します。複数の SAML IdP がサポートされます。一部の IdP はデフォルト OID とは異なる属性名を使用してユーザーデータを提供することがあります。それぞれの IdP について属性名が上書きされる可能性があります。追加の詳細および構文については、Ansible ドキュメントを参照してください。" -#: awx/sso/conf.py:1082 +#: awx/sso/conf.py:1093 msgid "SAML Security Config" msgstr "SAML セキュリティー設定" -#: awx/sso/conf.py:1083 +#: awx/sso/conf.py:1094 msgid "" "A dict of key value pairs that are passed to the underlying python-saml " "security setting https://github.com/onelogin/python-saml#settings" msgstr "基礎となる python-saml セキュリティー設定に渡されるキー値ペアの辞書: https://github.com/onelogin/python-saml#settings" -#: awx/sso/conf.py:1114 +#: awx/sso/conf.py:1125 msgid "SAML Service Provider extra configuration data" msgstr "SAML サービスプロバイダーの追加設定データ" -#: awx/sso/conf.py:1115 +#: awx/sso/conf.py:1126 msgid "" "A dict of key value pairs to be passed to the underlying python-saml Service " "Provider configuration setting." msgstr "基礎となる python-saml サービスプロバイダー設定に渡されるキー値ペアの辞書。" -#: awx/sso/conf.py:1127 +#: awx/sso/conf.py:1138 msgid "SAML IDP to extra_data attribute mapping" msgstr "SAML IDP の extra_data 属性へのマッピング" -#: awx/sso/conf.py:1128 +#: awx/sso/conf.py:1139 msgid "" "A list of tuples that maps IDP attributes to extra_attributes. Each " "attribute will be a list of values, even if only 1 value." msgstr "IDP 属性を extra_attributes にマップするタプルの一覧です。各属性は 1 つの値のみの場合も値の一覧となります。" -#: awx/sso/conf.py:1144 +#: awx/sso/conf.py:1155 msgid "SAML Organization Map" msgstr "SAML 組織マップ" -#: awx/sso/conf.py:1156 +#: awx/sso/conf.py:1167 msgid "SAML Team Map" msgstr "SAML チームマップ" -#: awx/sso/conf.py:1168 +#: awx/sso/conf.py:1179 msgid "SAML Organization Attribute Mapping" msgstr "SAML 組織属性マッピング" -#: awx/sso/conf.py:1169 +#: awx/sso/conf.py:1180 msgid "Used to translate user organization membership into Tower." msgstr "ユーザー組織メンバーシップを Tower に変換するために使用されます。" -#: awx/sso/conf.py:1187 +#: awx/sso/conf.py:1198 msgid "SAML Team Attribute Mapping" msgstr "SAML チーム属性マッピング" -#: awx/sso/conf.py:1188 +#: awx/sso/conf.py:1199 msgid "Used to translate user team membership into Tower." msgstr "ユーザーチームメンバーシップを Tower に変換するために使用されます。" @@ -6005,12 +5788,12 @@ msgstr "無効なユーザーフラグ: \"{invalid_flag}\"" msgid "Invalid language code(s) for org info: {invalid_lang_codes}." msgstr "組織情報の無効な言語コード: {invalid_lang_codes}" -#: awx/sso/pipeline.py:27 +#: awx/sso/pipeline.py:28 #, python-brace-format msgid "An account cannot be found for {0}" msgstr "{0} のアカウントが見つかりません" -#: awx/sso/pipeline.py:33 +#: awx/sso/pipeline.py:34 msgid "Your account is inactive" msgstr "アカウントが非アクティブです" @@ -6082,9 +5865,9 @@ msgstr "カスタムログイン情報" msgid "" "If needed, you can add specific information (such as a legal notice or a " "disclaimer) to a text box in the login modal using this setting. Any content " -"added must be in plain text, as custom HTML or other markup languages are " -"not supported." -msgstr "必要な場合は、この設定を使ってログインモーダルのテキストボックスに特定の情報 (法律上の通知または免責事項など) を追加できます。追加されるすべてのコンテンツは、カスタム HTML や他のマークアップ言語がサポートされないため、プレーンテキストでなければなりません。" +"added must be in plain text or an HTML fragment, as other markup languages " +"are not supported." +msgstr "必要に応じて、この設定を使用して、ログインモーダルのテキストボックスに特定の情報 (法律上の通知または免責事項など) を追加できます。他のマークアップ言語はサポートされていないため、追加するコンテンツはすべてプレーンテキストまたは HTML フラグメントでなければなりません。" #: awx/ui/conf.py:45 msgid "Custom Logo" diff --git a/awx/main/access.py b/awx/main/access.py index 4f54be6e12..0ecb025d92 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -1103,11 +1103,6 @@ class CredentialTypeAccess(BaseAccess): def can_use(self, obj): return True - def get_method_capability(self, method, obj, parent_obj): - if obj.managed_by_tower: - return False - return super(CredentialTypeAccess, self).get_method_capability(method, obj, parent_obj) - def filtered_queryset(self): return self.model.objects.all() @@ -1182,6 +1177,8 @@ class CredentialAccess(BaseAccess): def get_user_capabilities(self, obj, **kwargs): user_capabilities = super(CredentialAccess, self).get_user_capabilities(obj, **kwargs) user_capabilities['use'] = self.can_use(obj) + if getattr(obj, 'managed_by_tower', False) is True: + user_capabilities['edit'] = user_capabilities['delete'] = False return user_capabilities @@ -2753,6 +2750,9 @@ class WorkflowApprovalTemplateAccess(BaseAccess): else: return (self.check_related('workflow_approval_template', UnifiedJobTemplate, role_field='admin_role')) + def can_change(self, obj, data): + return self.user.can_access(WorkflowJobTemplate, 'change', obj.workflow_job_template, data={}) + def can_start(self, obj, validate_license=False): # for copying WFJTs that contain approval nodes if self.user.is_superuser: diff --git a/awx/main/analytics/__init__.py b/awx/main/analytics/__init__.py index 9ab526f29b..6aee1cef91 100644 --- a/awx/main/analytics/__init__.py +++ b/awx/main/analytics/__init__.py @@ -1 +1 @@ -from .core import register, gather, ship, table_version # noqa +from .core import all_collectors, expensive_collectors, register, gather, ship # noqa diff --git a/awx/main/analytics/broadcast_websocket.py b/awx/main/analytics/broadcast_websocket.py index 5cfda529eb..d8abcb4745 100644 --- a/awx/main/analytics/broadcast_websocket.py +++ b/awx/main/analytics/broadcast_websocket.py @@ -20,7 +20,7 @@ from django.conf import settings BROADCAST_WEBSOCKET_REDIS_KEY_NAME = 'broadcast_websocket_stats' -logger = logging.getLogger('awx.main.analytics.broadcast_websocket') +logger = logging.getLogger('awx.analytics.broadcast_websocket') def dt_to_seconds(dt): diff --git a/awx/main/analytics/collectors.py b/awx/main/analytics/collectors.py index 45cc247bf4..e1dc468d51 100644 --- a/awx/main/analytics/collectors.py +++ b/awx/main/analytics/collectors.py @@ -1,3 +1,4 @@ +import io import os import os.path import platform @@ -6,13 +7,14 @@ from django.db import connection from django.db.models import Count from django.conf import settings from django.utils.timezone import now +from django.utils.translation import ugettext_lazy as _ from awx.conf.license import get_license from awx.main.utils import (get_awx_version, get_ansible_version, get_custom_venv_choices, camelcase_to_underscore) from awx.main import models from django.contrib.sessions.models import Session -from awx.main.analytics import register, table_version +from awx.main.analytics import register ''' This module is used to define metrics collected by awx.main.analytics.gather() @@ -31,8 +33,8 @@ data _since_ the last report date - i.e., new data in the last 24 hours) ''' -@register('config', '1.1') -def config(since): +@register('config', '1.1', description=_('General platform configuration.')) +def config(since, **kwargs): license_info = get_license(show_key=False) install_type = 'traditional' if os.environ.get('container') == 'oci': @@ -63,8 +65,8 @@ def config(since): } -@register('counts', '1.0') -def counts(since): +@register('counts', '1.0', description=_('Counts of objects such as organizations, inventories, and projects')) +def counts(since, **kwargs): counts = {} for cls in (models.Organization, models.Team, models.User, models.Inventory, models.Credential, models.Project, @@ -98,8 +100,8 @@ def counts(since): return counts -@register('org_counts', '1.0') -def org_counts(since): +@register('org_counts', '1.0', description=_('Counts of users and teams by organization')) +def org_counts(since, **kwargs): counts = {} for org in models.Organization.objects.annotate(num_users=Count('member_role__members', distinct=True), num_teams=Count('teams', distinct=True)).values('name', 'id', 'num_users', 'num_teams'): @@ -110,8 +112,8 @@ def org_counts(since): return counts -@register('cred_type_counts', '1.0') -def cred_type_counts(since): +@register('cred_type_counts', '1.0', description=_('Counts of credentials by credential type')) +def cred_type_counts(since, **kwargs): counts = {} for cred_type in models.CredentialType.objects.annotate(num_credentials=Count( 'credentials', distinct=True)).values('name', 'id', 'managed_by_tower', 'num_credentials'): @@ -122,8 +124,8 @@ def cred_type_counts(since): return counts -@register('inventory_counts', '1.2') -def inventory_counts(since): +@register('inventory_counts', '1.2', description=_('Inventories, their inventory sources, and host counts')) +def inventory_counts(since, **kwargs): counts = {} for inv in models.Inventory.objects.filter(kind='').annotate(num_sources=Count('inventory_sources', distinct=True), num_hosts=Count('hosts', distinct=True)).only('id', 'name', 'kind'): @@ -147,8 +149,8 @@ def inventory_counts(since): return counts -@register('projects_by_scm_type', '1.0') -def projects_by_scm_type(since): +@register('projects_by_scm_type', '1.0', description=_('Counts of projects by source control type')) +def projects_by_scm_type(since, **kwargs): counts = dict( (t[0] or 'manual', 0) for t in models.Project.SCM_TYPE_CHOICES @@ -166,8 +168,8 @@ def _get_isolated_datetime(last_check): return last_check -@register('instance_info', '1.0') -def instance_info(since, include_hostnames=False): +@register('instance_info', '1.0', description=_('Cluster topology and capacity')) +def instance_info(since, include_hostnames=False, **kwargs): info = {} instances = models.Instance.objects.values_list('hostname').values( 'uuid', 'version', 'capacity', 'cpu', 'memory', 'managed_by_policy', 'hostname', 'last_isolated_check', 'enabled') @@ -192,8 +194,8 @@ def instance_info(since, include_hostnames=False): return info -@register('job_counts', '1.0') -def job_counts(since): +@register('job_counts', '1.0', description=_('Counts of jobs by status')) +def job_counts(since, **kwargs): counts = {} counts['total_jobs'] = models.UnifiedJob.objects.exclude(launch_type='sync').count() counts['status'] = dict(models.UnifiedJob.objects.exclude(launch_type='sync').values_list('status').annotate(Count('status')).order_by()) @@ -202,8 +204,8 @@ def job_counts(since): return counts -@register('job_instance_counts', '1.0') -def job_instance_counts(since): +@register('job_instance_counts', '1.0', description=_('Counts of jobs by execution node')) +def job_instance_counts(since, **kwargs): counts = {} job_types = models.UnifiedJob.objects.exclude(launch_type='sync').values_list( 'execution_node', 'launch_type').annotate(job_launch_type=Count('launch_type')).order_by() @@ -217,30 +219,71 @@ def job_instance_counts(since): return counts -@register('query_info', '1.0') -def query_info(since, collection_type): +@register('query_info', '1.0', description=_('Metadata about the analytics collected')) +def query_info(since, collection_type, until, **kwargs): query_info = {} query_info['last_run'] = str(since) - query_info['current_time'] = str(now()) + query_info['current_time'] = str(until) query_info['collection_type'] = collection_type return query_info -# Copies Job Events from db to a .csv to be shipped -@table_version('events_table.csv', '1.1') -@table_version('unified_jobs_table.csv', '1.1') -@table_version('unified_job_template_table.csv', '1.0') -@table_version('workflow_job_node_table.csv', '1.0') -@table_version('workflow_job_template_node_table.csv', '1.0') -def copy_tables(since, full_path, subset=None): - def _copy_table(table, query, path): - file_path = os.path.join(path, table + '_table.csv') - file = open(file_path, 'w', encoding='utf-8') - with connection.cursor() as cursor: - cursor.copy_expert(query, file) - file.close() - return file_path +''' +The event table can be *very* large, and we have a 100MB upload limit. +Split large table dumps at dump time into a series of files. +''' +MAX_TABLE_SIZE = 200 * 1048576 + + +class FileSplitter(io.StringIO): + def __init__(self, filespec=None, *args, **kwargs): + self.filespec = filespec + self.files = [] + self.currentfile = None + self.header = None + self.counter = 0 + self.cycle_file() + + def cycle_file(self): + if self.currentfile: + self.currentfile.close() + self.counter = 0 + fname = '{}_split{}'.format(self.filespec, len(self.files)) + self.currentfile = open(fname, 'w', encoding='utf-8') + self.files.append(fname) + if self.header: + self.currentfile.write('{}\n'.format(self.header)) + + def file_list(self): + self.currentfile.close() + # Check for an empty dump + if len(self.header) + 1 == self.counter: + os.remove(self.files[-1]) + self.files = self.files[:-1] + # If we only have one file, remove the suffix + if len(self.files) == 1: + os.rename(self.files[0],self.files[0].replace('_split0','')) + return self.files + + def write(self, s): + if not self.header: + self.header = s[0:s.index('\n')] + self.counter += self.currentfile.write(s) + if self.counter >= MAX_TABLE_SIZE: + self.cycle_file() + + +def _copy_table(table, query, path): + file_path = os.path.join(path, table + '_table.csv') + file = FileSplitter(filespec=file_path) + with connection.cursor() as cursor: + cursor.copy_expert(query, file) + return file.file_list() + + +@register('events_table', '1.1', format='csv', description=_('Automation task records'), expensive=True) +def events_table(since, full_path, until, **kwargs): events_query = '''COPY (SELECT main_jobevent.id, main_jobevent.created, main_jobevent.uuid, @@ -262,18 +305,21 @@ def copy_tables(since, full_path, subset=None): main_jobevent.event_data::json->'res'->'warnings' AS warnings, main_jobevent.event_data::json->'res'->'deprecations' AS deprecations FROM main_jobevent - WHERE main_jobevent.created > {} - ORDER BY main_jobevent.id ASC) TO STDOUT WITH CSV HEADER'''.format(since.strftime("'%Y-%m-%d %H:%M:%S'")) - if not subset or 'events' in subset: - _copy_table(table='events', query=events_query, path=full_path) + WHERE (main_jobevent.created > '{}' AND main_jobevent.created <= '{}') + ORDER BY main_jobevent.id ASC) TO STDOUT WITH CSV HEADER + '''.format(since.isoformat(),until.isoformat()) + return _copy_table(table='events', query=events_query, path=full_path) + +@register('unified_jobs_table', '1.1', format='csv', description=_('Data on jobs run'), expensive=True) +def unified_jobs_table(since, full_path, until, **kwargs): unified_job_query = '''COPY (SELECT main_unifiedjob.id, main_unifiedjob.polymorphic_ctype_id, django_content_type.model, main_unifiedjob.organization_id, main_organization.name as organization_name, main_job.inventory_id, - main_inventory.name, + main_inventory.name as inventory_name, main_unifiedjob.created, main_unifiedjob.name, main_unifiedjob.unified_job_template_id, @@ -294,12 +340,16 @@ def copy_tables(since, full_path, subset=None): LEFT JOIN main_job ON main_unifiedjob.id = main_job.unifiedjob_ptr_id LEFT JOIN main_inventory ON main_job.inventory_id = main_inventory.id LEFT JOIN main_organization ON main_organization.id = main_unifiedjob.organization_id - WHERE (main_unifiedjob.created > {0} OR main_unifiedjob.finished > {0}) + WHERE ((main_unifiedjob.created > '{0}' AND main_unifiedjob.created <= '{1}') + OR (main_unifiedjob.finished > '{0}' AND main_unifiedjob.finished <= '{1}')) AND main_unifiedjob.launch_type != 'sync' - ORDER BY main_unifiedjob.id ASC) TO STDOUT WITH CSV HEADER'''.format(since.strftime("'%Y-%m-%d %H:%M:%S'")) - if not subset or 'unified_jobs' in subset: - _copy_table(table='unified_jobs', query=unified_job_query, path=full_path) + ORDER BY main_unifiedjob.id ASC) TO STDOUT WITH CSV HEADER + '''.format(since.isoformat(),until.isoformat()) + return _copy_table(table='unified_jobs', query=unified_job_query, path=full_path) + +@register('unified_job_template_table', '1.0', format='csv', description=_('Data on job templates')) +def unified_job_template_table(since, full_path, **kwargs): unified_job_template_query = '''COPY (SELECT main_unifiedjobtemplate.id, main_unifiedjobtemplate.polymorphic_ctype_id, django_content_type.model, @@ -318,9 +368,11 @@ def copy_tables(since, full_path, subset=None): FROM main_unifiedjobtemplate, django_content_type WHERE main_unifiedjobtemplate.polymorphic_ctype_id = django_content_type.id ORDER BY main_unifiedjobtemplate.id ASC) TO STDOUT WITH CSV HEADER''' - if not subset or 'unified_job_template' in subset: - _copy_table(table='unified_job_template', query=unified_job_template_query, path=full_path) + return _copy_table(table='unified_job_template', query=unified_job_template_query, path=full_path) + +@register('workflow_job_node_table', '1.0', format='csv', description=_('Data on workflow runs'), expensive=True) +def workflow_job_node_table(since, full_path, until, **kwargs): workflow_job_node_query = '''COPY (SELECT main_workflowjobnode.id, main_workflowjobnode.created, main_workflowjobnode.modified, @@ -349,11 +401,14 @@ def copy_tables(since, full_path, subset=None): FROM main_workflowjobnode_always_nodes GROUP BY from_workflowjobnode_id ) always_nodes ON main_workflowjobnode.id = always_nodes.from_workflowjobnode_id - WHERE main_workflowjobnode.modified > {} - ORDER BY main_workflowjobnode.id ASC) TO STDOUT WITH CSV HEADER'''.format(since.strftime("'%Y-%m-%d %H:%M:%S'")) - if not subset or 'workflow_job_node' in subset: - _copy_table(table='workflow_job_node', query=workflow_job_node_query, path=full_path) + WHERE (main_workflowjobnode.modified > '{}' AND main_workflowjobnode.modified <= '{}') + ORDER BY main_workflowjobnode.id ASC) TO STDOUT WITH CSV HEADER + '''.format(since.isoformat(),until.isoformat()) + return _copy_table(table='workflow_job_node', query=workflow_job_node_query, path=full_path) + +@register('workflow_job_template_node_table', '1.0', format='csv', description=_('Data on workflows')) +def workflow_job_template_node_table(since, full_path, **kwargs): workflow_job_template_node_query = '''COPY (SELECT main_workflowjobtemplatenode.id, main_workflowjobtemplatenode.created, main_workflowjobtemplatenode.modified, @@ -381,7 +436,4 @@ def copy_tables(since, full_path, subset=None): GROUP BY from_workflowjobtemplatenode_id ) always_nodes ON main_workflowjobtemplatenode.id = always_nodes.from_workflowjobtemplatenode_id ORDER BY main_workflowjobtemplatenode.id ASC) TO STDOUT WITH CSV HEADER''' - if not subset or 'workflow_job_template_node' in subset: - _copy_table(table='workflow_job_template_node', query=workflow_job_template_node_query, path=full_path) - - return + return _copy_table(table='workflow_job_template_node', query=workflow_job_template_node_query, path=full_path) diff --git a/awx/main/analytics/core.py b/awx/main/analytics/core.py index bab62b4a3c..fe48fb30bf 100644 --- a/awx/main/analytics/core.py +++ b/awx/main/analytics/core.py @@ -14,17 +14,13 @@ from rest_framework.exceptions import PermissionDenied from awx.conf.license import get_license from awx.main.models import Job from awx.main.access import access_registry -from awx.main.models.ha import TowerAnalyticsState from awx.main.utils import get_awx_http_client_headers, set_environ - -__all__ = ['register', 'gather', 'ship', 'table_version'] +__all__ = ['register', 'gather', 'ship'] logger = logging.getLogger('awx.main.analytics') -manifest = dict() - def _valid_license(): try: @@ -37,11 +33,38 @@ def _valid_license(): return True -def register(key, version): +def all_collectors(): + from awx.main.analytics import collectors + + collector_dict = {} + module = collectors + for name, func in inspect.getmembers(module): + if inspect.isfunction(func) and hasattr(func, '__awx_analytics_key__'): + key = func.__awx_analytics_key__ + desc = func.__awx_analytics_description__ or '' + version = func.__awx_analytics_version__ + collector_dict[key] = { 'name': key, 'version': version, 'description': desc} + return collector_dict + + +def expensive_collectors(): + from awx.main.analytics import collectors + + ret = [] + module = collectors + for name, func in inspect.getmembers(module): + if inspect.isfunction(func) and hasattr(func, '__awx_analytics_key__') and func.__awx_expensive__: + ret.append(func.__awx_analytics_key__) + return ret + + +def register(key, version, description=None, format='json', expensive=False): """ A decorator used to register a function as a metric collector. - Decorated functions should return JSON-serializable objects. + Decorated functions should do the following based on format: + - json: return JSON-serializable objects. + - csv: write CSV data to a filename named 'key' @register('projects_by_scm_type', 1) def projects_by_scm_type(): @@ -51,100 +74,153 @@ def register(key, version): def decorate(f): f.__awx_analytics_key__ = key f.__awx_analytics_version__ = version + f.__awx_analytics_description__ = description + f.__awx_analytics_type__ = format + f.__awx_expensive__ = expensive return f return decorate -def table_version(file_name, version): - - global manifest - manifest[file_name] = version - - def decorate(f): - return f - - return decorate - - -def gather(dest=None, module=None, collection_type='scheduled'): +def gather(dest=None, module=None, subset = None, since = None, until = now(), collection_type='scheduled'): """ Gather all defined metrics and write them as JSON files in a .tgz :param dest: the (optional) absolute path to write a compressed tarball - :pararm module: the module to search for registered analytic collector + :param module: the module to search for registered analytic collector functions; defaults to awx.main.analytics.collectors """ + def _write_manifest(destdir, manifest): + path = os.path.join(destdir, 'manifest.json') + with open(path, 'w', encoding='utf-8') as f: + try: + json.dump(manifest, f) + except Exception: + f.close() + os.remove(f.name) + logger.exception("Could not generate manifest.json") - run_now = now() - state = TowerAnalyticsState.get_solo() - last_run = state.last_run - logger.debug("Last analytics run was: {}".format(last_run)) + last_run = since or settings.AUTOMATION_ANALYTICS_LAST_GATHER or (now() - timedelta(weeks=4)) + logger.debug("Last analytics run was: {}".format(settings.AUTOMATION_ANALYTICS_LAST_GATHER)) - max_interval = now() - timedelta(weeks=4) - if last_run < max_interval or not last_run: - last_run = max_interval - if _valid_license() is False: logger.exception("Invalid License provided, or No License Provided") - return "Error: Invalid License provided, or No License Provided" + return None if collection_type != 'dry-run' and not settings.INSIGHTS_TRACKING_STATE: logger.error("Automation Analytics not enabled. Use --dry-run to gather locally without sending.") - return + return None - if module is None: + collector_list = [] + if module: + collector_module = module + else: from awx.main.analytics import collectors - module = collectors - + collector_module = collectors + for name, func in inspect.getmembers(collector_module): + if ( + inspect.isfunction(func) and + hasattr(func, '__awx_analytics_key__') and + (not subset or name in subset) + ): + collector_list.append((name, func)) + manifest = dict() dest = dest or tempfile.mkdtemp(prefix='awx_analytics') - for name, func in inspect.getmembers(module): - if inspect.isfunction(func) and hasattr(func, '__awx_analytics_key__'): + gather_dir = os.path.join(dest, 'stage') + os.mkdir(gather_dir, 0o700) + num_splits = 1 + for name, func in collector_list: + if func.__awx_analytics_type__ == 'json': key = func.__awx_analytics_key__ - manifest['{}.json'.format(key)] = func.__awx_analytics_version__ - path = '{}.json'.format(os.path.join(dest, key)) + path = '{}.json'.format(os.path.join(gather_dir, key)) with open(path, 'w', encoding='utf-8') as f: try: - if func.__name__ == 'query_info': - json.dump(func(last_run, collection_type=collection_type), f) - else: - json.dump(func(last_run), f) + json.dump(func(last_run, collection_type=collection_type, until=until), f) + manifest['{}.json'.format(key)] = func.__awx_analytics_version__ except Exception: logger.exception("Could not generate metric {}.json".format(key)) f.close() os.remove(f.name) - - path = os.path.join(dest, 'manifest.json') - with open(path, 'w', encoding='utf-8') as f: - try: - json.dump(manifest, f) - except Exception: - logger.exception("Could not generate manifest.json") - f.close() - os.remove(f.name) + elif func.__awx_analytics_type__ == 'csv': + key = func.__awx_analytics_key__ + try: + files = func(last_run, full_path=gather_dir, until=until) + if files: + manifest['{}.csv'.format(key)] = func.__awx_analytics_version__ + if len(files) > num_splits: + num_splits = len(files) + except Exception: + logger.exception("Could not generate metric {}.csv".format(key)) - try: - collectors.copy_tables(since=last_run, full_path=dest) - except Exception: - logger.exception("Could not copy tables") - - # can't use isoformat() since it has colons, which GNU tar doesn't like - tarname = '_'.join([ - settings.SYSTEM_UUID, - run_now.strftime('%Y-%m-%d-%H%M%S%z') - ]) - try: - tgz = shutil.make_archive( - os.path.join(os.path.dirname(dest), tarname), - 'gztar', - dest - ) - return tgz - except Exception: - logger.exception("Failed to write analytics archive file") - finally: + if not manifest: + # No data was collected + logger.warning("No data from {} to {}".format(last_run, until)) shutil.rmtree(dest) + return None + + # Always include config.json if we're using our collectors + if 'config.json' not in manifest.keys() and not module: + from awx.main.analytics import collectors + config = collectors.config + path = '{}.json'.format(os.path.join(gather_dir, config.__awx_analytics_key__)) + with open(path, 'w', encoding='utf-8') as f: + try: + json.dump(collectors.config(last_run), f) + manifest['config.json'] = config.__awx_analytics_version__ + except Exception: + logger.exception("Could not generate metric {}.json".format(key)) + f.close() + os.remove(f.name) + shutil.rmtree(dest) + return None + + stage_dirs = [gather_dir] + if num_splits > 1: + for i in range(0, num_splits): + split_path = os.path.join(dest, 'split{}'.format(i)) + os.mkdir(split_path, 0o700) + filtered_manifest = {} + shutil.copy(os.path.join(gather_dir, 'config.json'), split_path) + filtered_manifest['config.json'] = manifest['config.json'] + suffix = '_split{}'.format(i) + for file in os.listdir(gather_dir): + if file.endswith(suffix): + old_file = os.path.join(gather_dir, file) + new_filename = file.replace(suffix, '') + new_file = os.path.join(split_path, new_filename) + shutil.move(old_file, new_file) + filtered_manifest[new_filename] = manifest[new_filename] + _write_manifest(split_path, filtered_manifest) + stage_dirs.append(split_path) + + for item in list(manifest.keys()): + if not os.path.exists(os.path.join(gather_dir, item)): + manifest.pop(item) + _write_manifest(gather_dir, manifest) + + tarfiles = [] + try: + for i in range(0, len(stage_dirs)): + stage_dir = stage_dirs[i] + # can't use isoformat() since it has colons, which GNU tar doesn't like + tarname = '_'.join([ + settings.SYSTEM_UUID, + until.strftime('%Y-%m-%d-%H%M%S%z'), + str(i) + ]) + tgz = shutil.make_archive( + os.path.join(os.path.dirname(dest), tarname), + 'gztar', + stage_dir + ) + tarfiles.append(tgz) + except Exception: + shutil.rmtree(stage_dir, ignore_errors = True) + logger.exception("Failed to write analytics archive file") + finally: + shutil.rmtree(dest, ignore_errors = True) + return tarfiles def ship(path): @@ -154,6 +230,9 @@ def ship(path): if not path: logger.error('Automation Analytics TAR not found') return + if not os.path.exists(path): + logger.error('Automation Analytics TAR {} not found'.format(path)) + return if "Error:" in str(path): return try: @@ -184,10 +263,7 @@ def ship(path): if response.status_code >= 300: return logger.exception('Upload failed with status {}, {}'.format(response.status_code, response.text)) - run_now = now() - state = TowerAnalyticsState.get_solo() - state.last_run = run_now - state.save() finally: # cleanup tar.gz - os.remove(path) + if os.path.exists(path): + os.remove(path) diff --git a/awx/main/conf.py b/awx/main/conf.py index 8d091894d6..3b41c3a19b 100644 --- a/awx/main/conf.py +++ b/awx/main/conf.py @@ -2,7 +2,6 @@ import json import logging import os -from distutils.version import LooseVersion as Version # Django from django.utils.translation import ugettext_lazy as _ @@ -149,7 +148,7 @@ register( default='https://example.com', schemes=('http', 'https'), allow_plain_hostname=True, # Allow hostname only without TLD. - label=_('Automation Analytics upload URL.'), + label=_('Automation Analytics upload URL'), help_text=_('This setting is used to to configure data collection for the Automation Analytics dashboard'), category=_('System'), category_slug='system', @@ -254,6 +253,7 @@ register( help_text=_('The number of seconds to sleep between status checks for jobs running on isolated instances.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -265,6 +265,7 @@ register( 'This includes the time needed to copy source control files (playbooks) to the isolated instance.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -277,6 +278,7 @@ register( 'Value should be substantially greater than expected network latency.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -436,93 +438,12 @@ register( category_slug='jobs', ) -register( - 'PRIMARY_GALAXY_URL', - field_class=fields.URLField, - required=False, - allow_blank=True, - label=_('Primary Galaxy Server URL'), - help_text=_( - 'For organizations that run their own Galaxy service, this gives the option to specify a ' - 'host as the primary galaxy server. Requirements will be downloaded from the primary if the ' - 'specific role or collection is available there. If the content is not avilable in the primary, ' - 'or if this field is left blank, it will default to galaxy.ansible.com.' - ), - category=_('Jobs'), - category_slug='jobs' -) - -register( - 'PRIMARY_GALAXY_USERNAME', - field_class=fields.CharField, - required=False, - allow_blank=True, - label=_('Primary Galaxy Server Username'), - help_text=_('(This setting is deprecated and will be removed in a future release) ' - 'For using a galaxy server at higher precedence than the public Ansible Galaxy. ' - 'The username to use for basic authentication against the Galaxy instance, ' - 'this is mutually exclusive with PRIMARY_GALAXY_TOKEN.'), - category=_('Jobs'), - category_slug='jobs' -) - -register( - 'PRIMARY_GALAXY_PASSWORD', - field_class=fields.CharField, - encrypted=True, - required=False, - allow_blank=True, - label=_('Primary Galaxy Server Password'), - help_text=_('(This setting is deprecated and will be removed in a future release) ' - 'For using a galaxy server at higher precedence than the public Ansible Galaxy. ' - 'The password to use for basic authentication against the Galaxy instance, ' - 'this is mutually exclusive with PRIMARY_GALAXY_TOKEN.'), - category=_('Jobs'), - category_slug='jobs' -) - -register( - 'PRIMARY_GALAXY_TOKEN', - field_class=fields.CharField, - encrypted=True, - required=False, - allow_blank=True, - label=_('Primary Galaxy Server Token'), - help_text=_('For using a galaxy server at higher precedence than the public Ansible Galaxy. ' - 'The token to use for connecting with the Galaxy instance, ' - 'this is mutually exclusive with corresponding username and password settings.'), - category=_('Jobs'), - category_slug='jobs' -) - -register( - 'PRIMARY_GALAXY_AUTH_URL', - field_class=fields.CharField, - required=False, - allow_blank=True, - label=_('Primary Galaxy Authentication URL'), - help_text=_('For using a galaxy server at higher precedence than the public Ansible Galaxy. ' - 'The token_endpoint of a Keycloak server.'), - category=_('Jobs'), - category_slug='jobs' -) - -register( - 'PUBLIC_GALAXY_ENABLED', - field_class=fields.BooleanField, - default=True, - label=_('Allow Access to Public Galaxy'), - help_text=_('Allow or deny access to the public Ansible Galaxy during project updates.'), - category=_('Jobs'), - category_slug='jobs' -) - register( 'GALAXY_IGNORE_CERTS', field_class=fields.BooleanField, default=False, label=_('Ignore Ansible Galaxy SSL Certificate Verification'), - help_text=_('If set to true, certificate validation will not be done when' + help_text=_('If set to true, certificate validation will not be done when ' 'installing content from any Galaxy server.'), category=_('Jobs'), category_slug='jobs' @@ -579,6 +500,7 @@ register( 'timeout should be imposed. A timeout set on an individual job template will override this.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -591,6 +513,7 @@ register( 'timeout should be imposed. A timeout set on an individual inventory source will override this.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -603,6 +526,7 @@ register( 'timeout should be imposed. A timeout set on an individual project will override this.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -617,6 +541,7 @@ register( 'Use a value of 0 to indicate that no timeout should be imposed.'), category=_('Jobs'), category_slug='jobs', + unit=_('seconds'), ) register( @@ -624,7 +549,7 @@ register( field_class=fields.IntegerField, allow_null=False, default=200, - label=_('Maximum number of forks per job.'), + label=_('Maximum number of forks per job'), help_text=_('Saving a Job Template with more than this number of forks will result in an error. ' 'When set to 0, no limit is applied.'), category=_('Jobs'), @@ -754,6 +679,7 @@ register( 'aggregator protocols.'), category=_('Logging'), category_slug='logging', + unit=_('seconds'), ) register( 'LOG_AGGREGATOR_VERIFY_CERT', @@ -834,7 +760,8 @@ register( default=14400, # every 4 hours min_value=1800, # every 30 minutes category=_('System'), - category_slug='system' + category_slug='system', + unit=_('seconds'), ) @@ -856,84 +783,4 @@ def logging_validate(serializer, attrs): return attrs -def galaxy_validate(serializer, attrs): - """Ansible Galaxy config options have mutual exclusivity rules, these rules - are enforced here on serializer validation so that users will not be able - to save settings which obviously break all project updates. - """ - prefix = 'PRIMARY_GALAXY_' - errors = {} - - def _new_value(setting_name): - if setting_name in attrs: - return attrs[setting_name] - elif not serializer.instance: - return '' - return getattr(serializer.instance, setting_name, '') - - if not _new_value('PRIMARY_GALAXY_URL'): - if _new_value('PUBLIC_GALAXY_ENABLED') is False: - msg = _('A URL for Primary Galaxy must be defined before disabling public Galaxy.') - # put error in both keys because UI has trouble with errors in toggles - for key in ('PRIMARY_GALAXY_URL', 'PUBLIC_GALAXY_ENABLED'): - errors.setdefault(key, []) - errors[key].append(msg) - raise serializers.ValidationError(errors) - - from awx.main.constants import GALAXY_SERVER_FIELDS - if not any('{}{}'.format(prefix, subfield.upper()) in attrs for subfield in GALAXY_SERVER_FIELDS): - return attrs - - galaxy_data = {} - for subfield in GALAXY_SERVER_FIELDS: - galaxy_data[subfield] = _new_value('{}{}'.format(prefix, subfield.upper())) - if not galaxy_data['url']: - for k, v in galaxy_data.items(): - if v: - setting_name = '{}{}'.format(prefix, k.upper()) - errors.setdefault(setting_name, []) - errors[setting_name].append(_( - 'Cannot provide field if PRIMARY_GALAXY_URL is not set.' - )) - for k in GALAXY_SERVER_FIELDS: - if galaxy_data[k]: - setting_name = '{}{}'.format(prefix, k.upper()) - if (not serializer.instance) or (not getattr(serializer.instance, setting_name, '')): - # new auth is applied, so check if compatible with version - from awx.main.utils import get_ansible_version - current_version = get_ansible_version() - min_version = '2.9' - if Version(current_version) < Version(min_version): - errors.setdefault(setting_name, []) - errors[setting_name].append(_( - 'Galaxy server settings are not available until Ansible {min_version}, ' - 'you are running {current_version}.' - ).format(min_version=min_version, current_version=current_version)) - if (galaxy_data['password'] or galaxy_data['username']) and (galaxy_data['token'] or galaxy_data['auth_url']): - for k in ('password', 'username', 'token', 'auth_url'): - setting_name = '{}{}'.format(prefix, k.upper()) - if setting_name in attrs: - errors.setdefault(setting_name, []) - errors[setting_name].append(_( - 'Setting Galaxy token and authentication URL is mutually exclusive with username and password.' - )) - if bool(galaxy_data['username']) != bool(galaxy_data['password']): - msg = _('If authenticating via username and password, both must be provided.') - for k in ('username', 'password'): - setting_name = '{}{}'.format(prefix, k.upper()) - errors.setdefault(setting_name, []) - errors[setting_name].append(msg) - if bool(galaxy_data['token']) != bool(galaxy_data['auth_url']): - msg = _('If authenticating via token, both token and authentication URL must be provided.') - for k in ('token', 'auth_url'): - setting_name = '{}{}'.format(prefix, k.upper()) - errors.setdefault(setting_name, []) - errors[setting_name].append(msg) - - if errors: - raise serializers.ValidationError(errors) - return attrs - - register_validate('logging', logging_validate) -register_validate('jobs', galaxy_validate) diff --git a/awx/main/constants.py b/awx/main/constants.py index c0382c7504..323f61f311 100644 --- a/awx/main/constants.py +++ b/awx/main/constants.py @@ -50,7 +50,3 @@ LOGGER_BLOCKLIST = ( # loggers that may be called getting logging settings 'awx.conf' ) - -# these correspond to both AWX and Ansible settings to keep naming consistent -# for instance, settings.PRIMARY_GALAXY_AUTH_URL vs env var ANSIBLE_GALAXY_SERVER_FOO_AUTH_URL -GALAXY_SERVER_FIELDS = ('url', 'username', 'password', 'token', 'auth_url') diff --git a/awx/main/consumers.py b/awx/main/consumers.py index d32219b3ac..b6d8872ebd 100644 --- a/awx/main/consumers.py +++ b/awx/main/consumers.py @@ -1,5 +1,3 @@ -import collections -import functools import json import logging import time @@ -14,40 +12,12 @@ from django.contrib.auth.models import User from channels.generic.websocket import AsyncJsonWebsocketConsumer from channels.layers import get_channel_layer from channels.db import database_sync_to_async -from channels_redis.core import RedisChannelLayer logger = logging.getLogger('awx.main.consumers') XRF_KEY = '_auth_user_xrf' -class BoundedQueue(asyncio.Queue): - - def put_nowait(self, item): - if self.full(): - # dispose the oldest item - # if we actually get into this code block, it likely means that - # this specific consumer has stopped reading - # unfortunately, channels_redis will just happily continue to - # queue messages specific to their channel until the heat death - # of the sun: https://github.com/django/channels_redis/issues/212 - # this isn't a huge deal for browser clients that disconnect, - # but it *does* cause a problem for our global broadcast topic - # that's used to broadcast messages to peers in a cluster - # if we get into this code block, it's better to drop messages - # than to continue to malloc() forever - self.get_nowait() - return super(BoundedQueue, self).put_nowait(item) - - -class ExpiringRedisChannelLayer(RedisChannelLayer): - def __init__(self, *args, **kw): - super(ExpiringRedisChannelLayer, self).__init__(*args, **kw) - self.receive_buffer = collections.defaultdict( - functools.partial(BoundedQueue, self.capacity) - ) - - class WebsocketSecretAuthHelper: """ Middlewareish for websockets to verify node websocket broadcast interconnect. diff --git a/awx/main/credential_plugins/hashivault.py b/awx/main/credential_plugins/hashivault.py index 28f213061b..7e262912a4 100644 --- a/awx/main/credential_plugins/hashivault.py +++ b/awx/main/credential_plugins/hashivault.py @@ -40,6 +40,13 @@ base_inputs = { 'multiline': False, 'secret': True, 'help_text': _('The Secret ID for AppRole Authentication') + }, { + 'id': 'default_auth_path', + 'label': _('Path to Approle Auth'), + 'type': 'string', + 'multiline': False, + 'default': 'approle', + 'help_text': _('The AppRole Authentication path to use if one isn\'t provided in the metadata when linking to an input field. Defaults to \'approle\'') } ], 'metadata': [{ @@ -47,10 +54,11 @@ base_inputs = { 'label': _('Path to Secret'), 'type': 'string', 'help_text': _('The path to the secret stored in the secret backend e.g, /some/secret/') - },{ + }, { 'id': 'auth_path', 'label': _('Path to Auth'), 'type': 'string', + 'multiline': False, 'help_text': _('The path where the Authentication method is mounted e.g, approle') }], 'required': ['url', 'secret_path'], @@ -118,7 +126,9 @@ def handle_auth(**kwargs): def approle_auth(**kwargs): role_id = kwargs['role_id'] secret_id = kwargs['secret_id'] - auth_path = kwargs.get('auth_path') or 'approle' + # we first try to use the 'auth_path' from the metadata + # if not found we try to fetch the 'default_auth_path' from inputs + auth_path = kwargs.get('auth_path') or kwargs['default_auth_path'] url = urljoin(kwargs['url'], 'v1') cacert = kwargs.get('cacert', None) diff --git a/awx/main/dispatch/control.py b/awx/main/dispatch/control.py index 6b3c13499d..47cc60b40d 100644 --- a/awx/main/dispatch/control.py +++ b/awx/main/dispatch/control.py @@ -2,6 +2,9 @@ import logging import uuid import json +from django.conf import settings +import redis + from awx.main.dispatch import get_local_queuename from . import pg_bus_conn @@ -21,7 +24,15 @@ class Control(object): self.queuename = host or get_local_queuename() def status(self, *args, **kwargs): - return self.control_with_reply('status', *args, **kwargs) + r = redis.Redis.from_url(settings.BROKER_URL) + if self.service == 'dispatcher': + stats = r.get(f'awx_{self.service}_statistics') or b'' + return stats.decode('utf-8') + else: + workers = [] + for key in r.keys('awx_callback_receiver_statistics_*'): + workers.append(r.get(key).decode('utf-8')) + return '\n'.join(workers) def running(self, *args, **kwargs): return self.control_with_reply('running', *args, **kwargs) diff --git a/awx/main/dispatch/pool.py b/awx/main/dispatch/pool.py index f5b38262ad..dc97402788 100644 --- a/awx/main/dispatch/pool.py +++ b/awx/main/dispatch/pool.py @@ -5,6 +5,7 @@ import signal import sys import time import traceback +from datetime import datetime from uuid import uuid4 import collections @@ -27,6 +28,12 @@ else: logger = logging.getLogger('awx.main.dispatch') +class NoOpResultQueue(object): + + def put(self, item): + pass + + class PoolWorker(object): ''' Used to track a worker child process and its pending and finished messages. @@ -56,11 +63,13 @@ class PoolWorker(object): It is "idle" when self.managed_tasks is empty. ''' - def __init__(self, queue_size, target, args): + track_managed_tasks = False + + def __init__(self, queue_size, target, args, **kwargs): self.messages_sent = 0 self.messages_finished = 0 self.managed_tasks = collections.OrderedDict() - self.finished = MPQueue(queue_size) + self.finished = MPQueue(queue_size) if self.track_managed_tasks else NoOpResultQueue() self.queue = MPQueue(queue_size) self.process = Process(target=target, args=(self.queue, self.finished) + args) self.process.daemon = True @@ -74,7 +83,8 @@ class PoolWorker(object): if not body.get('uuid'): body['uuid'] = str(uuid4()) uuid = body['uuid'] - self.managed_tasks[uuid] = body + if self.track_managed_tasks: + self.managed_tasks[uuid] = body self.queue.put(body, block=True, timeout=5) self.messages_sent += 1 self.calculate_managed_tasks() @@ -111,6 +121,8 @@ class PoolWorker(object): return str(self.process.exitcode) def calculate_managed_tasks(self): + if not self.track_managed_tasks: + return # look to see if any tasks were finished finished = [] for _ in range(self.finished.qsize()): @@ -135,6 +147,8 @@ class PoolWorker(object): @property def current_task(self): + if not self.track_managed_tasks: + return None self.calculate_managed_tasks() # the task at [0] is the one that's running right now (or is about to # be running) @@ -145,6 +159,8 @@ class PoolWorker(object): @property def orphaned_tasks(self): + if not self.track_managed_tasks: + return [] orphaned = [] if not self.alive: # if this process had a running task that never finished, @@ -179,6 +195,11 @@ class PoolWorker(object): return not self.busy +class StatefulPoolWorker(PoolWorker): + + track_managed_tasks = True + + class WorkerPool(object): ''' Creates a pool of forked PoolWorkers. @@ -200,6 +221,7 @@ class WorkerPool(object): ) ''' + pool_cls = PoolWorker debug_meta = '' def __init__(self, min_workers=None, queue_size=None): @@ -225,7 +247,7 @@ class WorkerPool(object): # for the DB and cache connections (that way lies race conditions) django_connection.close() django_cache.close() - worker = PoolWorker(self.queue_size, self.target, (idx,) + self.target_args) + worker = self.pool_cls(self.queue_size, self.target, (idx,) + self.target_args) self.workers.append(worker) try: worker.start() @@ -236,13 +258,13 @@ class WorkerPool(object): return idx, worker def debug(self, *args, **kwargs): - self.cleanup() tmpl = Template( + 'Recorded at: {{ dt }} \n' '{{ pool.name }}[pid:{{ pool.pid }}] workers total={{ workers|length }} {{ meta }} \n' '{% for w in workers %}' '. worker[pid:{{ w.pid }}]{% if not w.alive %} GONE exit={{ w.exitcode }}{% endif %}' ' sent={{ w.messages_sent }}' - ' finished={{ w.messages_finished }}' + '{% if w.messages_finished %} finished={{ w.messages_finished }}{% endif %}' ' qsize={{ w.managed_tasks|length }}' ' rss={{ w.mb }}MB' '{% for task in w.managed_tasks.values() %}' @@ -260,7 +282,11 @@ class WorkerPool(object): '\n' '{% endfor %}' ) - return tmpl.render(pool=self, workers=self.workers, meta=self.debug_meta) + now = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S UTC') + return tmpl.render( + pool=self, workers=self.workers, meta=self.debug_meta, + dt=now + ) def write(self, preferred_queue, body): queue_order = sorted(range(len(self.workers)), key=lambda x: -1 if x==preferred_queue else x) @@ -293,6 +319,8 @@ class AutoscalePool(WorkerPool): down based on demand ''' + pool_cls = StatefulPoolWorker + def __init__(self, *args, **kwargs): self.max_workers = kwargs.pop('max_workers', None) super(AutoscalePool, self).__init__(*args, **kwargs) @@ -309,6 +337,10 @@ class AutoscalePool(WorkerPool): # max workers can't be less than min_workers self.max_workers = max(self.min_workers, self.max_workers) + def debug(self, *args, **kwargs): + self.cleanup() + return super(AutoscalePool, self).debug(*args, **kwargs) + @property def should_grow(self): if len(self.workers) < self.min_workers: diff --git a/awx/main/dispatch/worker/base.py b/awx/main/dispatch/worker/base.py index 7001cd9bb9..8b44c71e43 100644 --- a/awx/main/dispatch/worker/base.py +++ b/awx/main/dispatch/worker/base.py @@ -43,6 +43,9 @@ class WorkerSignalHandler: class AWXConsumerBase(object): + + last_stats = time.time() + def __init__(self, name, worker, queues=[], pool=None): self.should_stop = False @@ -54,6 +57,7 @@ class AWXConsumerBase(object): if pool is None: self.pool = WorkerPool() self.pool.init_workers(self.worker.work_loop) + self.redis = redis.Redis.from_url(settings.BROKER_URL) @property def listening_on(self): @@ -99,6 +103,16 @@ class AWXConsumerBase(object): queue = 0 self.pool.write(queue, body) self.total_messages += 1 + self.record_statistics() + + def record_statistics(self): + if time.time() - self.last_stats > 1: # buffer stat recording to once per second + try: + self.redis.set(f'awx_{self.name}_statistics', self.pool.debug()) + self.last_stats = time.time() + except Exception: + logger.exception(f"encountered an error communicating with redis to store {self.name} statistics") + self.last_stats = time.time() def run(self, *args, **kwargs): signal.signal(signal.SIGINT, self.stop) @@ -118,23 +132,9 @@ class AWXConsumerRedis(AWXConsumerBase): super(AWXConsumerRedis, self).run(*args, **kwargs) self.worker.on_start() - time_to_sleep = 1 while True: - queue = redis.Redis.from_url(settings.BROKER_URL) - while True: - try: - res = queue.blpop(self.queues) - time_to_sleep = 1 - res = json.loads(res[1]) - self.process_task(res) - except redis.exceptions.RedisError: - time_to_sleep = min(time_to_sleep * 2, 30) - logger.exception(f"encountered an error communicating with redis. Reconnect attempt in {time_to_sleep} seconds") - time.sleep(time_to_sleep) - except (json.JSONDecodeError, KeyError): - logger.exception("failed to decode JSON message from redis") - if self.should_stop: - return + logger.debug(f'{os.getpid()} is alive') + time.sleep(60) class AWXConsumerPG(AWXConsumerBase): diff --git a/awx/main/dispatch/worker/callback.py b/awx/main/dispatch/worker/callback.py index 1314f33f7c..79033e329a 100644 --- a/awx/main/dispatch/worker/callback.py +++ b/awx/main/dispatch/worker/callback.py @@ -1,4 +1,5 @@ import cProfile +import json import logging import os import pstats @@ -6,12 +7,15 @@ import signal import tempfile import time import traceback -from queue import Empty as QueueEmpty from django.conf import settings from django.utils.timezone import now as tz_now from django.db import DatabaseError, OperationalError, connection as django_connection -from django.db.utils import InterfaceError, InternalError, IntegrityError +from django.db.utils import InterfaceError, InternalError + +import psutil + +import redis from awx.main.consumers import emit_channel_notification from awx.main.models import (JobEvent, AdHocCommandEvent, ProjectUpdateEvent, @@ -24,10 +28,6 @@ from .base import BaseWorker logger = logging.getLogger('awx.main.commands.run_callback_receiver') -# the number of seconds to buffer events in memory before flushing -# using JobEvent.objects.bulk_create() -BUFFER_SECONDS = .1 - class CallbackBrokerWorker(BaseWorker): ''' @@ -39,21 +39,57 @@ class CallbackBrokerWorker(BaseWorker): ''' MAX_RETRIES = 2 + last_stats = time.time() + total = 0 + last_event = '' prof = None def __init__(self): self.buff = {} + self.pid = os.getpid() + self.redis = redis.Redis.from_url(settings.BROKER_URL) + for key in self.redis.keys('awx_callback_receiver_statistics_*'): + self.redis.delete(key) def read(self, queue): try: - return queue.get(block=True, timeout=BUFFER_SECONDS) - except QueueEmpty: - return {'event': 'FLUSH'} + res = self.redis.blpop(settings.CALLBACK_QUEUE, timeout=settings.JOB_EVENT_BUFFER_SECONDS) + if res is None: + return {'event': 'FLUSH'} + self.total += 1 + return json.loads(res[1]) + except redis.exceptions.RedisError: + logger.exception("encountered an error communicating with redis") + time.sleep(1) + except (json.JSONDecodeError, KeyError): + logger.exception("failed to decode JSON message from redis") + finally: + self.record_statistics() + return {'event': 'FLUSH'} + + def record_statistics(self): + # buffer stat recording to once per (by default) 5s + if time.time() - self.last_stats > settings.JOB_EVENT_STATISTICS_INTERVAL: + try: + self.redis.set(f'awx_callback_receiver_statistics_{self.pid}', self.debug()) + self.last_stats = time.time() + except Exception: + logger.exception("encountered an error communicating with redis") + self.last_stats = time.time() + + def debug(self): + return f'. worker[pid:{self.pid}] sent={self.total} rss={self.mb}MB {self.last_event}' + + @property + def mb(self): + return '{:0.3f}'.format( + psutil.Process(self.pid).memory_info().rss / 1024.0 / 1024.0 + ) def toggle_profiling(self, *args): if self.prof: self.prof.disable() - filename = f'callback-{os.getpid()}.pstats' + filename = f'callback-{self.pid}.pstats' filepath = os.path.join(tempfile.gettempdir(), filename) with open(filepath, 'w') as f: pstats.Stats(self.prof, stream=f).sort_stats('cumulative').print_stats() @@ -84,20 +120,12 @@ class CallbackBrokerWorker(BaseWorker): e.modified = now try: cls.objects.bulk_create(events) - except Exception as exc: + except Exception: # if an exception occurs, we should re-attempt to save the # events one-by-one, because something in the list is - # broken/stale (e.g., an IntegrityError on a specific event) + # broken/stale for e in events: try: - if ( - isinstance(exc, IntegrityError) and - getattr(e, 'host_id', '') - ): - # this is one potential IntegrityError we can - # work around - if the host disappears before - # the event can be processed - e.host_id = None e.save() except Exception: logger.exception('Database Error Saving Job Event') @@ -108,6 +136,8 @@ class CallbackBrokerWorker(BaseWorker): def perform_work(self, body): try: flush = body.get('event') == 'FLUSH' + if flush: + self.last_event = '' if not flush: event_map = { 'job_id': JobEvent, @@ -123,6 +153,8 @@ class CallbackBrokerWorker(BaseWorker): job_identifier = body[key] break + self.last_event = f'\n\t- {cls.__name__} for #{job_identifier} ({body.get("event", "")} {body.get("uuid", "")})' # noqa + if body.get('event') == 'EOF': try: final_counter = body.get('final_counter', 0) diff --git a/awx/main/management/commands/create_preload_data.py b/awx/main/management/commands/create_preload_data.py index 297622af46..9b1d131735 100644 --- a/awx/main/management/commands/create_preload_data.py +++ b/awx/main/management/commands/create_preload_data.py @@ -42,6 +42,16 @@ class Command(BaseCommand): }, created_by=superuser) c.admin_role.members.add(superuser) + public_galaxy_credential = Credential( + name='Ansible Galaxy', + managed_by_tower=True, + credential_type=CredentialType.objects.get(kind='galaxy'), + inputs = { + 'url': 'https://galaxy.ansible.com/' + } + ) + public_galaxy_credential.save() + o.galaxy_credentials.add(public_galaxy_credential) i = Inventory.objects.create(name='Demo Inventory', organization=o, created_by=superuser) diff --git a/awx/main/management/commands/gather_analytics.py b/awx/main/management/commands/gather_analytics.py index aa096d6f28..b5e8427955 100644 --- a/awx/main/management/commands/gather_analytics.py +++ b/awx/main/management/commands/gather_analytics.py @@ -1,6 +1,9 @@ import logging + from awx.main.analytics import gather, ship +from dateutil import parser from django.core.management.base import BaseCommand +from django.utils.timezone import now class Command(BaseCommand): @@ -15,6 +18,10 @@ class Command(BaseCommand): help='Gather analytics without shipping. Works even if analytics are disabled in settings.') parser.add_argument('--ship', dest='ship', action='store_true', help='Enable to ship metrics to the Red Hat Cloud') + parser.add_argument('--since', dest='since', action='store', + help='Start date for collection') + parser.add_argument('--until', dest='until', action='store', + help='End date for collection') def init_logging(self): self.logger = logging.getLogger('awx.main.analytics') @@ -28,11 +35,28 @@ class Command(BaseCommand): self.init_logging() opt_ship = options.get('ship') opt_dry_run = options.get('dry-run') + opt_since = options.get('since') or None + opt_until = options.get('until') or None + + if opt_since: + since = parser.parse(opt_since) + else: + since = None + if opt_until: + until = parser.parse(opt_until) + else: + until = now() + if opt_ship and opt_dry_run: self.logger.error('Both --ship and --dry-run cannot be processed at the same time.') return - tgz = gather(collection_type='manual' if not opt_dry_run else 'dry-run') - if tgz: - self.logger.debug(tgz) + tgzfiles = gather(collection_type='manual' if not opt_dry_run else 'dry-run', since = since, until = until) + if tgzfiles: + for tgz in tgzfiles: + self.logger.info(tgz) + else: + self.logger.error('No analytics collected') if opt_ship: - ship(tgz) + if tgzfiles: + for tgz in tgzfiles: + ship(tgz) diff --git a/awx/main/management/commands/run_callback_receiver.py b/awx/main/management/commands/run_callback_receiver.py index 7e28330067..23922a7537 100644 --- a/awx/main/management/commands/run_callback_receiver.py +++ b/awx/main/management/commands/run_callback_receiver.py @@ -4,6 +4,7 @@ from django.conf import settings from django.core.management.base import BaseCommand +from awx.main.dispatch.control import Control from awx.main.dispatch.worker import AWXConsumerRedis, CallbackBrokerWorker @@ -15,7 +16,14 @@ class Command(BaseCommand): ''' help = 'Launch the job callback receiver' + def add_arguments(self, parser): + parser.add_argument('--status', dest='status', action='store_true', + help='print the internal state of any running dispatchers') + def handle(self, *arg, **options): + if options.get('status'): + print(Control('callback_receiver').status()) + return consumer = None try: consumer = AWXConsumerRedis( diff --git a/awx/main/migrations/0120_galaxy_credentials.py b/awx/main/migrations/0120_galaxy_credentials.py new file mode 100644 index 0000000000..a94c22e30b --- /dev/null +++ b/awx/main/migrations/0120_galaxy_credentials.py @@ -0,0 +1,51 @@ +# Generated by Django 2.2.11 on 2020-08-04 15:19 + +import logging + +import awx.main.fields +from awx.main.utils.encryption import encrypt_field, decrypt_field + +from django.db import migrations, models +from django.utils.timezone import now +import django.db.models.deletion + +from awx.main.migrations import _galaxy as galaxy +from awx.main.models import CredentialType as ModernCredentialType +from awx.main.utils.common import set_current_apps + +logger = logging.getLogger('awx.main.migrations') + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0119_inventory_plugins'), + ] + + operations = [ + migrations.AlterField( + model_name='credentialtype', + name='kind', + field=models.CharField(choices=[('ssh', 'Machine'), ('vault', 'Vault'), ('net', 'Network'), ('scm', 'Source Control'), ('cloud', 'Cloud'), ('token', 'Personal Access Token'), ('insights', 'Insights'), ('external', 'External'), ('kubernetes', 'Kubernetes'), ('galaxy', 'Galaxy/Automation Hub')], max_length=32), + ), + migrations.CreateModel( + name='OrganizationGalaxyCredentialMembership', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('position', models.PositiveIntegerField(db_index=True, default=None, null=True)), + ('credential', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.Credential')), + ('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.Organization')), + ], + ), + migrations.AddField( + model_name='organization', + name='galaxy_credentials', + field=awx.main.fields.OrderedManyToManyField(blank=True, related_name='organization_galaxy_credentials', through='main.OrganizationGalaxyCredentialMembership', to='main.Credential'), + ), + migrations.AddField( + model_name='credential', + name='managed_by_tower', + field=models.BooleanField(default=False, editable=False), + ), + migrations.RunPython(galaxy.migrate_galaxy_settings) + ] diff --git a/awx/main/migrations/0121_delete_toweranalyticsstate.py b/awx/main/migrations/0121_delete_toweranalyticsstate.py new file mode 100644 index 0000000000..d1e1ceb37c --- /dev/null +++ b/awx/main/migrations/0121_delete_toweranalyticsstate.py @@ -0,0 +1,16 @@ +# Generated by Django 2.2.11 on 2020-07-24 17:41 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0120_galaxy_credentials'), + ] + + operations = [ + migrations.DeleteModel( + name='TowerAnalyticsState', + ), + ] diff --git a/awx/main/migrations/_galaxy.py b/awx/main/migrations/_galaxy.py new file mode 100644 index 0000000000..b85b7b3aaf --- /dev/null +++ b/awx/main/migrations/_galaxy.py @@ -0,0 +1,125 @@ +# Generated by Django 2.2.11 on 2020-08-04 15:19 + +import logging + +from awx.main.utils.encryption import encrypt_field, decrypt_field + +from django.conf import settings +from django.utils.timezone import now + +from awx.main.models import CredentialType as ModernCredentialType +from awx.main.utils.common import set_current_apps + +logger = logging.getLogger('awx.main.migrations') + + +def migrate_galaxy_settings(apps, schema_editor): + Organization = apps.get_model('main', 'Organization') + if Organization.objects.count() == 0: + # nothing to migrate + return + set_current_apps(apps) + ModernCredentialType.setup_tower_managed_defaults() + CredentialType = apps.get_model('main', 'CredentialType') + Credential = apps.get_model('main', 'Credential') + Setting = apps.get_model('conf', 'Setting') + + galaxy_type = CredentialType.objects.get(kind='galaxy') + private_galaxy_url = Setting.objects.filter(key='PRIMARY_GALAXY_URL').first() + + # by default, prior versions of AWX/Tower automatically pulled content + # from galaxy.ansible.com + public_galaxy_enabled = True + public_galaxy_setting = Setting.objects.filter(key='PUBLIC_GALAXY_ENABLED').first() + if public_galaxy_setting and public_galaxy_setting.value is False: + # ...UNLESS this behavior was explicitly disabled via this setting + public_galaxy_enabled = False + + public_galaxy_credential = Credential( + created=now(), + modified=now(), + name='Ansible Galaxy', + managed_by_tower=True, + credential_type=galaxy_type, + inputs = { + 'url': 'https://galaxy.ansible.com/' + } + ) + public_galaxy_credential.save() + + for org in Organization.objects.all(): + if private_galaxy_url and private_galaxy_url.value: + # If a setting exists for a private Galaxy URL, make a credential for it + username = Setting.objects.filter(key='PRIMARY_GALAXY_USERNAME').first() + password = Setting.objects.filter(key='PRIMARY_GALAXY_PASSWORD').first() + if (username and username.value) or (password and password.value): + logger.error( + f'Specifying HTTP basic auth for the Ansible Galaxy API ' + f'({private_galaxy_url.value}) is no longer supported. ' + 'Please provide an API token instead after your upgrade ' + 'has completed', + ) + inputs = { + 'url': private_galaxy_url.value + } + token = Setting.objects.filter(key='PRIMARY_GALAXY_TOKEN').first() + if token and token.value: + inputs['token'] = decrypt_field(token, 'value') + auth_url = Setting.objects.filter(key='PRIMARY_GALAXY_AUTH_URL').first() + if auth_url and auth_url.value: + inputs['auth_url'] = auth_url.value + name = f'Private Galaxy ({private_galaxy_url.value})' + if 'cloud.redhat.com' in inputs['url']: + name = f'Ansible Automation Hub ({private_galaxy_url.value})' + cred = Credential( + created=now(), + modified=now(), + name=name, + organization=org, + credential_type=galaxy_type, + inputs=inputs + ) + cred.save() + if token and token.value: + # encrypt based on the primary key from the prior save + cred.inputs['token'] = encrypt_field(cred, 'token') + cred.save() + org.galaxy_credentials.add(cred) + + fallback_servers = getattr(settings, 'FALLBACK_GALAXY_SERVERS', []) + for fallback in fallback_servers: + url = fallback.get('url', None) + auth_url = fallback.get('auth_url', None) + username = fallback.get('username', None) + password = fallback.get('password', None) + token = fallback.get('token', None) + if username or password: + logger.error( + f'Specifying HTTP basic auth for the Ansible Galaxy API ' + f'({url}) is no longer supported. ' + 'Please provide an API token instead after your upgrade ' + 'has completed', + ) + inputs = {'url': url} + if token: + inputs['token'] = token + if auth_url: + inputs['auth_url'] = auth_url + cred = Credential( + created=now(), + modified=now(), + name=f'Ansible Galaxy ({url})', + organization=org, + credential_type=galaxy_type, + inputs=inputs + ) + cred.save() + if token: + # encrypt based on the primary key from the prior save + cred.inputs['token'] = encrypt_field(cred, 'token') + cred.save() + org.galaxy_credentials.add(cred) + + if public_galaxy_enabled: + # If public Galaxy was enabled, associate it to the org + org.galaxy_credentials.add(public_galaxy_credential) diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py index 36bb2684ea..df12177aae 100644 --- a/awx/main/models/credential/__init__.py +++ b/awx/main/models/credential/__init__.py @@ -96,6 +96,10 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin): help_text=_('Specify the type of credential you want to create. Refer ' 'to the Ansible Tower documentation for details on each type.') ) + managed_by_tower = models.BooleanField( + default=False, + editable=False + ) organization = models.ForeignKey( 'Organization', null=True, @@ -331,6 +335,7 @@ class CredentialType(CommonModelNameNotUnique): ('insights', _('Insights')), ('external', _('External')), ('kubernetes', _('Kubernetes')), + ('galaxy', _('Galaxy/Automation Hub')), ) kind = models.CharField( @@ -1173,6 +1178,38 @@ ManagedCredentialType( ) +ManagedCredentialType( + namespace='galaxy_api_token', + kind='galaxy', + name=ugettext_noop('Ansible Galaxy/Automation Hub API Token'), + inputs={ + 'fields': [{ + 'id': 'url', + 'label': ugettext_noop('Galaxy Server URL'), + 'type': 'string', + 'help_text': ugettext_noop('The URL of the Galaxy instance to connect to.') + },{ + 'id': 'auth_url', + 'label': ugettext_noop('Auth Server URL'), + 'type': 'string', + 'help_text': ugettext_noop( + 'The URL of a Keycloak server token_endpoint, if using ' + 'SSO auth.' + ) + },{ + 'id': 'token', + 'label': ugettext_noop('API Token'), + 'type': 'string', + 'secret': True, + 'help_text': ugettext_noop( + 'A token to use for authentication against the Galaxy instance.' + ) + }], + 'required': ['url'], + } +) + + class CredentialInputSource(PrimordialModel): class Meta: diff --git a/awx/main/models/events.py b/awx/main/models/events.py index 1f79b0e24b..90cc6f6094 100644 --- a/awx/main/models/events.py +++ b/awx/main/models/events.py @@ -4,6 +4,8 @@ import datetime import logging from collections import defaultdict +from django.conf import settings +from django.core.exceptions import ObjectDoesNotExist from django.db import models, DatabaseError, connection from django.utils.dateparse import parse_datetime from django.utils.text import Truncator @@ -57,7 +59,18 @@ def create_host_status_counts(event_data): return dict(host_status_counts) +MINIMAL_EVENTS = set([ + 'playbook_on_play_start', 'playbook_on_task_start', + 'playbook_on_stats', 'EOF' +]) + + def emit_event_detail(event): + if ( + settings.UI_LIVE_UPDATES_ENABLED is False and + event.event not in MINIMAL_EVENTS + ): + return cls = event.__class__ relation = { JobEvent: 'job_id', @@ -337,41 +350,47 @@ class BasePlaybookEvent(CreatedModifiedModel): pass if isinstance(self, JobEvent): - hostnames = self._hostnames() - self._update_host_summary_from_stats(set(hostnames)) - if self.job.inventory: - try: - self.job.inventory.update_computed_fields() - except DatabaseError: - logger.exception('Computed fields database error saving event {}'.format(self.pk)) + try: + job = self.job + except ObjectDoesNotExist: + job = None + if job: + hostnames = self._hostnames() + self._update_host_summary_from_stats(set(hostnames)) + if job.inventory: + try: + job.inventory.update_computed_fields() + except DatabaseError: + logger.exception('Computed fields database error saving event {}'.format(self.pk)) - # find parent links and progagate changed=T and failed=T - changed = self.job.job_events.filter(changed=True).exclude(parent_uuid=None).only('parent_uuid').values_list('parent_uuid', flat=True).distinct() # noqa - failed = self.job.job_events.filter(failed=True).exclude(parent_uuid=None).only('parent_uuid').values_list('parent_uuid', flat=True).distinct() # noqa + # find parent links and progagate changed=T and failed=T + changed = job.job_events.filter(changed=True).exclude(parent_uuid=None).only('parent_uuid').values_list('parent_uuid', flat=True).distinct() # noqa + failed = job.job_events.filter(failed=True).exclude(parent_uuid=None).only('parent_uuid').values_list('parent_uuid', flat=True).distinct() # noqa - JobEvent.objects.filter( - job_id=self.job_id, uuid__in=changed - ).update(changed=True) - JobEvent.objects.filter( - job_id=self.job_id, uuid__in=failed - ).update(failed=True) + JobEvent.objects.filter( + job_id=self.job_id, uuid__in=changed + ).update(changed=True) + JobEvent.objects.filter( + job_id=self.job_id, uuid__in=failed + ).update(failed=True) - # send success/failure notifications when we've finished handling the playbook_on_stats event - from awx.main.tasks import handle_success_and_failure_notifications # circular import + # send success/failure notifications when we've finished handling the playbook_on_stats event + from awx.main.tasks import handle_success_and_failure_notifications # circular import - def _send_notifications(): - handle_success_and_failure_notifications.apply_async([self.job.id]) - connection.on_commit(_send_notifications) + def _send_notifications(): + handle_success_and_failure_notifications.apply_async([job.id]) + connection.on_commit(_send_notifications) for field in ('playbook', 'play', 'task', 'role'): value = force_text(event_data.get(field, '')).strip() if value != getattr(self, field): setattr(self, field, value) - analytics_logger.info( - 'Event data saved.', - extra=dict(python_objects=dict(job_event=self)) - ) + if settings.LOG_AGGREGATOR_ENABLED: + analytics_logger.info( + 'Event data saved.', + extra=dict(python_objects=dict(job_event=self)) + ) @classmethod def create_from_data(cls, **kwargs): @@ -484,7 +503,11 @@ class JobEvent(BasePlaybookEvent): def _update_host_summary_from_stats(self, hostnames): with ignore_inventory_computed_fields(): - if not self.job or not self.job.inventory: + try: + if not self.job or not self.job.inventory: + logger.info('Event {} missing job or inventory, host summaries not updated'.format(self.pk)) + return + except ObjectDoesNotExist: logger.info('Event {} missing job or inventory, host summaries not updated'.format(self.pk)) return job = self.job @@ -520,13 +543,21 @@ class JobEvent(BasePlaybookEvent): (summary['host_id'], summary['id']) for summary in JobHostSummary.objects.filter(job_id=job.id).values('id', 'host_id') ) + updated_hosts = set() for h in all_hosts: # if the hostname *shows up* in the playbook_on_stats event if h.name in hostnames: h.last_job_id = job.id + updated_hosts.add(h) if h.id in host_mapping: h.last_job_host_summary_id = host_mapping[h.id] - Host.objects.bulk_update(all_hosts, ['last_job_id', 'last_job_host_summary_id']) + updated_hosts.add(h) + + Host.objects.bulk_update( + list(updated_hosts), + ['last_job_id', 'last_job_host_summary_id'], + batch_size=100 + ) @property diff --git a/awx/main/models/ha.py b/awx/main/models/ha.py index ac7df97e10..fc4e9c022e 100644 --- a/awx/main/models/ha.py +++ b/awx/main/models/ha.py @@ -12,6 +12,7 @@ from django.utils.translation import ugettext_lazy as _ from django.conf import settings from django.utils.timezone import now, timedelta +import redis from solo.models import SingletonModel from awx import __version__ as awx_application_version @@ -23,7 +24,7 @@ from awx.main.models.unified_jobs import UnifiedJob from awx.main.utils import get_cpu_capacity, get_mem_capacity, get_system_task_capacity from awx.main.models.mixins import RelatedJobsMixin -__all__ = ('Instance', 'InstanceGroup', 'TowerScheduleState', 'TowerAnalyticsState') +__all__ = ('Instance', 'InstanceGroup', 'TowerScheduleState') class HasPolicyEditsMixin(HasEditsMixin): @@ -152,6 +153,14 @@ class Instance(HasPolicyEditsMixin, BaseModel): self.capacity = get_system_task_capacity(self.capacity_adjustment) else: self.capacity = 0 + + try: + # if redis is down for some reason, that means we can't persist + # playbook event data; we should consider this a zero capacity event + redis.Redis.from_url(settings.BROKER_URL).ping() + except redis.ConnectionError: + self.capacity = 0 + self.cpu = cpu[0] self.memory = mem[0] self.cpu_capacity = cpu[1] @@ -287,10 +296,6 @@ class TowerScheduleState(SingletonModel): schedule_last_run = models.DateTimeField(auto_now_add=True) -class TowerAnalyticsState(SingletonModel): - last_run = models.DateTimeField(auto_now_add=True) - - def schedule_policy_task(): from awx.main.tasks import apply_cluster_membership_policies connection.on_commit(lambda: apply_cluster_membership_policies.apply_async()) diff --git a/awx/main/models/notifications.py b/awx/main/models/notifications.py index c374f60420..11d97c7690 100644 --- a/awx/main/models/notifications.py +++ b/awx/main/models/notifications.py @@ -393,7 +393,11 @@ class JobNotificationMixin(object): 'job': job_context, 'job_friendly_name': self.get_notification_friendly_name(), 'url': self.get_ui_url(), - 'job_metadata': json.dumps(self.notification_data(), indent=4) + 'job_metadata': json.dumps( + self.notification_data(), + ensure_ascii=False, + indent=4 + ) } def build_context(node, fields, allowed_fields): diff --git a/awx/main/models/organization.py b/awx/main/models/organization.py index 23ce65f5e9..bf2e07d255 100644 --- a/awx/main/models/organization.py +++ b/awx/main/models/organization.py @@ -45,6 +45,12 @@ class Organization(CommonModel, NotificationFieldsModel, ResourceMixin, CustomVi blank=True, through='OrganizationInstanceGroupMembership' ) + galaxy_credentials = OrderedManyToManyField( + 'Credential', + blank=True, + through='OrganizationGalaxyCredentialMembership', + related_name='%(class)s_galaxy_credentials' + ) max_hosts = models.PositiveIntegerField( blank=True, default=0, @@ -108,6 +114,23 @@ class Organization(CommonModel, NotificationFieldsModel, ResourceMixin, CustomVi return UnifiedJob.objects.non_polymorphic().filter(organization=self) +class OrganizationGalaxyCredentialMembership(models.Model): + + organization = models.ForeignKey( + 'Organization', + on_delete=models.CASCADE + ) + credential = models.ForeignKey( + 'Credential', + on_delete=models.CASCADE + ) + position = models.PositiveIntegerField( + null=True, + default=None, + db_index=True, + ) + + class Team(CommonModelNameNotUnique, ResourceMixin): ''' A team is a group of users that work on common projects. diff --git a/awx/main/models/schedules.py b/awx/main/models/schedules.py index a3dd32d6b0..5b907a4333 100644 --- a/awx/main/models/schedules.py +++ b/awx/main/models/schedules.py @@ -205,10 +205,15 @@ class Schedule(PrimordialModel, LaunchTimeConfig): 'A valid TZID must be provided (e.g., America/New_York)' ) - if fast_forward and ('MINUTELY' in rrule or 'HOURLY' in rrule): + if ( + fast_forward and + ('MINUTELY' in rrule or 'HOURLY' in rrule) and + 'COUNT=' not in rrule + ): try: first_event = x[0] - if first_event < now(): + # If the first event was over a week ago... + if (now() - first_event).days > 7: # hourly/minutely rrules with far-past DTSTART values # are *really* slow to precompute # start *from* one week ago to speed things up drastically diff --git a/awx/main/models/workflow.py b/awx/main/models/workflow.py index aaf59b25b8..d3e9d2d957 100644 --- a/awx/main/models/workflow.py +++ b/awx/main/models/workflow.py @@ -776,6 +776,10 @@ class WorkflowApproval(UnifiedJob, JobNotificationMixin): self.send_approval_notification('running') return can_start + @property + def event_processing_finished(self): + return True + def send_approval_notification(self, approval_status): from awx.main.tasks import send_notifications # avoid circular import if self.workflow_job_template is None: diff --git a/awx/main/redact.py b/awx/main/redact.py index 4c286eb9a8..32899d935e 100644 --- a/awx/main/redact.py +++ b/awx/main/redact.py @@ -1,8 +1,6 @@ import re import urllib.parse as urlparse -from django.conf import settings - REPLACE_STR = '$encrypted$' @@ -12,12 +10,6 @@ class UriCleaner(object): @staticmethod def remove_sensitive(cleartext): - # exclude_list contains the items that will _not_ be redacted - exclude_list = [settings.PUBLIC_GALAXY_SERVER['url']] - if settings.PRIMARY_GALAXY_URL: - exclude_list += [settings.PRIMARY_GALAXY_URL] - if settings.FALLBACK_GALAXY_SERVERS: - exclude_list += [server['url'] for server in settings.FALLBACK_GALAXY_SERVERS] redactedtext = cleartext text_index = 0 while True: @@ -25,10 +17,6 @@ class UriCleaner(object): if not match: break uri_str = match.group(1) - # Do not redact items from the exclude list - if any(uri_str.startswith(exclude_uri) for exclude_uri in exclude_list): - text_index = match.start() + len(uri_str) - continue try: # May raise a ValueError if invalid URI for one reason or another o = urlparse.urlsplit(uri_str) diff --git a/awx/main/scheduler/task_manager.py b/awx/main/scheduler/task_manager.py index cba6161d83..9f4818bd37 100644 --- a/awx/main/scheduler/task_manager.py +++ b/awx/main/scheduler/task_manager.py @@ -12,6 +12,7 @@ import random from django.db import transaction, connection from django.utils.translation import ugettext_lazy as _, gettext_noop from django.utils.timezone import now as tz_now +from django.conf import settings # AWX from awx.main.dispatch.reaper import reap_job @@ -45,6 +46,12 @@ class TaskManager(): def __init__(self): self.graph = dict() + # start task limit indicates how many pending jobs can be started on this + # .schedule() run. Starting jobs is expensive, and there is code in place to reap + # the task manager after 5 minutes. At scale, the task manager can easily take more than + # 5 minutes to start pending jobs. If this limit is reached, pending jobs + # will no longer be started and will be started on the next task manager cycle. + self.start_task_limit = settings.START_TASK_LIMIT for rampart_group in InstanceGroup.objects.prefetch_related('instances'): self.graph[rampart_group.name] = dict(graph=DependencyGraph(rampart_group.name), capacity_total=rampart_group.capacity, @@ -189,6 +196,10 @@ class TaskManager(): return result def start_task(self, task, rampart_group, dependent_tasks=None, instance=None): + self.start_task_limit -= 1 + if self.start_task_limit == 0: + # schedule another run immediately after this task manager + schedule_task_manager() from awx.main.tasks import handle_work_error, handle_work_success dependent_tasks = dependent_tasks or [] @@ -448,6 +459,8 @@ class TaskManager(): def process_pending_tasks(self, pending_tasks): running_workflow_templates = set([wf.unified_job_template_id for wf in self.get_running_workflow_jobs()]) for task in pending_tasks: + if self.start_task_limit <= 0: + break if self.is_job_blocked(task): logger.debug("{} is blocked from running".format(task.log_format)) continue diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 0c7b219371..0e03055055 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -51,8 +51,9 @@ import ansible_runner # AWX from awx import __version__ as awx_application_version -from awx.main.constants import PRIVILEGE_ESCALATION_METHODS, STANDARD_INVENTORY_UPDATE_ENV, GALAXY_SERVER_FIELDS +from awx.main.constants import PRIVILEGE_ESCALATION_METHODS, STANDARD_INVENTORY_UPDATE_ENV from awx.main.access import access_registry +from awx.main.analytics import all_collectors, expensive_collectors from awx.main.redact import UriCleaner from awx.main.models import ( Schedule, TowerScheduleState, Instance, InstanceGroup, @@ -355,6 +356,26 @@ def send_notifications(notification_list, job_id=None): @task(queue=get_local_queuename) def gather_analytics(): + def _gather_and_ship(subset, since, until): + tgzfiles = [] + try: + tgzfiles = analytics.gather(subset=subset, since=since, until=until) + # empty analytics without raising an exception is not an error + if not tgzfiles: + return True + logger.info('Gathered analytics from {} to {}: {}'.format(since, until, tgzfiles)) + for tgz in tgzfiles: + analytics.ship(tgz) + except Exception: + logger.exception('Error gathering and sending analytics for {} to {}.'.format(since,until)) + return False + finally: + if tgzfiles: + for tgz in tgzfiles: + if os.path.exists(tgz): + os.remove(tgz) + return True + from awx.conf.models import Setting from rest_framework.fields import DateTimeField if not settings.INSIGHTS_TRACKING_STATE: @@ -373,16 +394,29 @@ def gather_analytics(): if acquired is False: logger.debug('Not gathering analytics, another task holds lock') return - try: - tgz = analytics.gather() - if not tgz: - return - logger.info('gathered analytics: {}'.format(tgz)) - analytics.ship(tgz) - settings.AUTOMATION_ANALYTICS_LAST_GATHER = gather_time - finally: - if os.path.exists(tgz): - os.remove(tgz) + subset = list(all_collectors().keys()) + incremental_collectors = [] + for collector in expensive_collectors(): + if collector in subset: + subset.remove(collector) + incremental_collectors.append(collector) + + # Cap gathering at 4 weeks of data if there has been no data gathering + since = last_time or (gather_time - timedelta(weeks=4)) + + if incremental_collectors: + start = since + until = None + while start < gather_time: + until = start + timedelta(hours = 4) + if (until > gather_time): + until = gather_time + if not _gather_and_ship(incremental_collectors, since=start, until=until): + break + start = until + settings.AUTOMATION_ANALYTICS_LAST_GATHER = until + if subset: + _gather_and_ship(subset, since=since, until=gather_time) @task(queue=get_local_queuename) @@ -1472,6 +1506,8 @@ class BaseTask(object): self.instance.job_explanation = "Job terminated due to timeout" status = 'failed' extra_update_fields['job_explanation'] = self.instance.job_explanation + # ensure failure notification sends even if playbook_on_stats event is not triggered + handle_success_and_failure_notifications.apply_async([self.instance.job.id]) except InvalidVirtualenvError as e: extra_update_fields['job_explanation'] = e.message @@ -1632,11 +1668,6 @@ class RunJob(BaseTask): # callbacks to work. env['JOB_ID'] = str(job.pk) env['INVENTORY_ID'] = str(job.inventory.pk) - if job.use_fact_cache: - library_source = self.get_path_to('..', 'plugins', 'library') - library_dest = os.path.join(private_data_dir, 'library') - copy_tree(library_source, library_dest) - env['ANSIBLE_LIBRARY'] = library_dest if job.project: env['PROJECT_REVISION'] = job.project.scm_revision env['ANSIBLE_RETRY_FILES_ENABLED'] = "False" @@ -2020,35 +2051,25 @@ class RunProjectUpdate(BaseTask): env['PROJECT_UPDATE_ID'] = str(project_update.pk) if settings.GALAXY_IGNORE_CERTS: env['ANSIBLE_GALAXY_IGNORE'] = True - # Set up the public Galaxy server, if enabled - galaxy_configured = False - if settings.PUBLIC_GALAXY_ENABLED: - galaxy_servers = [settings.PUBLIC_GALAXY_SERVER] # static setting - else: - galaxy_configured = True - galaxy_servers = [] - # Set up fallback Galaxy servers, if configured - if settings.FALLBACK_GALAXY_SERVERS: - galaxy_configured = True - galaxy_servers = settings.FALLBACK_GALAXY_SERVERS + galaxy_servers - # Set up the primary Galaxy server, if configured - if settings.PRIMARY_GALAXY_URL: - galaxy_configured = True - galaxy_servers = [{'id': 'primary_galaxy'}] + galaxy_servers - for key in GALAXY_SERVER_FIELDS: - value = getattr(settings, 'PRIMARY_GALAXY_{}'.format(key.upper())) - if value: - galaxy_servers[0][key] = value - if galaxy_configured: - for server in galaxy_servers: - for key in GALAXY_SERVER_FIELDS: - if not server.get(key): - continue - env_key = ('ANSIBLE_GALAXY_SERVER_{}_{}'.format(server.get('id', 'unnamed'), key)).upper() - env[env_key] = server[key] - if galaxy_servers: - # now set the precedence of galaxy servers - env['ANSIBLE_GALAXY_SERVER_LIST'] = ','.join([server.get('id', 'unnamed') for server in galaxy_servers]) + + # build out env vars for Galaxy credentials (in order) + galaxy_server_list = [] + if project_update.project.organization: + for i, cred in enumerate( + project_update.project.organization.galaxy_credentials.all() + ): + env[f'ANSIBLE_GALAXY_SERVER_SERVER{i}_URL'] = cred.get_input('url') + auth_url = cred.get_input('auth_url', default=None) + token = cred.get_input('token', default=None) + if token: + env[f'ANSIBLE_GALAXY_SERVER_SERVER{i}_TOKEN'] = token + if auth_url: + env[f'ANSIBLE_GALAXY_SERVER_SERVER{i}_AUTH_URL'] = auth_url + galaxy_server_list.append(f'server{i}') + + if galaxy_server_list: + env['ANSIBLE_GALAXY_SERVER_LIST'] = ','.join(galaxy_server_list) + return env def _build_scm_url_extra_vars(self, project_update): @@ -2121,6 +2142,19 @@ class RunProjectUpdate(BaseTask): raise RuntimeError('Could not determine a revision to run from project.') elif not scm_branch: scm_branch = {'hg': 'tip'}.get(project_update.scm_type, 'HEAD') + + galaxy_creds_are_defined = ( + project_update.project.organization and + project_update.project.organization.galaxy_credentials.exists() + ) + if not galaxy_creds_are_defined and ( + settings.AWX_ROLES_ENABLED or settings.AWX_COLLECTIONS_ENABLED + ): + logger.debug( + 'Galaxy role/collection syncing is enabled, but no ' + f'credentials are configured for {project_update.project.organization}.' + ) + extra_vars.update({ 'projects_root': settings.PROJECTS_ROOT.rstrip('/'), 'local_path': os.path.basename(project_update.project.local_path), @@ -2131,8 +2165,8 @@ class RunProjectUpdate(BaseTask): 'scm_url': scm_url, 'scm_branch': scm_branch, 'scm_clean': project_update.scm_clean, - 'roles_enabled': settings.AWX_ROLES_ENABLED, - 'collections_enabled': settings.AWX_COLLECTIONS_ENABLED, + 'roles_enabled': galaxy_creds_are_defined and settings.AWX_ROLES_ENABLED, + 'collections_enabled': galaxy_creds_are_defined and settings.AWX_COLLECTIONS_ENABLED, }) # apply custom refspec from user for PR refs and the like if project_update.scm_refspec: diff --git a/awx/main/tests/factories/README.md b/awx/main/tests/factories/README.md index c451c02598..916c996cfa 100644 --- a/awx/main/tests/factories/README.md +++ b/awx/main/tests/factories/README.md @@ -52,11 +52,11 @@ patterns -------- `mk` functions are single object fixtures. They should create only a single object with the minimum deps. -They should also accept a `persited` flag, if they must be persisted to work, they raise an error if persisted=False +They should also accept a `persisted` flag, if they must be persisted to work, they raise an error if persisted=False `generate` and `apply` functions are helpers that build up the various parts of a `create` functions objects. These should be useful for more than one create function to use and should explicitly accept all of the values needed -to execute. These functions should also be robust and have very speciifc error reporting about constraints and/or +to execute. These functions should also be robust and have very specific error reporting about constraints and/or bad values. `create` functions compose many of the `mk` and `generate` functions to make different object diff --git a/awx/main/tests/functional/analytics/test_collectors.py b/awx/main/tests/functional/analytics/test_collectors.py index ff53ac6bb4..1d643588d1 100644 --- a/awx/main/tests/functional/analytics/test_collectors.py +++ b/awx/main/tests/functional/analytics/test_collectors.py @@ -1,6 +1,7 @@ import pytest import tempfile import os +import re import shutil import csv @@ -27,7 +28,8 @@ def sqlite_copy_expert(request): def write_stdout(self, sql, fd): # Would be cool if we instead properly disected the SQL query and verified - # it that way. But instead, we just take the nieve approach here. + # it that way. But instead, we just take the naive approach here. + sql = sql.strip() assert sql.startswith("COPY (") assert sql.endswith(") TO STDOUT WITH CSV HEADER") @@ -35,6 +37,10 @@ def sqlite_copy_expert(request): sql = sql.replace(") TO STDOUT WITH CSV HEADER", "") # sqlite equivalent sql = sql.replace("ARRAY_AGG", "GROUP_CONCAT") + # SQLite doesn't support isoformatted dates, because that would be useful + sql = sql.replace("+00:00", "") + i = re.compile(r'(?P\d\d\d\d-\d\d-\d\d)T') + sql = i.sub(r'\g ', sql) # Remove JSON style queries # TODO: could replace JSON style queries with sqlite kind of equivalents @@ -86,7 +92,7 @@ def test_copy_tables_unified_job_query( job_name = job_template.create_unified_job().name with tempfile.TemporaryDirectory() as tmpdir: - collectors.copy_tables(time_start, tmpdir, subset="unified_jobs") + collectors.unified_jobs_table(time_start, tmpdir, until = now() + timedelta(seconds=1)) with open(os.path.join(tmpdir, "unified_jobs_table.csv")) as f: lines = "".join([line for line in f]) @@ -134,7 +140,7 @@ def test_copy_tables_workflow_job_node_query(sqlite_copy_expert, workflow_job): time_start = now() - timedelta(hours=9) with tempfile.TemporaryDirectory() as tmpdir: - collectors.copy_tables(time_start, tmpdir, subset="workflow_job_node_query") + collectors.workflow_job_node_table(time_start, tmpdir, until = now() + timedelta(seconds=1)) with open(os.path.join(tmpdir, "workflow_job_node_table.csv")) as f: reader = csv.reader(f) # Pop the headers diff --git a/awx/main/tests/functional/analytics/test_core.py b/awx/main/tests/functional/analytics/test_core.py index 01f3858661..f3cc1fcd4b 100644 --- a/awx/main/tests/functional/analytics/test_core.py +++ b/awx/main/tests/functional/analytics/test_core.py @@ -10,17 +10,17 @@ from awx.main.analytics import gather, register @register('example', '1.0') -def example(since): +def example(since, **kwargs): return {'awx': 123} @register('bad_json', '1.0') -def bad_json(since): +def bad_json(since, **kwargs): return set() @register('throws_error', '1.0') -def throws_error(since): +def throws_error(since, **kwargs): raise ValueError() @@ -39,9 +39,9 @@ def mock_valid_license(): def test_gather(mock_valid_license): settings.INSIGHTS_TRACKING_STATE = True - tgz = gather(module=importlib.import_module(__name__)) + tgzfiles = gather(module=importlib.import_module(__name__)) files = {} - with tarfile.open(tgz, "r:gz") as archive: + with tarfile.open(tgzfiles[0], "r:gz") as archive: for member in archive.getmembers(): files[member.name] = archive.extractfile(member) @@ -53,7 +53,8 @@ def test_gather(mock_valid_license): assert './bad_json.json' not in files.keys() assert './throws_error.json' not in files.keys() try: - os.remove(tgz) + for tgz in tgzfiles: + os.remove(tgz) except Exception: pass diff --git a/awx/main/tests/functional/api/test_credential_type.py b/awx/main/tests/functional/api/test_credential_type.py index c8f87f0c57..bf7aa4ceff 100644 --- a/awx/main/tests/functional/api/test_credential_type.py +++ b/awx/main/tests/functional/api/test_credential_type.py @@ -220,7 +220,7 @@ def test_create_valid_kind(kind, get, post, admin): @pytest.mark.django_db -@pytest.mark.parametrize('kind', ['ssh', 'vault', 'scm', 'insights', 'kubernetes']) +@pytest.mark.parametrize('kind', ['ssh', 'vault', 'scm', 'insights', 'kubernetes', 'galaxy']) def test_create_invalid_kind(kind, get, post, admin): response = post(reverse('api:credential_type_list'), { 'kind': kind, diff --git a/awx/main/tests/functional/api/test_job_runtime_params.py b/awx/main/tests/functional/api/test_job_runtime_params.py index e628623cf1..80b2fcadfb 100644 --- a/awx/main/tests/functional/api/test_job_runtime_params.py +++ b/awx/main/tests/functional/api/test_job_runtime_params.py @@ -359,6 +359,71 @@ def test_job_launch_fails_with_missing_vault_password(machine_credential, vault_ assert response.data['passwords_needed_to_start'] == ['vault_password'] +@pytest.mark.django_db +def test_job_launch_with_added_cred_and_vault_password(credential, machine_credential, vault_credential, + deploy_jobtemplate, post, admin): + # see: https://github.com/ansible/awx/issues/8202 + vault_credential.inputs['vault_password'] = 'ASK' + vault_credential.save() + payload = { + 'credentials': [vault_credential.id, machine_credential.id], + 'credential_passwords': {'vault_password': 'vault-me'}, + } + + deploy_jobtemplate.ask_credential_on_launch = True + deploy_jobtemplate.credentials.remove(credential) + deploy_jobtemplate.credentials.add(vault_credential) + deploy_jobtemplate.save() + + with mock.patch.object(Job, 'signal_start') as signal_start: + post( + reverse('api:job_template_launch', kwargs={'pk': deploy_jobtemplate.pk}), + payload, + admin, + expect=201, + ) + signal_start.assert_called_with(**{ + 'vault_password': 'vault-me' + }) + + +@pytest.mark.django_db +def test_job_launch_with_multiple_launch_time_passwords(credential, machine_credential, vault_credential, + deploy_jobtemplate, post, admin): + # see: https://github.com/ansible/awx/issues/8202 + deploy_jobtemplate.ask_credential_on_launch = True + deploy_jobtemplate.credentials.remove(credential) + deploy_jobtemplate.credentials.add(machine_credential) + deploy_jobtemplate.credentials.add(vault_credential) + deploy_jobtemplate.save() + + second_machine_credential = Credential( + name='SSH #2', + credential_type=machine_credential.credential_type, + inputs={'password': 'ASK'} + ) + second_machine_credential.save() + + vault_credential.inputs['vault_password'] = 'ASK' + vault_credential.save() + payload = { + 'credentials': [vault_credential.id, second_machine_credential.id], + 'credential_passwords': {'ssh_password': 'ssh-me', 'vault_password': 'vault-me'}, + } + + with mock.patch.object(Job, 'signal_start') as signal_start: + post( + reverse('api:job_template_launch', kwargs={'pk': deploy_jobtemplate.pk}), + payload, + admin, + expect=201, + ) + signal_start.assert_called_with(**{ + 'ssh_password': 'ssh-me', + 'vault_password': 'vault-me', + }) + + @pytest.mark.django_db @pytest.mark.parametrize('launch_kwargs', [ {'vault_password.abc': 'vault-me-1', 'vault_password.xyz': 'vault-me-2'}, diff --git a/awx/main/tests/functional/api/test_organizations.py b/awx/main/tests/functional/api/test_organizations.py index 0827199676..6c45c0c681 100644 --- a/awx/main/tests/functional/api/test_organizations.py +++ b/awx/main/tests/functional/api/test_organizations.py @@ -9,7 +9,7 @@ from django.conf import settings import pytest # AWX -from awx.main.models import ProjectUpdate +from awx.main.models import ProjectUpdate, CredentialType, Credential from awx.api.versioning import reverse @@ -288,3 +288,90 @@ def test_organization_delete_with_active_jobs(delete, admin, organization, organ assert resp.data['error'] == u"Resource is being used by running jobs." assert resp_sorted == expect_sorted + + +@pytest.mark.django_db +def test_galaxy_credential_association_forbidden(alice, organization, post): + galaxy = CredentialType.defaults['galaxy_api_token']() + galaxy.save() + + cred = Credential.objects.create( + credential_type=galaxy, + name='Public Galaxy', + organization=organization, + inputs={ + 'url': 'https://galaxy.ansible.com/' + } + ) + url = reverse('api:organization_galaxy_credentials_list', kwargs={'pk': organization.id}) + post( + url, + {'associate': True, 'id': cred.pk}, + user=alice, + expect=403 + ) + + +@pytest.mark.django_db +def test_galaxy_credential_type_enforcement(admin, organization, post): + ssh = CredentialType.defaults['ssh']() + ssh.save() + + cred = Credential.objects.create( + credential_type=ssh, + name='SSH Credential', + organization=organization, + ) + url = reverse('api:organization_galaxy_credentials_list', kwargs={'pk': organization.id}) + resp = post( + url, + {'associate': True, 'id': cred.pk}, + user=admin, + expect=400 + ) + assert resp.data['msg'] == 'Credential must be a Galaxy credential, not Machine.' + + +@pytest.mark.django_db +def test_galaxy_credential_association(alice, admin, organization, post, get): + galaxy = CredentialType.defaults['galaxy_api_token']() + galaxy.save() + + for i in range(5): + cred = Credential.objects.create( + credential_type=galaxy, + name=f'Public Galaxy {i + 1}', + organization=organization, + inputs={ + 'url': 'https://galaxy.ansible.com/' + } + ) + url = reverse('api:organization_galaxy_credentials_list', kwargs={'pk': organization.id}) + post( + url, + {'associate': True, 'id': cred.pk}, + user=admin, + expect=204 + ) + resp = get(url, user=admin) + assert [cred['name'] for cred in resp.data['results']] == [ + 'Public Galaxy 1', + 'Public Galaxy 2', + 'Public Galaxy 3', + 'Public Galaxy 4', + 'Public Galaxy 5', + ] + + post( + url, + {'disassociate': True, 'id': Credential.objects.get(name='Public Galaxy 3').pk}, + user=admin, + expect=204 + ) + resp = get(url, user=admin) + assert [cred['name'] for cred in resp.data['results']] == [ + 'Public Galaxy 1', + 'Public Galaxy 2', + 'Public Galaxy 4', + 'Public Galaxy 5', + ] diff --git a/awx/main/tests/functional/models/test_notifications.py b/awx/main/tests/functional/models/test_notifications.py index 8d514312ae..5e5f19f0fd 100644 --- a/awx/main/tests/functional/models/test_notifications.py +++ b/awx/main/tests/functional/models/test_notifications.py @@ -123,6 +123,15 @@ class TestJobNotificationMixin(object): context = job.context(job_serialization) check_structure(TestJobNotificationMixin.CONTEXT_STRUCTURE, context) + + @pytest.mark.django_db + def test_context_job_metadata_with_unicode(self): + job = Job.objects.create(name='批量安装项目') + job_serialization = UnifiedJobSerializer(job).to_representation(job) + context = job.context(job_serialization) + assert '批量安装项目' in context['job_metadata'] + + def test_context_stub(self): """The context stub is a fake context used to validate custom notification messages. Ensure that this also has the expected structure. Furthermore, ensure that the stub context contains diff --git a/awx/main/tests/functional/models/test_project.py b/awx/main/tests/functional/models/test_project.py index 2cf43c5690..d3c34498b0 100644 --- a/awx/main/tests/functional/models/test_project.py +++ b/awx/main/tests/functional/models/test_project.py @@ -1,7 +1,7 @@ import pytest from unittest import mock -from awx.main.models import Project +from awx.main.models import Project, Credential, CredentialType from awx.main.models.organization import Organization @@ -57,3 +57,31 @@ def test_foreign_key_change_changes_modified_by(project, organization): def test_project_related_jobs(project): update = project.create_unified_job() assert update.id in [u.id for u in project._get_related_jobs()] + + +@pytest.mark.django_db +def test_galaxy_credentials(project): + org = project.organization + galaxy = CredentialType.defaults['galaxy_api_token']() + galaxy.save() + for i in range(5): + cred = Credential.objects.create( + name=f'Ansible Galaxy {i + 1}', + organization=org, + credential_type=galaxy, + inputs={ + 'url': 'https://galaxy.ansible.com/' + } + ) + cred.save() + org.galaxy_credentials.add(cred) + + assert [ + cred.name for cred in org.galaxy_credentials.all() + ] == [ + 'Ansible Galaxy 1', + 'Ansible Galaxy 2', + 'Ansible Galaxy 3', + 'Ansible Galaxy 4', + 'Ansible Galaxy 5', + ] diff --git a/awx/main/tests/functional/models/test_schedule.py b/awx/main/tests/functional/models/test_schedule.py index 5575921ff0..fb5bfbf271 100644 --- a/awx/main/tests/functional/models/test_schedule.py +++ b/awx/main/tests/functional/models/test_schedule.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timedelta from contextlib import contextmanager from django.utils.timezone import now @@ -161,6 +161,58 @@ class TestComputedFields: assert job_template.next_schedule == expected_schedule +@pytest.mark.django_db +@pytest.mark.parametrize('freq, delta', ( + ('MINUTELY', 1), + ('HOURLY', 1) +)) +def test_past_week_rrule(job_template, freq, delta): + # see: https://github.com/ansible/awx/issues/8071 + recent = (datetime.utcnow() - timedelta(days=3)) + recent = recent.replace(hour=0, minute=0, second=0, microsecond=0) + recent_dt = recent.strftime('%Y%m%d') + rrule = f'DTSTART;TZID=America/New_York:{recent_dt}T000000 RRULE:FREQ={freq};INTERVAL={delta};COUNT=5' # noqa + sched = Schedule.objects.create( + name='example schedule', + rrule=rrule, + unified_job_template=job_template + ) + first_event = sched.rrulestr(sched.rrule)[0] + assert first_event.replace(tzinfo=None) == recent + + +@pytest.mark.django_db +@pytest.mark.parametrize('freq, delta', ( + ('MINUTELY', 1), + ('HOURLY', 1) +)) +def test_really_old_dtstart(job_template, freq, delta): + # see: https://github.com/ansible/awx/issues/8071 + # If an event is per-minute/per-hour and was created a *really long* + # time ago, we should just bump forward to start counting "in the last week" + rrule = f'DTSTART;TZID=America/New_York:20150101T000000 RRULE:FREQ={freq};INTERVAL={delta}' # noqa + sched = Schedule.objects.create( + name='example schedule', + rrule=rrule, + unified_job_template=job_template + ) + last_week = (datetime.utcnow() - timedelta(days=7)).date() + first_event = sched.rrulestr(sched.rrule)[0] + assert last_week == first_event.date() + + # the next few scheduled events should be the next minute/hour incremented + next_five_events = list(sched.rrulestr(sched.rrule).xafter(now(), count=5)) + + assert next_five_events[0] > now() + last = None + for event in next_five_events: + if last: + assert event == last + ( + timedelta(minutes=1) if freq == 'MINUTELY' else timedelta(hours=1) + ) + last = event + + @pytest.mark.django_db def test_repeats_forever(job_template): s = Schedule( diff --git a/awx/main/tests/functional/test_credential.py b/awx/main/tests/functional/test_credential.py index 721bf5c043..684f9dd5a7 100644 --- a/awx/main/tests/functional/test_credential.py +++ b/awx/main/tests/functional/test_credential.py @@ -81,6 +81,7 @@ def test_default_cred_types(): 'azure_rm', 'cloudforms', 'conjur', + 'galaxy_api_token', 'gce', 'github_token', 'gitlab_token', diff --git a/awx/main/tests/functional/test_dispatch.py b/awx/main/tests/functional/test_dispatch.py index caf54f0161..e92867d6a5 100644 --- a/awx/main/tests/functional/test_dispatch.py +++ b/awx/main/tests/functional/test_dispatch.py @@ -10,7 +10,7 @@ import pytest from awx.main.models import Job, WorkflowJob, Instance from awx.main.dispatch import reaper -from awx.main.dispatch.pool import PoolWorker, WorkerPool, AutoscalePool +from awx.main.dispatch.pool import StatefulPoolWorker, WorkerPool, AutoscalePool from awx.main.dispatch.publish import task from awx.main.dispatch.worker import BaseWorker, TaskWorker @@ -80,7 +80,7 @@ class SlowResultWriter(BaseWorker): class TestPoolWorker: def setup_method(self, test_method): - self.worker = PoolWorker(1000, self.tick, tuple()) + self.worker = StatefulPoolWorker(1000, self.tick, tuple()) def tick(self): self.worker.finished.put(self.worker.queue.get()['uuid']) diff --git a/awx/main/tests/functional/test_galaxy_credential_migration.py b/awx/main/tests/functional/test_galaxy_credential_migration.py new file mode 100644 index 0000000000..110628e19c --- /dev/null +++ b/awx/main/tests/functional/test_galaxy_credential_migration.py @@ -0,0 +1,115 @@ +import importlib + +from django.conf import settings +from django.contrib.contenttypes.models import ContentType +import pytest + +from awx.main.models import Credential, Organization +from awx.conf.models import Setting +from awx.main.migrations import _galaxy as galaxy + + +class FakeApps(object): + def get_model(self, app, model): + if app == 'contenttypes': + return ContentType + return getattr(importlib.import_module(f'awx.{app}.models'), model) + + +apps = FakeApps() + + +@pytest.mark.django_db +def test_default_public_galaxy(): + org = Organization.objects.create() + assert org.galaxy_credentials.count() == 0 + galaxy.migrate_galaxy_settings(apps, None) + assert org.galaxy_credentials.count() == 1 + creds = org.galaxy_credentials.all() + assert creds[0].name == 'Ansible Galaxy' + assert creds[0].inputs['url'] == 'https://galaxy.ansible.com/' + + +@pytest.mark.django_db +def test_public_galaxy_disabled(): + Setting.objects.create(key='PUBLIC_GALAXY_ENABLED', value=False) + org = Organization.objects.create() + assert org.galaxy_credentials.count() == 0 + galaxy.migrate_galaxy_settings(apps, None) + assert org.galaxy_credentials.count() == 0 + + +@pytest.mark.django_db +def test_rh_automation_hub(): + Setting.objects.create(key='PRIMARY_GALAXY_URL', value='https://cloud.redhat.com/api/automation-hub/') + Setting.objects.create(key='PRIMARY_GALAXY_TOKEN', value='secret123') + org = Organization.objects.create() + assert org.galaxy_credentials.count() == 0 + galaxy.migrate_galaxy_settings(apps, None) + assert org.galaxy_credentials.count() == 2 + assert org.galaxy_credentials.first().name == 'Ansible Automation Hub (https://cloud.redhat.com/api/automation-hub/)' # noqa + + +@pytest.mark.django_db +def test_multiple_galaxies(): + for i in range(5): + Organization.objects.create(name=f'Org {i}') + + Setting.objects.create(key='PRIMARY_GALAXY_URL', value='https://example.org/') + Setting.objects.create(key='PRIMARY_GALAXY_AUTH_URL', value='https://auth.example.org/') + Setting.objects.create(key='PRIMARY_GALAXY_USERNAME', value='user') + Setting.objects.create(key='PRIMARY_GALAXY_PASSWORD', value='pass') + Setting.objects.create(key='PRIMARY_GALAXY_TOKEN', value='secret123') + + for org in Organization.objects.all(): + assert org.galaxy_credentials.count() == 0 + + galaxy.migrate_galaxy_settings(apps, None) + + for org in Organization.objects.all(): + assert org.galaxy_credentials.count() == 2 + creds = org.galaxy_credentials.all() + assert creds[0].name == 'Private Galaxy (https://example.org/)' + assert creds[0].inputs['url'] == 'https://example.org/' + assert creds[0].inputs['auth_url'] == 'https://auth.example.org/' + assert creds[0].inputs['token'].startswith('$encrypted$') + assert creds[0].get_input('token') == 'secret123' + + assert creds[1].name == 'Ansible Galaxy' + assert creds[1].inputs['url'] == 'https://galaxy.ansible.com/' + + public_galaxy_creds = Credential.objects.filter(name='Ansible Galaxy') + assert public_galaxy_creds.count() == 1 + assert public_galaxy_creds.first().managed_by_tower is True + + +@pytest.mark.django_db +def test_fallback_galaxies(): + org = Organization.objects.create() + assert org.galaxy_credentials.count() == 0 + Setting.objects.create(key='PRIMARY_GALAXY_URL', value='https://example.org/') + Setting.objects.create(key='PRIMARY_GALAXY_AUTH_URL', value='https://auth.example.org/') + Setting.objects.create(key='PRIMARY_GALAXY_TOKEN', value='secret123') + try: + settings.FALLBACK_GALAXY_SERVERS = [{ + 'id': 'abc123', + 'url': 'https://some-other-galaxy.example.org/', + 'auth_url': 'https://some-other-galaxy.sso.example.org/', + 'username': 'user', + 'password': 'pass', + 'token': 'fallback123', + }] + galaxy.migrate_galaxy_settings(apps, None) + finally: + settings.FALLBACK_GALAXY_SERVERS = [] + assert org.galaxy_credentials.count() == 3 + creds = org.galaxy_credentials.all() + assert creds[0].name == 'Private Galaxy (https://example.org/)' + assert creds[0].inputs['url'] == 'https://example.org/' + assert creds[1].name == 'Ansible Galaxy (https://some-other-galaxy.example.org/)' + assert creds[1].inputs['url'] == 'https://some-other-galaxy.example.org/' + assert creds[1].inputs['auth_url'] == 'https://some-other-galaxy.sso.example.org/' + assert creds[1].inputs['token'].startswith('$encrypted$') + assert creds[1].get_input('token') == 'fallback123' + assert creds[2].name == 'Ansible Galaxy' + assert creds[2].inputs['url'] == 'https://galaxy.ansible.com/' diff --git a/awx/main/tests/functional/test_jobs.py b/awx/main/tests/functional/test_jobs.py index 2bc10fa0df..b4754a6803 100644 --- a/awx/main/tests/functional/test_jobs.py +++ b/awx/main/tests/functional/test_jobs.py @@ -1,3 +1,4 @@ +import redis import pytest from unittest import mock import json @@ -25,7 +26,8 @@ def test_orphan_unified_job_creation(instance, inventory): @mock.patch('awx.main.utils.common.get_mem_capacity', lambda: (8000,62)) def test_job_capacity_and_with_inactive_node(): i = Instance.objects.create(hostname='test-1') - i.refresh_capacity() + with mock.patch.object(redis.client.Redis, 'ping', lambda self: True): + i.refresh_capacity() assert i.capacity == 62 i.enabled = False i.save() @@ -35,6 +37,19 @@ def test_job_capacity_and_with_inactive_node(): assert i.capacity == 0 +@pytest.mark.django_db +@mock.patch('awx.main.utils.common.get_cpu_capacity', lambda: (2,8)) +@mock.patch('awx.main.utils.common.get_mem_capacity', lambda: (8000,62)) +def test_job_capacity_with_redis_disabled(): + i = Instance.objects.create(hostname='test-1') + + def _raise(self): + raise redis.ConnectionError() + with mock.patch.object(redis.client.Redis, 'ping', _raise): + i.refresh_capacity() + assert i.capacity == 0 + + @pytest.mark.django_db def test_job_type_name(): job = Job.objects.create() diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index 5ce64894e6..b49af2efd0 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -25,6 +25,7 @@ from awx.main.models import ( Job, JobTemplate, Notification, + Organization, Project, ProjectUpdate, UnifiedJob, @@ -59,6 +60,19 @@ def patch_Job(): yield +@pytest.fixture +def patch_Organization(): + _credentials = [] + credentials_mock = mock.Mock(**{ + 'all': lambda: _credentials, + 'add': _credentials.append, + 'exists': lambda: len(_credentials) > 0, + 'spec_set': ['all', 'add', 'exists'], + }) + with mock.patch.object(Organization, 'galaxy_credentials', credentials_mock): + yield + + @pytest.fixture def job(): return Job( @@ -131,7 +145,6 @@ def test_send_notifications_list(mock_notifications_filter, mock_job_get, mocker ('SECRET_KEY', 'SECRET'), ('VMWARE_PASSWORD', 'SECRET'), ('API_SECRET', 'SECRET'), - ('ANSIBLE_GALAXY_SERVER_PRIMARY_GALAXY_PASSWORD', 'SECRET'), ('ANSIBLE_GALAXY_SERVER_PRIMARY_GALAXY_TOKEN', 'SECRET'), ]) def test_safe_env_filtering(key, value): @@ -1780,10 +1793,108 @@ class TestJobCredentials(TestJobExecution): assert env['FOO'] == 'BAR' +@pytest.mark.usefixtures("patch_Organization") +class TestProjectUpdateGalaxyCredentials(TestJobExecution): + + @pytest.fixture + def project_update(self): + org = Organization(pk=1) + proj = Project(pk=1, organization=org) + project_update = ProjectUpdate(pk=1, project=proj, scm_type='git') + project_update.websocket_emit_status = mock.Mock() + return project_update + + parametrize = { + 'test_galaxy_credentials_ignore_certs': [ + dict(ignore=True), + dict(ignore=False), + ], + } + + def test_galaxy_credentials_ignore_certs(self, private_data_dir, project_update, ignore): + settings.GALAXY_IGNORE_CERTS = ignore + task = tasks.RunProjectUpdate() + env = task.build_env(project_update, private_data_dir) + if ignore: + assert env['ANSIBLE_GALAXY_IGNORE'] is True + else: + assert 'ANSIBLE_GALAXY_IGNORE' not in env + + def test_galaxy_credentials_empty(self, private_data_dir, project_update): + + class RunProjectUpdate(tasks.RunProjectUpdate): + __vars__ = {} + + def _write_extra_vars_file(self, private_data_dir, extra_vars, *kw): + self.__vars__ = extra_vars + + task = RunProjectUpdate() + env = task.build_env(project_update, private_data_dir) + task.build_extra_vars_file(project_update, private_data_dir) + assert task.__vars__['roles_enabled'] is False + assert task.__vars__['collections_enabled'] is False + for k in env: + assert not k.startswith('ANSIBLE_GALAXY_SERVER') + + def test_single_public_galaxy(self, private_data_dir, project_update): + class RunProjectUpdate(tasks.RunProjectUpdate): + __vars__ = {} + + def _write_extra_vars_file(self, private_data_dir, extra_vars, *kw): + self.__vars__ = extra_vars + + credential_type = CredentialType.defaults['galaxy_api_token']() + public_galaxy = Credential(pk=1, credential_type=credential_type, inputs={ + 'url': 'https://galaxy.ansible.com/', + }) + project_update.project.organization.galaxy_credentials.add(public_galaxy) + task = RunProjectUpdate() + env = task.build_env(project_update, private_data_dir) + task.build_extra_vars_file(project_update, private_data_dir) + assert task.__vars__['roles_enabled'] is True + assert task.__vars__['collections_enabled'] is True + assert sorted([ + (k, v) for k, v in env.items() + if k.startswith('ANSIBLE_GALAXY') + ]) == [ + ('ANSIBLE_GALAXY_SERVER_LIST', 'server0'), + ('ANSIBLE_GALAXY_SERVER_SERVER0_URL', 'https://galaxy.ansible.com/'), + ] + + def test_multiple_galaxy_endpoints(self, private_data_dir, project_update): + credential_type = CredentialType.defaults['galaxy_api_token']() + public_galaxy = Credential(pk=1, credential_type=credential_type, inputs={ + 'url': 'https://galaxy.ansible.com/', + }) + rh = Credential(pk=2, credential_type=credential_type, inputs={ + 'url': 'https://cloud.redhat.com/api/automation-hub/', + 'auth_url': 'https://sso.redhat.com/example/openid-connect/token/', + 'token': 'secret123' + }) + project_update.project.organization.galaxy_credentials.add(public_galaxy) + project_update.project.organization.galaxy_credentials.add(rh) + task = tasks.RunProjectUpdate() + env = task.build_env(project_update, private_data_dir) + assert sorted([ + (k, v) for k, v in env.items() + if k.startswith('ANSIBLE_GALAXY') + ]) == [ + ('ANSIBLE_GALAXY_SERVER_LIST', 'server0,server1'), + ('ANSIBLE_GALAXY_SERVER_SERVER0_URL', 'https://galaxy.ansible.com/'), + ('ANSIBLE_GALAXY_SERVER_SERVER1_AUTH_URL', 'https://sso.redhat.com/example/openid-connect/token/'), # noqa + ('ANSIBLE_GALAXY_SERVER_SERVER1_TOKEN', 'secret123'), + ('ANSIBLE_GALAXY_SERVER_SERVER1_URL', 'https://cloud.redhat.com/api/automation-hub/'), + ] + + +@pytest.mark.usefixtures("patch_Organization") class TestProjectUpdateCredentials(TestJobExecution): @pytest.fixture def project_update(self): - project_update = ProjectUpdate(pk=1, project=Project(pk=1)) + project_update = ProjectUpdate( + pk=1, + project=Project(pk=1, organization=Organization(pk=1)), + ) project_update.websocket_emit_status = mock.Mock() return project_update diff --git a/awx/playbooks/scan_facts.yml b/awx/playbooks/scan_facts.yml deleted file mode 100644 index 884760f717..0000000000 --- a/awx/playbooks/scan_facts.yml +++ /dev/null @@ -1,36 +0,0 @@ ---- -- hosts: all - vars: - scan_use_checksum: false - scan_use_recursive: false - tasks: - - - name: "Scan packages (Unix/Linux)" - scan_packages: - os_family: '{{ ansible_os_family }}' - when: ansible_os_family != "Windows" - - name: "Scan services (Unix/Linux)" - scan_services: - when: ansible_os_family != "Windows" - - name: "Scan files (Unix/Linux)" - scan_files: - paths: '{{ scan_file_paths }}' - get_checksum: '{{ scan_use_checksum }}' - recursive: '{{ scan_use_recursive }}' - when: scan_file_paths is defined and ansible_os_family != "Windows" - - name: "Scan Insights for Machine ID (Unix/Linux)" - scan_insights: - when: ansible_os_family != "Windows" - - - name: "Scan packages (Windows)" - win_scan_packages: - when: ansible_os_family == "Windows" - - name: "Scan services (Windows)" - win_scan_services: - when: ansible_os_family == "Windows" - - name: "Scan files (Windows)" - win_scan_files: - paths: '{{ scan_file_paths }}' - get_checksum: '{{ scan_use_checksum }}' - recursive: '{{ scan_use_recursive }}' - when: scan_file_paths is defined and ansible_os_family == "Windows" diff --git a/awx/plugins/library/scan_files.py b/awx/plugins/library/scan_files.py deleted file mode 100644 index b3b07ad64a..0000000000 --- a/awx/plugins/library/scan_files.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env python - -import os -import stat -from ansible.module_utils.basic import * # noqa - -DOCUMENTATION = ''' ---- -module: scan_files -short_description: Return file state information as fact data for a directory tree -description: - - Return file state information recursively for a directory tree on the filesystem -version_added: "1.9" -options: - path: - description: The path containing files to be analyzed - required: true - default: null - recursive: - description: scan this directory and all subdirectories - required: false - default: no - get_checksum: - description: Checksum files that you can access - required: false - default: false -requirements: [ ] -author: Matthew Jones -''' - -EXAMPLES = ''' -# Example fact output: -# host | success >> { -# "ansible_facts": { -# "files": [ -# { -# "atime": 1427313854.0755742, -# "checksum": "cf7566e6149ad9af91e7589e0ea096a08de9c1e5", -# "ctime": 1427129299.22948, -# "dev": 51713, -# "gid": 0, -# "inode": 149601, -# "isblk": false, -# "ischr": false, -# "isdir": false, -# "isfifo": false, -# "isgid": false, -# "islnk": false, -# "isreg": true, -# "issock": false, -# "isuid": false, -# "mode": "0644", -# "mtime": 1427112663.0321455, -# "nlink": 1, -# "path": "/var/log/dmesg.1.gz", -# "rgrp": true, -# "roth": true, -# "rusr": true, -# "size": 28, -# "uid": 0, -# "wgrp": false, -# "woth": false, -# "wusr": true, -# "xgrp": false, -# "xoth": false, -# "xusr": false -# }, -# { -# "atime": 1427314385.1155744, -# "checksum": "16fac7be61a6e4591a33ef4b729c5c3302307523", -# "ctime": 1427384148.5755742, -# "dev": 51713, -# "gid": 43, -# "inode": 149564, -# "isblk": false, -# "ischr": false, -# "isdir": false, -# "isfifo": false, -# "isgid": false, -# "islnk": false, -# "isreg": true, -# "issock": false, -# "isuid": false, -# "mode": "0664", -# "mtime": 1427384148.5755742, -# "nlink": 1, -# "path": "/var/log/wtmp", -# "rgrp": true, -# "roth": true, -# "rusr": true, -# "size": 48768, -# "uid": 0, -# "wgrp": true, -# "woth": false, -# "wusr": true, -# "xgrp": false, -# "xoth": false, -# "xusr": false -# }, -''' - - -def main(): - module = AnsibleModule( # noqa - argument_spec = dict(paths=dict(required=True, type='list'), - recursive=dict(required=False, default='no', type='bool'), - get_checksum=dict(required=False, default='no', type='bool'))) - files = [] - paths = module.params.get('paths') - for path in paths: - path = os.path.expanduser(path) - if not os.path.exists(path) or not os.path.isdir(path): - module.fail_json(msg = "Given path must exist and be a directory") - - get_checksum = module.params.get('get_checksum') - should_recurse = module.params.get('recursive') - if not should_recurse: - path_list = [os.path.join(path, subpath) for subpath in os.listdir(path)] - else: - path_list = [os.path.join(w_path, f) for w_path, w_names, w_file in os.walk(path) for f in w_file] - for filepath in path_list: - try: - st = os.stat(filepath) - except OSError: - continue - - mode = st.st_mode - d = { - 'path' : filepath, - 'mode' : "%04o" % stat.S_IMODE(mode), - 'isdir' : stat.S_ISDIR(mode), - 'ischr' : stat.S_ISCHR(mode), - 'isblk' : stat.S_ISBLK(mode), - 'isreg' : stat.S_ISREG(mode), - 'isfifo' : stat.S_ISFIFO(mode), - 'islnk' : stat.S_ISLNK(mode), - 'issock' : stat.S_ISSOCK(mode), - 'uid' : st.st_uid, - 'gid' : st.st_gid, - 'size' : st.st_size, - 'inode' : st.st_ino, - 'dev' : st.st_dev, - 'nlink' : st.st_nlink, - 'atime' : st.st_atime, - 'mtime' : st.st_mtime, - 'ctime' : st.st_ctime, - 'wusr' : bool(mode & stat.S_IWUSR), - 'rusr' : bool(mode & stat.S_IRUSR), - 'xusr' : bool(mode & stat.S_IXUSR), - 'wgrp' : bool(mode & stat.S_IWGRP), - 'rgrp' : bool(mode & stat.S_IRGRP), - 'xgrp' : bool(mode & stat.S_IXGRP), - 'woth' : bool(mode & stat.S_IWOTH), - 'roth' : bool(mode & stat.S_IROTH), - 'xoth' : bool(mode & stat.S_IXOTH), - 'isuid' : bool(mode & stat.S_ISUID), - 'isgid' : bool(mode & stat.S_ISGID), - } - if get_checksum and stat.S_ISREG(mode) and os.access(filepath, os.R_OK): - d['checksum'] = module.sha1(filepath) - files.append(d) - results = dict(ansible_facts=dict(files=files)) - module.exit_json(**results) - - -main() diff --git a/awx/plugins/library/scan_insights.py b/awx/plugins/library/scan_insights.py deleted file mode 100755 index f7b7919bca..0000000000 --- a/awx/plugins/library/scan_insights.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python - -from ansible.module_utils.basic import * # noqa - -DOCUMENTATION = ''' ---- -module: scan_insights -short_description: Return insights id as fact data -description: - - Inspects the /etc/redhat-access-insights/machine-id file for insights id and returns the found id as fact data -version_added: "2.3" -options: -requirements: [ ] -author: Chris Meyers -''' - -EXAMPLES = ''' -# Example fact output: -# host | success >> { -# "ansible_facts": { -# "insights": { -# "system_id": "4da7d1f8-14f3-4cdc-acd5-a3465a41f25d" -# }, ... } -''' - - -INSIGHTS_SYSTEM_ID_FILE='/etc/redhat-access-insights/machine-id' - - -def get_system_id(filname): - system_id = None - try: - f = open(INSIGHTS_SYSTEM_ID_FILE, "r") - except IOError: - return None - else: - try: - data = f.readline() - system_id = str(data) - except (IOError, ValueError): - pass - finally: - f.close() - if system_id: - system_id = system_id.strip() - return system_id - - -def main(): - module = AnsibleModule( # noqa - argument_spec = dict() - ) - - system_id = get_system_id(INSIGHTS_SYSTEM_ID_FILE) - - results = { - 'ansible_facts': { - 'insights': { - 'system_id': system_id - } - } - } - module.exit_json(**results) - - -main() diff --git a/awx/plugins/library/scan_packages.py b/awx/plugins/library/scan_packages.py deleted file mode 100755 index d0b544bb9f..0000000000 --- a/awx/plugins/library/scan_packages.py +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env python - -from ansible.module_utils.basic import * # noqa - -DOCUMENTATION = ''' ---- -module: scan_packages -short_description: Return installed packages information as fact data -description: - - Return information about installed packages as fact data -version_added: "1.9" -options: -requirements: [ ] -author: Matthew Jones -''' - -EXAMPLES = ''' -# Example fact output: -# host | success >> { -# "ansible_facts": { -# "packages": { -# "libbz2-1.0": [ -# { -# "version": "1.0.6-5", -# "source": "apt", -# "arch": "amd64", -# "name": "libbz2-1.0" -# } -# ], -# "patch": [ -# { -# "version": "2.7.1-4ubuntu1", -# "source": "apt", -# "arch": "amd64", -# "name": "patch" -# } -# ], -# "gcc-4.8-base": [ -# { -# "version": "4.8.2-19ubuntu1", -# "source": "apt", -# "arch": "amd64", -# "name": "gcc-4.8-base" -# }, -# { -# "version": "4.9.2-19ubuntu1", -# "source": "apt", -# "arch": "amd64", -# "name": "gcc-4.8-base" -# } -# ] -# } -''' - - -def rpm_package_list(): - import rpm - trans_set = rpm.TransactionSet() - installed_packages = {} - for package in trans_set.dbMatch(): - package_details = dict(name=package[rpm.RPMTAG_NAME], - version=package[rpm.RPMTAG_VERSION], - release=package[rpm.RPMTAG_RELEASE], - epoch=package[rpm.RPMTAG_EPOCH], - arch=package[rpm.RPMTAG_ARCH], - source='rpm') - if package_details['name'] not in installed_packages: - installed_packages[package_details['name']] = [package_details] - else: - installed_packages[package_details['name']].append(package_details) - return installed_packages - - -def deb_package_list(): - import apt - apt_cache = apt.Cache() - installed_packages = {} - apt_installed_packages = [pk for pk in apt_cache.keys() if apt_cache[pk].is_installed] - for package in apt_installed_packages: - ac_pkg = apt_cache[package].installed - package_details = dict(name=package, - version=ac_pkg.version, - arch=ac_pkg.architecture, - source='apt') - if package_details['name'] not in installed_packages: - installed_packages[package_details['name']] = [package_details] - else: - installed_packages[package_details['name']].append(package_details) - return installed_packages - - -def main(): - module = AnsibleModule( # noqa - argument_spec = dict(os_family=dict(required=True)) - ) - ans_os = module.params['os_family'] - if ans_os in ('RedHat', 'Suse', 'openSUSE Leap'): - packages = rpm_package_list() - elif ans_os == 'Debian': - packages = deb_package_list() - else: - packages = None - - if packages is not None: - results = dict(ansible_facts=dict(packages=packages)) - else: - results = dict(skipped=True, msg="Unsupported Distribution") - module.exit_json(**results) - - -main() diff --git a/awx/plugins/library/scan_services.py b/awx/plugins/library/scan_services.py deleted file mode 100644 index 5d8ccdbb74..0000000000 --- a/awx/plugins/library/scan_services.py +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/env python - -import re -from ansible.module_utils.basic import * # noqa - -DOCUMENTATION = ''' ---- -module: scan_services -short_description: Return service state information as fact data -description: - - Return service state information as fact data for various service management utilities -version_added: "1.9" -options: -requirements: [ ] -author: Matthew Jones -''' - -EXAMPLES = ''' -- monit: scan_services -# Example fact output: -# host | success >> { -# "ansible_facts": { -# "services": { -# "network": { -# "source": "sysv", -# "state": "running", -# "name": "network" -# }, -# "arp-ethers.service": { -# "source": "systemd", -# "state": "stopped", -# "name": "arp-ethers.service" -# } -# } -# } -''' - - -class BaseService(object): - - def __init__(self, module): - self.module = module - self.incomplete_warning = False - - -class ServiceScanService(BaseService): - - def gather_services(self): - services = {} - service_path = self.module.get_bin_path("service") - if service_path is None: - return None - initctl_path = self.module.get_bin_path("initctl") - chkconfig_path = self.module.get_bin_path("chkconfig") - - # sysvinit - if service_path is not None and chkconfig_path is None: - rc, stdout, stderr = self.module.run_command("%s --status-all 2>&1 | grep -E \"\\[ (\\+|\\-) \\]\"" % service_path, use_unsafe_shell=True) - for line in stdout.split("\n"): - line_data = line.split() - if len(line_data) < 4: - continue # Skipping because we expected more data - service_name = " ".join(line_data[3:]) - if line_data[1] == "+": - service_state = "running" - else: - service_state = "stopped" - services[service_name] = {"name": service_name, "state": service_state, "source": "sysv"} - - # Upstart - if initctl_path is not None and chkconfig_path is None: - p = re.compile(r'^\s?(?P.*)\s(?P\w+)\/(?P\w+)(\,\sprocess\s(?P[0-9]+))?\s*$') - rc, stdout, stderr = self.module.run_command("%s list" % initctl_path) - real_stdout = stdout.replace("\r","") - for line in real_stdout.split("\n"): - m = p.match(line) - if not m: - continue - service_name = m.group('name') - service_goal = m.group('goal') - service_state = m.group('state') - if m.group('pid'): - pid = m.group('pid') - else: - pid = None # NOQA - payload = {"name": service_name, "state": service_state, "goal": service_goal, "source": "upstart"} - services[service_name] = payload - - # RH sysvinit - elif chkconfig_path is not None: - #print '%s --status-all | grep -E "is (running|stopped)"' % service_path - p = re.compile( - r'(?P.*?)\s+[0-9]:(?Pon|off)\s+[0-9]:(?Pon|off)\s+[0-9]:(?Pon|off)\s+' - r'[0-9]:(?Pon|off)\s+[0-9]:(?Pon|off)\s+[0-9]:(?Pon|off)\s+[0-9]:(?Pon|off)') - rc, stdout, stderr = self.module.run_command('%s' % chkconfig_path, use_unsafe_shell=True) - # Check for special cases where stdout does not fit pattern - match_any = False - for line in stdout.split('\n'): - if p.match(line): - match_any = True - if not match_any: - p_simple = re.compile(r'(?P.*?)\s+(?Pon|off)') - match_any = False - for line in stdout.split('\n'): - if p_simple.match(line): - match_any = True - if match_any: - # Try extra flags " -l --allservices" needed for SLES11 - rc, stdout, stderr = self.module.run_command('%s -l --allservices' % chkconfig_path, use_unsafe_shell=True) - elif '--list' in stderr: - # Extra flag needed for RHEL5 - rc, stdout, stderr = self.module.run_command('%s --list' % chkconfig_path, use_unsafe_shell=True) - for line in stdout.split('\n'): - m = p.match(line) - if m: - service_name = m.group('service') - service_state = 'stopped' - if m.group('rl3') == 'on': - rc, stdout, stderr = self.module.run_command('%s %s status' % (service_path, service_name), use_unsafe_shell=True) - service_state = rc - if rc in (0,): - service_state = 'running' - #elif rc in (1,3): - else: - if 'root' in stderr or 'permission' in stderr.lower() or 'not in sudoers' in stderr.lower(): - self.incomplete_warning = True - continue - else: - service_state = 'stopped' - service_data = {"name": service_name, "state": service_state, "source": "sysv"} - services[service_name] = service_data - return services - - -class SystemctlScanService(BaseService): - - def systemd_enabled(self): - # Check if init is the systemd command, using comm as cmdline could be symlink - try: - f = open('/proc/1/comm', 'r') - except IOError: - # If comm doesn't exist, old kernel, no systemd - return False - for line in f: - if 'systemd' in line: - return True - return False - - def gather_services(self): - services = {} - if not self.systemd_enabled(): - return None - systemctl_path = self.module.get_bin_path("systemctl", opt_dirs=["/usr/bin", "/usr/local/bin"]) - if systemctl_path is None: - return None - rc, stdout, stderr = self.module.run_command("%s list-unit-files --type=service | tail -n +2 | head -n -2" % systemctl_path, use_unsafe_shell=True) - for line in stdout.split("\n"): - line_data = line.split() - if len(line_data) != 2: - continue - if line_data[1] == "enabled": - state_val = "running" - else: - state_val = "stopped" - services[line_data[0]] = {"name": line_data[0], "state": state_val, "source": "systemd"} - return services - - -def main(): - module = AnsibleModule(argument_spec = dict()) # noqa - service_modules = (ServiceScanService, SystemctlScanService) - all_services = {} - incomplete_warning = False - for svc_module in service_modules: - svcmod = svc_module(module) - svc = svcmod.gather_services() - if svc is not None: - all_services.update(svc) - if svcmod.incomplete_warning: - incomplete_warning = True - if len(all_services) == 0: - results = dict(skipped=True, msg="Failed to find any services. Sometimes this is due to insufficient privileges.") - else: - results = dict(ansible_facts=dict(services=all_services)) - if incomplete_warning: - results['msg'] = "WARNING: Could not find status for all services. Sometimes this is due to insufficient privileges." - module.exit_json(**results) - - -main() diff --git a/awx/plugins/library/win_scan_files.ps1 b/awx/plugins/library/win_scan_files.ps1 deleted file mode 100644 index 6d114dfcc8..0000000000 --- a/awx/plugins/library/win_scan_files.ps1 +++ /dev/null @@ -1,102 +0,0 @@ -#!powershell -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -# WANT_JSON -# POWERSHELL_COMMON - -$params = Parse-Args $args $true; - -$paths = Get-Attr $params "paths" $FALSE; -If ($paths -eq $FALSE) -{ - Fail-Json (New-Object psobject) "missing required argument: paths"; -} - -$get_checksum = Get-Attr $params "get_checksum" $false | ConvertTo-Bool; -$recursive = Get-Attr $params "recursive" $false | ConvertTo-Bool; - -function Date_To_Timestamp($start_date, $end_date) -{ - If($start_date -and $end_date) - { - Write-Output (New-TimeSpan -Start $start_date -End $end_date).TotalSeconds - } -} - -$files = @() - -ForEach ($path In $paths) -{ - "Path: " + $path - ForEach ($file in Get-ChildItem $path -Recurse: $recursive) - { - "File: " + $file.FullName - $fileinfo = New-Object psobject - Set-Attr $fileinfo "path" $file.FullName - $info = Get-Item $file.FullName; - $iscontainer = Get-Attr $info "PSIsContainer" $null; - $length = Get-Attr $info "Length" $null; - $extension = Get-Attr $info "Extension" $null; - $attributes = Get-Attr $info "Attributes" ""; - If ($info) - { - $accesscontrol = $info.GetAccessControl(); - } - Else - { - $accesscontrol = $null; - } - $owner = Get-Attr $accesscontrol "Owner" $null; - $creationtime = Get-Attr $info "CreationTime" $null; - $lastaccesstime = Get-Attr $info "LastAccessTime" $null; - $lastwritetime = Get-Attr $info "LastWriteTime" $null; - - $epoch_date = Get-Date -Date "01/01/1970" - If ($iscontainer) - { - Set-Attr $fileinfo "isdir" $TRUE; - } - Else - { - Set-Attr $fileinfo "isdir" $FALSE; - Set-Attr $fileinfo "size" $length; - } - Set-Attr $fileinfo "extension" $extension; - Set-Attr $fileinfo "attributes" $attributes.ToString(); - # Set-Attr $fileinfo "owner" $getaccesscontrol.Owner; - # Set-Attr $fileinfo "owner" $info.GetAccessControl().Owner; - Set-Attr $fileinfo "owner" $owner; - Set-Attr $fileinfo "creationtime" (Date_To_Timestamp $epoch_date $creationtime); - Set-Attr $fileinfo "lastaccesstime" (Date_To_Timestamp $epoch_date $lastaccesstime); - Set-Attr $fileinfo "lastwritetime" (Date_To_Timestamp $epoch_date $lastwritetime); - - If (($get_checksum) -and -not $fileinfo.isdir) - { - $hash = Get-FileChecksum($file.FullName); - Set-Attr $fileinfo "checksum" $hash; - } - - $files += $fileinfo - } -} - -$result = New-Object psobject @{ - ansible_facts = New-Object psobject @{ - files = $files - } -} - -Exit-Json $result; diff --git a/awx/plugins/library/win_scan_packages.ps1 b/awx/plugins/library/win_scan_packages.ps1 deleted file mode 100644 index 2ab3fdbec6..0000000000 --- a/awx/plugins/library/win_scan_packages.ps1 +++ /dev/null @@ -1,66 +0,0 @@ -#!powershell -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -# WANT_JSON -# POWERSHELL_COMMON - -$uninstall_native_path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" -$uninstall_wow6432_path = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" - -if ([System.IntPtr]::Size -eq 4) { - - # This is a 32-bit Windows system, so we only check for 32-bit programs, which will be - # at the native registry location. - - [PSObject []]$packages = Get-ChildItem -Path $uninstall_native_path | - Get-ItemProperty | - Select-Object -Property @{Name="name"; Expression={$_."DisplayName"}}, - @{Name="version"; Expression={$_."DisplayVersion"}}, - @{Name="publisher"; Expression={$_."Publisher"}}, - @{Name="arch"; Expression={ "Win32" }} | - Where-Object { $_.name } - -} else { - - # This is a 64-bit Windows system, so we check for 64-bit programs in the native - # registry location, and also for 32-bit programs under Wow6432Node. - - [PSObject []]$packages = Get-ChildItem -Path $uninstall_native_path | - Get-ItemProperty | - Select-Object -Property @{Name="name"; Expression={$_."DisplayName"}}, - @{Name="version"; Expression={$_."DisplayVersion"}}, - @{Name="publisher"; Expression={$_."Publisher"}}, - @{Name="arch"; Expression={ "Win64" }} | - Where-Object { $_.name } - - $packages += Get-ChildItem -Path $uninstall_wow6432_path | - Get-ItemProperty | - Select-Object -Property @{Name="name"; Expression={$_."DisplayName"}}, - @{Name="version"; Expression={$_."DisplayVersion"}}, - @{Name="publisher"; Expression={$_."Publisher"}}, - @{Name="arch"; Expression={ "Win32" }} | - Where-Object { $_.name } - -} - -$result = New-Object psobject @{ - ansible_facts = New-Object psobject @{ - packages = $packages - } - changed = $false -} - -Exit-Json $result; diff --git a/awx/plugins/library/win_scan_services.ps1 b/awx/plugins/library/win_scan_services.ps1 deleted file mode 100644 index 3de8ac4c9b..0000000000 --- a/awx/plugins/library/win_scan_services.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -#!powershell -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -# WANT_JSON -# POWERSHELL_COMMON - -$result = New-Object psobject @{ - ansible_facts = New-Object psobject @{ - services = Get-Service | - Select-Object -Property @{Name="name"; Expression={$_."DisplayName"}}, - @{Name="win_svc_name"; Expression={$_."Name"}}, - @{Name="state"; Expression={$_."Status".ToString().ToLower()}} - } - changed = $false -} - -Exit-Json $result; diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 5aa0b834ea..618f5282a4 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -197,12 +197,23 @@ LOCAL_STDOUT_EXPIRE_TIME = 2592000 # events into the database JOB_EVENT_WORKERS = 4 +# The number of seconds (must be an integer) to buffer callback receiver bulk +# writes in memory before flushing via JobEvent.objects.bulk_create() +JOB_EVENT_BUFFER_SECONDS = 1 + +# The interval at which callback receiver statistics should be +# recorded +JOB_EVENT_STATISTICS_INTERVAL = 5 + # The maximum size of the job event worker queue before requests are blocked JOB_EVENT_MAX_QUEUE_SIZE = 10000 # The number of job events to migrate per-transaction when moving from int -> bigint JOB_EVENT_MIGRATION_CHUNK_SIZE = 1000000 +# The maximum allowed jobs to start on a given task manager cycle +START_TASK_LIMIT = 100 + # Disallow sending session cookies over insecure connections SESSION_COOKIE_SECURE = True @@ -477,6 +488,7 @@ SOCIAL_AUTH_SAML_PIPELINE = _SOCIAL_AUTH_PIPELINE_BASE + ( 'awx.sso.pipeline.update_user_orgs', 'awx.sso.pipeline.update_user_teams', ) +SAML_AUTO_CREATE_OBJECTS = True SOCIAL_AUTH_LOGIN_URL = '/' SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/sso/complete/' @@ -567,28 +579,9 @@ AWX_COLLECTIONS_ENABLED = True # Follow symlinks when scanning for playbooks AWX_SHOW_PLAYBOOK_LINKS = False -# Settings for primary galaxy server, should be set in the UI -PRIMARY_GALAXY_URL = '' -PRIMARY_GALAXY_USERNAME = '' -PRIMARY_GALAXY_TOKEN = '' -PRIMARY_GALAXY_PASSWORD = '' -PRIMARY_GALAXY_AUTH_URL = '' - -# Settings for the public galaxy server(s). -PUBLIC_GALAXY_ENABLED = True -PUBLIC_GALAXY_SERVER = { - 'id': 'galaxy', - 'url': 'https://galaxy.ansible.com' -} - # Applies to any galaxy server GALAXY_IGNORE_CERTS = False -# List of dicts of fallback (additional) Galaxy servers. If configured, these -# will be higher precedence than public Galaxy, but lower than primary Galaxy. -# Available options: 'id', 'url', 'username', 'password', 'token', 'auth_url' -FALLBACK_GALAXY_SERVERS = [] - # Enable bubblewrap support for running jobs (playbook runs only). # Note: This setting may be overridden by database settings. AWX_PROOT_ENABLED = True @@ -789,7 +782,7 @@ ASGI_APPLICATION = "awx.main.routing.application" CHANNEL_LAYERS = { "default": { - "BACKEND": "awx.main.consumers.ExpiringRedisChannelLayer", + "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": [BROKER_URL], "capacity": 10000, @@ -1002,6 +995,11 @@ LOGGING = { 'handlers': ['task_system', 'external_logger'], 'propagate': False }, + 'awx.main.analytics': { + 'handlers': ['task_system', 'external_logger'], + 'level': 'INFO', + 'propagate': False + }, 'awx.main.scheduler': { 'handlers': ['task_system', 'external_logger'], 'propagate': False diff --git a/awx/sso/conf.py b/awx/sso/conf.py index c408d72b40..5f595517cc 100644 --- a/awx/sso/conf.py +++ b/awx/sso/conf.py @@ -515,6 +515,7 @@ register( help_text=_('TACACS+ session timeout value in seconds, 0 disables timeout.'), category=_('TACACS+'), category_slug='tacacsplus', + unit=_('seconds'), ) register( @@ -575,7 +576,7 @@ register( 'SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS', field_class=fields.StringListField, default=[], - label=_('Google OAuth2 Whitelisted Domains'), + label=_('Google OAuth2 Allowed Domains'), help_text=_('Update this setting to restrict the domains who are allowed to ' 'login using Google OAuth2.'), category=_('Google OAuth2'), @@ -919,6 +920,17 @@ def get_saml_entity_id(): return settings.TOWER_URL_BASE +register( + 'SAML_AUTO_CREATE_OBJECTS', + field_class=fields.BooleanField, + default=True, + label=_('Automatically Create Organizations and Teams on SAML Login'), + help_text=_('When enabled (the default), mapped Organizations and Teams ' + 'will be created automatically on successful SAML login.'), + category=_('SAML'), + category_slug='saml', +) + register( 'SOCIAL_AUTH_SAML_CALLBACK_URL', field_class=fields.CharField, diff --git a/awx/sso/pipeline.py b/awx/sso/pipeline.py index 6d7e05da90..3e73974474 100644 --- a/awx/sso/pipeline.py +++ b/awx/sso/pipeline.py @@ -10,6 +10,7 @@ import logging from social_core.exceptions import AuthException # Django +from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import ugettext_lazy as _ from django.db.models import Q @@ -80,11 +81,18 @@ def _update_m2m_from_expression(user, related, expr, remove=True): def _update_org_from_attr(user, related, attr, remove, remove_admins, remove_auditors): from awx.main.models import Organization + from django.conf import settings org_ids = [] for org_name in attr: - org = Organization.objects.get_or_create(name=org_name)[0] + try: + if settings.SAML_AUTO_CREATE_OBJECTS: + org = Organization.objects.get_or_create(name=org_name)[0] + else: + org = Organization.objects.get(name=org_name) + except ObjectDoesNotExist: + continue org_ids.append(org.id) getattr(org, related).members.add(user) @@ -199,11 +207,24 @@ def update_user_teams_by_saml_attr(backend, details, user=None, *args, **kwargs) if organization_alias: organization_name = organization_alias - org = Organization.objects.get_or_create(name=organization_name)[0] + + try: + if settings.SAML_AUTO_CREATE_OBJECTS: + org = Organization.objects.get_or_create(name=organization_name)[0] + else: + org = Organization.objects.get(name=organization_name) + except ObjectDoesNotExist: + continue if team_alias: team_name = team_alias - team = Team.objects.get_or_create(name=team_name, organization=org)[0] + try: + if settings.SAML_AUTO_CREATE_OBJECTS: + team = Team.objects.get_or_create(name=team_name, organization=org)[0] + else: + team = Team.objects.get(name=team_name, organization=org) + except ObjectDoesNotExist: + continue team_ids.append(team.id) team.member_role.members.add(user) diff --git a/awx/sso/tests/functional/test_pipeline.py b/awx/sso/tests/functional/test_pipeline.py index 06d5503db8..e691939752 100644 --- a/awx/sso/tests/functional/test_pipeline.py +++ b/awx/sso/tests/functional/test_pipeline.py @@ -174,8 +174,15 @@ class TestSAMLAttr(): return (o1, o2, o3) @pytest.fixture - def mock_settings(self): + def mock_settings(self, request): + fixture_args = request.node.get_closest_marker('fixture_args') + if fixture_args and 'autocreate' in fixture_args.kwargs: + autocreate = fixture_args.kwargs['autocreate'] + else: + autocreate = True + class MockSettings(): + SAML_AUTO_CREATE_OBJECTS = autocreate SOCIAL_AUTH_SAML_ORGANIZATION_ATTR = { 'saml_attr': 'memberOf', 'saml_admin_attr': 'admins', @@ -304,3 +311,41 @@ class TestSAMLAttr(): assert Team.objects.get( name='Yellow_Alias', organization__name='Default4_Alias').member_role.members.count() == 1 + @pytest.mark.fixture_args(autocreate=False) + def test_autocreate_disabled(self, users, kwargs, mock_settings): + kwargs['response']['attributes']['memberOf'] = ['Default1', 'Default2', 'Default3'] + kwargs['response']['attributes']['groups'] = ['Blue', 'Red', 'Green'] + with mock.patch('django.conf.settings', mock_settings): + for u in users: + update_user_orgs_by_saml_attr(None, None, u, **kwargs) + update_user_teams_by_saml_attr(None, None, u, **kwargs) + assert Organization.objects.count() == 0 + assert Team.objects.count() == 0 + + # precreate everything + o1 = Organization.objects.create(name='Default1') + o2 = Organization.objects.create(name='Default2') + o3 = Organization.objects.create(name='Default3') + Team.objects.create(name='Blue', organization_id=o1.id) + Team.objects.create(name='Blue', organization_id=o2.id) + Team.objects.create(name='Blue', organization_id=o3.id) + Team.objects.create(name='Red', organization_id=o1.id) + Team.objects.create(name='Green', organization_id=o1.id) + Team.objects.create(name='Green', organization_id=o3.id) + + for u in users: + update_user_orgs_by_saml_attr(None, None, u, **kwargs) + update_user_teams_by_saml_attr(None, None, u, **kwargs) + + assert o1.member_role.members.count() == 3 + assert o2.member_role.members.count() == 3 + assert o3.member_role.members.count() == 3 + + assert Team.objects.get(name='Blue', organization__name='Default1').member_role.members.count() == 3 + assert Team.objects.get(name='Blue', organization__name='Default2').member_role.members.count() == 3 + assert Team.objects.get(name='Blue', organization__name='Default3').member_role.members.count() == 3 + + assert Team.objects.get(name='Red', organization__name='Default1').member_role.members.count() == 3 + + assert Team.objects.get(name='Green', organization__name='Default1').member_role.members.count() == 3 + assert Team.objects.get(name='Green', organization__name='Default3').member_role.members.count() == 3 diff --git a/awx/ui/client/features/output/stream.service.js b/awx/ui/client/features/output/stream.service.js index 11198e0752..c3cfa10622 100644 --- a/awx/ui/client/features/output/stream.service.js +++ b/awx/ui/client/features/output/stream.service.js @@ -119,6 +119,10 @@ function OutputStream ($q) { this.counters.ready = ready; this.counters.used = used; this.counters.missing = missing; + + if (!window.liveUpdates) { + this.counters.ready = event.counter; + } }; this.bufferEmpty = threshold => { @@ -141,6 +145,10 @@ function OutputStream ($q) { const { total } = this.counters; const readyCount = this.getReadyCount(); + if (!window.liveUpdates) { + return true; + } + if (readyCount <= 0) { return false; } diff --git a/awx/ui/client/lib/components/input/secret.partial.html b/awx/ui/client/lib/components/input/secret.partial.html index 7c0f1e8e61..9f72965e7c 100644 --- a/awx/ui/client/lib/components/input/secret.partial.html +++ b/awx/ui/client/lib/components/input/secret.partial.html @@ -23,12 +23,12 @@ icon="external" tag="state._tagValue" remove-tag="state._onRemoveTag(state)" - /> + > + > { + angular.forEach($cookies.getAll(), (val, name) => { + $cookies.remove(name); + }); + $window.location.reload(); + // this is used because $location only lets you navigate inside + // the "/#/" path, and these are API urls. + $window.location.href = link; + }); }; }]; diff --git a/awx/ui/client/src/organizations/add/organizations-add.controller.js b/awx/ui/client/src/organizations/add/organizations-add.controller.js index cd6aea6cb9..4b86b0db74 100644 --- a/awx/ui/client/src/organizations/add/organizations-add.controller.js +++ b/awx/ui/client/src/organizations/add/organizations-add.controller.js @@ -4,11 +4,12 @@ * All Rights Reserved *************************************************/ -export default ['$scope', '$rootScope', '$location', '$stateParams', - 'OrganizationForm', 'GenerateForm', 'Rest', 'Alert', - 'ProcessErrors', 'GetBasePath', 'Wait', 'CreateSelect2', '$state','InstanceGroupsService', 'ConfigData', - function($scope, $rootScope, $location, $stateParams, OrganizationForm, - GenerateForm, Rest, Alert, ProcessErrors, GetBasePath, Wait, CreateSelect2, $state, InstanceGroupsService, ConfigData) { +export default ['$scope', '$rootScope', '$location', '$stateParams', 'OrganizationForm', + 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', 'Wait', 'CreateSelect2', + '$state','InstanceGroupsService', 'ConfigData', 'MultiCredentialService', 'defaultGalaxyCredential', + function($scope, $rootScope, $location, $stateParams, OrganizationForm, + GenerateForm, Rest, Alert, ProcessErrors, GetBasePath, Wait, CreateSelect2, + $state, InstanceGroupsService, ConfigData, MultiCredentialService, defaultGalaxyCredential) { Rest.setUrl(GetBasePath('organizations')); Rest.options() @@ -37,6 +38,8 @@ export default ['$scope', '$rootScope', '$location', '$stateParams', // apply form definition's default field values GenerateForm.applyDefaults(form, $scope); + + $scope.credentials = defaultGalaxyCredential || []; } // Save @@ -57,18 +60,32 @@ export default ['$scope', '$rootScope', '$location', '$stateParams', const organization_id = data.id, instance_group_url = data.related.instance_groups; - InstanceGroupsService.addInstanceGroups(instance_group_url, $scope.instance_groups) + MultiCredentialService + .saveRelatedSequentially({ + related: { + credentials: data.related.galaxy_credentials + } + }, $scope.credentials) .then(() => { - Wait('stop'); - $rootScope.$broadcast("EditIndicatorChange", "organizations", organization_id); - $state.go('organizations.edit', {organization_id: organization_id}, {reload: true}); - }) - .catch(({data, status}) => { + InstanceGroupsService.addInstanceGroups(instance_group_url, $scope.instance_groups) + .then(() => { + Wait('stop'); + $rootScope.$broadcast("EditIndicatorChange", "organizations", organization_id); + $state.go('organizations.edit', {organization_id: organization_id}, {reload: true}); + }) + .catch(({data, status}) => { + ProcessErrors($scope, data, status, form, { + hdr: 'Error!', + msg: 'Failed to save instance groups. POST returned status: ' + status + }); + }); + }).catch(({data, status}) => { ProcessErrors($scope, data, status, form, { hdr: 'Error!', - msg: 'Failed to save instance groups. POST returned status: ' + status + msg: 'Failed to save Galaxy credentials. POST returned status: ' + status }); }); + }) .catch(({data, status}) => { let explanation = _.has(data, "name") ? data.name[0] : ""; diff --git a/awx/ui/client/src/organizations/edit/organizations-edit.controller.js b/awx/ui/client/src/organizations/edit/organizations-edit.controller.js index 74397b02cc..9ffe8e859b 100644 --- a/awx/ui/client/src/organizations/edit/organizations-edit.controller.js +++ b/awx/ui/client/src/organizations/edit/organizations-edit.controller.js @@ -6,10 +6,12 @@ export default ['$scope', '$location', '$stateParams', 'isOrgAdmin', 'isNotificationAdmin', 'OrganizationForm', 'Rest', 'ProcessErrors', 'Prompt', 'i18n', 'isOrgAuditor', - 'GetBasePath', 'Wait', '$state', 'ToggleNotification', 'CreateSelect2', 'InstanceGroupsService', 'InstanceGroupsData', 'ConfigData', + 'GetBasePath', 'Wait', '$state', 'ToggleNotification', 'CreateSelect2', 'InstanceGroupsService', + 'InstanceGroupsData', 'ConfigData', 'GalaxyCredentialsData', 'MultiCredentialService', function($scope, $location, $stateParams, isOrgAdmin, isNotificationAdmin, OrganizationForm, Rest, ProcessErrors, Prompt, i18n, isOrgAuditor, - GetBasePath, Wait, $state, ToggleNotification, CreateSelect2, InstanceGroupsService, InstanceGroupsData, ConfigData) { + GetBasePath, Wait, $state, ToggleNotification, CreateSelect2, InstanceGroupsService, + InstanceGroupsData, ConfigData, GalaxyCredentialsData, MultiCredentialService) { let form = OrganizationForm(), defaultUrl = GetBasePath('organizations'), @@ -29,6 +31,7 @@ export default ['$scope', '$location', '$stateParams', 'isOrgAdmin', 'isNotifica }); $scope.instance_groups = InstanceGroupsData; + $scope.credentials = GalaxyCredentialsData; const virtualEnvs = ConfigData.custom_virtualenvs || []; $scope.custom_virtualenvs_visible = virtualEnvs.length > 1; $scope.custom_virtualenvs_options = virtualEnvs.filter( @@ -100,7 +103,14 @@ export default ['$scope', '$location', '$stateParams', 'isOrgAdmin', 'isNotifica Rest.setUrl(defaultUrl + id + '/'); Rest.put(params) .then(() => { - InstanceGroupsService.editInstanceGroups(instance_group_url, $scope.instance_groups) + MultiCredentialService + .saveRelatedSequentially({ + related: { + credentials: $scope.organization_obj.related.galaxy_credentials + } + }, $scope.credentials) + .then(() => { + InstanceGroupsService.editInstanceGroups(instance_group_url, $scope.instance_groups) .then(() => { Wait('stop'); $state.go($state.current, {}, { reload: true }); @@ -111,6 +121,12 @@ export default ['$scope', '$location', '$stateParams', 'isOrgAdmin', 'isNotifica msg: 'Failed to update instance groups. POST returned status: ' + status }); }); + }).catch(({data, status}) => { + ProcessErrors($scope, data, status, form, { + hdr: 'Error!', + msg: 'Failed to save Galaxy credentials. POST returned status: ' + status + }); + }); $scope.organization_name = $scope.name; main = params; }) diff --git a/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials-modal/galaxy-credentials-modal.directive.js b/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials-modal/galaxy-credentials-modal.directive.js new file mode 100644 index 0000000000..c91726a570 --- /dev/null +++ b/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials-modal/galaxy-credentials-modal.directive.js @@ -0,0 +1,123 @@ +export default ['templateUrl', '$window', function(templateUrl, $window) { + return { + restrict: 'E', + scope: { + galaxyCredentials: '=' + }, + templateUrl: templateUrl('organizations/galaxy-credentials-multiselect/galaxy-credentials-modal/galaxy-credentials-modal'), + + link: function(scope, element) { + + $('#galaxy-credentials-modal').on('hidden.bs.modal', function () { + $('#galaxy-credentials-modal').off('hidden.bs.modal'); + $(element).remove(); + }); + + scope.showModal = function() { + $('#galaxy-credentials-modal').modal('show'); + }; + + scope.destroyModal = function() { + $('#galaxy-credentials-modal').modal('hide'); + }; + }, + + controller: ['$scope', '$compile', 'QuerySet', 'GetBasePath','generateList', 'CredentialList', function($scope, $compile, qs, GetBasePath, GenerateList, CredentialList) { + + function init() { + + $scope.credential_queryset = { + order_by: 'name', + page_size: 5, + credential_type__kind: 'galaxy' + }; + + $scope.credential_default_params = { + order_by: 'name', + page_size: 5, + credential_type__kind: 'galaxy' + }; + + qs.search(GetBasePath('credentials'), $scope.credential_queryset) + .then(res => { + $scope.credential_dataset = res.data; + $scope.credentials = $scope.credential_dataset.results; + + let credentialList = _.cloneDeep(CredentialList); + + credentialList.listTitle = false; + credentialList.well = false; + credentialList.multiSelect = true; + credentialList.multiSelectPreview = { + selectedRows: 'credTags', + availableRows: 'credentials' + }; + credentialList.fields.name.ngClick = "linkoutCredential(credential)"; + credentialList.fields.name.columnClass = 'col-md-11 col-sm-11 col-xs-11'; + delete credentialList.fields.consumed_capacity; + delete credentialList.fields.jobs_running; + + let html = `${GenerateList.build({ + list: credentialList, + input_type: 'galaxy-credentials-modal-body', + hideViewPerPage: true, + mode: 'lookup' + })}`; + + $scope.list = credentialList; + $('#galaxy-credentials-modal-body').append($compile(html)($scope)); + + if ($scope.galaxyCredentials) { + $scope.galaxyCredentials = $scope.galaxyCredentials.map( (item) => { + item.isSelected = true; + if (!$scope.credTags) { + $scope.credTags = []; + } + $scope.credTags.push(item); + return item; + }); + } + + $scope.showModal(); + }); + + $scope.$watch('credentials', function(){ + angular.forEach($scope.credentials, function(credentialRow) { + angular.forEach($scope.credTags, function(selectedCredential){ + if(selectedCredential.id === credentialRow.id) { + credentialRow.isSelected = true; + } + }); + }); + }); + } + + init(); + + $scope.$on("selectedOrDeselected", function(e, value) { + let item = value.value; + if (value.isSelected) { + if(!$scope.credTags) { + $scope.credTags = []; + } + $scope.credTags.push(item); + } else { + _.remove($scope.credTags, { id: item.id }); + } + }); + + $scope.linkoutCredential = function(credential) { + $window.open('/#/credentials/' + credential.id,'_blank'); + }; + + $scope.cancelForm = function() { + $scope.destroyModal(); + }; + + $scope.saveForm = function() { + $scope.galaxyCredentials = $scope.credTags; + $scope.destroyModal(); + }; + }] + }; +}]; diff --git a/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials-modal/galaxy-credentials-modal.partial.html b/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials-modal/galaxy-credentials-modal.partial.html new file mode 100644 index 0000000000..dbf481005e --- /dev/null +++ b/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials-modal/galaxy-credentials-modal.partial.html @@ -0,0 +1,22 @@ + diff --git a/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials-multiselect.controller.js b/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials-multiselect.controller.js new file mode 100644 index 0000000000..548f528798 --- /dev/null +++ b/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials-multiselect.controller.js @@ -0,0 +1,14 @@ +export default ['$scope', + function($scope) { + + $scope.galaxyCredentialsTags = []; + + $scope.$watch('galaxyCredentials', function() { + $scope.galaxyCredentialsTags = $scope.galaxyCredentials; + }, true); + + $scope.deleteTag = function(tag){ + _.remove($scope.galaxyCredentials, {id: tag.id}); + }; + } +]; \ No newline at end of file diff --git a/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials.block.less b/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials.block.less new file mode 100644 index 0000000000..bbfef9de99 --- /dev/null +++ b/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials.block.less @@ -0,0 +1,15 @@ +#instance-groups-panel { + table { + overflow: hidden; + } + .List-header { + margin-bottom: 20px; + } + .isActive { + border-left: 10px solid @list-row-select-bord; + } + .instances-list, + .instance-jobs-list { + margin-top: 20px; + } +} diff --git a/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials.directive.js b/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials.directive.js new file mode 100644 index 0000000000..d966c5e519 --- /dev/null +++ b/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials.directive.js @@ -0,0 +1,19 @@ +import galaxyCredentialsMultiselectController from './galaxy-credentials-multiselect.controller'; +export default ['templateUrl', '$compile', + function(templateUrl, $compile) { + return { + scope: { + galaxyCredentials: '=', + fieldIsDisabled: '=' + }, + restrict: 'E', + templateUrl: templateUrl('organizations/galaxy-credentials-multiselect/galaxy-credentials'), + controller: galaxyCredentialsMultiselectController, + link: function(scope) { + scope.openInstanceGroupsModal = function() { + $('#content-container').append($compile('')(scope)); + }; + } + }; + } +]; diff --git a/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials.partial.html b/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials.partial.html new file mode 100644 index 0000000000..09202327b4 --- /dev/null +++ b/awx/ui/client/src/organizations/galaxy-credentials-multiselect/galaxy-credentials.partial.html @@ -0,0 +1,18 @@ +
+ + + + +
+ +
+
+ {{tag.name | sanitize}} +
+
+
diff --git a/awx/ui/client/src/organizations/main.js b/awx/ui/client/src/organizations/main.js index 8540261ede..365f4e6e67 100644 --- a/awx/ui/client/src/organizations/main.js +++ b/awx/ui/client/src/organizations/main.js @@ -12,8 +12,10 @@ import organizationsLinkout from './linkout/main'; import OrganizationsLinkoutStates from './linkout/organizations-linkout.route'; import OrganizationForm from './organizations.form'; import OrganizationList from './organizations.list'; -import { N_ } from '../i18n'; +import galaxyCredentialsMultiselect from './galaxy-credentials-multiselect/galaxy-credentials.directive'; +import galaxyCredentialsModal from './galaxy-credentials-multiselect/galaxy-credentials-modal/galaxy-credentials-modal.directive'; +import { N_ } from '../i18n'; export default angular.module('Organizations', [ @@ -24,6 +26,8 @@ angular.module('Organizations', [ .controller('OrganizationsEdit', OrganizationsEdit) .factory('OrganizationForm', OrganizationForm) .factory('OrganizationList', OrganizationList) + .directive('galaxyCredentialsMultiselect', galaxyCredentialsMultiselect) + .directive('galaxyCredentialsModal', galaxyCredentialsModal) .config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider', function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) { let stateExtender = $stateExtenderProvider.$get(), @@ -67,7 +71,29 @@ angular.module('Organizations', [ }); }); - }] + }], + defaultGalaxyCredential: ['Rest', 'GetBasePath', 'ProcessErrors', + function(Rest, GetBasePath, ProcessErrors){ + Rest.setUrl(GetBasePath('credentials')); + return Rest.get({ + params: { + credential_type__kind: 'galaxy', + managed_by_tower: true + } + }) + .then(({data}) => { + if (data.results.length > 0) { + return data.results; + } + }) + .catch(({data, status}) => { + ProcessErrors(null, data, status, null, { + hdr: 'Error!', + msg: 'Failed to get default Galaxy credential. GET returned ' + + 'status: ' + status + }); + }); + }], }, edit: { ConfigData: ['ConfigService', 'ProcessErrors', (ConfigService, ProcessErrors) => { @@ -81,6 +107,24 @@ angular.module('Organizations', [ }); }); }], + GalaxyCredentialsData: ['$stateParams', 'Rest', 'GetBasePath', 'ProcessErrors', + function($stateParams, Rest, GetBasePath, ProcessErrors){ + let path = `${GetBasePath('organizations')}${$stateParams.organization_id}/galaxy_credentials/`; + Rest.setUrl(path); + return Rest.get() + .then(({data}) => { + if (data.results.length > 0) { + return data.results; + } + }) + .catch(({data, status}) => { + ProcessErrors(null, data, status, null, { + hdr: 'Error!', + msg: 'Failed to get credentials. GET returned ' + + 'status: ' + status + }); + }); + }], InstanceGroupsData: ['$stateParams', 'Rest', 'GetBasePath', 'ProcessErrors', function($stateParams, Rest, GetBasePath, ProcessErrors){ let path = `${GetBasePath('organizations')}${$stateParams.organization_id}/instance_groups/`; diff --git a/awx/ui/client/src/organizations/organizations.form.js b/awx/ui/client/src/organizations/organizations.form.js index 19f6e87419..48446d4599 100644 --- a/awx/ui/client/src/organizations/organizations.form.js +++ b/awx/ui/client/src/organizations/organizations.form.js @@ -55,6 +55,15 @@ export default ['NotificationsList', 'i18n', ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || canAdd)', ngShow: 'custom_virtualenvs_visible' }, + credential: { + label: i18n._('Galaxy Credentials'), + type: 'custom', + awPopOver: "

" + i18n._("Select Galaxy credentials. The selection order sets the order in which Tower will download roles/collections using `ansible-galaxy`.") + "

", + dataTitle: i18n._('Galaxy Credentials'), + dataContainer: 'body', + dataPlacement: 'right', + control: '', + }, max_hosts: { label: i18n._('Max Hosts'), type: 'number', @@ -69,7 +78,7 @@ export default ['NotificationsList', 'i18n', awPopOver: "

" + i18n._("The maximum number of hosts allowed to be managed by this organization. Value defaults to 0 which means no limit. Refer to the Ansible documentation for more details.") + "

", ngDisabled: '!current_user.is_superuser', ngShow: 'BRAND_NAME === "Tower"' - } + }, }, buttons: { //for now always generates + + ); +} + +DetailPopover.propTypes = { + content: node, + header: node, + id: string, +}; +DetailPopover.defaultProps = { + content: null, + header: null, + id: 'detail-popover', +}; + +export default DetailPopover; diff --git a/awx/ui_next/src/components/DetailPopover/index.js b/awx/ui_next/src/components/DetailPopover/index.js new file mode 100644 index 0000000000..02b42518c5 --- /dev/null +++ b/awx/ui_next/src/components/DetailPopover/index.js @@ -0,0 +1 @@ +export { default } from './DetailPopover'; diff --git a/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx b/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx index cef7bf885a..a6f5b5d8ea 100644 --- a/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx +++ b/awx/ui_next/src/components/ErrorDetail/ErrorDetail.jsx @@ -64,9 +64,9 @@ class ErrorDetail extends Component { {Array.isArray(message) ? (
    - {message.map(m => ( -
  • {m}
  • - ))} + {message.map(m => + typeof m === 'string' ?
  • {m}
  • : null + )}
) : ( message diff --git a/awx/ui_next/src/components/FieldWithPrompt/FieldWithPrompt.jsx b/awx/ui_next/src/components/FieldWithPrompt/FieldWithPrompt.jsx index b0d27ccc6e..fd82ae9c7e 100644 --- a/awx/ui_next/src/components/FieldWithPrompt/FieldWithPrompt.jsx +++ b/awx/ui_next/src/components/FieldWithPrompt/FieldWithPrompt.jsx @@ -7,6 +7,7 @@ import { CheckboxField, FieldTooltip } from '../FormField'; const FieldHeader = styled.div` display: flex; + padding-bottom: var(--pf-c-form__group-label--PaddingBottom); `; const StyledCheckboxField = styled(CheckboxField)` @@ -23,6 +24,7 @@ function FieldWithPrompt({ promptId, promptName, tooltip, + isDisabled, }) { return (
@@ -39,6 +41,7 @@ function FieldWithPrompt({ {tooltip && }
{ ); expect(wrapper.find('.pf-c-form__label-required')).toHaveLength(1); - expect(wrapper.find('Tooltip')).toHaveLength(1); + expect(wrapper.find('Popover')).toHaveLength(1); }); }); diff --git a/awx/ui_next/src/components/FormActionGroup/FormActionGroup.jsx b/awx/ui_next/src/components/FormActionGroup/FormActionGroup.jsx index 07620204eb..0662f145dd 100644 --- a/awx/ui_next/src/components/FormActionGroup/FormActionGroup.jsx +++ b/awx/ui_next/src/components/FormActionGroup/FormActionGroup.jsx @@ -5,37 +5,57 @@ import { t } from '@lingui/macro'; import { ActionGroup, Button } from '@patternfly/react-core'; import { FormFullWidthLayout } from '../FormLayout'; -const FormActionGroup = ({ onSubmit, submitDisabled, onCancel, i18n }) => ( - - - - - - -); +const FormActionGroup = ({ + onCancel, + onRevert, + onSubmit, + submitDisabled, + i18n, +}) => { + return ( + + + + {onRevert && ( + + )} + + + + ); +}; FormActionGroup.propTypes = { onCancel: PropTypes.func.isRequired, + onRevert: PropTypes.func, onSubmit: PropTypes.func.isRequired, submitDisabled: PropTypes.bool, }; FormActionGroup.defaultProps = { + onRevert: null, submitDisabled: false, }; diff --git a/awx/ui_next/src/components/FormField/ArrayTextField.jsx b/awx/ui_next/src/components/FormField/ArrayTextField.jsx new file mode 100644 index 0000000000..862d48901b --- /dev/null +++ b/awx/ui_next/src/components/FormField/ArrayTextField.jsx @@ -0,0 +1,70 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { useField } from 'formik'; +import { FormGroup, TextArea } from '@patternfly/react-core'; +import FieldTooltip from './FieldTooltip'; + +function ArrayTextField(props) { + const { + id, + helperText, + name, + label, + tooltip, + tooltipMaxWidth, + validate, + isRequired, + type, + ...rest + } = props; + + const [field, meta, helpers] = useField({ name, validate }); + const isValid = !(meta.touched && meta.error); + const value = field.value || []; + + return ( + } + > +