From 278d70ed49bcffd0b183d527fdbd869a7128d811 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Mon, 12 Dec 2016 11:21:21 -0500 Subject: [PATCH 01/49] do not copy WFJT labels in 3.1 --- awx/main/models/workflow.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/awx/main/models/workflow.py b/awx/main/models/workflow.py index 1cd4d44348..2a0b25f718 100644 --- a/awx/main/models/workflow.py +++ b/awx/main/models/workflow.py @@ -366,7 +366,9 @@ class WorkflowJobTemplate(UnifiedJobTemplate, WorkflowJobOptions, SurveyJobTempl @classmethod def _get_unified_jt_copy_names(cls): - return (super(WorkflowJobTemplate, cls)._get_unified_jt_copy_names() + + base_list = super(WorkflowJobTemplate, cls)._get_unified_jt_copy_names() + base_list.remove('labels') + return (base_list + ['survey_spec', 'survey_enabled', 'organization']) def get_absolute_url(self): From ea10ff8b936ac5bdaf852d48522b996f356c2b0d Mon Sep 17 00:00:00 2001 From: Chris Church Date: Tue, 13 Dec 2016 21:44:09 -0500 Subject: [PATCH 02/49] Initial pass at related search fields. --- awx/api/filters.py | 29 +++++++++++++++++++++++++-- awx/api/generics.py | 17 +++++++++++++++- awx/api/metadata.py | 4 ++++ awx/api/templates/api/_list_common.md | 4 ++++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/awx/api/filters.py b/awx/api/filters.py index 5146ff0cd2..9128af7e81 100644 --- a/awx/api/filters.py +++ b/awx/api/filters.py @@ -77,7 +77,7 @@ class FieldLookupBackend(BaseFilterBackend): SUPPORTED_LOOKUPS = ('exact', 'iexact', 'contains', 'icontains', 'startswith', 'istartswith', 'endswith', 'iendswith', 'regex', 'iregex', 'gt', 'gte', 'lt', 'lte', 'in', - 'isnull') + 'isnull', 'search') def get_field_from_lookup(self, model, lookup): field = None @@ -148,6 +148,15 @@ class FieldLookupBackend(BaseFilterBackend): re.compile(value) except re.error as e: raise ValueError(e.args[0]) + elif new_lookup.endswith('__search'): + related_model = getattr(field, 'related_model', None) + if not related_model: + raise ValueError('%s is not searchable' % new_lookup[:-8]) + new_lookups = [] + for rm_field in related_model._meta.fields: + if rm_field.name in ('username', 'first_name', 'last_name', 'email', 'name', 'description'): + new_lookups.append('{}__{}__icontains'.format(new_lookup[:-8], rm_field.name)) + return value, new_lookups else: value = self.value_to_python_for_field(field, value) return value, new_lookup @@ -160,6 +169,7 @@ class FieldLookupBackend(BaseFilterBackend): or_filters = [] chain_filters = [] role_filters = [] + search_filters = [] for key, values in request.query_params.lists(): if key in self.RESERVED_NAMES: continue @@ -181,6 +191,16 @@ class FieldLookupBackend(BaseFilterBackend): role_filters.append(values[0]) continue + # Search across related objects. + if key.endswith('__search'): + for value in values: + for search_term in force_text(value).replace(',', ' ').split(): + search_value, new_keys = self.value_to_python(queryset.model, key, search_term) + assert isinstance(new_keys, list) + for new_key in new_keys: + search_filters.append((new_key, search_value)) + continue + # Custom chain__ and or__ filters, mutually exclusive (both can # precede not__). q_chain = False @@ -211,7 +231,7 @@ class FieldLookupBackend(BaseFilterBackend): and_filters.append((q_not, new_key, value)) # Now build Q objects for database query filter. - if and_filters or or_filters or chain_filters or role_filters: + if and_filters or or_filters or chain_filters or role_filters or search_filters: args = [] for n, k, v in and_filters: if n: @@ -234,6 +254,11 @@ class FieldLookupBackend(BaseFilterBackend): else: q |= Q(**{k:v}) args.append(q) + if search_filters: + q = Q() + for k,v in search_filters: + q |= Q(**{k:v}) + args.append(q) for n,k,v in chain_filters: if n: q = ~Q(**{k:v}) diff --git a/awx/api/generics.py b/awx/api/generics.py index 1062135a28..73b92cfcc5 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -267,10 +267,25 @@ class ListAPIView(generics.ListAPIView, GenericAPIView): fields = [] for field in self.model._meta.fields: if field.name in ('username', 'first_name', 'last_name', 'email', - 'name', 'description', 'email'): + 'name', 'description'): fields.append(field.name) return fields + @property + def related_search_fields(self): + fields = [] + for field in self.model._meta.fields: + if field.name.endswith('_role'): + continue + if getattr(field, 'related_model', None): + fields.append('{}__search'.format(field.name)) + for rel in self.model._meta.related_objects: + name = rel.get_accessor_name() + if name.endswith('_set'): + continue + fields.append('{}__search'.format(name)) + return fields + class ListCreateAPIView(ListAPIView, generics.ListCreateAPIView): # Base class for a list view that allows creating new objects. diff --git a/awx/api/metadata.py b/awx/api/metadata.py index 6dd186c9ef..fb5c4d6493 100644 --- a/awx/api/metadata.py +++ b/awx/api/metadata.py @@ -182,6 +182,10 @@ class Metadata(metadata.SimpleMetadata): if getattr(view, 'search_fields', None): metadata['search_fields'] = view.search_fields + # Add related search fields if available from the view. + if getattr(view, 'related_search_fields', None): + metadata['related_search_fields'] = view.related_search_fields + return metadata diff --git a/awx/api/templates/api/_list_common.md b/awx/api/templates/api/_list_common.md index 36e6819276..706ae732a5 100644 --- a/awx/api/templates/api/_list_common.md +++ b/awx/api/templates/api/_list_common.md @@ -56,6 +56,10 @@ within all designated text fields of a model. _Added in AWX 1.4_ +(_Added in Ansible Tower 3.1.0_) Search across related fields: + + ?related__search=findme + ## Filtering Any additional query string parameters may be used to filter the list of From 912af09831f57c7f5be47ec698bcc316fe1e5645 Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Wed, 14 Dec 2016 15:35:04 -0500 Subject: [PATCH 03/49] Fixed organization add user/admin save button functionality --- .../linkout/addUsers/addUsers.controller.js | 23 +++++++++++++++---- .../organizations-admins.controller.js | 2 +- .../organizations-users.controller.js | 2 +- .../linkout/organizations-linkout.route.js | 3 +++ 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/awx/ui/client/src/organizations/linkout/addUsers/addUsers.controller.js b/awx/ui/client/src/organizations/linkout/addUsers/addUsers.controller.js index b42d143b00..b45797a70b 100644 --- a/awx/ui/client/src/organizations/linkout/addUsers/addUsers.controller.js +++ b/awx/ui/client/src/organizations/linkout/addUsers/addUsers.controller.js @@ -12,9 +12,9 @@ */ export default ['$scope', '$rootScope', 'ProcessErrors', 'GetBasePath', -'SelectionInit', 'templateUrl', '$state', 'Rest', '$q', 'Wait', +'SelectionInit', 'templateUrl', '$state', 'Rest', '$q', 'Wait', '$window', function($scope, $rootScope, ProcessErrors, GetBasePath, - SelectionInit, templateUrl, $state, Rest, $q, Wait) { + SelectionInit, templateUrl, $state, Rest, $q, Wait, $window) { $scope.$on("linkLists", function() { if ($state.current.name.split(".")[1] === "users") { @@ -32,8 +32,15 @@ function($scope, $rootScope, ProcessErrors, GetBasePath, $scope.add_users = $scope.$parent.add_user_dataset.results; $scope.selectedItems = []; - $scope.$on('selectedOrDeselected', ()=>{ - throw {name: 'NotYetImplemented'}; + $scope.$on('selectedOrDeselected', function(e, value) { + let item = value.value; + + if (item.isSelected) { + $scope.selectedItems.push(item.id); + } + else { + $scope.selectedItems = _.remove($scope.selectedItems, { id: item.id }); + } }); } @@ -42,7 +49,7 @@ function($scope, $rootScope, ProcessErrors, GetBasePath, var url, listToClose, payloads = $scope.selectedItems.map(function(val) { - return {id: val.id}; + return {id: val}; }); url = $scope.$parent.orgRelatedUrls[$scope.addUsersType]; @@ -69,5 +76,11 @@ function($scope, $rootScope, ProcessErrors, GetBasePath, }); }); }; + + $scope.linkoutUser = function(userId) { + // Open the edit user form in a new tab so as not to navigate the user + // away from the modal + $window.open('/#/users/' + userId,'_blank'); + }; }); }]; diff --git a/awx/ui/client/src/organizations/linkout/controllers/organizations-admins.controller.js b/awx/ui/client/src/organizations/linkout/controllers/organizations-admins.controller.js index dc8c5b0c12..df3075305e 100644 --- a/awx/ui/client/src/organizations/linkout/controllers/organizations-admins.controller.js +++ b/awx/ui/client/src/organizations/linkout/controllers/organizations-admins.controller.js @@ -37,7 +37,7 @@ export default ['$stateParams', '$scope', 'UserList', 'Rest', '$state', } $scope.addUsers = function() { - $compile("")($scope); + $compile("")($scope); }; $scope.editUser = function(id) { diff --git a/awx/ui/client/src/organizations/linkout/controllers/organizations-users.controller.js b/awx/ui/client/src/organizations/linkout/controllers/organizations-users.controller.js index 8baede4f2e..2f3e0f7872 100644 --- a/awx/ui/client/src/organizations/linkout/controllers/organizations-users.controller.js +++ b/awx/ui/client/src/organizations/linkout/controllers/organizations-users.controller.js @@ -37,7 +37,7 @@ export default ['$stateParams', '$scope', 'OrgUserList', 'AddUserList','Rest', ' } $scope.addUsers = function() { - $compile("")($scope); + $compile("")($scope); }; $scope.editUser = function(id) { diff --git a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js index 3f65c144ab..d8efd0c813 100644 --- a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js +++ b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js @@ -99,6 +99,7 @@ export default [{ list.iterator = 'add_user'; list.name = 'add_users'; list.multiSelect = true; + list.fields.username.ngClick = 'linkoutUser(add_user.id)'; delete list.actions; delete list.fieldActions; return list; @@ -386,6 +387,7 @@ export default [{ } }; list.searchSize = "col-lg-12 col-md-12 col-sm-12 col-xs-12"; + list.listTitle = 'Admins'; return list; }], AddAdminList: ['UserList', function(UserList) { @@ -394,6 +396,7 @@ export default [{ list.iterator = 'add_user'; list.name = 'add_users'; list.multiSelect = true; + list.fields.username.ngClick = 'linkoutUser(add_user.id)'; delete list.actions; delete list.fieldActions; return list; From a25853a3a53b30bd138e334a7b47adcc40e09f68 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Wed, 14 Dec 2016 15:57:40 -0500 Subject: [PATCH 04/49] Updating translation artifacts * also installing translation tools into dev environment * Removing fedora install instructions, no one here uses fedora --- awx/locale/django.pot | 1801 ++++++++++++++------------ awx/ui/po/ansible-tower-ui.pot | 291 +++-- tools/docker-compose/Dockerfile | 2 +- tools/scripts/manage_translations.py | 5 +- 4 files changed, 1179 insertions(+), 920 deletions(-) mode change 100644 => 100755 tools/scripts/manage_translations.py diff --git a/awx/locale/django.pot b/awx/locale/django.pot index 7b67733c8b..81993a2a2b 100644 --- a/awx/locale/django.pot +++ b/awx/locale/django.pot @@ -1,12 +1,14 @@ -# Ansible Tower POT file. -# Copyright (c) 2016 Ansible, Inc. -# All Rights Reserved. +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. # +#, fuzzy msgid "" msgstr "" -"Project-Id-Version: ansible-tower-3.1.0\n" +"Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-12-01 06:37-0500\n" +"POT-Creation-Date: 2016-12-14 19:21+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,1033 +17,1039 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: awx/api/authentication.py:67 +#: api/authentication.py:67 msgid "Invalid token header. No credentials provided." msgstr "" -#: awx/api/authentication.py:70 +#: api/authentication.py:70 msgid "Invalid token header. Token string should not contain spaces." msgstr "" -#: awx/api/authentication.py:105 +#: api/authentication.py:105 msgid "User inactive or deleted" msgstr "" -#: awx/api/authentication.py:161 +#: api/authentication.py:161 msgid "Invalid task token" msgstr "" -#: awx/api/conf.py:12 +#: api/conf.py:12 msgid "Idle Time Force Log Out" msgstr "" -#: awx/api/conf.py:13 +#: api/conf.py:13 msgid "" "Number of seconds that a user is inactive before they will need to login " "again." msgstr "" -#: awx/api/conf.py:14 awx/api/conf.py:24 awx/api/conf.py:33 awx/sso/conf.py:124 -#: awx/sso/conf.py:135 awx/sso/conf.py:147 awx/sso/conf.py:162 +#: api/conf.py:14 api/conf.py:24 api/conf.py:33 sso/conf.py:124 +#: sso/conf.py:135 sso/conf.py:147 sso/conf.py:162 msgid "Authentication" msgstr "" -#: awx/api/conf.py:22 +#: api/conf.py:22 msgid "Maximum number of simultaneous logins" msgstr "" -#: awx/api/conf.py:23 +#: api/conf.py:23 msgid "" "Maximum number of simultaneous logins a user may have. To disable enter -1." msgstr "" -#: awx/api/conf.py:31 +#: api/conf.py:31 msgid "Enable HTTP Basic Auth" msgstr "" -#: awx/api/conf.py:32 +#: api/conf.py:32 msgid "Enable HTTP Basic Auth for the API Browser." msgstr "" -#: awx/api/generics.py:446 +#: api/generics.py:446 msgid "\"id\" is required to disassociate" msgstr "" -#: awx/api/metadata.py:50 +#: api/metadata.py:50 msgid "Database ID for this {}." msgstr "" -#: awx/api/metadata.py:51 +#: api/metadata.py:51 msgid "Name of this {}." msgstr "" -#: awx/api/metadata.py:52 +#: api/metadata.py:52 msgid "Optional description of this {}." msgstr "" -#: awx/api/metadata.py:53 +#: api/metadata.py:53 msgid "Data type for this {}." msgstr "" -#: awx/api/metadata.py:54 +#: api/metadata.py:54 msgid "URL for this {}." msgstr "" -#: awx/api/metadata.py:55 +#: api/metadata.py:55 msgid "Data structure with URLs of related resources." msgstr "" -#: awx/api/metadata.py:56 +#: api/metadata.py:56 msgid "Data structure with name/description for related resources." msgstr "" -#: awx/api/metadata.py:57 +#: api/metadata.py:57 msgid "Timestamp when this {} was created." msgstr "" -#: awx/api/metadata.py:58 +#: api/metadata.py:58 msgid "Timestamp when this {} was last modified." msgstr "" -#: awx/api/parsers.py:31 +#: api/parsers.py:31 #, python-format msgid "JSON parse error - %s" msgstr "" -#: awx/api/serializers.py:248 +#: api/serializers.py:248 msgid "Playbook Run" msgstr "" -#: awx/api/serializers.py:249 +#: api/serializers.py:249 msgid "Command" msgstr "" -#: awx/api/serializers.py:250 +#: api/serializers.py:250 msgid "SCM Update" msgstr "" -#: awx/api/serializers.py:251 +#: api/serializers.py:251 msgid "Inventory Sync" msgstr "" -#: awx/api/serializers.py:252 +#: api/serializers.py:252 msgid "Management Job" msgstr "" -#: awx/api/serializers.py:636 awx/api/serializers.py:694 awx/api/views.py:3994 +#: api/serializers.py:253 +msgid "Workflow Job" +msgstr "" + +#: api/serializers.py:655 api/serializers.py:713 api/views.py:3914 #, python-format msgid "" "Standard Output too large to display (%(text_size)d bytes), only download " "supported for sizes over %(supported_size)d bytes" msgstr "" -#: awx/api/serializers.py:709 +#: api/serializers.py:728 msgid "Write-only field used to change the password." msgstr "" -#: awx/api/serializers.py:711 +#: api/serializers.py:730 msgid "Set if the account is managed by an external service" msgstr "" -#: awx/api/serializers.py:735 +#: api/serializers.py:754 msgid "Password required for new User." msgstr "" -#: awx/api/serializers.py:819 +#: api/serializers.py:838 #, python-format msgid "Unable to change %s on user managed by LDAP." msgstr "" -#: awx/api/serializers.py:971 +#: api/serializers.py:990 msgid "Organization is missing" msgstr "" -#: awx/api/serializers.py:977 +#: api/serializers.py:996 msgid "Array of playbooks available within this project." msgstr "" -#: awx/api/serializers.py:1159 +#: api/serializers.py:1178 #, python-format msgid "Invalid port specification: %s" msgstr "" -#: awx/api/serializers.py:1187 awx/main/validators.py:192 +#: api/serializers.py:1206 main/validators.py:192 msgid "Must be valid JSON or YAML." msgstr "" -#: awx/api/serializers.py:1244 +#: api/serializers.py:1263 msgid "Invalid group name." msgstr "" -#: awx/api/serializers.py:1319 +#: api/serializers.py:1338 msgid "" "Script must begin with a hashbang sequence: i.e.... #!/usr/bin/env python" msgstr "" -#: awx/api/serializers.py:1372 +#: api/serializers.py:1391 msgid "If 'source' is 'custom', 'source_script' must be provided." msgstr "" -#: awx/api/serializers.py:1376 +#: api/serializers.py:1395 msgid "" "The 'source_script' does not belong to the same organization as the " "inventory." msgstr "" -#: awx/api/serializers.py:1378 +#: api/serializers.py:1397 msgid "'source_script' doesn't exist." msgstr "" -#: awx/api/serializers.py:1737 +#: api/serializers.py:1756 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:1742 +#: api/serializers.py:1761 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:1747 +#: api/serializers.py:1766 msgid "" -"Write-only field used to add organization to owner role. If provided, do not " -"give either team or team. Only valid for creation." +"Inherit permissions from organization roles. If provided on creation, do not " +"give either user or team." msgstr "" -#: awx/api/serializers.py:1763 +#: api/serializers.py:1782 msgid "Missing 'user', 'team', or 'organization'." msgstr "" -#: awx/api/serializers.py:1776 +#: api/serializers.py:1795 msgid "" "Credential organization must be set and match before assigning to a team" msgstr "" -#: awx/api/serializers.py:1868 +#: api/serializers.py:1887 msgid "This field is required." msgstr "" -#: awx/api/serializers.py:1870 awx/api/serializers.py:1872 +#: api/serializers.py:1889 api/serializers.py:1891 msgid "Playbook not found for project." msgstr "" -#: awx/api/serializers.py:1874 +#: api/serializers.py:1893 msgid "Must select playbook for project." msgstr "" -#: awx/api/serializers.py:1938 awx/main/models/jobs.py:279 +#: api/serializers.py:1957 main/models/jobs.py:280 msgid "Scan jobs must be assigned a fixed inventory." msgstr "" -#: awx/api/serializers.py:1940 awx/main/models/jobs.py:282 +#: api/serializers.py:1959 main/models/jobs.py:283 msgid "Job types 'run' and 'check' must have assigned a project." msgstr "" -#: awx/api/serializers.py:1943 +#: api/serializers.py:1962 msgid "Survey Enabled cannot be used with scan jobs." msgstr "" -#: awx/api/serializers.py:2005 +#: api/serializers.py:2024 msgid "Invalid job template." msgstr "" -#: awx/api/serializers.py:2090 +#: api/serializers.py:2109 msgid "Credential not found or deleted." msgstr "" -#: awx/api/serializers.py:2092 +#: api/serializers.py:2111 msgid "Job Template Project is missing or undefined." msgstr "" -#: awx/api/serializers.py:2094 +#: api/serializers.py:2113 msgid "Job Template Inventory is missing or undefined." msgstr "" -#: awx/api/serializers.py:2379 +#: api/serializers.py:2398 #, python-format msgid "%(job_type)s is not a valid job type. The choices are %(choices)s." msgstr "" -#: awx/api/serializers.py:2384 +#: api/serializers.py:2403 msgid "Workflow job template is missing during creation." msgstr "" -#: awx/api/serializers.py:2389 +#: api/serializers.py:2408 #, python-format msgid "Cannot nest a %s inside a WorkflowJobTemplate" msgstr "" -#: awx/api/serializers.py:2625 +#: api/serializers.py:2646 #, python-format msgid "Job Template '%s' is missing or undefined." msgstr "" -#: awx/api/serializers.py:2651 +#: api/serializers.py:2672 msgid "Must be a valid JSON or YAML dictionary." msgstr "" -#: awx/api/serializers.py:2796 +#: api/serializers.py:2817 msgid "" "Missing required fields for Notification Configuration: notification_type" msgstr "" -#: awx/api/serializers.py:2819 +#: api/serializers.py:2840 msgid "No values specified for field '{}'" msgstr "" -#: awx/api/serializers.py:2824 +#: api/serializers.py:2845 msgid "Missing required fields for Notification Configuration: {}." msgstr "" -#: awx/api/serializers.py:2827 +#: api/serializers.py:2848 msgid "Configuration field '{}' incorrect type, expected {}." msgstr "" -#: awx/api/serializers.py:2880 +#: api/serializers.py:2901 msgid "Inventory Source must be a cloud resource." msgstr "" -#: awx/api/serializers.py:2882 +#: api/serializers.py:2903 msgid "Manual Project can not have a schedule set." msgstr "" -#: awx/api/serializers.py:2904 +#: api/serializers.py:2925 msgid "DTSTART required in rrule. Value should match: DTSTART:YYYYMMDDTHHMMSSZ" msgstr "" -#: awx/api/serializers.py:2906 +#: api/serializers.py:2927 msgid "Multiple DTSTART is not supported." msgstr "" -#: awx/api/serializers.py:2908 +#: api/serializers.py:2929 msgid "RRULE require in rrule." msgstr "" -#: awx/api/serializers.py:2910 +#: api/serializers.py:2931 msgid "Multiple RRULE is not supported." msgstr "" -#: awx/api/serializers.py:2912 +#: api/serializers.py:2933 msgid "INTERVAL required in rrule." msgstr "" -#: awx/api/serializers.py:2914 +#: api/serializers.py:2935 msgid "TZID is not supported." msgstr "" -#: awx/api/serializers.py:2916 +#: api/serializers.py:2937 msgid "SECONDLY is not supported." msgstr "" -#: awx/api/serializers.py:2918 +#: api/serializers.py:2939 msgid "Multiple BYMONTHDAYs not supported." msgstr "" -#: awx/api/serializers.py:2920 +#: api/serializers.py:2941 msgid "Multiple BYMONTHs not supported." msgstr "" -#: awx/api/serializers.py:2922 +#: api/serializers.py:2943 msgid "BYDAY with numeric prefix not supported." msgstr "" -#: awx/api/serializers.py:2924 +#: api/serializers.py:2945 msgid "BYYEARDAY not supported." msgstr "" -#: awx/api/serializers.py:2926 +#: api/serializers.py:2947 msgid "BYWEEKNO not supported." msgstr "" -#: awx/api/serializers.py:2930 +#: api/serializers.py:2951 msgid "COUNT > 999 is unsupported." msgstr "" -#: awx/api/serializers.py:2934 +#: api/serializers.py:2955 msgid "rrule parsing failed validation." msgstr "" -#: awx/api/serializers.py:2952 +#: api/serializers.py:2973 msgid "" "A summary of the new and changed values when an object is created, updated, " "or deleted" msgstr "" -#: awx/api/serializers.py:2954 +#: api/serializers.py:2975 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:2957 +#: api/serializers.py:2978 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:2960 +#: api/serializers.py:2981 msgid "The action taken with respect to the given object(s)." msgstr "" -#: awx/api/serializers.py:3060 +#: api/serializers.py:3081 msgid "Unable to login with provided credentials." msgstr "" -#: awx/api/serializers.py:3062 +#: api/serializers.py:3083 msgid "Must include \"username\" and \"password\"." msgstr "" -#: awx/api/views.py:95 awx/templates/rest_framework/api.html:28 +#: api/views.py:96 +msgid "Your license does not allow use of the activity stream." +msgstr "" + +#: api/views.py:106 +msgid "Your license does not permit use of system tracking." +msgstr "" + +#: api/views.py:116 +msgid "Your license does not allow use of workflows." +msgstr "" + +#: api/views.py:124 templates/rest_framework/api.html:28 msgid "REST API" msgstr "" -#: awx/api/views.py:102 awx/templates/rest_framework/api.html:4 +#: api/views.py:131 templates/rest_framework/api.html:4 msgid "Ansible Tower REST API" msgstr "" -#: awx/api/views.py:118 +#: api/views.py:147 msgid "Version 1" msgstr "" -#: awx/api/views.py:169 +#: api/views.py:198 msgid "Ping" msgstr "" -#: awx/api/views.py:198 awx/conf/apps.py:10 +#: api/views.py:227 conf/apps.py:12 msgid "Configuration" msgstr "" -#: awx/api/views.py:248 +#: api/views.py:280 msgid "Invalid license data" msgstr "" -#: awx/api/views.py:250 +#: api/views.py:282 msgid "Missing 'eula_accepted' property" msgstr "" -#: awx/api/views.py:254 +#: api/views.py:286 msgid "'eula_accepted' value is invalid" msgstr "" -#: awx/api/views.py:257 +#: api/views.py:289 msgid "'eula_accepted' must be True" msgstr "" -#: awx/api/views.py:263 +#: api/views.py:296 msgid "Invalid JSON" msgstr "" -#: awx/api/views.py:270 +#: api/views.py:304 msgid "Invalid License" msgstr "" -#: awx/api/views.py:278 +#: api/views.py:314 msgid "Invalid license" msgstr "" -#: awx/api/views.py:289 +#: api/views.py:322 #, python-format msgid "Failed to remove license (%s)" msgstr "" -#: awx/api/views.py:294 +#: api/views.py:327 msgid "Dashboard" msgstr "" -#: awx/api/views.py:400 +#: api/views.py:433 msgid "Dashboard Jobs Graphs" msgstr "" -#: awx/api/views.py:436 +#: api/views.py:469 #, python-format msgid "Unknown period \"%s\"" msgstr "" -#: awx/api/views.py:450 +#: api/views.py:483 msgid "Schedules" msgstr "" -#: awx/api/views.py:469 +#: api/views.py:502 msgid "Schedule Jobs List" msgstr "" -#: awx/api/views.py:675 +#: api/views.py:711 msgid "Your Tower license only permits a single organization to exist." msgstr "" -#: awx/api/views.py:803 awx/api/views.py:968 awx/api/views.py:1069 -#: awx/api/views.py:1356 awx/api/views.py:1517 awx/api/views.py:1614 -#: awx/api/views.py:1758 awx/api/views.py:1954 awx/api/views.py:2212 -#: awx/api/views.py:2528 awx/api/views.py:3121 awx/api/views.py:3194 -#: awx/api/views.py:3330 awx/api/views.py:3911 awx/api/views.py:4163 -#: awx/api/views.py:4180 -msgid "Your license does not allow use of the activity stream." -msgstr "" - -#: awx/api/views.py:906 awx/api/views.py:1270 +#: api/views.py:932 api/views.py:1284 msgid "Role 'id' field is missing." msgstr "" -#: awx/api/views.py:912 awx/api/views.py:4282 +#: api/views.py:938 api/views.py:4182 msgid "You cannot assign an Organization role as a child role for a Team." msgstr "" -#: awx/api/views.py:919 awx/api/views.py:4288 +#: api/views.py:942 api/views.py:4196 +msgid "You cannot grant system-level permissions to a team." +msgstr "" + +#: api/views.py:949 api/views.py:4188 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.py:1018 +#: api/views.py:1039 msgid "Cannot delete project." msgstr "" -#: awx/api/views.py:1047 +#: api/views.py:1068 msgid "Project Schedules" msgstr "" -#: awx/api/views.py:1156 awx/api/views.py:2307 awx/api/views.py:3301 +#: api/views.py:1168 api/views.py:2252 api/views.py:3225 msgid "Cannot delete job resource when associated workflow job is running." msgstr "" -#: awx/api/views.py:1230 +#: api/views.py:1244 msgid "Me" msgstr "" -#: awx/api/views.py:1274 awx/api/views.py:4237 +#: api/views.py:1288 api/views.py:4137 msgid "You may not perform any action with your own admin_role." msgstr "" -#: awx/api/views.py:1280 awx/api/views.py:4241 +#: api/views.py:1294 api/views.py:4141 msgid "You may not change the membership of a users admin_role" msgstr "" -#: awx/api/views.py:1285 awx/api/views.py:4246 +#: api/views.py:1299 api/views.py:4146 msgid "" "You cannot grant credential access to a user not in the credentials' " "organization" msgstr "" -#: awx/api/views.py:1289 awx/api/views.py:4250 +#: api/views.py:1303 api/views.py:4150 msgid "You cannot grant private credential access to another user" msgstr "" -#: awx/api/views.py:1397 +#: api/views.py:1401 #, python-format msgid "Cannot change %s." msgstr "" -#: awx/api/views.py:1403 +#: api/views.py:1407 msgid "Cannot delete user." msgstr "" -#: awx/api/views.py:1559 +#: api/views.py:1553 msgid "Cannot delete inventory script." msgstr "" -#: awx/api/views.py:1777 -msgid "Your license does not permit use of system tracking." -msgstr "" - -#: awx/api/views.py:1824 +#: api/views.py:1788 msgid "Fact not found." msgstr "" -#: awx/api/views.py:2154 +#: api/views.py:2108 msgid "Inventory Source List" msgstr "" -#: awx/api/views.py:2182 +#: api/views.py:2136 msgid "Cannot delete inventory source." msgstr "" -#: awx/api/views.py:2190 +#: api/views.py:2144 msgid "Inventory Source Schedules" msgstr "" -#: awx/api/views.py:2229 +#: api/views.py:2173 msgid "Notification Templates can only be assigned when source is one of {}." msgstr "" -#: awx/api/views.py:2433 +#: api/views.py:2380 msgid "Job Template Schedules" msgstr "" -#: awx/api/views.py:2452 awx/api/views.py:2462 +#: api/views.py:2399 api/views.py:2409 msgid "Your license does not allow adding surveys." msgstr "" -#: awx/api/views.py:2469 +#: api/views.py:2416 msgid "'name' missing from survey spec." msgstr "" -#: awx/api/views.py:2471 +#: api/views.py:2418 msgid "'description' missing from survey spec." msgstr "" -#: awx/api/views.py:2473 +#: api/views.py:2420 msgid "'spec' missing from survey spec." msgstr "" -#: awx/api/views.py:2475 +#: api/views.py:2422 msgid "'spec' must be a list of items." msgstr "" -#: awx/api/views.py:2477 +#: api/views.py:2424 msgid "'spec' doesn't contain any items." msgstr "" -#: awx/api/views.py:2482 +#: api/views.py:2429 #, python-format msgid "Survey question %s is not a json object." msgstr "" -#: awx/api/views.py:2484 +#: api/views.py:2431 #, python-format msgid "'type' missing from survey question %s." msgstr "" -#: awx/api/views.py:2486 +#: api/views.py:2433 #, python-format msgid "'question_name' missing from survey question %s." msgstr "" -#: awx/api/views.py:2488 +#: api/views.py:2435 #, python-format msgid "'variable' missing from survey question %s." msgstr "" -#: awx/api/views.py:2490 +#: api/views.py:2437 #, python-format msgid "'variable' '%(item)s' duplicated in survey question %(survey)s." msgstr "" -#: awx/api/views.py:2495 +#: api/views.py:2442 #, python-format msgid "'required' missing from survey question %s." msgstr "" -#: awx/api/views.py:2702 +#: api/views.py:2641 msgid "No matching host could be found!" msgstr "" -#: awx/api/views.py:2705 +#: api/views.py:2644 msgid "Multiple hosts matched the request!" msgstr "" -#: awx/api/views.py:2710 +#: api/views.py:2649 msgid "Cannot start automatically, user input required!" msgstr "" -#: awx/api/views.py:2717 +#: api/views.py:2656 msgid "Host callback job already pending." msgstr "" -#: awx/api/views.py:2730 +#: api/views.py:2669 msgid "Error starting job!" msgstr "" -#: awx/api/views.py:3053 +#: api/views.py:2995 msgid "Workflow Job Template Schedules" msgstr "" -#: awx/api/views.py:3208 awx/api/views.py:3933 +#: api/views.py:3131 api/views.py:3853 msgid "Superuser privileges needed." msgstr "" -#: awx/api/views.py:3238 +#: api/views.py:3161 msgid "System Job Template Schedules" msgstr "" -#: awx/api/views.py:3428 +#: api/views.py:3344 msgid "Job Host Summaries List" msgstr "" -#: awx/api/views.py:3470 +#: api/views.py:3386 msgid "Job Event Children List" msgstr "" -#: awx/api/views.py:3479 +#: api/views.py:3395 msgid "Job Event Hosts List" msgstr "" -#: awx/api/views.py:3488 +#: api/views.py:3404 msgid "Job Events List" msgstr "" -#: awx/api/views.py:3509 +#: api/views.py:3436 msgid "Job Plays List" msgstr "" -#: awx/api/views.py:3584 +#: api/views.py:3513 msgid "Job Play Tasks List" msgstr "" -#: awx/api/views.py:3599 +#: api/views.py:3529 msgid "Job not found." msgstr "" -#: awx/api/views.py:3603 +#: api/views.py:3533 msgid "'event_id' not provided." msgstr "" -#: awx/api/views.py:3607 +#: api/views.py:3537 msgid "Parent event not found." msgstr "" -#: awx/api/views.py:3879 +#: api/views.py:3809 msgid "Ad Hoc Command Events List" msgstr "" -#: awx/api/views.py:4043 +#: api/views.py:3963 #, python-format msgid "Error generating stdout download file: %s" msgstr "" -#: awx/api/views.py:4089 +#: api/views.py:4009 msgid "Delete not allowed while there are pending notifications" msgstr "" -#: awx/api/views.py:4096 +#: api/views.py:4016 msgid "NotificationTemplate Test" msgstr "" -#: awx/api/views.py:4231 +#: api/views.py:4131 msgid "User 'id' field is missing." msgstr "" -#: awx/api/views.py:4274 +#: api/views.py:4174 msgid "Team 'id' field is missing." msgstr "" -#: awx/conf/conf.py:20 +#: conf/conf.py:20 msgid "Bud Frogs" msgstr "" -#: awx/conf/conf.py:21 +#: conf/conf.py:21 msgid "Bunny" msgstr "" -#: awx/conf/conf.py:22 +#: conf/conf.py:22 msgid "Cheese" msgstr "" -#: awx/conf/conf.py:23 +#: conf/conf.py:23 msgid "Daemon" msgstr "" -#: awx/conf/conf.py:24 +#: conf/conf.py:24 msgid "Default Cow" msgstr "" -#: awx/conf/conf.py:25 +#: conf/conf.py:25 msgid "Dragon" msgstr "" -#: awx/conf/conf.py:26 +#: conf/conf.py:26 msgid "Elephant in Snake" msgstr "" -#: awx/conf/conf.py:27 +#: conf/conf.py:27 msgid "Elephant" msgstr "" -#: awx/conf/conf.py:28 +#: conf/conf.py:28 msgid "Eyes" msgstr "" -#: awx/conf/conf.py:29 +#: conf/conf.py:29 msgid "Hello Kitty" msgstr "" -#: awx/conf/conf.py:30 +#: conf/conf.py:30 msgid "Kitty" msgstr "" -#: awx/conf/conf.py:31 +#: conf/conf.py:31 msgid "Luke Koala" msgstr "" -#: awx/conf/conf.py:32 +#: conf/conf.py:32 msgid "Meow" msgstr "" -#: awx/conf/conf.py:33 +#: conf/conf.py:33 msgid "Milk" msgstr "" -#: awx/conf/conf.py:34 +#: conf/conf.py:34 msgid "Moofasa" msgstr "" -#: awx/conf/conf.py:35 +#: conf/conf.py:35 msgid "Moose" msgstr "" -#: awx/conf/conf.py:36 +#: conf/conf.py:36 msgid "Ren" msgstr "" -#: awx/conf/conf.py:37 +#: conf/conf.py:37 msgid "Sheep" msgstr "" -#: awx/conf/conf.py:38 +#: conf/conf.py:38 msgid "Small Cow" msgstr "" -#: awx/conf/conf.py:39 +#: conf/conf.py:39 msgid "Stegosaurus" msgstr "" -#: awx/conf/conf.py:40 +#: conf/conf.py:40 msgid "Stimpy" msgstr "" -#: awx/conf/conf.py:41 +#: conf/conf.py:41 msgid "Super Milker" msgstr "" -#: awx/conf/conf.py:42 +#: conf/conf.py:42 msgid "Three Eyes" msgstr "" -#: awx/conf/conf.py:43 +#: conf/conf.py:43 msgid "Turkey" msgstr "" -#: awx/conf/conf.py:44 +#: conf/conf.py:44 msgid "Turtle" msgstr "" -#: awx/conf/conf.py:45 +#: conf/conf.py:45 msgid "Tux" msgstr "" -#: awx/conf/conf.py:46 +#: conf/conf.py:46 msgid "Udder" msgstr "" -#: awx/conf/conf.py:47 +#: conf/conf.py:47 msgid "Vader Koala" msgstr "" -#: awx/conf/conf.py:48 +#: conf/conf.py:48 msgid "Vader" msgstr "" -#: awx/conf/conf.py:49 +#: conf/conf.py:49 msgid "WWW" msgstr "" -#: awx/conf/conf.py:52 +#: conf/conf.py:52 msgid "Cow Selection" msgstr "" -#: awx/conf/conf.py:53 +#: conf/conf.py:53 msgid "Select which cow to use with cowsay when running jobs." msgstr "" -#: awx/conf/conf.py:54 awx/conf/conf.py:75 +#: conf/conf.py:54 conf/conf.py:75 msgid "Cows" msgstr "" -#: awx/conf/conf.py:73 +#: conf/conf.py:73 msgid "Example Read-Only Setting" msgstr "" -#: awx/conf/conf.py:74 +#: conf/conf.py:74 msgid "Example setting that cannot be changed." msgstr "" -#: awx/conf/conf.py:90 +#: conf/conf.py:93 msgid "Example Setting" msgstr "" -#: awx/conf/conf.py:91 +#: conf/conf.py:94 msgid "Example setting which can be different for each user." msgstr "" -#: awx/conf/conf.py:92 awx/conf/registry.py:67 awx/conf/views.py:46 +#: conf/conf.py:95 conf/registry.py:67 conf/views.py:46 msgid "User" msgstr "" -#: awx/conf/fields.py:38 +#: conf/fields.py:38 msgid "Enter a valid URL" msgstr "" -#: awx/conf/license.py:23 +#: conf/license.py:19 msgid "Your Tower license does not allow that." msgstr "" -#: awx/conf/management/commands/migrate_to_database_settings.py:41 +#: conf/management/commands/migrate_to_database_settings.py:41 msgid "Only show which settings would be commented/migrated." msgstr "" -#: awx/conf/management/commands/migrate_to_database_settings.py:48 +#: conf/management/commands/migrate_to_database_settings.py:48 msgid "Skip over settings that would raise an error when commenting/migrating." msgstr "" -#: awx/conf/management/commands/migrate_to_database_settings.py:55 +#: conf/management/commands/migrate_to_database_settings.py:55 msgid "Skip commenting out settings in files." msgstr "" -#: awx/conf/management/commands/migrate_to_database_settings.py:61 +#: conf/management/commands/migrate_to_database_settings.py:61 msgid "Backup existing settings files with this suffix." msgstr "" -#: awx/conf/registry.py:55 +#: conf/registry.py:55 msgid "All" msgstr "" -#: awx/conf/registry.py:56 +#: conf/registry.py:56 msgid "Changed" msgstr "" -#: awx/conf/registry.py:68 +#: conf/registry.py:68 msgid "User-Defaults" msgstr "" -#: awx/conf/views.py:38 +#: conf/views.py:38 msgid "Setting Categories" msgstr "" -#: awx/conf/views.py:61 +#: conf/views.py:61 msgid "Setting Detail" msgstr "" -#: awx/main/access.py:255 +#: main/access.py:255 #, python-format msgid "Bad data found in related field %s." msgstr "" -#: awx/main/access.py:296 +#: main/access.py:296 msgid "License is missing." msgstr "" -#: awx/main/access.py:298 +#: main/access.py:298 msgid "License has expired." msgstr "" -#: awx/main/access.py:303 +#: main/access.py:303 #, python-format msgid "License count of %s instances has been reached." msgstr "" -#: awx/main/access.py:305 +#: main/access.py:305 #, python-format msgid "License count of %s instances has been exceeded." msgstr "" -#: awx/main/access.py:307 +#: main/access.py:307 msgid "Host count exceeds available instances." msgstr "" -#: awx/main/access.py:311 +#: main/access.py:311 #, python-format msgid "Feature %s is not enabled in the active license." msgstr "" -#: awx/main/access.py:313 +#: main/access.py:313 msgid "Features not found in active license." msgstr "" -#: awx/main/access.py:507 awx/main/access.py:574 awx/main/access.py:694 -#: awx/main/access.py:965 awx/main/access.py:1206 awx/main/access.py:1594 +#: main/access.py:507 main/access.py:574 main/access.py:694 main/access.py:957 +#: main/access.py:1198 main/access.py:1587 msgid "Resource is being used by running jobs" msgstr "" -#: awx/main/access.py:618 +#: main/access.py:618 msgid "Unable to change inventory on a host." msgstr "" -#: awx/main/access.py:630 awx/main/access.py:675 +#: main/access.py:630 main/access.py:675 msgid "Cannot associate two items from different inventories." msgstr "" -#: awx/main/access.py:663 +#: main/access.py:663 msgid "Unable to change inventory on a group." msgstr "" -#: awx/main/access.py:885 +#: main/access.py:877 msgid "Unable to change organization on a team." msgstr "" -#: awx/main/access.py:898 +#: main/access.py:890 msgid "The {} role cannot be assigned to a team" msgstr "" -#: awx/main/access.py:900 +#: main/access.py:892 msgid "The admin_role for a User cannot be assigned to a team" msgstr "" -#: awx/main/apps.py:9 +#: main/apps.py:9 msgid "Main" msgstr "" -#: awx/main/conf.py:17 +#: main/conf.py:17 msgid "Enable Activity Stream" msgstr "" -#: awx/main/conf.py:18 +#: main/conf.py:18 msgid "Enable capturing activity for the Tower activity stream." msgstr "" -#: awx/main/conf.py:19 awx/main/conf.py:29 awx/main/conf.py:39 -#: awx/main/conf.py:48 awx/main/conf.py:60 awx/main/conf.py:78 -#: awx/main/conf.py:103 +#: main/conf.py:19 main/conf.py:29 main/conf.py:39 main/conf.py:48 +#: main/conf.py:60 main/conf.py:78 main/conf.py:103 msgid "System" msgstr "" -#: awx/main/conf.py:27 +#: main/conf.py:27 msgid "Enable Activity Stream for Inventory Sync" msgstr "" -#: awx/main/conf.py:28 +#: main/conf.py:28 msgid "" "Enable capturing activity for the Tower activity stream when running " "inventory sync." msgstr "" -#: awx/main/conf.py:37 +#: main/conf.py:37 msgid "All Users Visible to Organization Admins" msgstr "" -#: awx/main/conf.py:38 +#: main/conf.py:38 msgid "" "Controls whether any Organization Admin can view all users, even those not " "associated with their Organization." msgstr "" -#: awx/main/conf.py:46 +#: main/conf.py:46 msgid "Enable Tower Administrator Alerts" msgstr "" -#: awx/main/conf.py:47 +#: main/conf.py:47 msgid "" "Allow Tower to email Admin users for system events that may require " "attention." msgstr "" -#: awx/main/conf.py:57 +#: main/conf.py:57 msgid "Base URL of the Tower host" msgstr "" -#: awx/main/conf.py:58 +#: main/conf.py:58 msgid "" "This setting is used by services like notifications to render a valid url to " "the Tower host." msgstr "" -#: awx/main/conf.py:67 +#: main/conf.py:67 msgid "Remote Host Headers" msgstr "" -#: awx/main/conf.py:68 +#: main/conf.py:68 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 " @@ -1056,1459 +1064,1615 @@ msgid "" "REMOTE_HOST_HEADERS = ['HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR', 'REMOTE_HOST']" msgstr "" -#: awx/main/conf.py:99 +#: main/conf.py:99 msgid "Tower License" msgstr "" -#: awx/main/conf.py:100 +#: main/conf.py:100 msgid "" "The license controls which features and functionality are enabled in Tower. " "Use /api/v1/config/ to update or change the license." msgstr "" -#: awx/main/conf.py:110 +#: main/conf.py:110 msgid "Ansible Modules Allowed for Ad Hoc Jobs" msgstr "" -#: awx/main/conf.py:111 +#: main/conf.py:111 msgid "List of modules allowed to be used by ad-hoc jobs." msgstr "" -#: awx/main/conf.py:112 awx/main/conf.py:121 awx/main/conf.py:130 -#: awx/main/conf.py:139 awx/main/conf.py:148 awx/main/conf.py:158 -#: awx/main/conf.py:168 awx/main/conf.py:178 awx/main/conf.py:187 -#: awx/main/conf.py:199 awx/main/conf.py:211 awx/main/conf.py:223 +#: main/conf.py:112 main/conf.py:121 main/conf.py:130 main/conf.py:139 +#: main/conf.py:148 main/conf.py:158 main/conf.py:168 main/conf.py:178 +#: main/conf.py:187 main/conf.py:199 main/conf.py:211 main/conf.py:223 msgid "Jobs" msgstr "" -#: awx/main/conf.py:119 +#: main/conf.py:119 msgid "Enable job isolation" msgstr "" -#: awx/main/conf.py:120 +#: main/conf.py:120 msgid "" "Isolates an Ansible job from protected parts of the Tower system to prevent " "exposing sensitive information." msgstr "" -#: awx/main/conf.py:128 +#: main/conf.py:128 msgid "Job isolation execution path" msgstr "" -#: awx/main/conf.py:129 +#: main/conf.py:129 msgid "" "Create temporary working directories for isolated jobs in this location." msgstr "" -#: awx/main/conf.py:137 +#: main/conf.py:137 msgid "Paths to hide from isolated jobs" msgstr "" -#: awx/main/conf.py:138 +#: main/conf.py:138 msgid "Additional paths to hide from isolated processes." msgstr "" -#: awx/main/conf.py:146 +#: main/conf.py:146 msgid "Paths to expose to isolated jobs" msgstr "" -#: awx/main/conf.py:147 +#: main/conf.py:147 msgid "" "Whitelist of paths that would otherwise be hidden to expose to isolated jobs." msgstr "" -#: awx/main/conf.py:156 +#: main/conf.py:156 msgid "Standard Output Maximum Display Size" msgstr "" -#: awx/main/conf.py:157 +#: main/conf.py:157 msgid "" "Maximum Size of Standard Output in bytes to display before requiring the " "output be downloaded." msgstr "" -#: awx/main/conf.py:166 +#: main/conf.py:166 msgid "Job Event Standard Output Maximum Display Size" msgstr "" -#: awx/main/conf.py:167 +#: main/conf.py:167 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:176 +#: main/conf.py:176 msgid "Maximum Scheduled Jobs" msgstr "" -#: awx/main/conf.py:177 +#: main/conf.py:177 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:185 +#: main/conf.py:185 msgid "Ansible Callback Plugins" msgstr "" -#: awx/main/conf.py:186 +#: main/conf.py:186 msgid "" "List of paths to search for extra callback plugins to be used when running " "jobs." msgstr "" -#: awx/main/conf.py:196 +#: main/conf.py:196 msgid "Default Job Timeout" msgstr "" -#: awx/main/conf.py:197 +#: main/conf.py:197 msgid "" "Maximum time 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:208 +#: main/conf.py:208 msgid "Default Inventory Update Timeout" msgstr "" -#: awx/main/conf.py:209 +#: main/conf.py:209 msgid "" "Maximum time 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:220 +#: main/conf.py:220 msgid "Default Project Update Timeout" msgstr "" -#: awx/main/conf.py:221 +#: main/conf.py:221 msgid "" "Maximum time 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/models/activity_stream.py:22 +#: main/conf.py:231 +msgid "Logging Aggregator Receiving Host" +msgstr "" + +#: main/conf.py:232 +msgid "External host maintain a log collector to send logs to" +msgstr "" + +#: main/conf.py:233 main/conf.py:242 main/conf.py:252 main/conf.py:261 +#: main/conf.py:271 main/conf.py:286 main/conf.py:297 main/conf.py:306 +msgid "Logging" +msgstr "" + +#: main/conf.py:240 +msgid "Logging Aggregator Receiving Port" +msgstr "" + +#: main/conf.py:241 +msgid "Port that the log collector is listening on" +msgstr "" + +#: main/conf.py:250 +msgid "Logging Aggregator Type: Logstash, Loggly, Datadog, etc" +msgstr "" + +#: main/conf.py:251 +msgid "The type of log aggregator service to format messages for" +msgstr "" + +#: main/conf.py:259 +msgid "Logging Aggregator Username to Authenticate With" +msgstr "" + +#: main/conf.py:260 +msgid "Username for Logstash or others (basic auth)" +msgstr "" + +#: main/conf.py:269 +msgid "Logging Aggregator Password to Authenticate With" +msgstr "" + +#: main/conf.py:270 +msgid "Password for Logstash or others (basic auth)" +msgstr "" + +#: main/conf.py:278 +msgid "Loggers to send data to the log aggregator from" +msgstr "" + +#: main/conf.py:279 +msgid "" +"List of loggers that will send HTTP logs to the collector, these can include " +"any or all of: \n" +"activity_stream - logs duplicate to records entered in activity stream\n" +"job_events - callback data from Ansible job events\n" +"system_tracking - data generated from scan jobs\n" +"Sending generic Tower logs must be configured through local_settings." +"pyinstead of this mechanism." +msgstr "" + +#: main/conf.py:293 +msgid "" +"Flag denoting to send individual messages for each fact in system tracking" +msgstr "" + +#: main/conf.py:294 +msgid "" +"If not set, the data from system tracking will be sent inside of a single " +"dictionary, but if set, separate requests will be sent for each package, " +"service, etc. that is found in the scan." +msgstr "" + +#: main/conf.py:304 +msgid "Flag denoting whether to use the external logger system" +msgstr "" + +#: main/conf.py:305 +msgid "" +"If not set, only normal settings data will be used to configure loggers." +msgstr "" + +#: main/models/activity_stream.py:22 msgid "Entity Created" msgstr "" -#: awx/main/models/activity_stream.py:23 +#: main/models/activity_stream.py:23 msgid "Entity Updated" msgstr "" -#: awx/main/models/activity_stream.py:24 +#: main/models/activity_stream.py:24 msgid "Entity Deleted" msgstr "" -#: awx/main/models/activity_stream.py:25 +#: main/models/activity_stream.py:25 msgid "Entity Associated with another Entity" msgstr "" -#: awx/main/models/activity_stream.py:26 +#: main/models/activity_stream.py:26 msgid "Entity was Disassociated with another Entity" msgstr "" -#: awx/main/models/ad_hoc_commands.py:96 +#: main/models/ad_hoc_commands.py:96 msgid "No valid inventory." msgstr "" -#: awx/main/models/ad_hoc_commands.py:103 awx/main/models/jobs.py:162 +#: main/models/ad_hoc_commands.py:103 main/models/jobs.py:163 msgid "You must provide a machine / SSH credential." msgstr "" -#: awx/main/models/ad_hoc_commands.py:114 -#: awx/main/models/ad_hoc_commands.py:122 +#: main/models/ad_hoc_commands.py:114 main/models/ad_hoc_commands.py:122 msgid "Invalid type for ad hoc command" msgstr "" -#: awx/main/models/ad_hoc_commands.py:117 +#: main/models/ad_hoc_commands.py:117 msgid "Unsupported module for ad hoc commands." msgstr "" -#: awx/main/models/ad_hoc_commands.py:125 +#: main/models/ad_hoc_commands.py:125 #, python-format msgid "No argument passed to %s module." msgstr "" -#: awx/main/models/ad_hoc_commands.py:220 awx/main/models/jobs.py:766 +#: main/models/ad_hoc_commands.py:220 main/models/jobs.py:767 msgid "Host Failed" msgstr "" -#: awx/main/models/ad_hoc_commands.py:221 awx/main/models/jobs.py:767 +#: main/models/ad_hoc_commands.py:221 main/models/jobs.py:768 msgid "Host OK" msgstr "" -#: awx/main/models/ad_hoc_commands.py:222 awx/main/models/jobs.py:770 +#: main/models/ad_hoc_commands.py:222 main/models/jobs.py:771 msgid "Host Unreachable" msgstr "" -#: awx/main/models/ad_hoc_commands.py:227 awx/main/models/jobs.py:769 +#: main/models/ad_hoc_commands.py:227 main/models/jobs.py:770 msgid "Host Skipped" msgstr "" -#: awx/main/models/ad_hoc_commands.py:237 awx/main/models/jobs.py:797 +#: main/models/ad_hoc_commands.py:237 main/models/jobs.py:798 msgid "Debug" msgstr "" -#: awx/main/models/ad_hoc_commands.py:238 awx/main/models/jobs.py:798 +#: main/models/ad_hoc_commands.py:238 main/models/jobs.py:799 msgid "Verbose" msgstr "" -#: awx/main/models/ad_hoc_commands.py:239 awx/main/models/jobs.py:799 +#: main/models/ad_hoc_commands.py:239 main/models/jobs.py:800 msgid "Deprecated" msgstr "" -#: awx/main/models/ad_hoc_commands.py:240 awx/main/models/jobs.py:800 +#: main/models/ad_hoc_commands.py:240 main/models/jobs.py:801 msgid "Warning" msgstr "" -#: awx/main/models/ad_hoc_commands.py:241 awx/main/models/jobs.py:801 +#: main/models/ad_hoc_commands.py:241 main/models/jobs.py:802 msgid "System Warning" msgstr "" -#: awx/main/models/ad_hoc_commands.py:242 awx/main/models/jobs.py:802 -#: awx/main/models/unified_jobs.py:62 +#: main/models/ad_hoc_commands.py:242 main/models/jobs.py:803 +#: main/models/unified_jobs.py:62 msgid "Error" msgstr "" -#: awx/main/models/base.py:45 awx/main/models/base.py:51 -#: awx/main/models/base.py:56 +#: main/models/base.py:45 main/models/base.py:51 main/models/base.py:56 msgid "Run" msgstr "" -#: awx/main/models/base.py:46 awx/main/models/base.py:52 -#: awx/main/models/base.py:57 +#: main/models/base.py:46 main/models/base.py:52 main/models/base.py:57 msgid "Check" msgstr "" -#: awx/main/models/base.py:47 +#: main/models/base.py:47 msgid "Scan" msgstr "" -#: awx/main/models/base.py:61 +#: main/models/base.py:61 msgid "Read Inventory" msgstr "" -#: awx/main/models/base.py:62 +#: main/models/base.py:62 msgid "Edit Inventory" msgstr "" -#: awx/main/models/base.py:63 +#: main/models/base.py:63 msgid "Administrate Inventory" msgstr "" -#: awx/main/models/base.py:64 +#: main/models/base.py:64 msgid "Deploy To Inventory" msgstr "" -#: awx/main/models/base.py:65 +#: main/models/base.py:65 msgid "Deploy To Inventory (Dry Run)" msgstr "" -#: awx/main/models/base.py:66 +#: main/models/base.py:66 msgid "Scan an Inventory" msgstr "" -#: awx/main/models/base.py:67 +#: main/models/base.py:67 msgid "Create a Job Template" msgstr "" -#: awx/main/models/credential.py:33 +#: main/models/credential.py:33 msgid "Machine" msgstr "" -#: awx/main/models/credential.py:34 +#: main/models/credential.py:34 msgid "Network" msgstr "" -#: awx/main/models/credential.py:35 +#: main/models/credential.py:35 msgid "Source Control" msgstr "" -#: awx/main/models/credential.py:36 +#: main/models/credential.py:36 msgid "Amazon Web Services" msgstr "" -#: awx/main/models/credential.py:37 +#: main/models/credential.py:37 msgid "Rackspace" msgstr "" -#: awx/main/models/credential.py:38 awx/main/models/inventory.py:712 +#: main/models/credential.py:38 main/models/inventory.py:713 msgid "VMware vCenter" msgstr "" -#: awx/main/models/credential.py:39 awx/main/models/inventory.py:713 +#: main/models/credential.py:39 main/models/inventory.py:714 msgid "Red Hat Satellite 6" msgstr "" -#: awx/main/models/credential.py:40 awx/main/models/inventory.py:714 +#: main/models/credential.py:40 main/models/inventory.py:715 msgid "Red Hat CloudForms" msgstr "" -#: awx/main/models/credential.py:41 awx/main/models/inventory.py:709 +#: main/models/credential.py:41 main/models/inventory.py:710 msgid "Google Compute Engine" msgstr "" -#: awx/main/models/credential.py:42 awx/main/models/inventory.py:710 +#: main/models/credential.py:42 main/models/inventory.py:711 msgid "Microsoft Azure Classic (deprecated)" msgstr "" -#: awx/main/models/credential.py:43 awx/main/models/inventory.py:711 +#: main/models/credential.py:43 main/models/inventory.py:712 msgid "Microsoft Azure Resource Manager" msgstr "" -#: awx/main/models/credential.py:44 awx/main/models/inventory.py:715 +#: main/models/credential.py:44 main/models/inventory.py:716 msgid "OpenStack" msgstr "" -#: awx/main/models/credential.py:48 +#: main/models/credential.py:48 msgid "None" msgstr "" -#: awx/main/models/credential.py:49 +#: main/models/credential.py:49 msgid "Sudo" msgstr "" -#: awx/main/models/credential.py:50 +#: main/models/credential.py:50 msgid "Su" msgstr "" -#: awx/main/models/credential.py:51 +#: main/models/credential.py:51 msgid "Pbrun" msgstr "" -#: awx/main/models/credential.py:52 +#: main/models/credential.py:52 msgid "Pfexec" msgstr "" -#: awx/main/models/credential.py:101 +#: main/models/credential.py:101 msgid "Host" msgstr "" -#: awx/main/models/credential.py:102 +#: main/models/credential.py:102 msgid "The hostname or IP address to use." msgstr "" -#: awx/main/models/credential.py:108 +#: main/models/credential.py:108 msgid "Username" msgstr "" -#: awx/main/models/credential.py:109 +#: main/models/credential.py:109 msgid "Username for this credential." msgstr "" -#: awx/main/models/credential.py:115 +#: main/models/credential.py:115 msgid "Password" msgstr "" -#: awx/main/models/credential.py:116 +#: main/models/credential.py:116 msgid "" "Password for this credential (or \"ASK\" to prompt the user for machine " "credentials)." msgstr "" -#: awx/main/models/credential.py:123 +#: main/models/credential.py:123 msgid "Security Token" msgstr "" -#: awx/main/models/credential.py:124 +#: main/models/credential.py:124 msgid "Security Token for this credential" msgstr "" -#: awx/main/models/credential.py:130 +#: main/models/credential.py:130 msgid "Project" msgstr "" -#: awx/main/models/credential.py:131 +#: main/models/credential.py:131 msgid "The identifier for the project." msgstr "" -#: awx/main/models/credential.py:137 +#: main/models/credential.py:137 msgid "Domain" msgstr "" -#: awx/main/models/credential.py:138 +#: main/models/credential.py:138 msgid "The identifier for the domain." msgstr "" -#: awx/main/models/credential.py:143 +#: main/models/credential.py:143 msgid "SSH private key" msgstr "" -#: awx/main/models/credential.py:144 +#: main/models/credential.py:144 msgid "RSA or DSA private key to be used instead of password." msgstr "" -#: awx/main/models/credential.py:150 +#: main/models/credential.py:150 msgid "SSH key unlock" msgstr "" -#: awx/main/models/credential.py:151 +#: main/models/credential.py:151 msgid "" "Passphrase to unlock SSH private key if encrypted (or \"ASK\" to prompt the " "user for machine credentials)." msgstr "" -#: awx/main/models/credential.py:159 +#: main/models/credential.py:159 msgid "Privilege escalation method." msgstr "" -#: awx/main/models/credential.py:165 +#: main/models/credential.py:165 msgid "Privilege escalation username." msgstr "" -#: awx/main/models/credential.py:171 +#: main/models/credential.py:171 msgid "Password for privilege escalation method." msgstr "" -#: awx/main/models/credential.py:177 +#: main/models/credential.py:177 msgid "Vault password (or \"ASK\" to prompt the user)." msgstr "" -#: awx/main/models/credential.py:181 +#: main/models/credential.py:181 msgid "Whether to use the authorize mechanism." msgstr "" -#: awx/main/models/credential.py:187 +#: main/models/credential.py:187 msgid "Password used by the authorize mechanism." msgstr "" -#: awx/main/models/credential.py:193 +#: main/models/credential.py:193 msgid "Client Id or Application Id for the credential" msgstr "" -#: awx/main/models/credential.py:199 +#: main/models/credential.py:199 msgid "Secret Token for this credential" msgstr "" -#: awx/main/models/credential.py:205 +#: main/models/credential.py:205 msgid "Subscription identifier for this credential" msgstr "" -#: awx/main/models/credential.py:211 +#: main/models/credential.py:211 msgid "Tenant identifier for this credential" msgstr "" -#: awx/main/models/credential.py:281 +#: main/models/credential.py:281 msgid "Host required for VMware credential." msgstr "" -#: awx/main/models/credential.py:283 +#: main/models/credential.py:283 msgid "Host required for OpenStack credential." msgstr "" -#: awx/main/models/credential.py:292 +#: main/models/credential.py:292 msgid "Access key required for AWS credential." msgstr "" -#: awx/main/models/credential.py:294 +#: main/models/credential.py:294 msgid "Username required for Rackspace credential." msgstr "" -#: awx/main/models/credential.py:297 +#: main/models/credential.py:297 msgid "Username required for VMware credential." msgstr "" -#: awx/main/models/credential.py:299 +#: main/models/credential.py:299 msgid "Username required for OpenStack credential." msgstr "" -#: awx/main/models/credential.py:305 +#: main/models/credential.py:305 msgid "Secret key required for AWS credential." msgstr "" -#: awx/main/models/credential.py:307 +#: main/models/credential.py:307 msgid "API key required for Rackspace credential." msgstr "" -#: awx/main/models/credential.py:309 +#: main/models/credential.py:309 msgid "Password required for VMware credential." msgstr "" -#: awx/main/models/credential.py:311 +#: main/models/credential.py:311 msgid "Password or API key required for OpenStack credential." msgstr "" -#: awx/main/models/credential.py:317 +#: main/models/credential.py:317 msgid "Project name required for OpenStack credential." msgstr "" -#: awx/main/models/credential.py:344 +#: main/models/credential.py:344 msgid "SSH key unlock must be set when SSH key is encrypted." msgstr "" -#: awx/main/models/credential.py:350 +#: main/models/credential.py:350 msgid "Credential cannot be assigned to both a user and team." msgstr "" -#: awx/main/models/fact.py:21 +#: main/models/fact.py:21 msgid "Host for the facts that the fact scan captured." msgstr "" -#: awx/main/models/fact.py:26 +#: main/models/fact.py:26 msgid "Date and time of the corresponding fact scan gathering time." msgstr "" -#: awx/main/models/fact.py:29 +#: main/models/fact.py:29 msgid "" "Arbitrary JSON structure of module facts captured at timestamp for a single " "host." msgstr "" -#: awx/main/models/inventory.py:45 +#: main/models/inventory.py:45 msgid "inventories" msgstr "" -#: awx/main/models/inventory.py:52 +#: main/models/inventory.py:52 msgid "Organization containing this inventory." msgstr "" -#: awx/main/models/inventory.py:58 +#: main/models/inventory.py:58 msgid "Inventory variables in JSON or YAML format." msgstr "" -#: awx/main/models/inventory.py:63 +#: main/models/inventory.py:63 msgid "Flag indicating whether any hosts in this inventory have failed." msgstr "" -#: awx/main/models/inventory.py:68 +#: main/models/inventory.py:68 msgid "Total number of hosts in this inventory." msgstr "" -#: awx/main/models/inventory.py:73 +#: main/models/inventory.py:73 msgid "Number of hosts in this inventory with active failures." msgstr "" -#: awx/main/models/inventory.py:78 +#: main/models/inventory.py:78 msgid "Total number of groups in this inventory." msgstr "" -#: awx/main/models/inventory.py:83 +#: main/models/inventory.py:83 msgid "Number of groups in this inventory with active failures." msgstr "" -#: awx/main/models/inventory.py:88 +#: main/models/inventory.py:88 msgid "" "Flag indicating whether this inventory has any external inventory sources." msgstr "" -#: awx/main/models/inventory.py:93 +#: main/models/inventory.py:93 msgid "" "Total number of external inventory sources configured within this inventory." msgstr "" -#: awx/main/models/inventory.py:98 +#: main/models/inventory.py:98 msgid "Number of external inventory sources in this inventory with failures." msgstr "" -#: awx/main/models/inventory.py:339 +#: main/models/inventory.py:339 msgid "Is this host online and available for running jobs?" msgstr "" -#: awx/main/models/inventory.py:349 +#: main/models/inventory.py:345 +msgid "" +"The value used by the remote inventory source to uniquely identify the host" +msgstr "" + +#: main/models/inventory.py:350 msgid "Host variables in JSON or YAML format." msgstr "" -#: awx/main/models/inventory.py:371 +#: main/models/inventory.py:372 msgid "Flag indicating whether the last job failed for this host." msgstr "" -#: awx/main/models/inventory.py:376 +#: main/models/inventory.py:377 msgid "" "Flag indicating whether this host was created/updated from any external " "inventory sources." msgstr "" -#: awx/main/models/inventory.py:382 +#: main/models/inventory.py:383 msgid "Inventory source(s) that created or modified this host." msgstr "" -#: awx/main/models/inventory.py:473 +#: main/models/inventory.py:474 msgid "Group variables in JSON or YAML format." msgstr "" -#: awx/main/models/inventory.py:479 +#: main/models/inventory.py:480 msgid "Hosts associated directly with this group." msgstr "" -#: awx/main/models/inventory.py:484 +#: main/models/inventory.py:485 msgid "Total number of hosts directly or indirectly in this group." msgstr "" -#: awx/main/models/inventory.py:489 +#: main/models/inventory.py:490 msgid "Flag indicating whether this group has any hosts with active failures." msgstr "" -#: awx/main/models/inventory.py:494 +#: main/models/inventory.py:495 msgid "Number of hosts in this group with active failures." msgstr "" -#: awx/main/models/inventory.py:499 +#: main/models/inventory.py:500 msgid "Total number of child groups contained within this group." msgstr "" -#: awx/main/models/inventory.py:504 +#: main/models/inventory.py:505 msgid "Number of child groups within this group that have active failures." msgstr "" -#: awx/main/models/inventory.py:509 +#: main/models/inventory.py:510 msgid "" "Flag indicating whether this group was created/updated from any external " "inventory sources." msgstr "" -#: awx/main/models/inventory.py:515 +#: main/models/inventory.py:516 msgid "Inventory source(s) that created or modified this group." msgstr "" -#: awx/main/models/inventory.py:705 awx/main/models/projects.py:42 -#: awx/main/models/unified_jobs.py:383 +#: main/models/inventory.py:706 main/models/projects.py:42 +#: main/models/unified_jobs.py:386 msgid "Manual" msgstr "" -#: awx/main/models/inventory.py:706 +#: main/models/inventory.py:707 msgid "Local File, Directory or Script" msgstr "" -#: awx/main/models/inventory.py:707 +#: main/models/inventory.py:708 msgid "Rackspace Cloud Servers" msgstr "" -#: awx/main/models/inventory.py:708 +#: main/models/inventory.py:709 msgid "Amazon EC2" msgstr "" -#: awx/main/models/inventory.py:716 +#: main/models/inventory.py:717 msgid "Custom Script" msgstr "" -#: awx/main/models/inventory.py:827 +#: main/models/inventory.py:828 msgid "Inventory source variables in YAML or JSON format." msgstr "" -#: awx/main/models/inventory.py:846 +#: main/models/inventory.py:847 msgid "" "Comma-separated list of filter expressions (EC2 only). Hosts are imported " "when ANY of the filters match." msgstr "" -#: awx/main/models/inventory.py:852 +#: main/models/inventory.py:853 msgid "Limit groups automatically created from inventory source (EC2 only)." msgstr "" -#: awx/main/models/inventory.py:856 +#: main/models/inventory.py:857 msgid "Overwrite local groups and hosts from remote inventory source." msgstr "" -#: awx/main/models/inventory.py:860 +#: main/models/inventory.py:861 msgid "Overwrite local variables from remote inventory source." msgstr "" -#: awx/main/models/inventory.py:892 +#: main/models/inventory.py:893 msgid "Availability Zone" msgstr "" -#: awx/main/models/inventory.py:893 +#: main/models/inventory.py:894 msgid "Image ID" msgstr "" -#: awx/main/models/inventory.py:894 +#: main/models/inventory.py:895 msgid "Instance ID" msgstr "" -#: awx/main/models/inventory.py:895 +#: main/models/inventory.py:896 msgid "Instance Type" msgstr "" -#: awx/main/models/inventory.py:896 +#: main/models/inventory.py:897 msgid "Key Name" msgstr "" -#: awx/main/models/inventory.py:897 +#: main/models/inventory.py:898 msgid "Region" msgstr "" -#: awx/main/models/inventory.py:898 +#: main/models/inventory.py:899 msgid "Security Group" msgstr "" -#: awx/main/models/inventory.py:899 +#: main/models/inventory.py:900 msgid "Tags" msgstr "" -#: awx/main/models/inventory.py:900 +#: main/models/inventory.py:901 msgid "VPC ID" msgstr "" -#: awx/main/models/inventory.py:901 +#: main/models/inventory.py:902 msgid "Tag None" msgstr "" -#: awx/main/models/inventory.py:972 +#: main/models/inventory.py:973 #, python-format msgid "" "Cloud-based inventory sources (such as %s) require credentials for the " "matching cloud service." msgstr "" -#: awx/main/models/inventory.py:979 +#: main/models/inventory.py:980 msgid "Credential is required for a cloud source." msgstr "" -#: awx/main/models/inventory.py:1004 +#: main/models/inventory.py:1005 #, python-format msgid "Invalid %(source)s region%(plural)s: %(region)s" msgstr "" -#: awx/main/models/inventory.py:1030 +#: main/models/inventory.py:1031 #, python-format msgid "Invalid filter expression%(plural)s: %(filter)s" msgstr "" -#: awx/main/models/inventory.py:1049 +#: main/models/inventory.py:1050 #, python-format msgid "Invalid group by choice%(plural)s: %(choice)s" msgstr "" -#: awx/main/models/inventory.py:1197 +#: main/models/inventory.py:1198 #, python-format msgid "" "Unable to configure this item for cloud sync. It is already managed by %s." msgstr "" -#: awx/main/models/inventory.py:1292 +#: main/models/inventory.py:1293 msgid "Inventory script contents" msgstr "" -#: awx/main/models/inventory.py:1297 +#: main/models/inventory.py:1298 msgid "Organization owning this inventory script" msgstr "" -#: awx/main/models/jobs.py:170 +#: main/models/jobs.py:171 msgid "You must provide a network credential." msgstr "" -#: awx/main/models/jobs.py:178 +#: main/models/jobs.py:179 msgid "" "Must provide a credential for a cloud provider, such as Amazon Web Services " "or Rackspace." msgstr "" -#: awx/main/models/jobs.py:270 +#: main/models/jobs.py:271 msgid "Job Template must provide 'inventory' or allow prompting for it." msgstr "" -#: awx/main/models/jobs.py:274 +#: main/models/jobs.py:275 msgid "Job Template must provide 'credential' or allow prompting for it." msgstr "" -#: awx/main/models/jobs.py:363 +#: main/models/jobs.py:364 msgid "Cannot override job_type to or from a scan job." msgstr "" -#: awx/main/models/jobs.py:366 +#: main/models/jobs.py:367 msgid "Inventory cannot be changed at runtime for scan jobs." msgstr "" -#: awx/main/models/jobs.py:432 awx/main/models/projects.py:235 +#: main/models/jobs.py:433 main/models/projects.py:243 msgid "SCM Revision" msgstr "" -#: awx/main/models/jobs.py:433 +#: main/models/jobs.py:434 msgid "The SCM Revision from the Project used for this job, if available" msgstr "" -#: awx/main/models/jobs.py:441 +#: main/models/jobs.py:442 msgid "" "The SCM Refresh task used to make sure the playbooks were available for the " "job run" msgstr "" -#: awx/main/models/jobs.py:665 +#: main/models/jobs.py:666 msgid "job host summaries" msgstr "" -#: awx/main/models/jobs.py:768 +#: main/models/jobs.py:769 msgid "Host Failure" msgstr "" -#: awx/main/models/jobs.py:771 awx/main/models/jobs.py:785 +#: main/models/jobs.py:772 main/models/jobs.py:786 msgid "No Hosts Remaining" msgstr "" -#: awx/main/models/jobs.py:772 +#: main/models/jobs.py:773 msgid "Host Polling" msgstr "" -#: awx/main/models/jobs.py:773 +#: main/models/jobs.py:774 msgid "Host Async OK" msgstr "" -#: awx/main/models/jobs.py:774 +#: main/models/jobs.py:775 msgid "Host Async Failure" msgstr "" -#: awx/main/models/jobs.py:775 +#: main/models/jobs.py:776 msgid "Item OK" msgstr "" -#: awx/main/models/jobs.py:776 +#: main/models/jobs.py:777 msgid "Item Failed" msgstr "" -#: awx/main/models/jobs.py:777 +#: main/models/jobs.py:778 msgid "Item Skipped" msgstr "" -#: awx/main/models/jobs.py:778 +#: main/models/jobs.py:779 msgid "Host Retry" msgstr "" -#: awx/main/models/jobs.py:780 +#: main/models/jobs.py:781 msgid "File Difference" msgstr "" -#: awx/main/models/jobs.py:781 +#: main/models/jobs.py:782 msgid "Playbook Started" msgstr "" -#: awx/main/models/jobs.py:782 +#: main/models/jobs.py:783 msgid "Running Handlers" msgstr "" -#: awx/main/models/jobs.py:783 +#: main/models/jobs.py:784 msgid "Including File" msgstr "" -#: awx/main/models/jobs.py:784 +#: main/models/jobs.py:785 msgid "No Hosts Matched" msgstr "" -#: awx/main/models/jobs.py:786 +#: main/models/jobs.py:787 msgid "Task Started" msgstr "" -#: awx/main/models/jobs.py:788 +#: main/models/jobs.py:789 msgid "Variables Prompted" msgstr "" -#: awx/main/models/jobs.py:789 +#: main/models/jobs.py:790 msgid "Gathering Facts" msgstr "" -#: awx/main/models/jobs.py:790 +#: main/models/jobs.py:791 msgid "internal: on Import for Host" msgstr "" -#: awx/main/models/jobs.py:791 +#: main/models/jobs.py:792 msgid "internal: on Not Import for Host" msgstr "" -#: awx/main/models/jobs.py:792 +#: main/models/jobs.py:793 msgid "Play Started" msgstr "" -#: awx/main/models/jobs.py:793 +#: main/models/jobs.py:794 msgid "Playbook Complete" msgstr "" -#: awx/main/models/jobs.py:1237 +#: main/models/jobs.py:1240 msgid "Remove jobs older than a certain number of days" msgstr "" -#: awx/main/models/jobs.py:1238 +#: main/models/jobs.py:1241 msgid "Remove activity stream entries older than a certain number of days" msgstr "" -#: awx/main/models/jobs.py:1239 +#: main/models/jobs.py:1242 msgid "Purge and/or reduce the granularity of system tracking data" msgstr "" -#: awx/main/models/label.py:29 +#: main/models/label.py:29 msgid "Organization this label belongs to." msgstr "" -#: awx/main/models/notifications.py:31 +#: main/models/notifications.py:31 msgid "Email" msgstr "" -#: awx/main/models/notifications.py:32 +#: main/models/notifications.py:32 msgid "Slack" msgstr "" -#: awx/main/models/notifications.py:33 +#: main/models/notifications.py:33 msgid "Twilio" msgstr "" -#: awx/main/models/notifications.py:34 +#: main/models/notifications.py:34 msgid "Pagerduty" msgstr "" -#: awx/main/models/notifications.py:35 +#: main/models/notifications.py:35 msgid "HipChat" msgstr "" -#: awx/main/models/notifications.py:36 +#: main/models/notifications.py:36 msgid "Webhook" msgstr "" -#: awx/main/models/notifications.py:37 +#: main/models/notifications.py:37 msgid "IRC" msgstr "" -#: awx/main/models/notifications.py:127 awx/main/models/unified_jobs.py:57 +#: main/models/notifications.py:127 main/models/unified_jobs.py:57 msgid "Pending" msgstr "" -#: awx/main/models/notifications.py:128 awx/main/models/unified_jobs.py:60 +#: main/models/notifications.py:128 main/models/unified_jobs.py:60 msgid "Successful" msgstr "" -#: awx/main/models/notifications.py:129 awx/main/models/unified_jobs.py:61 +#: main/models/notifications.py:129 main/models/unified_jobs.py:61 msgid "Failed" msgstr "" -#: awx/main/models/organization.py:157 +#: main/models/organization.py:157 msgid "Execute Commands on the Inventory" msgstr "" -#: awx/main/models/organization.py:211 +#: main/models/organization.py:211 msgid "Token not invalidated" msgstr "" -#: awx/main/models/organization.py:212 +#: main/models/organization.py:212 msgid "Token is expired" msgstr "" -#: awx/main/models/organization.py:213 +#: main/models/organization.py:213 msgid "Maximum per-user sessions reached" msgstr "" -#: awx/main/models/organization.py:216 +#: main/models/organization.py:216 msgid "Invalid token" msgstr "" -#: awx/main/models/organization.py:233 +#: main/models/organization.py:233 msgid "Reason the auth token was invalidated." msgstr "" -#: awx/main/models/organization.py:272 +#: main/models/organization.py:272 msgid "Invalid reason specified" msgstr "" -#: awx/main/models/projects.py:43 +#: main/models/projects.py:43 msgid "Git" msgstr "" -#: awx/main/models/projects.py:44 +#: main/models/projects.py:44 msgid "Mercurial" msgstr "" -#: awx/main/models/projects.py:45 +#: main/models/projects.py:45 msgid "Subversion" msgstr "" -#: awx/main/models/projects.py:71 +#: main/models/projects.py:71 msgid "" "Local path (relative to PROJECTS_ROOT) containing playbooks and related " "files for this project." msgstr "" -#: awx/main/models/projects.py:80 +#: main/models/projects.py:80 msgid "SCM Type" msgstr "" -#: awx/main/models/projects.py:86 +#: main/models/projects.py:81 +msgid "Specifies the source control system used to store the project." +msgstr "" + +#: main/models/projects.py:87 msgid "SCM URL" msgstr "" -#: awx/main/models/projects.py:92 +#: main/models/projects.py:88 +msgid "The location where the project is stored." +msgstr "" + +#: main/models/projects.py:94 msgid "SCM Branch" msgstr "" -#: awx/main/models/projects.py:93 +#: main/models/projects.py:95 msgid "Specific branch, tag or commit to checkout." msgstr "" -#: awx/main/models/projects.py:125 +#: main/models/projects.py:99 +msgid "Discard any local changes before syncing the project." +msgstr "" + +#: main/models/projects.py:103 +msgid "Delete the project before syncing." +msgstr "" + +#: main/models/projects.py:116 +msgid "The amount of time to run before the task is canceled." +msgstr "" + +#: main/models/projects.py:130 msgid "Invalid SCM URL." msgstr "" -#: awx/main/models/projects.py:128 +#: main/models/projects.py:133 msgid "SCM URL is required." msgstr "" -#: awx/main/models/projects.py:137 +#: main/models/projects.py:142 msgid "Credential kind must be 'scm'." msgstr "" -#: awx/main/models/projects.py:152 +#: main/models/projects.py:157 msgid "Invalid credential." msgstr "" -#: awx/main/models/projects.py:236 +#: main/models/projects.py:229 +msgid "Update the project when a job is launched that uses the project." +msgstr "" + +#: main/models/projects.py:234 +msgid "" +"The number of seconds after the last project update ran that a newproject " +"update will be launched as a job dependency." +msgstr "" + +#: main/models/projects.py:244 msgid "The last revision fetched by a project update" msgstr "" -#: awx/main/models/projects.py:243 +#: main/models/projects.py:251 msgid "Playbook Files" msgstr "" -#: awx/main/models/projects.py:244 +#: main/models/projects.py:252 msgid "List of playbooks found in the project" msgstr "" -#: awx/main/models/rbac.py:122 +#: main/models/rbac.py:122 msgid "roles" msgstr "" -#: awx/main/models/rbac.py:435 +#: main/models/rbac.py:438 msgid "role_ancestors" msgstr "" -#: awx/main/models/unified_jobs.py:56 +#: main/models/schedules.py:69 +msgid "Enables processing of this schedule by Tower." +msgstr "" + +#: main/models/schedules.py:75 +msgid "The first occurrence of the schedule occurs on or after this time." +msgstr "" + +#: main/models/schedules.py:81 +msgid "" +"The last occurrence of the schedule occurs before this time, aftewards the " +"schedule expires." +msgstr "" + +#: main/models/schedules.py:85 +msgid "A value representing the schedules iCal recurrence rule." +msgstr "" + +#: main/models/schedules.py:91 +msgid "The next time that the scheduled action will run." +msgstr "" + +#: main/models/unified_jobs.py:56 msgid "New" msgstr "" -#: awx/main/models/unified_jobs.py:58 +#: main/models/unified_jobs.py:58 msgid "Waiting" msgstr "" -#: awx/main/models/unified_jobs.py:59 +#: main/models/unified_jobs.py:59 msgid "Running" msgstr "" -#: awx/main/models/unified_jobs.py:63 +#: main/models/unified_jobs.py:63 msgid "Canceled" msgstr "" -#: awx/main/models/unified_jobs.py:67 +#: main/models/unified_jobs.py:67 msgid "Never Updated" msgstr "" -#: awx/main/models/unified_jobs.py:71 awx/ui/templates/ui/index.html:85 -#: awx/ui/templates/ui/index.html.py:104 +#: main/models/unified_jobs.py:71 ui/templates/ui/index.html:85 +#: ui/templates/ui/index.html.py:104 msgid "OK" msgstr "" -#: awx/main/models/unified_jobs.py:72 +#: main/models/unified_jobs.py:72 msgid "Missing" msgstr "" -#: awx/main/models/unified_jobs.py:76 +#: main/models/unified_jobs.py:76 msgid "No External Source" msgstr "" -#: awx/main/models/unified_jobs.py:83 +#: main/models/unified_jobs.py:83 msgid "Updating" msgstr "" -#: awx/main/models/unified_jobs.py:384 +#: main/models/unified_jobs.py:387 msgid "Relaunch" msgstr "" -#: awx/main/models/unified_jobs.py:385 +#: main/models/unified_jobs.py:388 msgid "Callback" msgstr "" -#: awx/main/models/unified_jobs.py:386 +#: main/models/unified_jobs.py:389 msgid "Scheduled" msgstr "" -#: awx/main/models/unified_jobs.py:387 +#: main/models/unified_jobs.py:390 msgid "Dependency" msgstr "" -#: awx/main/models/unified_jobs.py:388 +#: main/models/unified_jobs.py:391 msgid "Workflow" msgstr "" -#: awx/main/notifications/base.py:17 awx/main/notifications/email_backend.py:28 +#: main/models/unified_jobs.py:437 +msgid "The Tower node the job executed on." +msgstr "" + +#: main/models/unified_jobs.py:463 +msgid "The date and time the job was queued for starting." +msgstr "" + +#: main/models/unified_jobs.py:469 +msgid "The date and time the job finished execution." +msgstr "" + +#: main/models/unified_jobs.py:475 +msgid "Elapsed time in seconds that the job ran." +msgstr "" + +#: main/models/unified_jobs.py:497 +msgid "" +"A status field to indicate the state of the job if it wasn't able to run and " +"capture stdout" +msgstr "" + +#: main/notifications/base.py:17 main/notifications/email_backend.py:28 msgid "" "{} #{} had status {} on Ansible Tower, view details at {}\n" "\n" msgstr "" -#: awx/main/notifications/hipchat_backend.py:46 +#: main/notifications/hipchat_backend.py:46 msgid "Error sending messages: {}" msgstr "" -#: awx/main/notifications/hipchat_backend.py:48 +#: main/notifications/hipchat_backend.py:48 msgid "Error sending message to hipchat: {}" msgstr "" -#: awx/main/notifications/irc_backend.py:54 +#: main/notifications/irc_backend.py:54 msgid "Exception connecting to irc server: {}" msgstr "" -#: awx/main/notifications/pagerduty_backend.py:39 +#: main/notifications/pagerduty_backend.py:39 msgid "Exception connecting to PagerDuty: {}" msgstr "" -#: awx/main/notifications/pagerduty_backend.py:48 -#: awx/main/notifications/slack_backend.py:52 -#: awx/main/notifications/twilio_backend.py:46 +#: main/notifications/pagerduty_backend.py:48 +#: main/notifications/slack_backend.py:52 +#: main/notifications/twilio_backend.py:46 msgid "Exception sending messages: {}" msgstr "" -#: awx/main/notifications/twilio_backend.py:36 +#: main/notifications/twilio_backend.py:36 msgid "Exception connecting to Twilio: {}" msgstr "" -#: awx/main/notifications/webhook_backend.py:38 -#: awx/main/notifications/webhook_backend.py:40 +#: main/notifications/webhook_backend.py:38 +#: main/notifications/webhook_backend.py:40 msgid "Error sending notification webhook: {}" msgstr "" -#: awx/main/tasks.py:119 +#: main/tasks.py:139 msgid "Ansible Tower host usage over 90%" msgstr "" -#: awx/main/tasks.py:124 +#: main/tasks.py:144 msgid "Ansible Tower license will expire soon" msgstr "" -#: awx/main/tasks.py:177 +#: main/tasks.py:197 msgid "status_str must be either succeeded or failed" msgstr "" -#: awx/main/utils.py:88 +#: main/utils/common.py:88 #, python-format msgid "Unable to convert \"%s\" to boolean" msgstr "" -#: awx/main/utils.py:242 +#: main/utils/common.py:242 #, python-format msgid "Unsupported SCM type \"%s\"" msgstr "" -#: awx/main/utils.py:249 awx/main/utils.py:261 awx/main/utils.py:280 +#: main/utils/common.py:249 main/utils/common.py:261 main/utils/common.py:280 #, python-format msgid "Invalid %s URL" msgstr "" -#: awx/main/utils.py:251 awx/main/utils.py:289 +#: main/utils/common.py:251 main/utils/common.py:289 #, python-format msgid "Unsupported %s URL" msgstr "" -#: awx/main/utils.py:291 +#: main/utils/common.py:291 #, python-format msgid "Unsupported host \"%s\" for file:// URL" msgstr "" -#: awx/main/utils.py:293 +#: main/utils/common.py:293 #, python-format msgid "Host is required for %s URL" msgstr "" -#: awx/main/utils.py:311 +#: main/utils/common.py:311 #, python-format msgid "Username must be \"git\" for SSH access to %s." msgstr "" -#: awx/main/utils.py:317 +#: main/utils/common.py:317 #, python-format msgid "Username must be \"hg\" for SSH access to %s." msgstr "" -#: awx/main/validators.py:60 +#: main/validators.py:60 #, python-format msgid "Invalid certificate or key: %r..." msgstr "" -#: awx/main/validators.py:74 +#: main/validators.py:74 #, python-format msgid "Invalid private key: unsupported type \"%s\"" msgstr "" -#: awx/main/validators.py:78 +#: main/validators.py:78 #, python-format msgid "Unsupported PEM object type: \"%s\"" msgstr "" -#: awx/main/validators.py:103 +#: main/validators.py:103 msgid "Invalid base64-encoded data" msgstr "" -#: awx/main/validators.py:122 +#: main/validators.py:122 msgid "Exactly one private key is required." msgstr "" -#: awx/main/validators.py:124 +#: main/validators.py:124 msgid "At least one private key is required." msgstr "" -#: awx/main/validators.py:126 +#: main/validators.py:126 #, python-format msgid "" "At least %(min_keys)d private keys are required, only %(key_count)d provided." msgstr "" -#: awx/main/validators.py:129 +#: main/validators.py:129 #, python-format msgid "Only one private key is allowed, %(key_count)d provided." msgstr "" -#: awx/main/validators.py:131 +#: main/validators.py:131 #, python-format msgid "" "No more than %(max_keys)d private keys are allowed, %(key_count)d provided." msgstr "" -#: awx/main/validators.py:136 +#: main/validators.py:136 msgid "Exactly one certificate is required." msgstr "" -#: awx/main/validators.py:138 +#: main/validators.py:138 msgid "At least one certificate is required." msgstr "" -#: awx/main/validators.py:140 +#: main/validators.py:140 #, python-format msgid "" "At least %(min_certs)d certificates are required, only %(cert_count)d " "provided." msgstr "" -#: awx/main/validators.py:143 +#: main/validators.py:143 #, python-format msgid "Only one certificate is allowed, %(cert_count)d provided." msgstr "" -#: awx/main/validators.py:145 +#: main/validators.py:145 #, python-format msgid "" "No more than %(max_certs)d certificates are allowed, %(cert_count)d provided." msgstr "" -#: awx/main/views.py:20 +#: main/views.py:20 msgid "API Error" msgstr "" -#: awx/main/views.py:49 +#: main/views.py:49 msgid "Bad Request" msgstr "" -#: awx/main/views.py:50 +#: main/views.py:50 msgid "The request could not be understood by the server." msgstr "" -#: awx/main/views.py:57 +#: main/views.py:57 msgid "Forbidden" msgstr "" -#: awx/main/views.py:58 +#: main/views.py:58 msgid "You don't have permission to access the requested resource." msgstr "" -#: awx/main/views.py:65 +#: main/views.py:65 msgid "Not Found" msgstr "" -#: awx/main/views.py:66 +#: main/views.py:66 msgid "The requested resource could not be found." msgstr "" -#: awx/main/views.py:73 +#: main/views.py:73 msgid "Server Error" msgstr "" -#: awx/main/views.py:74 +#: main/views.py:74 msgid "A server error has occurred." msgstr "" -#: awx/settings/defaults.py:593 +#: settings/defaults.py:600 msgid "Chicago" msgstr "" -#: awx/settings/defaults.py:594 +#: settings/defaults.py:601 msgid "Dallas/Ft. Worth" msgstr "" -#: awx/settings/defaults.py:595 +#: settings/defaults.py:602 msgid "Northern Virginia" msgstr "" -#: awx/settings/defaults.py:596 +#: settings/defaults.py:603 msgid "London" msgstr "" -#: awx/settings/defaults.py:597 +#: settings/defaults.py:604 msgid "Sydney" msgstr "" -#: awx/settings/defaults.py:598 +#: settings/defaults.py:605 msgid "Hong Kong" msgstr "" -#: awx/settings/defaults.py:625 +#: settings/defaults.py:632 msgid "US East (Northern Virginia)" msgstr "" -#: awx/settings/defaults.py:626 +#: settings/defaults.py:633 msgid "US East (Ohio)" msgstr "" -#: awx/settings/defaults.py:627 +#: settings/defaults.py:634 msgid "US West (Oregon)" msgstr "" -#: awx/settings/defaults.py:628 +#: settings/defaults.py:635 msgid "US West (Northern California)" msgstr "" -#: awx/settings/defaults.py:629 +#: settings/defaults.py:636 msgid "EU (Frankfurt)" msgstr "" -#: awx/settings/defaults.py:630 +#: settings/defaults.py:637 msgid "EU (Ireland)" msgstr "" -#: awx/settings/defaults.py:631 +#: settings/defaults.py:638 msgid "Asia Pacific (Singapore)" msgstr "" -#: awx/settings/defaults.py:632 +#: settings/defaults.py:639 msgid "Asia Pacific (Sydney)" msgstr "" -#: awx/settings/defaults.py:633 +#: settings/defaults.py:640 msgid "Asia Pacific (Tokyo)" msgstr "" -#: awx/settings/defaults.py:634 +#: settings/defaults.py:641 msgid "Asia Pacific (Seoul)" msgstr "" -#: awx/settings/defaults.py:635 +#: settings/defaults.py:642 msgid "Asia Pacific (Mumbai)" msgstr "" -#: awx/settings/defaults.py:636 +#: settings/defaults.py:643 msgid "South America (Sao Paulo)" msgstr "" -#: awx/settings/defaults.py:637 +#: settings/defaults.py:644 msgid "US West (GovCloud)" msgstr "" -#: awx/settings/defaults.py:638 +#: settings/defaults.py:645 msgid "China (Beijing)" msgstr "" -#: awx/settings/defaults.py:687 +#: settings/defaults.py:694 msgid "US East (B)" msgstr "" -#: awx/settings/defaults.py:688 +#: settings/defaults.py:695 msgid "US East (C)" msgstr "" -#: awx/settings/defaults.py:689 +#: settings/defaults.py:696 msgid "US East (D)" msgstr "" -#: awx/settings/defaults.py:690 +#: settings/defaults.py:697 msgid "US Central (A)" msgstr "" -#: awx/settings/defaults.py:691 +#: settings/defaults.py:698 msgid "US Central (B)" msgstr "" -#: awx/settings/defaults.py:692 +#: settings/defaults.py:699 msgid "US Central (C)" msgstr "" -#: awx/settings/defaults.py:693 +#: settings/defaults.py:700 msgid "US Central (F)" msgstr "" -#: awx/settings/defaults.py:694 +#: settings/defaults.py:701 msgid "Europe West (B)" msgstr "" -#: awx/settings/defaults.py:695 +#: settings/defaults.py:702 msgid "Europe West (C)" msgstr "" -#: awx/settings/defaults.py:696 +#: settings/defaults.py:703 msgid "Europe West (D)" msgstr "" -#: awx/settings/defaults.py:697 +#: settings/defaults.py:704 msgid "Asia East (A)" msgstr "" -#: awx/settings/defaults.py:698 +#: settings/defaults.py:705 msgid "Asia East (B)" msgstr "" -#: awx/settings/defaults.py:699 +#: settings/defaults.py:706 msgid "Asia East (C)" msgstr "" -#: awx/settings/defaults.py:723 +#: settings/defaults.py:730 msgid "US Central" msgstr "" -#: awx/settings/defaults.py:724 +#: settings/defaults.py:731 msgid "US East" msgstr "" -#: awx/settings/defaults.py:725 +#: settings/defaults.py:732 msgid "US East 2" msgstr "" -#: awx/settings/defaults.py:726 +#: settings/defaults.py:733 msgid "US North Central" msgstr "" -#: awx/settings/defaults.py:727 +#: settings/defaults.py:734 msgid "US South Central" msgstr "" -#: awx/settings/defaults.py:728 +#: settings/defaults.py:735 msgid "US West" msgstr "" -#: awx/settings/defaults.py:729 +#: settings/defaults.py:736 msgid "Europe North" msgstr "" -#: awx/settings/defaults.py:730 +#: settings/defaults.py:737 msgid "Europe West" msgstr "" -#: awx/settings/defaults.py:731 +#: settings/defaults.py:738 msgid "Asia Pacific East" msgstr "" -#: awx/settings/defaults.py:732 +#: settings/defaults.py:739 msgid "Asia Pacific Southeast" msgstr "" -#: awx/settings/defaults.py:733 +#: settings/defaults.py:740 msgid "Japan East" msgstr "" -#: awx/settings/defaults.py:734 +#: settings/defaults.py:741 msgid "Japan West" msgstr "" -#: awx/settings/defaults.py:735 +#: settings/defaults.py:742 msgid "Brazil South" msgstr "" -#: awx/sso/apps.py:9 +#: sso/apps.py:9 msgid "Single Sign-On" msgstr "" -#: awx/sso/conf.py:27 +#: sso/conf.py:27 msgid "" "Mapping to organization admins/users from social auth accounts. This " "setting\n" @@ -2546,7 +2710,7 @@ msgid "" " remove_admins." msgstr "" -#: awx/sso/conf.py:76 +#: sso/conf.py:76 msgid "" "Mapping of team members (users) from social auth accounts. Keys are team\n" "names (will be created if not present). Values are dictionaries of options\n" @@ -2575,40 +2739,40 @@ msgid "" " the rules above will be removed from the team." msgstr "" -#: awx/sso/conf.py:119 +#: sso/conf.py:119 msgid "Authentication Backends" msgstr "" -#: awx/sso/conf.py:120 +#: sso/conf.py:120 msgid "" "List of authentication backends that are enabled based on license features " "and other authentication settings." msgstr "" -#: awx/sso/conf.py:133 +#: sso/conf.py:133 msgid "Social Auth Organization Map" msgstr "" -#: awx/sso/conf.py:145 +#: sso/conf.py:145 msgid "Social Auth Team Map" msgstr "" -#: awx/sso/conf.py:157 +#: sso/conf.py:157 msgid "Social Auth User Fields" msgstr "" -#: awx/sso/conf.py:158 +#: sso/conf.py:158 msgid "" "When set to an empty list `[]`, this setting prevents new user accounts from " "being created. Only users who have previously logged in using social auth or " "have a user account with a matching email address will be able to login." msgstr "" -#: awx/sso/conf.py:176 +#: sso/conf.py:176 msgid "LDAP Server URI" msgstr "" -#: awx/sso/conf.py:177 +#: sso/conf.py:177 msgid "" "URI to connect to LDAP server, such as \"ldap://ldap.example.com:389\" (non-" "SSL) or \"ldaps://ldap.example.com:636\" (SSL). Multiple LDAP servers may be " @@ -2616,19 +2780,18 @@ msgid "" "disabled if this parameter is empty." msgstr "" -#: awx/sso/conf.py:181 awx/sso/conf.py:199 awx/sso/conf.py:211 -#: awx/sso/conf.py:222 awx/sso/conf.py:238 awx/sso/conf.py:257 -#: awx/sso/conf.py:278 awx/sso/conf.py:294 awx/sso/conf.py:313 -#: awx/sso/conf.py:330 awx/sso/conf.py:345 awx/sso/conf.py:360 -#: awx/sso/conf.py:377 awx/sso/conf.py:415 awx/sso/conf.py:456 +#: sso/conf.py:181 sso/conf.py:199 sso/conf.py:211 sso/conf.py:223 +#: sso/conf.py:239 sso/conf.py:258 sso/conf.py:279 sso/conf.py:295 +#: sso/conf.py:314 sso/conf.py:331 sso/conf.py:347 sso/conf.py:362 +#: sso/conf.py:379 sso/conf.py:417 sso/conf.py:458 msgid "LDAP" msgstr "" -#: awx/sso/conf.py:193 +#: sso/conf.py:193 msgid "LDAP Bind DN" msgstr "" -#: awx/sso/conf.py:194 +#: sso/conf.py:194 msgid "" "DN (Distinguished Name) of user to bind for all search queries. Normally in " "the format \"CN=Some User,OU=Users,DC=example,DC=com\" but may also be " @@ -2636,27 +2799,27 @@ msgid "" "user account we will use to login to query LDAP for other user information." msgstr "" -#: awx/sso/conf.py:209 +#: sso/conf.py:209 msgid "LDAP Bind Password" msgstr "" -#: awx/sso/conf.py:210 +#: sso/conf.py:210 msgid "Password used to bind LDAP user account." msgstr "" -#: awx/sso/conf.py:220 +#: sso/conf.py:221 msgid "LDAP Start TLS" msgstr "" -#: awx/sso/conf.py:221 +#: sso/conf.py:222 msgid "Whether to enable TLS when the LDAP connection is not using SSL." msgstr "" -#: awx/sso/conf.py:231 +#: sso/conf.py:232 msgid "LDAP Connection Options" msgstr "" -#: awx/sso/conf.py:232 +#: sso/conf.py:233 msgid "" "Additional options to set for the LDAP connection. LDAP referrals are " "disabled by default (to prevent certain LDAP queries from hanging with AD). " @@ -2665,11 +2828,11 @@ msgid "" "values that can be set." msgstr "" -#: awx/sso/conf.py:250 +#: sso/conf.py:251 msgid "LDAP User Search" msgstr "" -#: awx/sso/conf.py:251 +#: sso/conf.py:252 msgid "" "LDAP search query to find users. Any user that matches the given pattern " "will be able to login to Tower. The user should also be mapped into an " @@ -2678,11 +2841,11 @@ msgid "" "possible. See python-ldap documentation as linked at the top of this section." msgstr "" -#: awx/sso/conf.py:272 +#: sso/conf.py:273 msgid "LDAP User DN Template" msgstr "" -#: awx/sso/conf.py:273 +#: sso/conf.py:274 msgid "" "Alternative to user search, if user DNs are all of the same format. This " "approach will be more efficient for user lookups than searching if it is " @@ -2690,11 +2853,11 @@ msgid "" "will be used instead of AUTH_LDAP_USER_SEARCH." msgstr "" -#: awx/sso/conf.py:288 +#: sso/conf.py:289 msgid "LDAP User Attribute Map" msgstr "" -#: awx/sso/conf.py:289 +#: sso/conf.py:290 msgid "" "Mapping of LDAP user schema to Tower API user attributes (key is user " "attribute name, value is LDAP attribute name). The default setting is valid " @@ -2702,54 +2865,54 @@ msgid "" "change the values (not the keys) of the dictionary/hash-table." msgstr "" -#: awx/sso/conf.py:308 +#: sso/conf.py:309 msgid "LDAP Group Search" msgstr "" -#: awx/sso/conf.py:309 +#: sso/conf.py:310 msgid "" "Users in Tower are mapped to organizations based on their membership in LDAP " "groups. This setting defines the LDAP search query to find groups. Note that " "this, unlike the user search above, does not support LDAPSearchUnion." msgstr "" -#: awx/sso/conf.py:326 +#: sso/conf.py:327 msgid "LDAP Group Type" msgstr "" -#: awx/sso/conf.py:327 +#: sso/conf.py:328 msgid "" "The group type may need to be changed based on the type of the LDAP server. " "Values are listed at: http://pythonhosted.org/django-auth-ldap/groups." "html#types-of-groups" msgstr "" -#: awx/sso/conf.py:340 +#: sso/conf.py:342 msgid "LDAP Require Group" msgstr "" -#: awx/sso/conf.py:341 +#: sso/conf.py:343 msgid "" "Group DN required to login. If specified, user must be a member of this " "group to login via LDAP. If not set, everyone in LDAP that matches the user " "search will be able to login via Tower. Only one require group is supported." msgstr "" -#: awx/sso/conf.py:356 +#: sso/conf.py:358 msgid "LDAP Deny Group" msgstr "" -#: awx/sso/conf.py:357 +#: sso/conf.py:359 msgid "" "Group DN denied from login. If specified, user will not be allowed to login " "if a member of this group. Only one deny group is supported." msgstr "" -#: awx/sso/conf.py:370 +#: sso/conf.py:372 msgid "LDAP User Flags By Group" msgstr "" -#: awx/sso/conf.py:371 +#: sso/conf.py:373 msgid "" "User profile flags updated from group membership (key is user attribute " "name, value is group DN). These are boolean fields that are matched based " @@ -2758,11 +2921,11 @@ msgid "" "false at login time based on current LDAP settings." msgstr "" -#: awx/sso/conf.py:389 +#: sso/conf.py:391 msgid "LDAP Organization Map" msgstr "" -#: awx/sso/conf.py:390 +#: sso/conf.py:392 msgid "" "Mapping between organization admins/users and LDAP groups. This controls " "what users are placed into what Tower organizations relative to their LDAP " @@ -2789,11 +2952,11 @@ msgid "" "remove_admins." msgstr "" -#: awx/sso/conf.py:438 +#: sso/conf.py:440 msgid "LDAP Team Map" msgstr "" -#: awx/sso/conf.py:439 +#: sso/conf.py:441 msgid "" "Mapping between team members (users) and LDAP groups. Keys are team names " "(will be created if not present). Values are dictionaries of options for " @@ -2812,88 +2975,87 @@ msgid "" "of the given groups will be removed from the team." msgstr "" -#: awx/sso/conf.py:482 +#: sso/conf.py:484 msgid "RADIUS Server" msgstr "" -#: awx/sso/conf.py:483 +#: sso/conf.py:485 msgid "" "Hostname/IP of RADIUS server. RADIUS authentication will be disabled if this " "setting is empty." msgstr "" -#: awx/sso/conf.py:485 awx/sso/conf.py:499 awx/sso/conf.py:511 +#: sso/conf.py:487 sso/conf.py:501 sso/conf.py:513 msgid "RADIUS" msgstr "" -#: awx/sso/conf.py:497 +#: sso/conf.py:499 msgid "RADIUS Port" msgstr "" -#: awx/sso/conf.py:498 +#: sso/conf.py:500 msgid "Port of RADIUS server." msgstr "" -#: awx/sso/conf.py:509 +#: sso/conf.py:511 msgid "RADIUS Secret" msgstr "" -#: awx/sso/conf.py:510 +#: sso/conf.py:512 msgid "Shared secret for authenticating to RADIUS server." msgstr "" -#: awx/sso/conf.py:525 +#: sso/conf.py:528 msgid "Google OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:526 +#: sso/conf.py:529 msgid "" "Create a project at https://console.developers.google.com/ to obtain an " "OAuth2 key and secret for a web application. Ensure that the Google+ API is " "enabled. Provide this URL as the callback URL for your application." msgstr "" -#: awx/sso/conf.py:530 awx/sso/conf.py:541 awx/sso/conf.py:552 -#: awx/sso/conf.py:564 awx/sso/conf.py:578 awx/sso/conf.py:590 -#: awx/sso/conf.py:602 +#: sso/conf.py:533 sso/conf.py:544 sso/conf.py:555 sso/conf.py:568 +#: sso/conf.py:582 sso/conf.py:594 sso/conf.py:606 msgid "Google OAuth2" msgstr "" -#: awx/sso/conf.py:539 +#: sso/conf.py:542 msgid "Google OAuth2 Key" msgstr "" -#: awx/sso/conf.py:540 +#: sso/conf.py:543 msgid "" "The OAuth2 key from your web application at https://console.developers." "google.com/." msgstr "" -#: awx/sso/conf.py:550 +#: sso/conf.py:553 msgid "Google OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:551 +#: sso/conf.py:554 msgid "" "The OAuth2 secret from your web application at https://console.developers." "google.com/." msgstr "" -#: awx/sso/conf.py:561 +#: sso/conf.py:565 msgid "Google OAuth2 Whitelisted Domains" msgstr "" -#: awx/sso/conf.py:562 +#: sso/conf.py:566 msgid "" "Update this setting to restrict the domains who are allowed to login using " "Google OAuth2." msgstr "" -#: awx/sso/conf.py:573 +#: sso/conf.py:577 msgid "Google OAuth2 Extra Arguments" msgstr "" -#: awx/sso/conf.py:574 +#: sso/conf.py:578 msgid "" "Extra arguments for Google OAuth2 login. When only allowing a single domain " "to authenticate, set to `{\"hd\": \"yourdomain.com\"}` and Google will not " @@ -2901,60 +3063,60 @@ msgid "" "Google accounts." msgstr "" -#: awx/sso/conf.py:588 +#: sso/conf.py:592 msgid "Google OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:600 +#: sso/conf.py:604 msgid "Google OAuth2 Team Map" msgstr "" -#: awx/sso/conf.py:616 +#: sso/conf.py:620 msgid "GitHub OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:617 +#: sso/conf.py:621 msgid "" "Create a developer application at https://github.com/settings/developers to " "obtain an OAuth2 key (Client ID) and secret (Client Secret). Provide this " "URL as the callback URL for your application." msgstr "" -#: awx/sso/conf.py:621 awx/sso/conf.py:632 awx/sso/conf.py:642 -#: awx/sso/conf.py:653 awx/sso/conf.py:665 +#: sso/conf.py:625 sso/conf.py:636 sso/conf.py:646 sso/conf.py:658 +#: sso/conf.py:670 msgid "GitHub OAuth2" msgstr "" -#: awx/sso/conf.py:630 +#: sso/conf.py:634 msgid "GitHub OAuth2 Key" msgstr "" -#: awx/sso/conf.py:631 +#: sso/conf.py:635 msgid "The OAuth2 key (Client ID) from your GitHub developer application." msgstr "" -#: awx/sso/conf.py:640 +#: sso/conf.py:644 msgid "GitHub OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:641 +#: sso/conf.py:645 msgid "" "The OAuth2 secret (Client Secret) from your GitHub developer application." msgstr "" -#: awx/sso/conf.py:651 +#: sso/conf.py:656 msgid "GitHub OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:663 +#: sso/conf.py:668 msgid "GitHub OAuth2 Team Map" msgstr "" -#: awx/sso/conf.py:679 +#: sso/conf.py:684 msgid "GitHub Organization OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:680 awx/sso/conf.py:754 +#: sso/conf.py:685 sso/conf.py:760 msgid "" "Create an organization-owned application at https://github.com/organizations/" "/settings/applications and obtain an OAuth2 key (Client ID) and " @@ -2962,86 +3124,86 @@ msgid "" "application." msgstr "" -#: awx/sso/conf.py:684 awx/sso/conf.py:695 awx/sso/conf.py:705 -#: awx/sso/conf.py:716 awx/sso/conf.py:727 awx/sso/conf.py:739 +#: sso/conf.py:689 sso/conf.py:700 sso/conf.py:710 sso/conf.py:722 +#: sso/conf.py:733 sso/conf.py:745 msgid "GitHub Organization OAuth2" msgstr "" -#: awx/sso/conf.py:693 +#: sso/conf.py:698 msgid "GitHub Organization OAuth2 Key" msgstr "" -#: awx/sso/conf.py:694 awx/sso/conf.py:768 +#: sso/conf.py:699 sso/conf.py:774 msgid "The OAuth2 key (Client ID) from your GitHub organization application." msgstr "" -#: awx/sso/conf.py:703 +#: sso/conf.py:708 msgid "GitHub Organization OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:704 awx/sso/conf.py:778 +#: sso/conf.py:709 sso/conf.py:784 msgid "" "The OAuth2 secret (Client Secret) from your GitHub organization application." msgstr "" -#: awx/sso/conf.py:713 +#: sso/conf.py:719 msgid "GitHub Organization Name" msgstr "" -#: awx/sso/conf.py:714 +#: sso/conf.py:720 msgid "" "The name of your GitHub organization, as used in your organization's URL: " "https://github.com//." msgstr "" -#: awx/sso/conf.py:725 +#: sso/conf.py:731 msgid "GitHub Organization OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:737 +#: sso/conf.py:743 msgid "GitHub Organization OAuth2 Team Map" msgstr "" -#: awx/sso/conf.py:753 +#: sso/conf.py:759 msgid "GitHub Team OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:758 awx/sso/conf.py:769 awx/sso/conf.py:779 -#: awx/sso/conf.py:790 awx/sso/conf.py:801 awx/sso/conf.py:813 +#: sso/conf.py:764 sso/conf.py:775 sso/conf.py:785 sso/conf.py:797 +#: sso/conf.py:808 sso/conf.py:820 msgid "GitHub Team OAuth2" msgstr "" -#: awx/sso/conf.py:767 +#: sso/conf.py:773 msgid "GitHub Team OAuth2 Key" msgstr "" -#: awx/sso/conf.py:777 +#: sso/conf.py:783 msgid "GitHub Team OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:787 +#: sso/conf.py:794 msgid "GitHub Team ID" msgstr "" -#: awx/sso/conf.py:788 +#: sso/conf.py:795 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:799 +#: sso/conf.py:806 msgid "GitHub Team OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:811 +#: sso/conf.py:818 msgid "GitHub Team OAuth2 Team Map" msgstr "" -#: awx/sso/conf.py:827 +#: sso/conf.py:834 msgid "Azure AD OAuth2 Callback URL" msgstr "" -#: awx/sso/conf.py:828 +#: sso/conf.py:835 msgid "" "Register an Azure AD application as described by https://msdn.microsoft.com/" "en-us/library/azure/dn132599.aspx and obtain an OAuth2 key (Client ID) and " @@ -3049,118 +3211,117 @@ msgid "" "application." msgstr "" -#: awx/sso/conf.py:832 awx/sso/conf.py:843 awx/sso/conf.py:853 -#: awx/sso/conf.py:864 awx/sso/conf.py:876 +#: sso/conf.py:839 sso/conf.py:850 sso/conf.py:860 sso/conf.py:872 +#: sso/conf.py:884 msgid "Azure AD OAuth2" msgstr "" -#: awx/sso/conf.py:841 +#: sso/conf.py:848 msgid "Azure AD OAuth2 Key" msgstr "" -#: awx/sso/conf.py:842 +#: sso/conf.py:849 msgid "The OAuth2 key (Client ID) from your Azure AD application." msgstr "" -#: awx/sso/conf.py:851 +#: sso/conf.py:858 msgid "Azure AD OAuth2 Secret" msgstr "" -#: awx/sso/conf.py:852 +#: sso/conf.py:859 msgid "The OAuth2 secret (Client Secret) from your Azure AD application." msgstr "" -#: awx/sso/conf.py:862 +#: sso/conf.py:870 msgid "Azure AD OAuth2 Organization Map" msgstr "" -#: awx/sso/conf.py:874 +#: sso/conf.py:882 msgid "Azure AD OAuth2 Team Map" msgstr "" -#: awx/sso/conf.py:895 +#: sso/conf.py:903 msgid "SAML Service Provider Callback URL" msgstr "" -#: awx/sso/conf.py:896 +#: sso/conf.py:904 msgid "" "Register Tower as a service provider (SP) with each identity provider (IdP) " "you have configured. Provide your SP Entity ID and this callback URL for " "your application." msgstr "" -#: awx/sso/conf.py:899 awx/sso/conf.py:913 awx/sso/conf.py:927 -#: awx/sso/conf.py:941 awx/sso/conf.py:955 awx/sso/conf.py:972 -#: awx/sso/conf.py:994 awx/sso/conf.py:1013 awx/sso/conf.py:1033 -#: awx/sso/conf.py:1067 awx/sso/conf.py:1080 +#: sso/conf.py:907 sso/conf.py:921 sso/conf.py:934 sso/conf.py:948 +#: sso/conf.py:962 sso/conf.py:980 sso/conf.py:1002 sso/conf.py:1021 +#: sso/conf.py:1041 sso/conf.py:1075 sso/conf.py:1088 msgid "SAML" msgstr "" -#: awx/sso/conf.py:910 +#: sso/conf.py:918 msgid "SAML Service Provider Metadata URL" msgstr "" -#: awx/sso/conf.py:911 +#: sso/conf.py:919 msgid "" "If your identity provider (IdP) allows uploading an XML metadata file, you " "can download one from this URL." msgstr "" -#: awx/sso/conf.py:924 +#: sso/conf.py:931 msgid "SAML Service Provider Entity ID" msgstr "" -#: awx/sso/conf.py:925 +#: sso/conf.py:932 msgid "" -"Set to a URL for a domain name you own (does not need to be a valid URL; " -"only used as a unique ID)." +"The application-defined unique identifier used as the audience of the SAML " +"service provider (SP) configuration." msgstr "" -#: awx/sso/conf.py:938 +#: sso/conf.py:945 msgid "SAML Service Provider Public Certificate" msgstr "" -#: awx/sso/conf.py:939 +#: sso/conf.py:946 msgid "" "Create a keypair for Tower to use as a service provider (SP) and include the " "certificate content here." msgstr "" -#: awx/sso/conf.py:952 +#: sso/conf.py:959 msgid "SAML Service Provider Private Key" msgstr "" -#: awx/sso/conf.py:953 +#: sso/conf.py:960 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:970 +#: sso/conf.py:978 msgid "SAML Service Provider Organization Info" msgstr "" -#: awx/sso/conf.py:971 +#: sso/conf.py:979 msgid "Configure this setting with information about your app." msgstr "" -#: awx/sso/conf.py:992 +#: sso/conf.py:1000 msgid "SAML Service Provider Technical Contact" msgstr "" -#: awx/sso/conf.py:993 awx/sso/conf.py:1012 +#: sso/conf.py:1001 sso/conf.py:1020 msgid "Configure this setting with your contact information." msgstr "" -#: awx/sso/conf.py:1011 +#: sso/conf.py:1019 msgid "SAML Service Provider Support Contact" msgstr "" -#: awx/sso/conf.py:1026 +#: sso/conf.py:1034 msgid "SAML Enabled Identity Providers" msgstr "" -#: awx/sso/conf.py:1027 +#: sso/conf.py:1035 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 " @@ -3169,237 +3330,217 @@ msgid "" "Attribute names may be overridden for each IdP." msgstr "" -#: awx/sso/conf.py:1065 +#: sso/conf.py:1073 msgid "SAML Organization Map" msgstr "" -#: awx/sso/conf.py:1078 +#: sso/conf.py:1086 msgid "SAML Team Map" msgstr "" -#: awx/sso/fields.py:123 -#, python-brace-format +#: sso/fields.py:123 msgid "Invalid connection option(s): {invalid_options}." msgstr "" -#: awx/sso/fields.py:182 +#: sso/fields.py:182 msgid "Base" msgstr "" -#: awx/sso/fields.py:183 +#: sso/fields.py:183 msgid "One Level" msgstr "" -#: awx/sso/fields.py:184 +#: sso/fields.py:184 msgid "Subtree" msgstr "" -#: awx/sso/fields.py:202 -#, python-brace-format +#: sso/fields.py:202 msgid "Expected a list of three items but got {length} instead." msgstr "" -#: awx/sso/fields.py:203 -#, python-brace-format +#: sso/fields.py:203 msgid "Expected an instance of LDAPSearch but got {input_type} instead." msgstr "" -#: awx/sso/fields.py:239 -#, python-brace-format +#: sso/fields.py:239 msgid "" "Expected an instance of LDAPSearch or LDAPSearchUnion but got {input_type} " "instead." msgstr "" -#: awx/sso/fields.py:266 -#, python-brace-format +#: sso/fields.py:266 msgid "Invalid user attribute(s): {invalid_attrs}." msgstr "" -#: awx/sso/fields.py:283 -#, python-brace-format +#: sso/fields.py:283 msgid "Expected an instance of LDAPGroupType but got {input_type} instead." msgstr "" -#: awx/sso/fields.py:308 -#, python-brace-format +#: sso/fields.py:308 msgid "Invalid user flag: \"{invalid_flag}\"." msgstr "" -#: awx/sso/fields.py:324 awx/sso/fields.py:491 -#, python-brace-format +#: sso/fields.py:324 sso/fields.py:491 msgid "" "Expected None, True, False, a string or list of strings but got {input_type} " "instead." msgstr "" -#: awx/sso/fields.py:360 -#, python-brace-format +#: sso/fields.py:360 msgid "Missing key(s): {missing_keys}." msgstr "" -#: awx/sso/fields.py:361 -#, python-brace-format +#: sso/fields.py:361 msgid "Invalid key(s): {invalid_keys}." msgstr "" -#: awx/sso/fields.py:410 awx/sso/fields.py:527 -#, python-brace-format +#: sso/fields.py:410 sso/fields.py:527 msgid "Invalid key(s) for organization map: {invalid_keys}." msgstr "" -#: awx/sso/fields.py:428 -#, python-brace-format +#: sso/fields.py:428 msgid "Missing required key for team map: {invalid_keys}." msgstr "" -#: awx/sso/fields.py:429 awx/sso/fields.py:546 -#, python-brace-format +#: sso/fields.py:429 sso/fields.py:546 msgid "Invalid key(s) for team map: {invalid_keys}." msgstr "" -#: awx/sso/fields.py:545 -#, python-brace-format +#: sso/fields.py:545 msgid "Missing required key for team map: {missing_keys}." msgstr "" -#: awx/sso/fields.py:563 -#, python-brace-format +#: sso/fields.py:563 msgid "Missing required key(s) for org info record: {missing_keys}." msgstr "" -#: awx/sso/fields.py:576 -#, python-brace-format +#: sso/fields.py:576 msgid "Invalid language code(s) for org info: {invalid_lang_codes}." msgstr "" -#: awx/sso/fields.py:595 -#, python-brace-format +#: sso/fields.py:595 msgid "Missing required key(s) for contact: {missing_keys}." msgstr "" -#: awx/sso/fields.py:607 -#, python-brace-format +#: sso/fields.py:607 msgid "Missing required key(s) for IdP: {missing_keys}." msgstr "" -#: awx/sso/pipeline.py:24 -#, python-brace-format +#: sso/pipeline.py:24 msgid "An account cannot be found for {0}" msgstr "" -#: awx/sso/pipeline.py:30 +#: sso/pipeline.py:30 msgid "Your account is inactive" msgstr "" -#: awx/sso/validators.py:19 awx/sso/validators.py:44 +#: sso/validators.py:19 sso/validators.py:44 #, python-format msgid "DN must include \"%%(user)s\" placeholder for username: %s" msgstr "" -#: awx/sso/validators.py:26 +#: sso/validators.py:26 #, python-format msgid "Invalid DN: %s" msgstr "" -#: awx/sso/validators.py:56 +#: sso/validators.py:56 #, python-format msgid "Invalid filter: %s" msgstr "" -#: awx/templates/error.html:4 awx/ui/templates/ui/index.html:8 +#: templates/error.html:4 ui/templates/ui/index.html:8 msgid "Ansible Tower" msgstr "" -#: awx/templates/rest_framework/api.html:39 +#: templates/rest_framework/api.html:39 msgid "Ansible Tower API Guide" msgstr "" -#: awx/templates/rest_framework/api.html:40 +#: templates/rest_framework/api.html:40 msgid "Back to Ansible Tower" msgstr "" -#: awx/templates/rest_framework/api.html:41 +#: templates/rest_framework/api.html:41 msgid "Resize" msgstr "" -#: awx/templates/rest_framework/base.html:78 -#: awx/templates/rest_framework/base.html:92 +#: templates/rest_framework/base.html:78 templates/rest_framework/base.html:92 #, python-format msgid "Make a GET request on the %(name)s resource" msgstr "" -#: awx/templates/rest_framework/base.html:80 +#: templates/rest_framework/base.html:80 msgid "Specify a format for the GET request" msgstr "" -#: awx/templates/rest_framework/base.html:86 +#: templates/rest_framework/base.html:86 #, python-format msgid "" "Make a GET request on the %(name)s resource with the format set to `" "%(format)s`" msgstr "" -#: awx/templates/rest_framework/base.html:100 +#: templates/rest_framework/base.html:100 #, python-format msgid "Make an OPTIONS request on the %(name)s resource" msgstr "" -#: awx/templates/rest_framework/base.html:106 +#: templates/rest_framework/base.html:106 #, python-format msgid "Make a DELETE request on the %(name)s resource" msgstr "" -#: awx/templates/rest_framework/base.html:113 +#: templates/rest_framework/base.html:113 msgid "Filters" msgstr "" -#: awx/templates/rest_framework/base.html:172 -#: awx/templates/rest_framework/base.html:186 +#: templates/rest_framework/base.html:172 +#: templates/rest_framework/base.html:186 #, python-format msgid "Make a POST request on the %(name)s resource" msgstr "" -#: awx/templates/rest_framework/base.html:216 -#: awx/templates/rest_framework/base.html:230 +#: templates/rest_framework/base.html:216 +#: templates/rest_framework/base.html:230 #, python-format msgid "Make a PUT request on the %(name)s resource" msgstr "" -#: awx/templates/rest_framework/base.html:233 +#: templates/rest_framework/base.html:233 #, python-format msgid "Make a PATCH request on the %(name)s resource" msgstr "" -#: awx/ui/apps.py:9 awx/ui/conf.py:22 awx/ui/conf.py:38 awx/ui/conf.py:53 +#: ui/apps.py:9 ui/conf.py:22 ui/conf.py:38 ui/conf.py:53 msgid "UI" msgstr "" -#: awx/ui/conf.py:16 +#: ui/conf.py:16 msgid "Off" msgstr "" -#: awx/ui/conf.py:17 +#: ui/conf.py:17 msgid "Anonymous" msgstr "" -#: awx/ui/conf.py:18 +#: ui/conf.py:18 msgid "Detailed" msgstr "" -#: awx/ui/conf.py:20 +#: ui/conf.py:20 msgid "Analytics Tracking State" msgstr "" -#: awx/ui/conf.py:21 +#: ui/conf.py:21 msgid "Enable or Disable Analytics Tracking." msgstr "" -#: awx/ui/conf.py:31 +#: ui/conf.py:31 msgid "Custom Login Info" msgstr "" -#: awx/ui/conf.py:32 +#: ui/conf.py:32 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 " @@ -3408,42 +3549,42 @@ msgid "" "(paragraphs) must be escaped as `\\n` within the block of text." msgstr "" -#: awx/ui/conf.py:48 +#: ui/conf.py:48 msgid "Custom Logo" msgstr "" -#: awx/ui/conf.py:49 +#: ui/conf.py:49 msgid "" "To set up a custom logo, provide a file that you create. For the custom logo " "to look its best, use a `.png` file with a transparent background. GIF, PNG " "and JPEG formats are supported." msgstr "" -#: awx/ui/fields.py:29 +#: ui/fields.py:29 msgid "" "Invalid format for custom logo. Must be a data URL with a base64-encoded " "GIF, PNG or JPEG image." msgstr "" -#: awx/ui/fields.py:30 +#: ui/fields.py:30 msgid "Invalid base64-encoded data in data URL." msgstr "" -#: awx/ui/templates/ui/index.html:49 +#: ui/templates/ui/index.html:49 msgid "" "Your session will expire in 60 seconds, would you like to continue?" msgstr "" -#: awx/ui/templates/ui/index.html:64 +#: ui/templates/ui/index.html:64 msgid "CANCEL" msgstr "" -#: awx/ui/templates/ui/index.html:116 +#: ui/templates/ui/index.html:116 msgid "Set how many days of data should be retained." msgstr "" -#: awx/ui/templates/ui/index.html:122 +#: ui/templates/ui/index.html:122 msgid "" "Please enter an integer that is not " @@ -3452,7 +3593,7 @@ msgid "" "span>." msgstr "" -#: awx/ui/templates/ui/index.html:127 +#: ui/templates/ui/index.html:127 msgid "" "For facts collected older than the time period specified, save one fact scan " "(snapshot) per time window (frequency). For example, facts older than 30 " @@ -3464,11 +3605,11 @@ msgid "" "
" msgstr "" -#: awx/ui/templates/ui/index.html:136 +#: ui/templates/ui/index.html:136 msgid "Select a time period after which to remove old facts" msgstr "" -#: awx/ui/templates/ui/index.html:150 +#: ui/templates/ui/index.html:150 msgid "" "Please enter an integer " @@ -3477,11 +3618,11 @@ msgid "" "that is lower than 9999." msgstr "" -#: awx/ui/templates/ui/index.html:155 +#: ui/templates/ui/index.html:155 msgid "Select a frequency for snapshot retention" msgstr "" -#: awx/ui/templates/ui/index.html:169 +#: ui/templates/ui/index.html:169 msgid "" "Please enter an integer." msgstr "" -#: awx/ui/templates/ui/index.html:175 +#: ui/templates/ui/index.html:175 msgid "working..." msgstr "" diff --git a/awx/ui/po/ansible-tower-ui.pot b/awx/ui/po/ansible-tower-ui.pot index e3226dc3f4..df8bbde43b 100644 --- a/awx/ui/po/ansible-tower-ui.pot +++ b/awx/ui/po/ansible-tower-ui.pot @@ -34,15 +34,15 @@ msgstr "" #: client/src/forms/Inventories.js:153 #: client/src/forms/JobTemplates.js:414 #: client/src/forms/Organizations.js:75 -#: client/src/forms/Projects.js:238 +#: client/src/forms/Projects.js:237 #: client/src/forms/Teams.js:86 -#: client/src/forms/Workflows.js:126 +#: client/src/forms/Workflows.js:127 #: client/src/inventory-scripts/inventory-scripts.list.js:45 #: client/src/lists/Credentials.js:59 #: client/src/lists/Inventories.js:68 #: client/src/lists/Projects.js:67 #: client/src/lists/Teams.js:50 -#: client/src/lists/Templates.js:61 +#: client/src/lists/Templates.js:62 #: client/src/lists/Users.js:58 #: client/src/notifications/notificationTemplates.list.js:52 msgid "ADD" @@ -81,7 +81,7 @@ msgid "Account Token" msgstr "" #: client/src/dashboard/lists/job-templates/job-templates-list.partial.html:20 -#: client/src/shared/list-generator/list-generator.factory.js:502 +#: client/src/shared/list-generator/list-generator.factory.js:538 msgid "Actions" msgstr "" @@ -94,7 +94,7 @@ msgstr "" #: client/src/forms/Inventories.js:150 #: client/src/forms/Organizations.js:72 #: client/src/forms/Teams.js:83 -#: client/src/forms/Workflows.js:123 +#: client/src/forms/Workflows.js:124 msgid "Add" msgstr "" @@ -119,8 +119,8 @@ msgid "Add Project" msgstr "" #: client/src/forms/JobTemplates.js:459 -#: client/src/forms/Workflows.js:171 -#: client/src/shared/form-generator.js:1703 +#: client/src/forms/Workflows.js:172 +#: client/src/shared/form-generator.js:1707 msgid "Add Survey" msgstr "" @@ -136,7 +136,7 @@ msgstr "" #: client/src/forms/Inventories.js:151 #: client/src/forms/JobTemplates.js:412 #: client/src/forms/Organizations.js:73 -#: client/src/forms/Projects.js:236 +#: client/src/forms/Projects.js:235 msgid "Add a permission" msgstr "" @@ -219,6 +219,10 @@ msgstr "" msgid "Authorize Password" msgstr "" +#: client/src/configuration/auth-form/configuration-auth.controller.js:101 +msgid "Azure AD" +msgstr "" + #: client/src/forms/Projects.js:80 msgid "Base path used for locating playbooks. Directories found inside this path will be listed in the playbook directory drop-down. Together the base path and selected playbook directory provide the full path used to locate playbooks." msgstr "" @@ -235,11 +239,11 @@ msgstr "" msgid "CREDENTIALS" msgstr "" -#: client/src/forms/Projects.js:195 +#: client/src/forms/Projects.js:194 msgid "Cache Timeout" msgstr "" -#: client/src/forms/Projects.js:184 +#: client/src/forms/Projects.js:183 msgid "Cache Timeout%s (seconds)%s" msgstr "" @@ -265,7 +269,8 @@ msgstr "" msgid "Call to get project failed. GET status:" msgstr "" -#: client/src/shared/form-generator.js:1691 +#: client/src/configuration/configuration.controller.js:414 +#: client/src/shared/form-generator.js:1695 msgid "Cancel" msgstr "" @@ -281,6 +286,10 @@ msgstr "" msgid "Canceled. Click for details" msgstr "" +#: client/src/forms/Projects.js:82 +msgid "Change %s under \"Configure Tower\" to change this location." +msgstr "" + #: client/src/shared/form-generator.js:1084 msgid "Choose a %s" msgstr "" @@ -289,7 +298,7 @@ msgstr "" msgid "Choose your license file, agree to the End User License Agreement, and click submit." msgstr "" -#: client/src/forms/Projects.js:152 +#: client/src/forms/Projects.js:151 msgid "Clean" msgstr "" @@ -317,7 +326,7 @@ msgstr "" msgid "Client Secret" msgstr "" -#: client/src/shared/form-generator.js:1695 +#: client/src/shared/form-generator.js:1699 msgid "Close" msgstr "" @@ -346,6 +355,14 @@ msgstr "" msgid "Confirm Password" msgstr "" +#: client/src/configuration/configuration.controller.js:421 +msgid "Confirm Reset" +msgstr "" + +#: client/src/configuration/configuration.controller.js:430 +msgid "Confirm factory reset" +msgstr "" + #: client/src/forms/JobTemplates.js:255 #: client/src/forms/JobTemplates.js:273 #: client/src/forms/WorkflowMaker.js:141 @@ -357,11 +374,11 @@ msgstr "" msgid "Control the level of output ansible will produce as the playbook executes." msgstr "" -#: client/src/lists/Templates.js:99 +#: client/src/lists/Templates.js:100 msgid "Copy" msgstr "" -#: client/src/lists/Templates.js:102 +#: client/src/lists/Templates.js:103 msgid "Copy template" msgstr "" @@ -402,7 +419,7 @@ msgstr "" msgid "Create a new team" msgstr "" -#: client/src/lists/Templates.js:59 +#: client/src/lists/Templates.js:60 msgid "Create a new template" msgstr "" @@ -435,7 +452,7 @@ msgstr "" msgid "Custom Script" msgstr "" -#: client/src/app.js:405 +#: client/src/app.js:409 msgid "DASHBOARD" msgstr "" @@ -451,7 +468,7 @@ msgstr "" #: client/src/lists/Credentials.js:90 #: client/src/lists/Inventories.js:92 #: client/src/lists/Teams.js:77 -#: client/src/lists/Templates.js:123 +#: client/src/lists/Templates.js:125 #: client/src/lists/Users.js:87 #: client/src/notifications/notificationTemplates.list.js:89 msgid "Delete" @@ -473,7 +490,7 @@ msgstr "" msgid "Delete notification" msgstr "" -#: client/src/forms/Projects.js:162 +#: client/src/forms/Projects.js:161 msgid "Delete on Update" msgstr "" @@ -481,7 +498,7 @@ msgstr "" msgid "Delete team" msgstr "" -#: client/src/lists/Templates.js:126 +#: client/src/lists/Templates.js:128 msgid "Delete template" msgstr "" @@ -489,7 +506,7 @@ msgstr "" msgid "Delete the job" msgstr "" -#: client/src/forms/Projects.js:164 +#: client/src/forms/Projects.js:163 msgid "Delete the local repository in its entirety prior to performing an update." msgstr "" @@ -505,7 +522,7 @@ msgstr "" msgid "Delete user" msgstr "" -#: client/src/forms/Projects.js:164 +#: client/src/forms/Projects.js:163 msgid "Depending on the size of the repository this may significantly increase the amount of time required to complete an update." msgstr "" @@ -517,7 +534,7 @@ msgstr "" #: client/src/forms/Teams.js:34 #: client/src/forms/Users.js:142 #: client/src/forms/Users.js:167 -#: client/src/forms/Workflows.js:40 +#: client/src/forms/Workflows.js:41 #: client/src/inventory-scripts/inventory-scripts.form.js:32 #: client/src/inventory-scripts/inventory-scripts.list.js:25 #: client/src/lists/Credentials.js:34 @@ -550,6 +567,12 @@ msgstr "" msgid "Details" msgstr "" +#: client/src/configuration/auth-form/configuration-auth.controller.js:70 +#: client/src/configuration/configuration.controller.js:159 +#: client/src/configuration/configuration.controller.js:212 +msgid "Discard changes" +msgstr "" + #: client/src/forms/Teams.js:148 msgid "Dissasociate permission from team" msgstr "" @@ -567,7 +590,7 @@ msgstr "" msgid "Drag and drop your custom inventory script file here or create one in the field to import your custom inventory." msgstr "" -#: client/src/forms/Projects.js:175 +#: client/src/forms/Projects.js:174 msgid "Each time a job runs using this project, perform an update to the local repository prior to starting the job." msgstr "" @@ -576,7 +599,7 @@ msgstr "" #: client/src/lists/Credentials.js:71 #: client/src/lists/Inventories.js:78 #: client/src/lists/Teams.js:60 -#: client/src/lists/Templates.js:107 +#: client/src/lists/Templates.js:108 #: client/src/lists/Users.js:68 #: client/src/notifications/notificationTemplates.list.js:63 #: client/src/notifications/notificationTemplates.list.js:72 @@ -584,8 +607,8 @@ msgid "Edit" msgstr "" #: client/src/forms/JobTemplates.js:466 -#: client/src/forms/Workflows.js:178 -#: client/src/shared/form-generator.js:1707 +#: client/src/forms/Workflows.js:179 +#: client/src/shared/form-generator.js:1711 msgid "Edit Survey" msgstr "" @@ -613,7 +636,7 @@ msgstr "" msgid "Edit team" msgstr "" -#: client/src/lists/Templates.js:109 +#: client/src/lists/Templates.js:110 msgid "Edit template" msgstr "" @@ -646,7 +669,7 @@ msgstr "" msgid "Enables creation of a provisioning callback URL. Using the URL a host can contact Tower and request a configuration update using this job template." msgstr "" -#: client/src/helpers/Credentials.js:308 +#: client/src/helpers/Credentials.js:306 msgid "Encrypted credentials are not supported." msgstr "" @@ -670,6 +693,10 @@ msgstr "" msgid "Enter the hostname or IP address which corresponds to your VMware vCenter." msgstr "" +#: client/src/configuration/configuration.controller.js:272 +#: client/src/configuration/configuration.controller.js:350 +#: client/src/configuration/configuration.controller.js:384 +#: client/src/configuration/configuration.controller.js:403 #: client/src/controllers/Projects.js:133 #: client/src/controllers/Projects.js:155 #: client/src/controllers/Projects.js:180 @@ -684,8 +711,8 @@ msgstr "" #: client/src/controllers/Users.js:267 #: client/src/controllers/Users.js:321 #: client/src/controllers/Users.js:94 -#: client/src/helpers/Credentials.js:312 -#: client/src/helpers/Credentials.js:328 +#: client/src/helpers/Credentials.js:310 +#: client/src/helpers/Credentials.js:326 #: client/src/login/loginModal/thirdPartySignOn/thirdPartySignOn.service.js:119 msgid "Error!" msgstr "" @@ -711,8 +738,8 @@ msgstr "" #: client/src/forms/JobTemplates.js:352 #: client/src/forms/JobTemplates.js:364 -#: client/src/forms/Workflows.js:71 -#: client/src/forms/Workflows.js:83 +#: client/src/forms/Workflows.js:72 +#: client/src/forms/Workflows.js:84 msgid "Extra Variables" msgstr "" @@ -732,7 +759,7 @@ msgstr "" msgid "Failed to add new user. POST returned status:" msgstr "" -#: client/src/helpers/Credentials.js:313 +#: client/src/helpers/Credentials.js:311 msgid "Failed to create new Credential. POST status:" msgstr "" @@ -753,7 +780,15 @@ msgstr "" msgid "Failed to retrieve user: %s. GET status:" msgstr "" -#: client/src/helpers/Credentials.js:329 +#: client/src/configuration/configuration.controller.js:351 +msgid "Failed to save settings. Returned status:" +msgstr "" + +#: client/src/configuration/configuration.controller.js:385 +msgid "Failed to save toggle settings. Returned status:" +msgstr "" + +#: client/src/helpers/Credentials.js:327 msgid "Failed to update Credential. PUT status:" msgstr "" @@ -802,6 +837,22 @@ msgstr "" msgid "Forks" msgstr "" +#: client/src/configuration/auth-form/configuration-auth.controller.js:102 +msgid "Github" +msgstr "" + +#: client/src/configuration/auth-form/configuration-auth.controller.js:103 +msgid "Github Org" +msgstr "" + +#: client/src/configuration/auth-form/configuration-auth.controller.js:104 +msgid "Github Team" +msgstr "" + +#: client/src/configuration/auth-form/configuration-auth.controller.js:105 +msgid "Google OAuth2" +msgstr "" + #: client/src/forms/Teams.js:118 msgid "Granted Permissions" msgstr "" @@ -949,7 +1000,7 @@ msgstr "" msgid "JOB TEMPLATE" msgstr "" -#: client/src/app.js:425 +#: client/src/app.js:429 #: client/src/dashboard/graphs/job-status/job-status-graph.directive.js:113 #: client/src/main-menu/main-menu.partial.html:122 #: client/src/main-menu/main-menu.partial.html:43 @@ -963,7 +1014,7 @@ msgstr "" msgid "Job Tags" msgstr "" -#: client/src/lists/Templates.js:64 +#: client/src/lists/Templates.js:65 msgid "Job Template" msgstr "" @@ -986,6 +1037,10 @@ msgstr "" msgid "Jobs" msgstr "" +#: client/src/configuration/auth-form/configuration-auth.controller.js:106 +msgid "LDAP" +msgstr "" + #: client/src/main-menu/main-menu.partial.html:83 msgid "LOG OUT" msgstr "" @@ -996,8 +1051,8 @@ msgstr "" #: client/src/forms/JobTemplates.js:340 #: client/src/forms/JobTemplates.js:345 -#: client/src/forms/Workflows.js:59 -#: client/src/forms/Workflows.js:64 +#: client/src/forms/Workflows.js:60 +#: client/src/forms/Workflows.js:65 #: client/src/lists/Templates.js:47 msgid "Labels" msgstr "" @@ -1012,8 +1067,8 @@ msgid "Last Updated" msgstr "" #: client/src/lists/PortalJobTemplates.js:39 -#: client/src/lists/Templates.js:83 -#: client/src/shared/form-generator.js:1699 +#: client/src/lists/Templates.js:84 +#: client/src/shared/form-generator.js:1703 msgid "Launch" msgstr "" @@ -1049,19 +1104,19 @@ msgstr "" msgid "Limit" msgstr "" -#: client/src/shared/socket/socket.service.js:176 +#: client/src/shared/socket/socket.service.js:170 msgid "Live events: attempting to connect to the Tower server." msgstr "" -#: client/src/shared/socket/socket.service.js:180 +#: client/src/shared/socket/socket.service.js:174 msgid "Live events: connected. Pages containing job status information will automatically update in real-time." msgstr "" -#: client/src/shared/socket/socket.service.js:184 +#: client/src/shared/socket/socket.service.js:178 msgid "Live events: error connecting to the Tower server." msgstr "" -#: client/src/shared/form-generator.js:1962 +#: client/src/shared/form-generator.js:1977 msgid "Loading..." msgstr "" @@ -1131,7 +1186,7 @@ msgstr "" #: client/src/forms/Users.js:139 #: client/src/forms/Users.js:164 #: client/src/forms/Users.js:190 -#: client/src/forms/Workflows.js:33 +#: client/src/forms/Workflows.js:34 #: client/src/inventory-scripts/inventory-scripts.form.js:25 #: client/src/inventory-scripts/inventory-scripts.list.js:20 #: client/src/lists/CompletedJobs.js:43 @@ -1195,7 +1250,7 @@ msgid "New User" msgstr "" #: client/src/forms/Workflows.js:19 -msgid "New Workflow" +msgid "New Workflow Job Template" msgstr "" #: client/src/controllers/Users.js:174 @@ -1293,7 +1348,7 @@ msgid "OpenStack domains define administrative boundaries. It is only needed for msgstr "" #: client/src/forms/JobTemplates.js:347 -#: client/src/forms/Workflows.js:66 +#: client/src/forms/Workflows.js:67 msgid "Optional labels that describe this job template, such as 'dev' or 'test'. Labels can be used to group and filter job templates and completed jobs in the Tower display." msgstr "" @@ -1309,8 +1364,8 @@ msgstr "" #: client/src/forms/Projects.js:49 #: client/src/forms/Teams.js:39 #: client/src/forms/Users.js:59 -#: client/src/forms/Workflows.js:46 -#: client/src/forms/Workflows.js:52 +#: client/src/forms/Workflows.js:47 +#: client/src/forms/Workflows.js:53 #: client/src/inventory-scripts/inventory-scripts.form.js:37 #: client/src/inventory-scripts/inventory-scripts.list.js:30 #: client/src/lists/Inventories.js:52 @@ -1337,7 +1392,7 @@ msgid "PASSWORD" msgstr "" #: client/src/organizations/list/organizations-list.partial.html:44 -#: client/src/shared/form-generator.js:1865 +#: client/src/shared/form-generator.js:1880 #: client/src/shared/list-generator/list-generator.factory.js:245 msgid "PLEASE ADD ITEMS TO THIS LIST" msgstr "" @@ -1356,7 +1411,7 @@ msgid "Pagerduty subdomain" msgstr "" #: client/src/forms/JobTemplates.js:358 -#: client/src/forms/Workflows.js:77 +#: client/src/forms/Workflows.js:78 msgid "Pass extra command line variables to the playbook. This is the %s or %s command line parameter for %s. Provide key/value pairs using either YAML or JSON." msgstr "" @@ -1419,8 +1474,8 @@ msgstr "" #: client/src/forms/Inventories.js:142 #: client/src/forms/JobTemplates.js:403 #: client/src/forms/Organizations.js:64 -#: client/src/forms/Projects.js:228 -#: client/src/forms/Workflows.js:115 +#: client/src/forms/Projects.js:227 +#: client/src/forms/Workflows.js:116 msgid "Permissions" msgstr "" @@ -1493,9 +1548,9 @@ msgstr "" #: client/src/forms/Inventories.js:91 #: client/src/forms/JobTemplates.js:396 #: client/src/forms/Organizations.js:57 -#: client/src/forms/Projects.js:220 +#: client/src/forms/Projects.js:219 #: client/src/forms/Teams.js:110 -#: client/src/forms/Workflows.js:108 +#: client/src/forms/Workflows.js:109 msgid "Please save before assigning permissions" msgstr "" @@ -1508,7 +1563,7 @@ msgstr "" msgid "Please save before assigning to teams" msgstr "" -#: client/src/forms/Workflows.js:184 +#: client/src/forms/Workflows.js:185 msgid "Please save before defining the workflow graph" msgstr "" @@ -1593,7 +1648,7 @@ msgstr "" msgid "Project Name" msgstr "" -#: client/src/forms/Projects.js:101 +#: client/src/forms/Projects.js:100 msgid "Project Path" msgstr "" @@ -1638,6 +1693,10 @@ msgstr "" msgid "Provisioning Callback URL" msgstr "" +#: client/src/configuration/auth-form/configuration-auth.controller.js:107 +msgid "RADIUS" +msgstr "" + #: client/src/dashboard/lists/jobs/jobs-list.partial.html:4 msgid "RECENT JOB RUNS" msgstr "" @@ -1684,7 +1743,7 @@ msgstr "" msgid "Remove" msgstr "" -#: client/src/forms/Projects.js:154 +#: client/src/forms/Projects.js:153 msgid "Remove any local modifications prior to performing an update." msgstr "" @@ -1692,6 +1751,20 @@ msgstr "" msgid "Request License" msgstr "" +#: client/src/configuration/auth-form/sub-forms/auth-azure.form.js:41 +#: client/src/configuration/auth-form/sub-forms/auth-github-org.form.js:31 +#: client/src/configuration/auth-form/sub-forms/auth-github-team.form.js:31 +#: client/src/configuration/auth-form/sub-forms/auth-github.form.js:27 +#: client/src/configuration/auth-form/sub-forms/auth-google-oauth2.form.js:39 +#: client/src/configuration/auth-form/sub-forms/auth-ldap.form.js:87 +#: client/src/configuration/auth-form/sub-forms/auth-radius.form.js:32 +#: client/src/configuration/auth-form/sub-forms/auth-saml.form.js:59 +#: client/src/configuration/jobs-form/configuration-jobs.form.js:55 +#: client/src/configuration/system-form/configuration-system.form.js:41 +#: client/src/configuration/ui-form/configuration-ui.form.js:35 +msgid "Reset All" +msgstr "" + #: client/src/lists/Projects.js:42 msgid "Revision" msgstr "" @@ -1704,26 +1777,30 @@ msgstr "" #: client/src/forms/Inventories.js:120 #: client/src/forms/Inventories.js:166 #: client/src/forms/Organizations.js:88 -#: client/src/forms/Projects.js:250 +#: client/src/forms/Projects.js:249 #: client/src/forms/Teams.js:137 #: client/src/forms/Teams.js:99 #: client/src/forms/Users.js:201 msgid "Role" msgstr "" +#: client/src/configuration/auth-form/configuration-auth.controller.js:108 +msgid "SAML" +msgstr "" + #: client/src/controllers/Projects.js:657 msgid "SCM Branch" msgstr "" -#: client/src/forms/Projects.js:155 +#: client/src/forms/Projects.js:154 msgid "SCM Clean" msgstr "" -#: client/src/forms/Projects.js:131 +#: client/src/forms/Projects.js:130 msgid "SCM Credential" msgstr "" -#: client/src/forms/Projects.js:166 +#: client/src/forms/Projects.js:165 msgid "SCM Delete" msgstr "" @@ -1736,7 +1813,7 @@ msgid "SCM Type" msgstr "" #: client/src/dashboard/graphs/dashboard-graphs.partial.html:49 -#: client/src/forms/Projects.js:176 +#: client/src/forms/Projects.js:175 msgid "SCM Update" msgstr "" @@ -1744,7 +1821,7 @@ msgstr "" msgid "SCM Update Cancel" msgstr "" -#: client/src/forms/Projects.js:146 +#: client/src/forms/Projects.js:145 msgid "SCM Update Options" msgstr "" @@ -1765,7 +1842,7 @@ msgstr "" msgid "SIGN IN WITH" msgstr "" -#: client/src/app.js:509 +#: client/src/app.js:513 msgid "SOCKETS" msgstr "" @@ -1798,11 +1875,17 @@ msgstr "" msgid "Save" msgstr "" +#: client/src/configuration/auth-form/configuration-auth.controller.js:81 +#: client/src/configuration/configuration.controller.js:170 +#: client/src/configuration/configuration.controller.js:220 +msgid "Save changes" +msgstr "" + #: client/src/license/license.partial.html:122 msgid "Save successful!" msgstr "" -#: client/src/lists/Templates.js:91 +#: client/src/lists/Templates.js:92 msgid "Schedule" msgstr "" @@ -1814,7 +1897,7 @@ msgstr "" msgid "Schedule future SCM updates" msgstr "" -#: client/src/lists/Templates.js:94 +#: client/src/lists/Templates.js:95 msgid "Schedule future job template runs" msgstr "" @@ -1838,8 +1921,21 @@ msgstr "" 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 "" +#: client/src/shared/form-generator.js:1691 +msgid "Select" +msgstr "" + +#: client/src/configuration/jobs-form/configuration-jobs.controller.js:87 +#: client/src/configuration/ui-form/configuration-ui.controller.js:82 +msgid "Select commands" +msgstr "" + #: client/src/forms/Projects.js:98 -msgid "Select from the list of directories found in the base path.Together the base path and the playbook directory provide the full path used to locate playbooks." +msgid "Select from the list of directories found in the Project Base Path. Together the base path and the playbook directory provide the full path used to locate playbooks." +msgstr "" + +#: client/src/configuration/auth-form/configuration-auth.controller.js:226 +msgid "Select group types" msgstr "" #: client/src/forms/JobTemplates.js:152 @@ -1945,7 +2041,7 @@ msgid "Split up your organization to associate content and control permissions f msgstr "" #: client/src/lists/PortalJobTemplates.js:42 -#: client/src/lists/Templates.js:86 +#: client/src/lists/Templates.js:87 msgid "Start a job using this template" msgstr "" @@ -2017,7 +2113,7 @@ msgstr "" #: client/src/forms/Inventories.js:126 #: client/src/forms/Inventories.js:173 #: client/src/forms/Organizations.js:95 -#: client/src/forms/Projects.js:256 +#: client/src/forms/Projects.js:255 msgid "Team Roles" msgstr "" @@ -2093,6 +2189,14 @@ msgstr "" msgid "There is no SCM update information available for this project. An update has not yet been completed. If you have not already done so, start an update for this project." msgstr "" +#: client/src/configuration/configuration.controller.js:273 +msgid "There was an error resetting value. Returned status:" +msgstr "" + +#: client/src/configuration/configuration.controller.js:404 +msgid "There was an error resetting values. Returned status:" +msgstr "" + #: client/src/helpers/Credentials.js:138 msgid "This is the tenant name. This value is usually the same as the username." msgstr "" @@ -2114,6 +2218,10 @@ msgstr "" msgid "This value does not match the password you entered previously. Please confirm that password." msgstr "" +#: client/src/configuration/configuration.controller.js:429 +msgid "This will reset all configuration values to their factory defaults. Are you sure you want to proceed?" +msgstr "" + #: client/src/dashboard/lists/jobs/jobs-list.partial.html:14 msgid "Time" msgstr "" @@ -2122,7 +2230,7 @@ msgstr "" msgid "Time Remaining" msgstr "" -#: client/src/forms/Projects.js:192 +#: client/src/forms/Projects.js:191 msgid "Time in seconds to consider a project to be current. During job runs and callbacks the task system will evaluate the timestamp of the latest project update. If it is older than Cache Timeout, it is not considered current, and a new project update will be performed." msgstr "" @@ -2192,7 +2300,7 @@ msgstr "" msgid "Update in Progress" msgstr "" -#: client/src/forms/Projects.js:173 +#: client/src/forms/Projects.js:172 msgid "Update on Launch" msgstr "" @@ -2200,11 +2308,6 @@ msgstr "" msgid "Upgrade" msgstr "" -#: client/src/forms/Projects.js:100 -#: client/src/forms/Projects.js:82 -msgid "Use %s in your environment settings file to determine the base path value." -msgstr "" - #: client/src/notifications/notificationTemplates.form.js:404 msgid "Use SSL" msgstr "" @@ -2221,7 +2324,7 @@ msgstr "" #: client/src/forms/Inventories.js:115 #: client/src/forms/Inventories.js:161 #: client/src/forms/Organizations.js:83 -#: client/src/forms/Projects.js:245 +#: client/src/forms/Projects.js:244 #: client/src/forms/Teams.js:94 msgid "User" msgstr "" @@ -2291,7 +2394,7 @@ msgstr "" #: client/src/lists/Credentials.js:80 #: client/src/lists/Inventories.js:85 #: client/src/lists/Teams.js:69 -#: client/src/lists/Templates.js:115 +#: client/src/lists/Templates.js:117 #: client/src/lists/Users.js:78 #: client/src/notifications/notificationTemplates.list.js:80 msgid "View" @@ -2306,8 +2409,8 @@ msgid "View JSON examples at %s" msgstr "" #: client/src/forms/JobTemplates.js:450 -#: client/src/forms/Workflows.js:162 -#: client/src/shared/form-generator.js:1711 +#: client/src/forms/Workflows.js:163 +#: client/src/shared/form-generator.js:1715 msgid "View Survey" msgstr "" @@ -2347,7 +2450,7 @@ msgstr "" msgid "View team" msgstr "" -#: client/src/lists/Templates.js:117 +#: client/src/lists/Templates.js:119 msgid "View template" msgstr "" @@ -2363,6 +2466,16 @@ msgstr "" msgid "View user" msgstr "" +#: client/src/forms/Workflows.js:22 +msgid "WORKFLOW" +msgstr "" + +#: client/src/configuration/auth-form/configuration-auth.controller.js:68 +#: client/src/configuration/configuration.controller.js:157 +#: client/src/configuration/configuration.controller.js:210 +msgid "Warning: Unsaved Changes" +msgstr "" + #: client/src/login/loginModal/loginModal.partial.html:17 msgid "Welcome to Ansible Tower!  Please sign in." msgstr "" @@ -2376,12 +2489,12 @@ msgstr "" msgid "When this template is submitted as a job, setting the type to %s will execute the playbook, running tasks on the selected hosts." msgstr "" -#: client/src/forms/Workflows.js:186 -#: client/src/shared/form-generator.js:1715 +#: client/src/forms/Workflows.js:187 +#: client/src/shared/form-generator.js:1719 msgid "Workflow Editor" msgstr "" -#: client/src/lists/Templates.js:69 +#: client/src/lists/Templates.js:70 msgid "Workflow Job Template" msgstr "" @@ -2397,6 +2510,12 @@ msgstr "" msgid "You do not have permission to add a user." msgstr "" +#: client/src/configuration/auth-form/configuration-auth.controller.js:67 +#: client/src/configuration/configuration.controller.js:156 +#: client/src/configuration/configuration.controller.js:209 +msgid "You have unsaved changes. Would you like to proceed without saving?" +msgstr "" + #: client/src/shared/form-generator.js:960 msgid "Your password must be %d characters long." msgstr "" diff --git a/tools/docker-compose/Dockerfile b/tools/docker-compose/Dockerfile index 37898a6854..6d6b930017 100644 --- a/tools/docker-compose/Dockerfile +++ b/tools/docker-compose/Dockerfile @@ -10,7 +10,7 @@ ADD requirements/requirements.txt requirements/requirements_ansible.txt requirem RUN yum -y update && yum -y install curl epel-release RUN curl --silent --location https://rpm.nodesource.com/setup_6.x | bash - RUN yum -y localinstall http://download.postgresql.org/pub/repos/yum/9.4/redhat/rhel-6-x86_64/pgdg-centos94-9.4-3.noarch.rpm -RUN yum -y update && yum -y install openssh-server ansible mg vim tmux git mercurial subversion python-devel python-psycopg2 make postgresql postgresql-devel nginx nodejs python-psutil libxml2-devel libxslt-devel libstdc++.so.6 gcc cyrus-sasl-devel cyrus-sasl openldap-devel libffi-devel zeromq-devel python-pip xmlsec1-devel swig krb5-devel xmlsec1-openssl xmlsec1 xmlsec1-openssl-devel libtool-ltdl-devel rabbitmq-server bubblewrap +RUN yum -y update && yum -y install openssh-server ansible mg vim tmux git mercurial subversion python-devel python-psycopg2 make postgresql postgresql-devel nginx nodejs python-psutil libxml2-devel libxslt-devel libstdc++.so.6 gcc cyrus-sasl-devel cyrus-sasl openldap-devel libffi-devel zeromq-devel python-pip xmlsec1-devel swig krb5-devel xmlsec1-openssl xmlsec1 xmlsec1-openssl-devel libtool-ltdl-devel rabbitmq-server bubblewrap zanata-python-client gettext RUN pip install virtualenv RUN /usr/bin/ssh-keygen -q -t rsa -N "" -f /root/.ssh/id_rsa RUN mkdir -p /etc/tower diff --git a/tools/scripts/manage_translations.py b/tools/scripts/manage_translations.py old mode 100644 new mode 100755 index f5df39e308..02bcc1c7b5 --- a/tools/scripts/manage_translations.py +++ b/tools/scripts/manage_translations.py @@ -194,9 +194,8 @@ if __name__ == "__main__": except OSError as e: if e.errno == os.errno.ENOENT: print(''' - You need zanata-python-client, install it. - 1. Install zanata-python-client, use - $ dnf install zanata-python-client + You need zanata python client, install it. + 1. Install zanta python client 2. Create ~/.config/zanata.ini file: $ vim ~/.config/zanata.ini [servers] From e8f01c90291d4573b41364704579f07dee1eb993 Mon Sep 17 00:00:00 2001 From: Chris Church Date: Wed, 14 Dec 2016 16:12:36 -0500 Subject: [PATCH 05/49] Stringify the MAX_EVENT_RES_DATA setting when sticking it into an environment variable. --- awx/main/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index addbe4c8f2..2f862e61c2 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -809,7 +809,7 @@ class RunJob(BaseTask): env['REST_API_URL'] = settings.INTERNAL_API_URL env['REST_API_TOKEN'] = job.task_auth_token or '' env['TOWER_HOST'] = settings.TOWER_URL_BASE - env['MAX_EVENT_RES'] = settings.MAX_EVENT_RES_DATA + env['MAX_EVENT_RES'] = str(settings.MAX_EVENT_RES_DATA) env['CALLBACK_QUEUE'] = settings.CALLBACK_QUEUE env['CALLBACK_CONNECTION'] = settings.BROKER_URL if getattr(settings, 'JOB_CALLBACK_DEBUG', False): From ffaa43941abca29ea54d667cff1ee4b8c4941738 Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Wed, 14 Dec 2016 16:24:55 -0500 Subject: [PATCH 06/49] Added UI support for simultaneous job template runs --- awx/ui/client/src/forms/JobTemplates.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/awx/ui/client/src/forms/JobTemplates.js b/awx/ui/client/src/forms/JobTemplates.js index 23be861723..07f7ddf7c2 100644 --- a/awx/ui/client/src/forms/JobTemplates.js +++ b/awx/ui/client/src/forms/JobTemplates.js @@ -307,6 +307,17 @@ export default dataContainer: "body", labelClass: 'stack-inline', ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)' + }, { + name: 'allow_simultaneous', + label: i18n._('Enable Concurrent Jobs'), + type: 'checkbox', + column: 2, + awPopOver: "

" + i18n._("If enabled, simultaneous runs of this job template will be allowed.") + "

", + dataPlacement: 'right', + dataTitle: i18n._('Enable Concurrent Jobs'), + dataContainer: "body", + labelClass: 'stack-inline', + ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)' }] }, callback_url: { From bdfac35f3f3695286bf019d4e0df5a497dafc89f Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Wed, 14 Dec 2016 16:26:18 -0500 Subject: [PATCH 07/49] cancel job also cancels project update related to #4225 --- awx/main/models/jobs.py | 9 +++++++++ awx/main/tasks.py | 20 +++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index b7acf21775..b0b98cb955 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -654,6 +654,15 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin): def get_notification_friendly_name(self): return "Job" + ''' + Canceling a job also cancels the implicit project update with launch_type + run. + ''' + def cancel(self): + res = super(Job, self).cancel() + if self.project_update: + self.project_update.cancel() + return res class JobHostSummary(CreatedModifiedModel): ''' diff --git a/awx/main/tasks.py b/awx/main/tasks.py index e0dcae0fca..6e7a1db6c5 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -708,6 +708,11 @@ class BaseTask(Task): stdout_handle.close() except Exception: pass + + instance = self.update_model(pk) + if instance.cancel_flag: + status = 'canceled' + instance = self.update_model(pk, status=status, result_traceback=tb, output_replacements=output_replacements, **extra_update_fields) @@ -1044,17 +1049,18 @@ class RunJob(BaseTask): local_project_sync = job.project.create_project_update(launch_type="sync") local_project_sync.job_type = 'run' local_project_sync.save() + # save the associated project update before calling run() so that a + # cancel() call on the job can cancel the project update + job = self.update_model(job.pk, project_update=local_project_sync) + project_update_task = local_project_sync._get_task_class() try: project_update_task().run(local_project_sync.id) - job.scm_revision = job.project.scm_revision - job.project_update = local_project_sync - job.save() + job = self.update_model(job.pk, scm_revision=project.scm_revision) except Exception: - job.status = 'failed' - job.job_explanation = 'Previous Task Failed: {"job_type": "%s", "job_name": "%s", "job_id": "%s"}' % \ - ('project_update', local_project_sync.name, local_project_sync.id) - job.save() + job = self.update_model(job.pk, status='failed', + job_explanation='Previous Task Failed: {"job_type": "%s", "job_name": "%s", "job_id": "%s"}' % \ + ('project_update', local_project_sync.name, local_project_sync.id)) raise def post_run_hook(self, job, status, **kwargs): From 9c444e084eebb7cafb30304e65f01c7520c4dd98 Mon Sep 17 00:00:00 2001 From: Chris Church Date: Wed, 14 Dec 2016 16:37:44 -0500 Subject: [PATCH 08/49] Handle psutil errors when terminating tasks. --- awx/main/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 2f862e61c2..6d119ce29a 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -618,7 +618,7 @@ class BaseTask(Task): for child_proc in child_procs: os.kill(child_proc.pid, signal.SIGKILL) os.kill(main_proc.pid, signal.SIGKILL) - except TypeError: + except (TypeError, psutil.Error): os.kill(job.pid, signal.SIGKILL) else: os.kill(job.pid, signal.SIGTERM) From d2f267c34c32998609e97ac6d65ca8d178ccfc2f Mon Sep 17 00:00:00 2001 From: jaredevantabor Date: Mon, 12 Dec 2016 18:21:35 -0800 Subject: [PATCH 09/49] fixing options request for template list --- .../list/templates-list.controller.js | 56 +++++++++---------- .../templates/list/templates-list.route.js | 14 +++++ 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/awx/ui/client/src/templates/list/templates-list.controller.js b/awx/ui/client/src/templates/list/templates-list.controller.js index 93f32807ea..ff75f8f79b 100644 --- a/awx/ui/client/src/templates/list/templates-list.controller.js +++ b/awx/ui/client/src/templates/list/templates-list.controller.js @@ -4,15 +4,16 @@ * All Rights Reserved *************************************************/ -export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest', 'Alert', - 'TemplateList', 'Prompt', 'ClearScope', 'ProcessErrors', 'GetBasePath', - 'InitiatePlaybookRun', 'Wait', '$state', '$filter', 'Dataset', 'rbacUiControlService', 'TemplatesService', - 'QuerySet', 'GetChoices', 'TemplateCopyService', +export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest', + 'Alert','TemplateList', 'Prompt', 'ClearScope', 'ProcessErrors', + 'GetBasePath', 'InitiatePlaybookRun', 'Wait', '$state', '$filter', + 'Dataset', 'rbacUiControlService', 'TemplatesService','QuerySet', + 'GetChoices', 'TemplateCopyService', 'DataOptions', function( $scope, $rootScope, $location, $stateParams, Rest, Alert, TemplateList, Prompt, ClearScope, ProcessErrors, GetBasePath, InitiatePlaybookRun, Wait, $state, $filter, Dataset, rbacUiControlService, TemplatesService, - qs, GetChoices, TemplateCopyService + qs, GetChoices, TemplateCopyService, DataOptions ) { ClearScope(); @@ -36,38 +37,31 @@ export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest', 'Al $scope.list = list; $scope[`${list.iterator}_dataset`] = Dataset.data; $scope[list.name] = $scope[`${list.iterator}_dataset`].results; + $scope.options = DataOptions; $rootScope.flashMessage = null; - if ($scope.removeChoicesReady) { - $scope.removeChoicesReady(); - } - $scope.removeChoicesReady = $scope.$on('choicesReady', function() { - $scope[list.name].forEach(function(item, item_idx) { - var itm = $scope[list.name][item_idx]; + $scope.$watchCollection('templates', function() { + $scope[list.name].forEach(function(item, item_idx) { + var itm = $scope[list.name][item_idx]; - // Set the item type label - if (list.fields.type) { - $scope.type_choices.every(function(choice) { - if (choice.value === item.type) { - itm.type_label = choice.label; - return false; - } - return true; - }); - } - }); - }); - - GetChoices({ - scope: $scope, - url: GetBasePath('unified_job_templates'), - field: 'type', - variable: 'type_choices', - callback: 'choicesReady' - }); + // Set the item type label + if (list.fields.type) { + $scope.options.type.choices.every(function(choice) { + if (choice[0] === item.type) { + itm.type_label = choice[1]; + return false; + } + return true; + }); + } + }); + } + ); } + + $scope.$on(`ws-jobs`, function () { // @issue - this is no longer quite as ham-fisted but I'd like for someone else to take a peek // calling $state.reload(); here was problematic when launching a job because job launch also diff --git a/awx/ui/client/src/templates/list/templates-list.route.js b/awx/ui/client/src/templates/list/templates-list.route.js index e615e52db1..5a79700bdb 100644 --- a/awx/ui/client/src/templates/list/templates-list.route.js +++ b/awx/ui/client/src/templates/list/templates-list.route.js @@ -46,6 +46,20 @@ export default { let path = GetBasePath(list.basePath) || GetBasePath(list.name); return qs.search(path, $stateParams[`${list.iterator}_search`]); } + ], + DataOptions: ['Rest', 'GetBasePath', '$stateParams', '$q', 'TemplateList', + function(Rest, GetBasePath, $stateParams, $q, list) { + let path = GetBasePath(list.basePath) || GetBasePath(list.name); + Rest.setUrl(path); + var val = $q.defer(); + Rest.options() + .then(function(data) { + val.resolve(data.data.actions.GET); + }, function(data) { + val.reject(data); + }); + return val.promise; + } ] } }; From 092c2be0e768da42cd17a6ad23c264d8b29a9507 Mon Sep 17 00:00:00 2001 From: Chris Church Date: Wed, 14 Dec 2016 16:57:32 -0500 Subject: [PATCH 10/49] Make any settings read-only that have been modified in custom Python config files. --- awx/conf/settings.py | 16 ++++++++++++++++ awx/settings/development.py | 15 +++++++-------- awx/settings/production.py | 15 +++++++-------- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/awx/conf/settings.py b/awx/conf/settings.py index c08b161237..de8c82e1c5 100644 --- a/awx/conf/settings.py +++ b/awx/conf/settings.py @@ -1,6 +1,7 @@ # Python import contextlib import logging +import sys import threading import time @@ -86,6 +87,7 @@ class SettingsWrapper(UserSettingsHolder): self.__dict__['_awx_conf_settings'] = self self.__dict__['_awx_conf_preload_expires'] = None self.__dict__['_awx_conf_preload_lock'] = threading.RLock() + self.__dict__['_awx_conf_init_readonly'] = False def _get_supported_settings(self): return settings_registry.get_registered_settings() @@ -110,6 +112,20 @@ class SettingsWrapper(UserSettingsHolder): return # Otherwise update local preload timeout. self.__dict__['_awx_conf_preload_expires'] = time.time() + SETTING_CACHE_TIMEOUT + # Check for any settings that have been defined in Python files and + # make those read-only to avoid overriding in the database. + if not self._awx_conf_init_readonly and 'migrate_to_database_settings' not in sys.argv: + defaults_snapshot = self._get_default('DEFAULTS_SNAPSHOT') + for key in self._get_writeable_settings(): + init_default = defaults_snapshot.get(key, None) + try: + file_default = self._get_default(key) + except AttributeError: + file_default = None + if file_default != init_default and file_default is not None: + logger.warning('Setting %s has been marked read-only!', key) + settings_registry._registry[key]['read_only'] = True + self.__dict__['_awx_conf_init_readonly'] = True # If local preload timer has expired, check to see if another process # has already preloaded the cache and skip preloading if so. if cache.get('_awx_conf_preload_expires', empty) is not empty: diff --git a/awx/settings/development.py b/awx/settings/development.py index f2d72a1113..ebe81260d1 100644 --- a/awx/settings/development.py +++ b/awx/settings/development.py @@ -82,14 +82,13 @@ PASSWORD_HASHERS = ( # Configure a default UUID for development only. SYSTEM_UUID = '00000000-0000-0000-0000-000000000000' -# Store a snapshot of default settings at this point (only for migrating from -# file to database settings). -if 'migrate_to_database_settings' in sys.argv: - DEFAULTS_SNAPSHOT = {} - this_module = sys.modules[__name__] - for setting in dir(this_module): - if setting == setting.upper(): - DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting)) +# Store a snapshot of default settings at this point before loading any +# customizable config files. +DEFAULTS_SNAPSHOT = {} +this_module = sys.modules[__name__] +for setting in dir(this_module): + if setting == setting.upper(): + DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting)) # If there is an `/etc/tower/settings.py`, include it. # If there is a `/etc/tower/conf.d/*.py`, include them. diff --git a/awx/settings/production.py b/awx/settings/production.py index 103f775d86..f056a4ea31 100644 --- a/awx/settings/production.py +++ b/awx/settings/production.py @@ -57,14 +57,13 @@ LOGGING['handlers']['fact_receiver']['filename'] = '/var/log/tower/fact_receiver LOGGING['handlers']['system_tracking_migrations']['filename'] = '/var/log/tower/tower_system_tracking_migrations.log' LOGGING['handlers']['rbac_migrations']['filename'] = '/var/log/tower/tower_rbac_migrations.log' -# Store a snapshot of default settings at this point (only for migrating from -# file to database settings). -if 'migrate_to_database_settings' in sys.argv: - DEFAULTS_SNAPSHOT = {} - this_module = sys.modules[__name__] - for setting in dir(this_module): - if setting == setting.upper(): - DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting)) +# Store a snapshot of default settings at this point before loading any +# customizable config files. +DEFAULTS_SNAPSHOT = {} +this_module = sys.modules[__name__] +for setting in dir(this_module): + if setting == setting.upper(): + DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting)) # Load settings from any .py files in the global conf.d directory specified in # the environment, defaulting to /etc/tower/conf.d/. From 0900484c621809847c2225d52914708074fa7366 Mon Sep 17 00:00:00 2001 From: jaredevantabor Date: Wed, 14 Dec 2016 14:25:08 -0800 Subject: [PATCH 11/49] hiding status icon on right hand side unless fullscreen is true for job results and workflow results, per request of trahman --- awx/ui/client/src/job-results/job-results.partial.html | 1 + awx/ui/client/src/workflow-results/workflow-results.partial.html | 1 + 2 files changed, 2 insertions(+) diff --git a/awx/ui/client/src/job-results/job-results.partial.html b/awx/ui/client/src/job-results/job-results.partial.html index c7fd2a1e0d..a763756769 100644 --- a/awx/ui/client/src/job-results/job-results.partial.html +++ b/awx/ui/client/src/job-results/job-results.partial.html @@ -414,6 +414,7 @@
diff --git a/awx/ui/client/src/workflow-results/workflow-results.partial.html b/awx/ui/client/src/workflow-results/workflow-results.partial.html index 998a37c956..ff2cd94a0d 100644 --- a/awx/ui/client/src/workflow-results/workflow-results.partial.html +++ b/awx/ui/client/src/workflow-results/workflow-results.partial.html @@ -183,6 +183,7 @@
From 976e9bfe82a51ee60c4a92ac036a19a87bf99f96 Mon Sep 17 00:00:00 2001 From: jaredevantabor Date: Wed, 14 Dec 2016 14:58:40 -0800 Subject: [PATCH 12/49] adding callback for when options are received --- .../shared/smart-search/queryset.service.js | 18 ++++++++++++++---- .../smart-search/smart-search.controller.js | 9 +++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/awx/ui/client/src/shared/smart-search/queryset.service.js b/awx/ui/client/src/shared/smart-search/queryset.service.js index a45f8af74f..359245bed4 100644 --- a/awx/ui/client/src/shared/smart-search/queryset.service.js +++ b/awx/ui/client/src/shared/smart-search/queryset.service.js @@ -18,13 +18,23 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear // grab a single model from the cache, if present if (cache.get(path)) { - defer.resolve({[name] : new DjangoSearchModel(name, path, cache.get(path), relations)}); + defer.resolve({ + models: { + [name] : new DjangoSearchModel(name, path, cache.get(path), relations) + }, + options: cache.get(path) + }); } else { this.url = path; resolve = this.options(path) .then((res) => { base = res.data.actions.GET; - defer.resolve({[name]: new DjangoSearchModel(name, path, base, relations)}); + defer.resolve({ + models: { + [name]: new DjangoSearchModel(name, path, base, relations) + }, + options: res + }); }); } return defer.promise; @@ -76,9 +86,9 @@ export default ['$q', 'Rest', 'ProcessErrors', '$rootScope', 'Wait', 'DjangoSear if (Array.isArray(value)){ return _.map(value, (item) => { return `${key.split('__').join(':')}:${item}`; - }); + }); } - else { + else { return `${key.split('__').join(':')}:${value}`; } }, diff --git a/awx/ui/client/src/shared/smart-search/smart-search.controller.js b/awx/ui/client/src/shared/smart-search/smart-search.controller.js index 87dc33198a..3352cb5495 100644 --- a/awx/ui/client/src/shared/smart-search/smart-search.controller.js +++ b/awx/ui/client/src/shared/smart-search/smart-search.controller.js @@ -15,8 +15,9 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', ' path = GetBasePath($scope.basePath) || $scope.basePath; relations = getRelationshipFields($scope.dataset.results); $scope.searchTags = stripDefaultParams($state.params[`${$scope.iterator}_search`]); - qs.initFieldset(path, $scope.djangoModel, relations).then((models) => { - $scope.models = models; + qs.initFieldset(path, $scope.djangoModel, relations).then((data) => { + $scope.models = data.models; + $scope.$emit(`${$scope.list.iterator}_options`, data.options); }); } @@ -102,12 +103,12 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', ' params.page = '1'; queryset = _.merge(queryset, params, (objectValue, sourceValue, key, object) => { if (object[key] && object[key] !== sourceValue){ - return [object[key], sourceValue]; + return [object[key], sourceValue]; } else { // // https://lodash.com/docs/3.10.1#merge // If customizer fn returns undefined merging is handled by default _.merge algorithm - return undefined; + return undefined; } }); // https://ui-router.github.io/docs/latest/interfaces/params.paramdeclaration.html#dynamic From c4ce68b4de8c1393fbade3ff191f14141ffe1686 Mon Sep 17 00:00:00 2001 From: jaredevantabor Date: Wed, 14 Dec 2016 14:59:05 -0800 Subject: [PATCH 13/49] applying callback mechanism to job tmeplate list --- .../list/templates-list.controller.js | 52 +++++++++++-------- .../templates/list/templates-list.route.js | 14 ----- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/awx/ui/client/src/templates/list/templates-list.controller.js b/awx/ui/client/src/templates/list/templates-list.controller.js index ff75f8f79b..51b353a51a 100644 --- a/awx/ui/client/src/templates/list/templates-list.controller.js +++ b/awx/ui/client/src/templates/list/templates-list.controller.js @@ -8,12 +8,12 @@ export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest', 'Alert','TemplateList', 'Prompt', 'ClearScope', 'ProcessErrors', 'GetBasePath', 'InitiatePlaybookRun', 'Wait', '$state', '$filter', 'Dataset', 'rbacUiControlService', 'TemplatesService','QuerySet', - 'GetChoices', 'TemplateCopyService', 'DataOptions', + 'GetChoices', 'TemplateCopyService', function( $scope, $rootScope, $location, $stateParams, Rest, Alert, TemplateList, Prompt, ClearScope, ProcessErrors, GetBasePath, InitiatePlaybookRun, Wait, $state, $filter, Dataset, rbacUiControlService, TemplatesService, - qs, GetChoices, TemplateCopyService, DataOptions + qs, GetChoices, TemplateCopyService ) { ClearScope(); @@ -37,29 +37,39 @@ export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest', $scope.list = list; $scope[`${list.iterator}_dataset`] = Dataset.data; $scope[list.name] = $scope[`${list.iterator}_dataset`].results; - $scope.options = DataOptions; + $scope.options = {}; $rootScope.flashMessage = null; - - $scope.$watchCollection('templates', function() { - $scope[list.name].forEach(function(item, item_idx) { - var itm = $scope[list.name][item_idx]; - - // Set the item type label - if (list.fields.type) { - $scope.options.type.choices.every(function(choice) { - if (choice[0] === item.type) { - itm.type_label = choice[1]; - return false; - } - return true; - }); - } - }); - } - ); } + $scope.$on(`${list.iterator}_options`, function(event, data){ + debugger; + $scope.options = data.data.actions.GET; + optionsRequestDataProcessing(); + }); + + $scope.$watchCollection('templates', function() { + optionsRequestDataProcessing(); + } + ); + // iterate over the list and add fields like type label, after the + // OPTIONS request returns, or the list is sorted/paginated/searched + function optionsRequestDataProcessing(){ + $scope[list.name].forEach(function(item, item_idx) { + var itm = $scope[list.name][item_idx]; + + // Set the item type label + if (list.fields.type && $scope.options.hasOwnProperty('type')) { + $scope.options.type.choices.every(function(choice) { + if (choice[0] === item.type) { + itm.type_label = choice[1]; + return false; + } + return true; + }); + } + }); + } $scope.$on(`ws-jobs`, function () { diff --git a/awx/ui/client/src/templates/list/templates-list.route.js b/awx/ui/client/src/templates/list/templates-list.route.js index 5a79700bdb..e615e52db1 100644 --- a/awx/ui/client/src/templates/list/templates-list.route.js +++ b/awx/ui/client/src/templates/list/templates-list.route.js @@ -46,20 +46,6 @@ export default { let path = GetBasePath(list.basePath) || GetBasePath(list.name); return qs.search(path, $stateParams[`${list.iterator}_search`]); } - ], - DataOptions: ['Rest', 'GetBasePath', '$stateParams', '$q', 'TemplateList', - function(Rest, GetBasePath, $stateParams, $q, list) { - let path = GetBasePath(list.basePath) || GetBasePath(list.name); - Rest.setUrl(path); - var val = $q.defer(); - Rest.options() - .then(function(data) { - val.resolve(data.data.actions.GET); - }, function(data) { - val.reject(data); - }); - return val.promise; - } ] } }; From 9e3b5abc37623711aab6bad99f1bd73cd3cb7f2e Mon Sep 17 00:00:00 2001 From: jaredevantabor Date: Wed, 14 Dec 2016 17:05:15 -0800 Subject: [PATCH 14/49] fixing post-processing for projects list --- awx/ui/client/src/controllers/Projects.js | 38 +++++++++++++++++-- awx/ui/client/src/lists/Projects.js | 1 + .../list/templates-list.controller.js | 1 - 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/awx/ui/client/src/controllers/Projects.js b/awx/ui/client/src/controllers/Projects.js index 17076a2a73..1eb8fbb2df 100644 --- a/awx/ui/client/src/controllers/Projects.js +++ b/awx/ui/client/src/controllers/Projects.js @@ -37,11 +37,43 @@ export function ProjectsList($scope, $rootScope, $location, $log, $stateParams, _.forEach($scope[list.name], buildTooltips); $rootScope.flashMessage = null; } - - $scope.$watch(`${list.name}`, function() { - _.forEach($scope[list.name], buildTooltips); + + $scope.$on(`${list.iterator}_options`, function(event, data){ + $scope.options = data.data.actions.GET; + optionsRequestDataProcessing(); }); + $scope.$watchCollection(`${$scope.list.name}`, function() { + optionsRequestDataProcessing(); + } + ); + + // iterate over the list and add fields like type label, after the + // OPTIONS request returns, or the list is sorted/paginated/searched + function optionsRequestDataProcessing(){ + $scope[list.name].forEach(function(item, item_idx) { + var itm = $scope[list.name][item_idx]; + + // Set the item type label + if (list.fields.scm_type && $scope.options && + $scope.options.hasOwnProperty('scm_type')) { + $scope.options.scm_type.choices.every(function(choice) { + if (choice[0] === item.scm_type) { + itm.type_label = choice[1]; + return false; + } + return true; + }); + } + + _.forEach($scope[list.name], buildTooltips); + + }); + } + // $scope.$watch(`${list.name}`, function() { + // _.forEach($scope[list.name], buildTooltips); + // }); + function buildTooltips(project) { project.statusIcon = GetProjectIcon(project.status); project.statusTip = GetProjectToolTip(project.status); diff --git a/awx/ui/client/src/lists/Projects.js b/awx/ui/client/src/lists/Projects.js index 83cb2eb7e7..5115b0d662 100644 --- a/awx/ui/client/src/lists/Projects.js +++ b/awx/ui/client/src/lists/Projects.js @@ -46,6 +46,7 @@ export default }, scm_type: { label: i18n._('Type'), + ngBind: 'project.type_label', excludeModal: true, columnClass: 'col-lg-3 col-md-2 col-sm-3 hidden-xs' }, diff --git a/awx/ui/client/src/templates/list/templates-list.controller.js b/awx/ui/client/src/templates/list/templates-list.controller.js index 51b353a51a..5bcd659a38 100644 --- a/awx/ui/client/src/templates/list/templates-list.controller.js +++ b/awx/ui/client/src/templates/list/templates-list.controller.js @@ -43,7 +43,6 @@ export default ['$scope', '$rootScope', '$location', '$stateParams', 'Rest', } $scope.$on(`${list.iterator}_options`, function(event, data){ - debugger; $scope.options = data.data.actions.GET; optionsRequestDataProcessing(); }); From db4bb50976cff6a60e292e2a4f32fee8b6e3ca8d Mon Sep 17 00:00:00 2001 From: jaredevantabor Date: Wed, 14 Dec 2016 17:24:21 -0800 Subject: [PATCH 15/49] adding post-processing for jobs list --- awx/ui/client/src/controllers/Jobs.js | 83 +++++++++++++++-------- awx/ui/client/src/controllers/Projects.js | 7 +- 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/awx/ui/client/src/controllers/Jobs.js b/awx/ui/client/src/controllers/Jobs.js index c68c6c5826..3ea53d3191 100644 --- a/awx/ui/client/src/controllers/Jobs.js +++ b/awx/ui/client/src/controllers/Jobs.js @@ -13,7 +13,7 @@ export function JobsListController($state, $rootScope, $log, $scope, $compile, $stateParams, - ClearScope, Find, DeleteJob, RelaunchJob, AllJobsList, ScheduledJobsList, GetBasePath, Dataset, GetChoices) { + ClearScope, Find, DeleteJob, RelaunchJob, AllJobsList, ScheduledJobsList, GetBasePath, Dataset) { ClearScope(); @@ -29,39 +29,68 @@ export function JobsListController($state, $rootScope, $log, $scope, $compile, $ $scope.showJobType = true; - _.forEach($scope[list.name], buildTooltips); - if ($scope.removeChoicesReady) { - $scope.removeChoicesReady(); + // _.forEach($scope[list.name], buildTooltips); + // if ($scope.removeChoicesReady) { + // $scope.removeChoicesReady(); + // } + // $scope.removeChoicesReady = $scope.$on('choicesReady', function() { + // $scope[list.name].forEach(function(item, item_idx) { + // var itm = $scope[list.name][item_idx]; + // if(item.summary_fields && item.summary_fields.source_workflow_job && + // item.summary_fields.source_workflow_job.id){ + // item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`; + // } + // // Set the item type label + // if (list.fields.type) { + // $scope.type_choices.every(function(choice) { + // if (choice.value === item.type) { + // itm.type_label = choice.label; + // return false; + // } + // return true; + // }); + // } + // }); + // }); + + + } + $scope.$on(`${list.iterator}_options`, function(event, data){ + $scope.options = data.data.actions.GET; + optionsRequestDataProcessing(); + }); + + $scope.$watchCollection(`${$scope.list.name}`, function() { + optionsRequestDataProcessing(); } - $scope.removeChoicesReady = $scope.$on('choicesReady', function() { - $scope[list.name].forEach(function(item, item_idx) { - var itm = $scope[list.name][item_idx]; - if(item.summary_fields && item.summary_fields.source_workflow_job && - item.summary_fields.source_workflow_job.id){ - item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`; - } - // Set the item type label - if (list.fields.type) { - $scope.type_choices.every(function(choice) { - if (choice.value === item.type) { - itm.type_label = choice.label; + ); + + // iterate over the list and add fields like type label, after the + // OPTIONS request returns, or the list is sorted/paginated/searched + function optionsRequestDataProcessing(){ + + $scope[list.name].forEach(function(item, item_idx) { + var itm = $scope[list.name][item_idx]; + + if(item.summary_fields && item.summary_fields.source_workflow_job && + item.summary_fields.source_workflow_job.id){ + item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`; + } + + // Set the item type label + if (list.fields.type && $scope.options && + $scope.options.hasOwnProperty('type')) { + $scope.options.type.choices.every(function(choice) { + if (choice[0] === item.type) { + itm.type_label = choice[1]; return false; } return true; }); } - }); - }); - - GetChoices({ - scope: $scope, - url: GetBasePath('unified_jobs'), - field: 'type', - variable: 'type_choices', - callback: 'choicesReady' + buildTooltips(itm); }); } - function buildTooltips(job) { job.status_tip = 'Job ' + job.status + ". Click for details."; } @@ -131,5 +160,5 @@ export function JobsListController($state, $rootScope, $log, $scope, $compile, $ } JobsListController.$inject = ['$state', '$rootScope', '$log', '$scope', '$compile', '$stateParams', - 'ClearScope', 'Find', 'DeleteJob', 'RelaunchJob', 'AllJobsList', 'ScheduledJobsList', 'GetBasePath', 'Dataset', 'GetChoices' + 'ClearScope', 'Find', 'DeleteJob', 'RelaunchJob', 'AllJobsList', 'ScheduledJobsList', 'GetBasePath', 'Dataset' ]; diff --git a/awx/ui/client/src/controllers/Projects.js b/awx/ui/client/src/controllers/Projects.js index 1eb8fbb2df..d0c845a058 100644 --- a/awx/ui/client/src/controllers/Projects.js +++ b/awx/ui/client/src/controllers/Projects.js @@ -37,7 +37,7 @@ export function ProjectsList($scope, $rootScope, $location, $log, $stateParams, _.forEach($scope[list.name], buildTooltips); $rootScope.flashMessage = null; } - + $scope.$on(`${list.iterator}_options`, function(event, data){ $scope.options = data.data.actions.GET; optionsRequestDataProcessing(); @@ -66,13 +66,10 @@ export function ProjectsList($scope, $rootScope, $location, $log, $stateParams, }); } - _.forEach($scope[list.name], buildTooltips); + buildTooltips(itm); }); } - // $scope.$watch(`${list.name}`, function() { - // _.forEach($scope[list.name], buildTooltips); - // }); function buildTooltips(project) { project.statusIcon = GetProjectIcon(project.status); From 8c55eaec99dde23c9e8ee66f377d47276edf04d5 Mon Sep 17 00:00:00 2001 From: jaredevantabor Date: Wed, 14 Dec 2016 17:38:09 -0800 Subject: [PATCH 16/49] post processing for credentials list --- awx/ui/client/src/controllers/Credentials.js | 30 ++++++++++++++++++++ awx/ui/client/src/controllers/Jobs.js | 27 +----------------- awx/ui/client/src/lists/Credentials.js | 1 + 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/awx/ui/client/src/controllers/Credentials.js b/awx/ui/client/src/controllers/Credentials.js index 1681205992..8ccc5afd78 100644 --- a/awx/ui/client/src/controllers/Credentials.js +++ b/awx/ui/client/src/controllers/Credentials.js @@ -36,6 +36,36 @@ export function CredentialsList($scope, $rootScope, $location, $log, $scope.selected = []; } + $scope.$on(`${list.iterator}_options`, function(event, data){ + $scope.options = data.data.actions.GET; + optionsRequestDataProcessing(); + }); + + $scope.$watchCollection(`${$scope.list.name}`, function() { + optionsRequestDataProcessing(); + } + ); + // iterate over the list and add fields like type label, after the + // OPTIONS request returns, or the list is sorted/paginated/searched + function optionsRequestDataProcessing(){ + $scope[list.name].forEach(function(item, item_idx) { + var itm = $scope[list.name][item_idx]; + + // Set the item type label + debugger; + if (list.fields.kind && $scope.options && + $scope.options.hasOwnProperty('kind')) { + $scope.options.kind.choices.every(function(choice) { + if (choice[0] === item.kind) { + itm.kind_label = choice[1]; + return false; + } + return true; + }); + } + }); + } + $scope.addCredential = function() { $state.go('credentials.add'); }; diff --git a/awx/ui/client/src/controllers/Jobs.js b/awx/ui/client/src/controllers/Jobs.js index 3ea53d3191..cbf588200c 100644 --- a/awx/ui/client/src/controllers/Jobs.js +++ b/awx/ui/client/src/controllers/Jobs.js @@ -28,33 +28,8 @@ export function JobsListController($state, $rootScope, $log, $scope, $compile, $ $scope[list.name] = $scope[`${list.iterator}_dataset`].results; $scope.showJobType = true; - - // _.forEach($scope[list.name], buildTooltips); - // if ($scope.removeChoicesReady) { - // $scope.removeChoicesReady(); - // } - // $scope.removeChoicesReady = $scope.$on('choicesReady', function() { - // $scope[list.name].forEach(function(item, item_idx) { - // var itm = $scope[list.name][item_idx]; - // if(item.summary_fields && item.summary_fields.source_workflow_job && - // item.summary_fields.source_workflow_job.id){ - // item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`; - // } - // // Set the item type label - // if (list.fields.type) { - // $scope.type_choices.every(function(choice) { - // if (choice.value === item.type) { - // itm.type_label = choice.label; - // return false; - // } - // return true; - // }); - // } - // }); - // }); - - } + $scope.$on(`${list.iterator}_options`, function(event, data){ $scope.options = data.data.actions.GET; optionsRequestDataProcessing(); diff --git a/awx/ui/client/src/lists/Credentials.js b/awx/ui/client/src/lists/Credentials.js index d099f2826e..c6e8d6bbae 100644 --- a/awx/ui/client/src/lists/Credentials.js +++ b/awx/ui/client/src/lists/Credentials.js @@ -37,6 +37,7 @@ export default }, kind: { label: i18n._('Type'), + ngBind: 'credential.kind_label', excludeModal: true, nosort: true, columnClass: 'col-md-2 hidden-sm hidden-xs' From 7d90a5205f27787d8f6a99d449d0c9255370aeff Mon Sep 17 00:00:00 2001 From: jaredevantabor Date: Wed, 14 Dec 2016 20:12:04 -0800 Subject: [PATCH 17/49] removing debugger statement --- awx/ui/client/src/controllers/Credentials.js | 1 - 1 file changed, 1 deletion(-) diff --git a/awx/ui/client/src/controllers/Credentials.js b/awx/ui/client/src/controllers/Credentials.js index 8ccc5afd78..a64056ccd4 100644 --- a/awx/ui/client/src/controllers/Credentials.js +++ b/awx/ui/client/src/controllers/Credentials.js @@ -52,7 +52,6 @@ export function CredentialsList($scope, $rootScope, $location, $log, var itm = $scope[list.name][item_idx]; // Set the item type label - debugger; if (list.fields.kind && $scope.options && $scope.options.hasOwnProperty('kind')) { $scope.options.kind.choices.every(function(choice) { From 3ee8c4159aac0222e264e612177c8202e6e8783e Mon Sep 17 00:00:00 2001 From: Jake McDermott Date: Thu, 15 Dec 2016 00:16:40 -0500 Subject: [PATCH 18/49] fix tooltip message for notification template (#4448) --- awx/ui/client/src/notifications/notificationTemplates.list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/notifications/notificationTemplates.list.js b/awx/ui/client/src/notifications/notificationTemplates.list.js index f0e1d527d5..eb1d0bbfa7 100644 --- a/awx/ui/client/src/notifications/notificationTemplates.list.js +++ b/awx/ui/client/src/notifications/notificationTemplates.list.js @@ -47,7 +47,7 @@ export default ['i18n', function(i18n){ add: { mode: 'all', // One of: edit, select, all ngClick: 'addNotification()', - awToolTip: i18n._('Create a new custom inventory'), + awToolTip: i18n._('Create a new notification template'), actionClass: 'btn List-buttonSubmit', buttonContent: '+ ' + i18n._('ADD'), ngShow: 'canAdd' From 817a57267462802ea38bf51b3dbe118df0a7c09d Mon Sep 17 00:00:00 2001 From: jaredevantabor Date: Wed, 14 Dec 2016 22:00:20 -0800 Subject: [PATCH 19/49] adding post processing for notification template list --- .../list.controller.js | 54 ++++++++++--------- .../notificationTemplates.list.js | 1 + 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/awx/ui/client/src/notifications/notification-templates-list/list.controller.js b/awx/ui/client/src/notifications/notification-templates-list/list.controller.js index e82cc8219e..208c4a3b8d 100644 --- a/awx/ui/client/src/notifications/notification-templates-list/list.controller.js +++ b/awx/ui/client/src/notifications/notification-templates-list/list.controller.js @@ -29,34 +29,38 @@ $scope.list = list; $scope[`${list.iterator}_dataset`] = Dataset.data; $scope[list.name] = $scope[`${list.iterator}_dataset`].results; - - GetChoices({ - scope: $scope, - url: defaultUrl, - field: 'notification_type', - variable: 'notification_type_options', - callback: 'choicesReadyNotifierList' - }); } - $scope.removeChoicesHere = $scope.$on('choicesReadyNotifierList', function() { - list.fields.notification_type.searchOptions = $scope.notification_type_options; - - if ($rootScope.addedItem) { - $scope.addedItem = $rootScope.addedItem; - delete $rootScope.addedItem; - } - $scope.notification_templates.forEach(function(notification_template, i) { - setStatus(notification_template); - $scope.notification_type_options.forEach(function(type) { - if (type.value === notification_template.notification_type) { - $scope.notification_templates[i].notification_type = type.label; - var recent_notifications = notification_template.summary_fields.recent_notifications; - $scope.notification_templates[i].status = recent_notifications && recent_notifications.length > 0 ? recent_notifications[0].status : "none"; - } - }); + $scope.$on(`notification_template_options`, function(event, data){ + $scope.options = data.data.actions.GET; + optionsRequestDataProcessing(); }); - }); + + $scope.$watchCollection("notification_templates", function() { + optionsRequestDataProcessing(); + } + ); + // iterate over the list and add fields like type label, after the + // OPTIONS request returns, or the list is sorted/paginated/searched. + function optionsRequestDataProcessing(){ + $scope[list.name].forEach(function(item, item_idx) { + var itm = $scope[list.name][item_idx]; + // Set the item type label + if (list.fields.notification_type && $scope.options && + $scope.options.hasOwnProperty('notification_type')) { + $scope.options.notification_type.choices.every(function(choice) { + if (choice[0] === item.notification_type) { + itm.type_label = choice[1]; + var recent_notifications = itm.summary_fields.recent_notifications; + itm.status = recent_notifications && recent_notifications.length > 0 ? recent_notifications[0].status : "none"; + return false; + } + return true; + }); + } + setStatus(itm); + }); + } function setStatus(notification_template) { var html, recent_notifications = notification_template.summary_fields.recent_notifications; diff --git a/awx/ui/client/src/notifications/notificationTemplates.list.js b/awx/ui/client/src/notifications/notificationTemplates.list.js index f0e1d527d5..0987e5fcab 100644 --- a/awx/ui/client/src/notifications/notificationTemplates.list.js +++ b/awx/ui/client/src/notifications/notificationTemplates.list.js @@ -36,6 +36,7 @@ export default ['i18n', function(i18n){ }, notification_type: { label: i18n._('Type'), + ngBind: "notification_template.type_label", searchType: 'select', searchOptions: [], excludeModal: true, From f026a96d94f8b241b68128b8c907f518ae900ae7 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Thu, 15 Dec 2016 08:59:24 -0500 Subject: [PATCH 20/49] flake8 fix --- awx/main/tasks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 705d2153e2..2ad3343607 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -1060,8 +1060,8 @@ class RunJob(BaseTask): job = self.update_model(job.pk, scm_revision=project.scm_revision) except Exception: job = self.update_model(job.pk, status='failed', - job_explanation='Previous Task Failed: {"job_type": "%s", "job_name": "%s", "job_id": "%s"}' % \ - ('project_update', local_project_sync.name, local_project_sync.id)) + job_explanation=('Previous Task Failed: {"job_type": "%s", "job_name": "%s", "job_id": "%s"}' % + ('project_update', local_project_sync.name, local_project_sync.id))) raise def post_run_hook(self, job, status, **kwargs): From 2ec47737bb0cb1d57c43e39a526b323f4f88ca29 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Thu, 15 Dec 2016 09:29:31 -0500 Subject: [PATCH 21/49] another flake8 fix --- awx/main/models/jobs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index 374cc002bb..8a6b9fc91d 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -664,6 +664,7 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin): self.project_update.cancel() return res + class JobHostSummary(CreatedModifiedModel): ''' Per-host statistics for each job. From e69f6726d0ed72eaa49cf14751cda6e03d3d4cb8 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Thu, 15 Dec 2016 09:31:58 -0500 Subject: [PATCH 22/49] fix unit test --- awx/main/tests/unit/test_network_credential.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/tests/unit/test_network_credential.py b/awx/main/tests/unit/test_network_credential.py index 6517c14a89..7ae97fe76b 100644 --- a/awx/main/tests/unit/test_network_credential.py +++ b/awx/main/tests/unit/test_network_credential.py @@ -77,7 +77,7 @@ def test_net_cred_ssh_agent(mocker, get_ssh_version): mocker.patch.object(run_job, 'post_run_hook', return_value=None) run_job.run(mock_job.id) - assert run_job.update_model.call_count == 3 + assert run_job.update_model.call_count == 4 job_args = run_job.update_model.call_args_list[1][1].get('job_args') assert 'ssh-add' in job_args From 69292df170fc0482e2c86b8db9a88ad6c4344442 Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Thu, 15 Dec 2016 12:05:22 -0500 Subject: [PATCH 23/49] Moved toggle password logic to a directive that can be added as an attribute of the button. --- awx/ui/client/src/shared/directives.js | 18 ++++++++++++++++++ awx/ui/client/src/shared/form-generator.js | 15 +-------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/awx/ui/client/src/shared/directives.js b/awx/ui/client/src/shared/directives.js index 16a0faea9a..3c938d343e 100644 --- a/awx/ui/client/src/shared/directives.js +++ b/awx/ui/client/src/shared/directives.js @@ -1133,4 +1133,22 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'JobsHelper']) }); } }; +}]) + +.directive('awPasswordToggle', [function() { + return { + restrict: 'A', + link: function(scope, element) { + $(element).click(function() { + var buttonInnerHTML = $(element).html(); + if (buttonInnerHTML.indexOf("Show") > -1) { + $(element).html("Hide"); + $(element).closest('.input-group').find('input').first().attr("type", "text"); + } else { + $(element).html("Show"); + $(element).closest('.input-group').find('input').first().attr("type", "password"); + } + }); + } + }; }]); diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js index aca1c2857c..d487efa925 100644 --- a/awx/ui/client/src/shared/form-generator.js +++ b/awx/ui/client/src/shared/form-generator.js @@ -846,28 +846,15 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat html += "\t" + label(); if (field.hasShowInputButton) { var tooltip = i18n._("Toggle the display of plaintext."); - field.toggleInput = function(id) { - var buttonId = id + "_show_input_button", - inputId = id + "_input", - buttonInnerHTML = $(buttonId).html(); - if (buttonInnerHTML.indexOf("Show") > -1) { - $(buttonId).html("Hide"); - $(inputId).attr("type", "text"); - } else { - $(buttonId).html("Show"); - $(inputId).attr("type", "password"); - } - }; html += "\
\n"; // TODO: make it so that the button won't show up if the mode is edit, hasShowInputButton !== true, and there are no contents in the field. html += "\n"; - html += "
-
+
Use the inventory root
diff --git a/awx/ui/client/src/inventories/manage/copy-move/copy-move.route.js b/awx/ui/client/src/inventories/manage/copy-move/copy-move.route.js index 6e2c190e3f..f15705778d 100644 --- a/awx/ui/client/src/inventories/manage/copy-move/copy-move.route.js +++ b/awx/ui/client/src/inventories/manage/copy-move/copy-move.route.js @@ -10,14 +10,31 @@ import CopyMoveHostsController from './copy-move-hosts.controller'; var copyMoveGroupRoute = { name: 'inventoryManage.copyMoveGroup', - url: '/copy-move-group/{group_id}', + url: '/copy-move-group/{group_id:int}', + searchPrefix: 'copy', data: { group_id: 'group_id', }, + params: { + copy_search: { + value: { + not__id__in: null + }, + dynamic: true, + squash: '' + } + }, ncyBreadcrumb: { label: "COPY OR MOVE {{item.name}}" }, resolve: { + Dataset: ['CopyMoveGroupList', 'QuerySet', '$stateParams', 'GetBasePath', 'group', + function(list, qs, $stateParams, GetBasePath, group) { + $stateParams.copy_search.not__id__in = ($stateParams.group.length > 0 ? group.id + ',' + _.last($stateParams.group) : group.id); + let path = GetBasePath(list.name); + return qs.search(path, $stateParams.copy_search); + } + ], group: ['GroupManageService', '$stateParams', function(GroupManageService, $stateParams){ return GroupManageService.get({id: $stateParams.group_id}).then(res => res.data.results[0]); }] @@ -26,16 +43,33 @@ var copyMoveGroupRoute = { 'form@inventoryManage' : { controller: CopyMoveGroupsController, templateUrl: templateUrl('inventories/manage/copy-move/copy-move'), + }, + 'copyMoveList@inventoryManage.copyMoveGroup': { + templateProvider: function(CopyMoveGroupList, generateList) { + let html = generateList.build({ + list: CopyMoveGroupList, + mode: 'lookup', + input_type: 'radio' + }); + return html; + } } } }; var copyMoveHostRoute = { name: 'inventoryManage.copyMoveHost', url: '/copy-move-host/{host_id}', + searchPrefix: 'copy', ncyBreadcrumb: { label: "COPY OR MOVE {{item.name}}" }, resolve: { + Dataset: ['CopyMoveGroupList', 'QuerySet', '$stateParams', 'GetBasePath', + function(list, qs, $stateParams, GetBasePath) { + let path = GetBasePath(list.name); + return qs.search(path, $stateParams.copy_search); + } + ], host: ['HostManageService', '$stateParams', function(HostManageService, $stateParams){ return HostManageService.get({id: $stateParams.host_id}).then(res => res.data.results[0]); }] @@ -44,6 +78,16 @@ var copyMoveHostRoute = { 'form@inventoryManage': { templateUrl: templateUrl('inventories/manage/copy-move/copy-move'), controller: CopyMoveHostsController, + }, + 'copyMoveList@inventoryManage.copyMoveHost': { + templateProvider: function(CopyMoveGroupList, generateList) { + let html = generateList.build({ + list: CopyMoveGroupList, + mode: 'lookup', + input_type: 'radio' + }); + return html; + } } } }; diff --git a/awx/ui/client/src/lists/Groups.js b/awx/ui/client/src/lists/Groups.js index 072c35f9a3..0bfc6a9f3f 100644 --- a/awx/ui/client/src/lists/Groups.js +++ b/awx/ui/client/src/lists/Groups.js @@ -12,7 +12,7 @@ export default .value('CopyMoveGroupList', { name: 'groups', - iterator: 'group', + iterator: 'copy', selectTitle: 'Copy Groups', index: false, well: false, diff --git a/awx/ui/client/src/shared/stateExtender.provider.js b/awx/ui/client/src/shared/stateExtender.provider.js index da8b34fbee..f9534c2665 100644 --- a/awx/ui/client/src/shared/stateExtender.provider.js +++ b/awx/ui/client/src/shared/stateExtender.provider.js @@ -31,7 +31,7 @@ export default function($stateProvider) { order_by: "name" }, dynamic: true, - squash: true + squash: '' } } }; From 55827611b6d8bec6b313ecc27b3a3d9ab4c31b44 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Thu, 15 Dec 2016 15:37:56 -0500 Subject: [PATCH 28/49] filter out ansible_* facts from provision callback extra_vars related to #4358 --- awx/api/views.py | 5 ++++- awx/main/utils/common.py | 12 +++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/awx/api/views.py b/awx/api/views.py index 826941ec5f..f19b61e4e9 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -65,6 +65,9 @@ from awx.api.generics import * # noqa from awx.conf.license import get_license, feature_enabled, feature_exists, LicenseForbids from awx.main.models import * # noqa from awx.main.utils import * # noqa +from awx.main.utils import ( + callback_filter_out_ansible_extra_vars +) from awx.api.permissions import * # noqa from awx.api.renderers import * # noqa from awx.api.serializers import * # noqa @@ -2663,7 +2666,7 @@ class JobTemplateCallback(GenericAPIView): # Send a signal to celery that the job should be started. kv = {"inventory_sources_already_updated": inventory_sources_already_updated} if extra_vars is not None: - kv['extra_vars'] = extra_vars + kv['extra_vars'] = callback_filter_out_ansible_extra_vars(extra_vars) result = job.signal_start(**kv) if not result: data = dict(msg=_('Error starting job!')) diff --git a/awx/main/utils/common.py b/awx/main/utils/common.py index 00937a84c1..bed0588287 100644 --- a/awx/main/utils/common.py +++ b/awx/main/utils/common.py @@ -43,7 +43,8 @@ __all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore', 'copy_m2m_relationships' ,'cache_list_capabilities', 'to_python_boolean', 'ignore_inventory_computed_fields', 'ignore_inventory_group_removal', '_inventory_updates', 'get_pk_from_dict', 'getattrd', 'NoDefaultProvided', - 'get_current_apps', 'set_current_apps', 'OutputEventFilter'] + 'get_current_apps', 'set_current_apps', 'OutputEventFilter', + 'callback_filter_out_ansible_extra_vars',] def get_object_or_400(klass, *args, **kwargs): @@ -824,3 +825,12 @@ class OutputEventFilter(object): self._current_event_data = next_event_data else: self._current_event_data = None + + +def callback_filter_out_ansible_extra_vars(extra_vars): + extra_vars_redacted = {} + for key, value in extra_vars.iteritems(): + if not key.startswith('ansible_'): + extra_vars_redacted[key] = value + return extra_vars_redacted + From 594f643fd8a19ee59dc9860d20d5a77ba0c5e2bf Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Thu, 15 Dec 2016 15:39:17 -0500 Subject: [PATCH 29/49] Fix projects modal in jt add/edit --- awx/ui/client/src/lists/Projects.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/awx/ui/client/src/lists/Projects.js b/awx/ui/client/src/lists/Projects.js index 83cb2eb7e7..1417201824 100644 --- a/awx/ui/client/src/lists/Projects.js +++ b/awx/ui/client/src/lists/Projects.js @@ -30,7 +30,8 @@ export default dataPlacement: 'right', icon: "icon-job-{{ project.statusIcon }}", columnClass: "List-staticColumn--smallStatus", - nosort: true + nosort: true, + excludeModal: true }, name: { key: true, From da5ff5908120fddde7ee1eb42430cf37fad82e71 Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Thu, 15 Dec 2016 16:08:03 -0500 Subject: [PATCH 30/49] Removed extra appendage of id to the edit user url --- awx/ui/client/src/controllers/Users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/controllers/Users.js b/awx/ui/client/src/controllers/Users.js index 9aade4fbf8..9a0aa2ea76 100644 --- a/awx/ui/client/src/controllers/Users.js +++ b/awx/ui/client/src/controllers/Users.js @@ -311,7 +311,7 @@ export function UsersEdit($scope, $rootScope, $location, $scope.formSave = function() { $rootScope.flashMessage = null; if ($scope[form.name + '_form'].$valid) { - Rest.setUrl(defaultUrl + id + '/'); + Rest.setUrl(defaultUrl + '/'); var data = processNewData(form.fields); Rest.put(data).success(function() { $state.go($state.current, null, { reload: true }); From ef1f77bf8e9fe4f72b9ff7e84cf35fce59417aa1 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Thu, 15 Dec 2016 16:12:59 -0500 Subject: [PATCH 31/49] fix what I broke with the job through cancel proj update --- awx/main/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 2ad3343607..63128635ab 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -1057,7 +1057,7 @@ class RunJob(BaseTask): project_update_task = local_project_sync._get_task_class() try: project_update_task().run(local_project_sync.id) - job = self.update_model(job.pk, scm_revision=project.scm_revision) + job = self.update_model(job.pk, scm_revision=job.project.scm_revision) except Exception: job = self.update_model(job.pk, status='failed', job_explanation=('Previous Task Failed: {"job_type": "%s", "job_name": "%s", "job_id": "%s"}' % From 888ec25c3cc529fb484ae108261991e3869c192a Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Thu, 15 Dec 2016 16:22:06 -0500 Subject: [PATCH 32/49] Adding new privilege escalation methods from core --- awx/main/migrations/0034_v310_release.py | 5 +++++ awx/main/models/credential.py | 2 ++ awx/main/tasks.py | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/awx/main/migrations/0034_v310_release.py b/awx/main/migrations/0034_v310_release.py index fa46beec20..c3849468f8 100644 --- a/awx/main/migrations/0034_v310_release.py +++ b/awx/main/migrations/0034_v310_release.py @@ -47,6 +47,11 @@ class Migration(migrations.Migration): name='uuid', field=models.CharField(max_length=40), ), + migrations.AlterField( + model_name='credential', + name='become_method', + field=models.CharField(default=b'', help_text='Privilege escalation method.', max_length=32, blank=True, choices=[(b'', 'None'), (b'sudo', 'Sudo'), (b'su', 'Su'), (b'pbrun', 'Pbrun'), (b'pfexec', 'Pfexec'), (b'dzdo', 'DZDO'), (b'pmrun', 'Pmrun')]), + ), # Add Workflows migrations.AlterField( model_name='unifiedjob', diff --git a/awx/main/models/credential.py b/awx/main/models/credential.py index aa0bf3243c..a7f77e87c2 100644 --- a/awx/main/models/credential.py +++ b/awx/main/models/credential.py @@ -50,6 +50,8 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin): ('su', _('Su')), ('pbrun', _('Pbrun')), ('pfexec', _('Pfexec')), + ('dzdo', _('DZDO')), + ('pmrun', _('Pmrun')), #('runas', _('Runas')), ] diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 63128635ab..de69a3ecd5 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -1743,6 +1743,10 @@ class RunAdHocCommand(BaseTask): d[re.compile(r'^pfexec password.*:\s*?$', re.M)] = 'become_password' d[re.compile(r'^RUNAS password.*:\s*?$', re.M)] = 'become_password' d[re.compile(r'^runas password.*:\s*?$', re.M)] = 'become_password' + d[re.compile(r'^DZDO password.*:\s*?$', re.M)] = 'become_password' + d[re.compile(r'^dzdo password.*:\s*?$', re.M)] = 'become_password' + d[re.compile(r'^PMRUN password.*:\s*?$', re.M)] = 'become_password' + d[re.compile(r'^pmrun password.*:\s*?$', re.M)] = 'become_password' d[re.compile(r'^SSH password:\s*?$', re.M)] = 'ssh_password' d[re.compile(r'^Password:\s*?$', re.M)] = 'ssh_password' return d From 4509f402a8a645c0ef6d219678d07b019bfb104c Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Thu, 15 Dec 2016 16:31:39 -0500 Subject: [PATCH 33/49] send notification when recifiying celery jobs --- awx/main/scheduler/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/main/scheduler/__init__.py b/awx/main/scheduler/__init__.py index efba3fdb75..e92eb429e1 100644 --- a/awx/main/scheduler/__init__.py +++ b/awx/main/scheduler/__init__.py @@ -346,6 +346,7 @@ class TaskManager(): 'Celery, so it has been marked as failed.', )) task_obj.save() + _send_notification_templates(task_obj, 'failed') connection.on_commit(lambda: task_obj.websocket_emit_status('failed')) logger.error("Task %s appears orphaned... marking as failed" % task) From 851dacd407542210a9557a15b703d4462ff86a9d Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Thu, 15 Dec 2016 16:44:10 -0500 Subject: [PATCH 34/49] Fixed recently used job templates edit link --- .../lists/job-templates/job-templates-list.directive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js b/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js index 928f03a1c4..82aca5b377 100644 --- a/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js +++ b/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js @@ -47,7 +47,7 @@ export default }; scope.editJobTemplate = function (jobTemplateId) { - $state.go('templates.editJobTemplate', {id: jobTemplateId}); + $state.go('templates.editJobTemplate', {job_template_id: jobTemplateId}); }; } }]; From b78ed36abf3e9e05cb56517bdb57408a5ca49be5 Mon Sep 17 00:00:00 2001 From: jaredevantabor Date: Thu, 15 Dec 2016 13:47:12 -0800 Subject: [PATCH 35/49] post processing for scheduled jobs list --- awx/ui/client/src/controllers/Schedules.js | 2 +- awx/ui/client/src/lists/ScheduledJobs.js | 3 +- awx/ui/client/src/scheduler/main.js | 54 +++++++++++++- .../src/scheduler/schedulerList.controller.js | 72 ++++++++++++++----- 4 files changed, 106 insertions(+), 25 deletions(-) diff --git a/awx/ui/client/src/controllers/Schedules.js b/awx/ui/client/src/controllers/Schedules.js index edebf66a84..5a29150215 100644 --- a/awx/ui/client/src/controllers/Schedules.js +++ b/awx/ui/client/src/controllers/Schedules.js @@ -65,7 +65,7 @@ GetBasePath, Wait, Find, LoadSchedulesScope, GetChoices) { msg: 'Call to ' + url + ' failed. GET returned: ' + status }); }); }); - + debugger; $scope.refreshJobs = function() { // @issue: OLD SEARCH // $scope.search(SchedulesList.iterator); diff --git a/awx/ui/client/src/lists/ScheduledJobs.js b/awx/ui/client/src/lists/ScheduledJobs.js index e4a17496c6..a771a75b8f 100644 --- a/awx/ui/client/src/lists/ScheduledJobs.js +++ b/awx/ui/client/src/lists/ScheduledJobs.js @@ -31,8 +31,7 @@ export default name: { label: i18n._('Name'), columnClass: 'col-lg-4 col-md-5 col-sm-5 col-xs-7 List-staticColumnAdjacent', - sourceModel: 'unified_job_template', - sourceField: 'name', + ngBind: 'schedule.summary_fields.unified_job_template.name', ngClick: "editSchedule(schedule)", awToolTip: "{{ schedule.nameTip | sanitize}}", dataTipWatch: 'schedule.nameTip', diff --git a/awx/ui/client/src/scheduler/main.js b/awx/ui/client/src/scheduler/main.js index 840197d944..f8620c880e 100644 --- a/awx/ui/client/src/scheduler/main.js +++ b/awx/ui/client/src/scheduler/main.js @@ -45,7 +45,19 @@ export default let path = `${GetBasePath('job_templates')}${$stateParams.id}`; Rest.setUrl(path); return Rest.get(path).then((res) => res.data); - }] + }], + UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q', + function(Rest, GetBasePath, $stateParams, $q) { + Rest.setUrl(GetBasePath('unified_jobs')); + var val = $q.defer(); + Rest.options() + .then(function(data) { + val.resolve(data.data); + }, function(data) { + val.reject(data); + }); + return val.promise; + }] }, views: { '@': { @@ -119,7 +131,19 @@ export default let path = `${GetBasePath('workflow_job_templates')}${$stateParams.id}`; Rest.setUrl(path); return Rest.get(path).then((res) => res.data); - }] + }], + UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q', + function(Rest, GetBasePath, $stateParams, $q) { + Rest.setUrl(GetBasePath('unified_jobs')); + var val = $q.defer(); + Rest.options() + .then(function(data) { + val.resolve(data.data); + }, function(data) { + val.reject(data); + }); + return val.promise; + }] }, views: { '@': { @@ -190,7 +214,19 @@ export default let path = `${GetBasePath('projects')}${$stateParams.id}`; Rest.setUrl(path); return Rest.get(path).then((res) => res.data); - }] + }], + UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q', + function(Rest, GetBasePath, $stateParams, $q) { + Rest.setUrl(GetBasePath('unified_jobs')); + var val = $q.defer(); + Rest.options() + .then(function(data) { + val.resolve(data.data); + }, function(data) { + val.reject(data); + }); + return val.promise; + }] }, views: { '@': { @@ -268,6 +304,18 @@ export default } ], ParentObject: [() =>{return {endpoint:'/api/v1/schedules'}; }], + UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q', + function(Rest, GetBasePath, $stateParams, $q) { + Rest.setUrl(GetBasePath('unified_jobs')); + var val = $q.defer(); + Rest.options() + .then(function(data) { + val.resolve(data.data); + }, function(data) { + val.reject(data); + }); + return val.promise; + }] }, views: { 'list@jobs': { diff --git a/awx/ui/client/src/scheduler/schedulerList.controller.js b/awx/ui/client/src/scheduler/schedulerList.controller.js index e003ea1fd2..4f9e9809d3 100644 --- a/awx/ui/client/src/scheduler/schedulerList.controller.js +++ b/awx/ui/client/src/scheduler/schedulerList.controller.js @@ -14,12 +14,12 @@ export default [ '$scope', '$compile', '$location', '$stateParams', 'SchedulesList', 'Rest', 'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'GetBasePath', 'Wait', 'rbacUiControlService', - 'Find', 'ToggleSchedule', 'DeleteSchedule', 'GetChoices', '$q', '$state', 'Dataset', 'ParentObject', + 'Find', 'ToggleSchedule', 'DeleteSchedule', 'GetChoices', '$q', '$state', 'Dataset', 'ParentObject', 'UnifiedJobsOptions', function($scope, $compile, $location, $stateParams, SchedulesList, Rest, ProcessErrors, ReturnToCaller, ClearScope, GetBasePath, Wait, rbacUiControlService, Find, ToggleSchedule, DeleteSchedule, GetChoices, - $q, $state, Dataset, ParentObject) { + $q, $state, Dataset, ParentObject, UnifiedJobsOptions) { ClearScope(); @@ -43,11 +43,45 @@ export default [ $scope.list = list; $scope[`${list.iterator}_dataset`] = Dataset.data; $scope[list.name] = $scope[`${list.iterator}_dataset`].results; + $scope.unified_job_options = UnifiedJobsOptions.actions.GET; - _.forEach($scope[list.name], buildTooltips); + // _.forEach($scope[list.name], buildTooltips); + } + + $scope.$on(`${list.iterator}_options`, function(event, data){ + $scope.options = data.data.actions.GET; + optionsRequestDataProcessing(); + }); + + $scope.$watchCollection(`${$scope.list.name}`, function() { + optionsRequestDataProcessing(); + } + ); + + // iterate over the list and add fields like type label, after the + // OPTIONS request returns, or the list is sorted/paginated/searched + function optionsRequestDataProcessing(){ + $scope[list.name].forEach(function(item, item_idx) { + var itm = $scope[list.name][item_idx]; + + // Set the item type label + if (list.fields.type && $scope.unified_job_options && + $scope.unified_job_options.hasOwnProperty('type')) { + $scope.unified_job_options.type.choices.every(function(choice) { + if (choice[0] === itm.summary_fields.unified_job_template.unified_job_type) { + itm.type_label = choice[1]; + return false; + } + return true; + }); + } + buildTooltips(itm); + + }); } function buildTooltips(schedule) { + var job = schedule.summary_fields.unified_job_template; if (schedule.enabled) { schedule.play_tip = 'Schedule is active. Click to stop.'; schedule.status = 'active'; @@ -57,6 +91,18 @@ export default [ schedule.status = 'stopped'; schedule.status_tip = 'Schedule is stopped. Click to activate.'; } + + schedule.nameTip = schedule.name; + // include the word schedule if the schedule name does not include the word schedule + if (schedule.name.indexOf("schedule") === -1 && schedule.name.indexOf("Schedule") === -1) { + schedule.nameTip += " schedule"; + } + schedule.nameTip += " for "; + if (job.name.indexOf("job") === -1 && job.name.indexOf("Job") === -1) { + schedule.nameTip += "job "; + } + schedule.nameTip += job.name; + schedule.nameTip += ". Click to edit schedule."; } $scope.refreshSchedules = function() { @@ -99,7 +145,7 @@ export default [ name: 'projectSchedules.edit', params: { id: schedule.unified_job_template, - schedule_id: schedule.id + schedule_id: schedule.id } }); break; @@ -109,7 +155,7 @@ export default [ name: 'managementJobSchedules.edit', params: { id: schedule.unified_job_template, - schedule_id: schedule.id + schedule_id: schedule.id } }); break; @@ -136,7 +182,7 @@ export default [ throw err; } }); - }); + }); } }; @@ -160,7 +206,7 @@ export default [ }; base = $location.path().replace(/^\//, '').split('/')[0]; - + if (base === 'management_jobs') { $scope.base = base = 'system_job_templates'; } @@ -175,17 +221,5 @@ export default [ $scope.formCancel = function() { $state.go('^', null, { reload: true }); }; - - // @issue - believe this is no longer necessary now that parent object is resolved prior to controller initilizing - - // Wait('start'); - - // GetChoices({ - // scope: $scope, - // url: GetBasePath('unified_jobs'), //'/static/sample/data/types/data.json' - // field: 'type', - // variable: 'type_choices', - // callback: 'choicesReady' - // }); } ]; From 0504348d6971ae5f172be0f9422c91e4bc70d01a Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Thu, 15 Dec 2016 16:59:06 -0500 Subject: [PATCH 36/49] Removed Job Templates and Workflow Job Templates from the activity stream dropdown in favor of the combined Templates --- .../streamDropdownNav/stream-dropdown-nav.directive.js | 4 +--- awx/ui/client/src/helpers/ActivityStream.js | 6 ------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js b/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js index cba0ecaddf..dc6c4a819d 100644 --- a/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js +++ b/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js @@ -20,15 +20,13 @@ export default ['templateUrl', function(templateUrl) { {label: 'Hosts', value: 'host'}, {label: 'Inventories', value: 'inventory'}, {label: 'Inventory Scripts', value: 'inventory_script'}, - {label: 'Job Templates', value: 'job_template'}, {label: 'Jobs', value: 'job'}, {label: 'Organizations', value: 'organization'}, {label: 'Projects', value: 'project'}, {label: 'Schedules', value: 'schedule'}, {label: 'Teams', value: 'team'}, {label: 'Templates', value: 'template'}, - {label: 'Users', value: 'user'}, - {label: 'Workflow Job Templates', value: 'workflow_job_template'} + {label: 'Users', value: 'user'} ]; CreateSelect2({ diff --git a/awx/ui/client/src/helpers/ActivityStream.js b/awx/ui/client/src/helpers/ActivityStream.js index 7dc8c9ecc0..37da4f2857 100644 --- a/awx/ui/client/src/helpers/ActivityStream.js +++ b/awx/ui/client/src/helpers/ActivityStream.js @@ -25,9 +25,6 @@ export default case 'inventory': rtnTitle = 'INVENTORIES'; break; - case 'job_template': - rtnTitle = 'JOB TEMPLATES'; - break; case 'credential': rtnTitle = 'CREDENTIALS'; break; @@ -55,9 +52,6 @@ export default case 'template': rtnTitle = 'TEMPLATES'; break; - case 'workflow_job_template': - rtnTitle = 'WORKFLOW JOB TEMPLATES'; - break; } return rtnTitle; From 0662720da34e55c374a3e84a72e65fb7e362c3a2 Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Thu, 15 Dec 2016 17:44:20 -0500 Subject: [PATCH 37/49] Set the url before attempting to update the team --- awx/ui/client/src/controllers/Teams.js | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/ui/client/src/controllers/Teams.js b/awx/ui/client/src/controllers/Teams.js index 0e3a47d855..464a10ee10 100644 --- a/awx/ui/client/src/controllers/Teams.js +++ b/awx/ui/client/src/controllers/Teams.js @@ -217,6 +217,7 @@ export function TeamsEdit($scope, $rootScope, $stateParams, $rootScope.flashMessage = null; if ($scope[form.name + '_form'].$valid) { var data = processNewData(form.fields); + Rest.setUrl(defaultUrl); Rest.put(data).success(function() { $state.go($state.current, null, { reload: true }); }) From 2b5d64d7468a518c64120758c575aef7654b7e87 Mon Sep 17 00:00:00 2001 From: jaredevantabor Date: Thu, 15 Dec 2016 15:16:25 -0800 Subject: [PATCH 38/49] removing debugger statements --- awx/ui/client/src/controllers/Schedules.js | 2 +- awx/ui/client/src/templates/main.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/awx/ui/client/src/controllers/Schedules.js b/awx/ui/client/src/controllers/Schedules.js index 5a29150215..edebf66a84 100644 --- a/awx/ui/client/src/controllers/Schedules.js +++ b/awx/ui/client/src/controllers/Schedules.js @@ -65,7 +65,7 @@ GetBasePath, Wait, Find, LoadSchedulesScope, GetChoices) { msg: 'Call to ' + url + ' failed. GET returned: ' + status }); }); }); - debugger; + $scope.refreshJobs = function() { // @issue: OLD SEARCH // $scope.search(SchedulesList.iterator); diff --git a/awx/ui/client/src/templates/main.js b/awx/ui/client/src/templates/main.js index b458121def..03b380bd58 100644 --- a/awx/ui/client/src/templates/main.js +++ b/awx/ui/client/src/templates/main.js @@ -115,7 +115,7 @@ angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplatesA }, 'jobTemplateList@templates.editWorkflowJobTemplate.workflowMaker': { templateProvider: function(WorkflowMakerJobTemplateList, generateList) { - //debugger; + let html = generateList.build({ list: WorkflowMakerJobTemplateList, input_type: 'radio', From 25ee37fd3a801f061e36a7c7abe7e482205dde7f Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Fri, 16 Dec 2016 09:30:23 -0500 Subject: [PATCH 39/49] change session limit message --- awx/main/models/organization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/models/organization.py b/awx/main/models/organization.py index 8f96d1656c..c2fe3b1c4f 100644 --- a/awx/main/models/organization.py +++ b/awx/main/models/organization.py @@ -210,7 +210,7 @@ class AuthToken(BaseModel): REASON_CHOICES = [ ('', _('Token not invalidated')), ('timeout_reached', _('Token is expired')), - ('limit_reached', _('Maximum per-user sessions reached')), + ('limit_reached', _('The maximum number of allowed sessions for this user has been exceeded.')), # invalid_token is not a used data-base value, but is returned by the # api when a token is not found ('invalid_token', _('Invalid token')), From a566cec5db50e3ddb6f64bce53ed0f127b84557c Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Fri, 16 Dec 2016 10:06:34 -0500 Subject: [PATCH 40/49] rectifiy arch name in fact scans --- awx/plugins/library/scan_packages.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/awx/plugins/library/scan_packages.py b/awx/plugins/library/scan_packages.py index 13b28542f6..d5aafc66e6 100755 --- a/awx/plugins/library/scan_packages.py +++ b/awx/plugins/library/scan_packages.py @@ -22,19 +22,19 @@ EXAMPLES = ''' # { # "source": "apt", # "version": "1.0.6-5", -# "architecture": "amd64", +# "arch": "amd64", # "name": "libbz2-1.0" # }, # { # "source": "apt", # "version": "2.7.1-4ubuntu1", -# "architecture": "amd64", +# "arch": "amd64", # "name": "patch" # }, # { # "source": "apt", # "version": "4.8.2-19ubuntu1", -# "architecture": "amd64", +# "arch": "amd64", # "name": "gcc-4.8-base" # }, ... ] } } ''' @@ -64,7 +64,7 @@ def deb_package_list(): ac_pkg = apt_cache[package].installed package_details = dict(name=package, version=ac_pkg.version, - architecture=ac_pkg.architecture, + arch=ac_pkg.architecture, source='apt') installed_packages.append(package_details) return installed_packages From 5d9d332792439b851a28566adc6a4c974d96db81 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Fri, 16 Dec 2016 10:36:38 -0500 Subject: [PATCH 41/49] rackspace deprecation warning on inv update --- awx/main/management/commands/inventory_import.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/awx/main/management/commands/inventory_import.py b/awx/main/management/commands/inventory_import.py index 7f87694cbe..797a211b79 100644 --- a/awx/main/management/commands/inventory_import.py +++ b/awx/main/management/commands/inventory_import.py @@ -1253,6 +1253,11 @@ class Command(NoArgsCommand): except re.error: raise CommandError('invalid regular expression for --host-filter') + ''' + TODO: Remove this deprecation when we remove support for rax.py + ''' + self.logger.info("Rackspace inventory sync is Deprecated in Tower 3.1.0 and support for Rackspace will be removed in a future release.") + begin = time.time() self.load_inventory_from_database() From bf23f6a52c9002a47425cec68afa58da11c5003f Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Fri, 16 Dec 2016 11:26:16 -0500 Subject: [PATCH 42/49] associate notifications with job before sending notifications related to #3391 --- awx/main/tasks.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 63128635ab..436ca60df4 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -109,8 +109,12 @@ def send_notifications(notification_list, job_id=None): raise TypeError("notification_list should be of type list") if job_id is not None: job_actual = UnifiedJob.objects.get(id=job_id) - for notification_id in notification_list: - notification = Notification.objects.get(id=notification_id) + + notifications = Notification.objects.filter(id__in=notification_list) + if job_id is not None: + job_actual.notifications.add(*notifications) + + for notification in notifications: try: sent = notification.notification_template.send(notification.subject, notification.body) notification.status = "successful" @@ -121,8 +125,6 @@ def send_notifications(notification_list, job_id=None): notification.error = smart_str(e) finally: notification.save() - if job_id is not None: - job_actual.notifications.add(notification) @task(bind=True, queue='default') From fc5b13f413713cacd147bbd29daac5df7f5bd2de Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Fri, 16 Dec 2016 11:40:35 -0500 Subject: [PATCH 43/49] update notification sending tests --- awx/main/tests/unit/test_tasks.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index b83772bb59..2cefe0007b 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -38,17 +38,17 @@ def test_send_notifications_list(mocker): mock_job = mocker.MagicMock(spec=UnifiedJob) patches.append(mocker.patch('awx.main.models.UnifiedJob.objects.get', return_value=mock_job)) - mock_notification = mocker.MagicMock(spec=Notification, subject="test", body={'hello': 'world'}) - patches.append(mocker.patch('awx.main.models.Notification.objects.get', return_value=mock_notification)) + mock_notifications = [mocker.MagicMock(spec=Notification, subject="test", body={'hello': 'world'})] + patches.append(mocker.patch('awx.main.models.Notification.objects.filter', return_value=mock_notifications)) with apply_patches(patches): send_notifications([1,2], job_id=1) - assert Notification.objects.get.call_count == 2 - assert mock_notification.status == "successful" - assert mock_notification.save.called + assert Notification.objects.filter.call_count == 1 + assert mock_notifications[0].status == "successful" + assert mock_notifications[0].save.called assert mock_job.notifications.add.called - assert mock_job.notifications.add.called_with(mock_notification) + assert mock_job.notifications.add.called_with(*mock_notifications) @pytest.mark.parametrize("current_instances,call_count", [(91, 2), (89,1)]) From 42713c9916fd44a6cf3411a17c88dbc17dfec846 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Mon, 12 Dec 2016 14:25:41 -0500 Subject: [PATCH 44/49] add requirements for service identity module causing warning messages --- requirements/requirements.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 778e016da9..8c465b9b3c 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -19,6 +19,7 @@ apache-libcloud==1.3.0 appdirs==1.4.0 # via os-client-config, python-ironicclient, rply asgi-amqp==0.3.1 asgiref==1.0.0 # via asgi-amqp, channels, daphne +attrs==16.3.0 # via twisted service_identity module autobahn==0.16.1 # via daphne azure-batch==1.0.0 # via azure azure-common==1.1.4 # via azure-batch, azure-mgmt-batch, azure-mgmt-compute, azure-mgmt-keyvault, azure-mgmt-logic, azure-mgmt-network, azure-mgmt-redis, azure-mgmt-resource, azure-mgmt-scheduler, azure-mgmt-storage, azure-servicebus, azure-servicemanagement-legacy, azure-storage @@ -135,6 +136,7 @@ psphere==0.5.2 psutil==5.0.0 psycopg2==2.6.2 pyasn1==0.1.9 # via cryptography +pyasn1-modules==0.0.8 # via twisted service_identity module pycparser==2.17 # via cffi pygerduty==0.35.1 PyJWT==1.4.2 # via python-social-auth @@ -176,6 +178,7 @@ requestsexceptions==1.1.3 # via os-client-config, shade rfc3986==0.4.1 # via oslo.config rply==0.7.4 # via baron secretstorage==2.3.1 # via keyring +service-identity==16.0.0 # via twisted shade==1.13.1 simplejson==3.10.0 # via osc-lib, python-cinderclient, python-neutronclient, python-novaclient, python-troveclient six==1.10.0 # via asgi-amqp, asgiref, autobahn, cliff, cryptography, debtcollector, django-extensions, irc, jaraco.classes, jaraco.collections, jaraco.itertools, jaraco.logging, jaraco.stream, keystoneauth1, mock, more-itertools, openstacksdk, osc-lib, oslo.config, oslo.i18n, oslo.serialization, oslo.utils, pygerduty, pyopenssl, pyrad, python-cinderclient, python-dateutil, python-designateclient, python-glanceclient, python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-memcached, python-mistralclient, python-neutronclient, python-novaclient, python-openstackclient, python-social-auth, python-swiftclient, python-troveclient, shade, slackclient, stevedore, tempora, twilio, txaio, warlock, websocket-client From 3f73e8f8d16fcb0f25b7ba35f3daf35cb15d2c4d Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Fri, 16 Dec 2016 11:48:17 -0500 Subject: [PATCH 45/49] Fixed system jobs notifications route --- .../notifications/notification.controller.js | 115 ++++-------------- .../notifications/notification.route.js | 63 +++++----- 2 files changed, 54 insertions(+), 124 deletions(-) diff --git a/awx/ui/client/src/management-jobs/notifications/notification.controller.js b/awx/ui/client/src/management-jobs/notifications/notification.controller.js index 2bcf063536..62e8dff8a3 100644 --- a/awx/ui/client/src/management-jobs/notifications/notification.controller.js +++ b/awx/ui/client/src/management-jobs/notifications/notification.controller.js @@ -5,55 +5,33 @@ *************************************************/ export default - [ '$rootScope','Wait', 'generateList', 'NotificationsList', - 'GetBasePath' , 'Rest' , - 'ProcessErrors', 'Prompt', '$state', 'GetChoices', 'Empty', 'Find', - 'ngToast', '$compile', '$filter','ToggleNotification', - 'NotificationsListInit', '$stateParams', 'management_job', + [ 'NotificationsList', 'GetBasePath', 'ToggleNotification', 'NotificationsListInit', + '$stateParams', 'Dataset', '$scope', function( - $rootScope,Wait, GenerateList, NotificationsList, - GetBasePath, Rest, - ProcessErrors, Prompt, $state, GetChoices, Empty, Find, ngToast, - $compile, $filter, ToggleNotification, NotificationsListInit, - $stateParams, management_job) { - var scope = $rootScope.$new(), - url = GetBasePath('notification_templates'), - defaultUrl = GetBasePath('system_job_templates'), - list, - view = GenerateList, + NotificationsList, GetBasePath, ToggleNotification, NotificationsListInit, + $stateParams, Dataset, $scope) { + var defaultUrl = GetBasePath('system_job_templates'), + list = NotificationsList, id = $stateParams.management_id; - list = _.cloneDeep(NotificationsList); - delete list.actions.add; - list.listTitle = `${management_job.name}
Notifications`; - list.searchSize = "col-lg-12 col-md-12 col-sm-12 col-xs-12"; - list.searchRowActions = { - add: { - label: 'Add Notification', - mode: 'all', // One of: edit, select, all - ngClick: 'addNotificationTemplate()', - awToolTip: 'Create a new notification template', - actionClass: 'btn List-buttonSubmit', - buttonContent: '+ ADD NOTIFICATION TEMPLATE' - } - }; - view.inject( list, { - mode: 'edit', - cancelButton: true, - scope: scope - }); + function init() { + $scope.list = list; + $scope[`${list.iterator}_dataset`] = Dataset.data; + $scope[list.name] = $scope[`${list.iterator}_dataset`].results; - NotificationsListInit({ - scope: scope, - url: defaultUrl, - id: id - }); + NotificationsListInit({ + scope: $scope, + url: defaultUrl, + id: id + }); - scope.formCancel = function() { - $state.go('managementJobsList'); - }; + $scope.$watch(`${list.iterator}_dataset`, function() { + // The list data has changed and we need to update which notifications are on/off + $scope.$emit('relatednotifications'); + }); + } - scope.toggleNotification = function(event, notifier_id, column) { + $scope.toggleNotification = function(event, notifier_id, column) { var notifier = this.notification; try { $(event.target).tooltip('hide'); @@ -62,60 +40,15 @@ export default // ignore } ToggleNotification({ - scope: scope, - url: defaultUrl, - id: id, + scope: $scope, + url: defaultUrl + id, notifier: notifier, column: column, callback: 'NotificationRefresh' }); }; - if (scope.removePostRefresh) { - scope.removePostRefresh(); - } - scope.removePostRefresh = scope.$on('PostRefresh', function () { - scope.$emit('relatednotifications'); - }); - - if (scope.removeChoicesHere) { - scope.removeChoicesHere(); - } - scope.removeChoicesHere = scope.$on('choicesReadyNotifierList', function () { - list.fields.notification_type.searchOptions = scope.notification_type_options; - - // @issue: OLD SEARCH - // SearchInit({ - // scope: scope, - // set: 'notifications', - // list: list, - // url: url - // }); - - if ($rootScope.addedItem) { - scope.addedItem = $rootScope.addedItem; - delete $rootScope.addedItem; - } - - // @issue: OLD SEARCH - // PaginateInit({ - // scope: scope, - // list: list, - // url: url - // }); - // - // scope.search(list.iterator); - - }); - - GetChoices({ - scope: scope, - url: url, - field: 'notification_type', - variable: 'notification_type_options', - callback: 'choicesReadyNotifierList' - }); - + init(); } ]; diff --git a/awx/ui/client/src/management-jobs/notifications/notification.route.js b/awx/ui/client/src/management-jobs/notifications/notification.route.js index 8995bd8e6c..0d72dc0319 100644 --- a/awx/ui/client/src/management-jobs/notifications/notification.route.js +++ b/awx/ui/client/src/management-jobs/notifications/notification.route.js @@ -4,43 +4,40 @@ * All Rights Reserved *************************************************/ -import {templateUrl} from '../../shared/template-url/template-url.factory'; - export default { name: 'managementJobsList.notifications', route: '/:management_id/notifications', - templateUrl: templateUrl('management-jobs/notifications/notifications'), - controller: 'managementJobsNotificationsController', - params: {card: null}, - resolve: { - management_job: - [ '$stateParams', - '$q', - 'Rest', - 'GetBasePath', - 'ProcessErrors', - function($stateParams, $q, rest, getBasePath, ProcessErrors) { - - if ($stateParams.card) { - return $q.when($stateParams.card); - } - - var managementJobId = $stateParams.management_id; - - var url = getBasePath('system_job_templates') + managementJobId + '/'; - rest.setUrl(url); - return rest.get() - .then(function(data) { - return data.data; - }).catch(function (response) { - ProcessErrors(null, response.data, response.status, null, { - hdr: 'Error!', - msg: 'Failed to get management job info. GET returned status: ' + - response.status - }); - }); + params: { + notification_search: {} + }, + searchPrefix: 'notification', + views: { + '@managementJobsList': { + controller: 'managementJobsNotificationsController', + templateProvider: function(NotificationsList, generateList, ParentObject) { + // include name of parent resource in listTitle + NotificationsList.listTitle = `${ParentObject.name}
Notifications`; + let html = generateList.build({ + list: NotificationsList, + mode: 'edit' + }); + html = generateList.wrapPanel(html); + return generateList.insertFormView() + html; } - ] + } + }, + resolve: { + Dataset: ['NotificationsList', 'QuerySet', '$stateParams', 'GetBasePath', + function(list, qs, $stateParams, GetBasePath) { + let path = `${GetBasePath('notification_templates')}`; + return qs.search(path, $stateParams[`${list.iterator}_search`]); + } + ], + ParentObject: ['$stateParams', 'Rest', 'GetBasePath', function($stateParams, Rest, GetBasePath) { + let path = `${GetBasePath('system_job_templates')}${$stateParams.management_id}`; + Rest.setUrl(path); + return Rest.get(path).then((res) => res.data); + }] }, ncyBreadcrumb: { parent: 'managementJobsList', From ce56a5e82495985a80a0219b3b24bb22e360c0c4 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Fri, 16 Dec 2016 12:46:33 -0500 Subject: [PATCH 46/49] documentation feedback from pip-complie tool incorporated --- requirements/requirements.in | 1 + requirements/requirements.txt | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/requirements/requirements.in b/requirements/requirements.in index 3ae18db466..2182c5aada 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -40,6 +40,7 @@ python-saml==2.2.0 python-social-auth==0.2.21 redbaron==0.6.2 requests-futures==0.9.7 +service-identity==16.0.0 shade==1.13.1 slackclient==1.0.2 twilio==5.6.0 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 8c465b9b3c..ef834b78f5 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -19,7 +19,7 @@ apache-libcloud==1.3.0 appdirs==1.4.0 # via os-client-config, python-ironicclient, rply asgi-amqp==0.3.1 asgiref==1.0.0 # via asgi-amqp, channels, daphne -attrs==16.3.0 # via twisted service_identity module +attrs==16.3.0 # via service-identity autobahn==0.16.1 # via daphne azure-batch==1.0.0 # via azure azure-common==1.1.4 # via azure-batch, azure-mgmt-batch, azure-mgmt-compute, azure-mgmt-keyvault, azure-mgmt-logic, azure-mgmt-network, azure-mgmt-redis, azure-mgmt-resource, azure-mgmt-scheduler, azure-mgmt-storage, azure-servicebus, azure-servicemanagement-legacy, azure-storage @@ -136,7 +136,7 @@ psphere==0.5.2 psutil==5.0.0 psycopg2==2.6.2 pyasn1==0.1.9 # via cryptography -pyasn1-modules==0.0.8 # via twisted service_identity module +pyasn1-modules==0.0.8 # via service-identity pycparser==2.17 # via cffi pygerduty==0.35.1 PyJWT==1.4.2 # via python-social-auth @@ -178,7 +178,7 @@ requestsexceptions==1.1.3 # via os-client-config, shade rfc3986==0.4.1 # via oslo.config rply==0.7.4 # via baron secretstorage==2.3.1 # via keyring -service-identity==16.0.0 # via twisted +service-identity==16.0.0 shade==1.13.1 simplejson==3.10.0 # via osc-lib, python-cinderclient, python-neutronclient, python-novaclient, python-troveclient six==1.10.0 # via asgi-amqp, asgiref, autobahn, cliff, cryptography, debtcollector, django-extensions, irc, jaraco.classes, jaraco.collections, jaraco.itertools, jaraco.logging, jaraco.stream, keystoneauth1, mock, more-itertools, openstacksdk, osc-lib, oslo.config, oslo.i18n, oslo.serialization, oslo.utils, pygerduty, pyopenssl, pyrad, python-cinderclient, python-dateutil, python-designateclient, python-glanceclient, python-heatclient, python-ironicclient, python-keystoneclient, python-magnumclient, python-memcached, python-mistralclient, python-neutronclient, python-novaclient, python-openstackclient, python-social-auth, python-swiftclient, python-troveclient, shade, slackclient, stevedore, tempora, twilio, txaio, warlock, websocket-client From f4a8d3c84cf5a01f03c8e92c1a87e98864f70669 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Fri, 16 Dec 2016 12:51:16 -0500 Subject: [PATCH 47/49] fix view job button on jobs page --- awx/ui/client/src/lists/AllJobs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/lists/AllJobs.js b/awx/ui/client/src/lists/AllJobs.js index 152bf1185d..16a37e5c04 100644 --- a/awx/ui/client/src/lists/AllJobs.js +++ b/awx/ui/client/src/lists/AllJobs.js @@ -86,7 +86,7 @@ export default columnClass: 'col-lg-2 col-md-2 col-sm-3 col-xs-4', "view": { mode: "all", - ngClick: "viewJob(job.id)", + ngClick: "viewJobDetails(job)", awToolTip: "View the job", dataPlacement: "top" }, From 6499eb97d3da62f97dd267df08e195dac965055a Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Fri, 16 Dec 2016 13:12:34 -0500 Subject: [PATCH 48/49] made extra vars look disabled on job results page --- .../src/job-results/job-results.block.less | 24 +++++++++++++++++++ .../src/job-results/job-results.partial.html | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/awx/ui/client/src/job-results/job-results.block.less b/awx/ui/client/src/job-results/job-results.block.less index 2f24e98cbc..ae10b535e7 100644 --- a/awx/ui/client/src/job-results/job-results.block.less +++ b/awx/ui/client/src/job-results/job-results.block.less @@ -176,3 +176,27 @@ job-results-standard-out { flex: 1; display: flex } + +.JobResults-extraVarsHelp { + margin-left: 10px; + color: @default-icon; +} + +.JobResults .CodeMirror.cm-s-default, +.JobResults .CodeMirror-line { + background-color: #f6f6f6; +} + +.JobResults .CodeMirror-gutter.CodeMirror-lint-markers, +.JobResults .CodeMirror-gutter.CodeMirror-linenumbers { + background-color: #ebebeb; + color: @b7grey; +} + +.JobResults .CodeMirror-lines { + cursor: default; +} + +.JobResults .CodeMirror-cursors { + display: none; +} diff --git a/awx/ui/client/src/job-results/job-results.partial.html b/awx/ui/client/src/job-results/job-results.partial.html index a763756769..5357c77749 100644 --- a/awx/ui/client/src/job-results/job-results.partial.html +++ b/awx/ui/client/src/job-results/job-results.partial.html @@ -353,6 +353,10 @@