From 0983bd8dc048431924421156fedd389bb6d047f9 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Tue, 23 Aug 2022 14:13:06 -0400 Subject: [PATCH] Adding prevent_instance_group_fallback --- awx/api/serializers.py | 2 + awx/main/models/inventory.py | 7 + awx/main/models/jobs.py | 28 +- .../Inventory/shared/Inventory.helptext.js | 1 + .../screens/Inventory/shared/InventoryForm.js | 13 +- .../Inventory/shared/data.inventory.json | 133 +++--- .../Template/shared/JobTemplate.helptext.js | 2 + .../Template/shared/JobTemplateForm.js | 8 + .../Template/shared/data.job_template.json | 383 +++++++++--------- 9 files changed, 308 insertions(+), 269 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index eaad921eb2..d5bb2cfe6b 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1680,6 +1680,7 @@ class InventorySerializer(LabelsListMixin, BaseSerializerWithVariables): 'total_inventory_sources', 'inventory_sources_with_failures', 'pending_deletion', + 'prevent_instance_group_fallback', ) def get_related(self, obj): @@ -2937,6 +2938,7 @@ class JobTemplateSerializer(JobTemplateMixin, UnifiedJobTemplateSerializer, JobO 'job_slice_count', 'webhook_service', 'webhook_credential', + 'prevent_instance_group_fallback', ) read_only_fields = ('*', 'custom_virtualenv') diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index 4a90ad5bad..8c9a9e6256 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -175,6 +175,13 @@ class Inventory(CommonModelNameNotUnique, ResourceMixin, RelatedJobsMixin): related_name='inventory_labels', help_text=_('Labels associated with this inventory.'), ) + prevent_instance_group_fallback = models.BooleanField( + default=False, + help_text=( + "If enabled, the job template will prevent adding any inventory or organization " + "instance groups to the list of preferred instances groups to run on." + ), + ) def get_absolute_url(self, request=None): return reverse('api:inventory_detail', kwargs={'pk': self.pk}, request=request) diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index b954c76e35..a9c6cf6907 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -274,6 +274,13 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour 'admin_role', ], ) + prevent_instance_group_fallback = models.BooleanField( + default=False, + help_text=( + "If enabled, the job template will prevent adding any inventory or organization " + "instance groups to the list of preferred instances groups to run on." + ), + ) @classmethod def _get_unified_job_class(cls): @@ -797,19 +804,14 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana def preferred_instance_groups(self): # If the user specified instance groups those will be handled by the unified_job.create_unified_job # This function handles only the defaults for a template w/o user specification - if self.organization is not None: - organization_groups = [x for x in self.organization.instance_groups.all()] - else: - organization_groups = [] - if self.inventory is not None: - inventory_groups = [x for x in self.inventory.instance_groups.all()] - else: - inventory_groups = [] - if self.job_template is not None: - template_groups = [x for x in self.job_template.instance_groups.all()] - else: - template_groups = [] - selected_groups = template_groups + inventory_groups + organization_groups + selected_groups = [] + for obj_type in ['job_template', 'inventory', 'organization']: + if getattr(self, obj_type) is not None: + for instance_group in getattr(self, obj_type).instance_groups.all(): + selected_groups.append(instance_group) + if getattr(getattr(self, obj_type), 'prevent_instance_group_fallback', False): + logger.error("Breaking in preferred instance group at {}".format(obj_type)) + break if not selected_groups: return self.global_instance_groups return selected_groups diff --git a/awx/ui/src/screens/Inventory/shared/Inventory.helptext.js b/awx/ui/src/screens/Inventory/shared/Inventory.helptext.js index 19636128f0..564add096d 100644 --- a/awx/ui/src/screens/Inventory/shared/Inventory.helptext.js +++ b/awx/ui/src/screens/Inventory/shared/Inventory.helptext.js @@ -191,6 +191,7 @@ const getInventoryHelpTextStrings = () => ({ sourcePath: t`The inventory file to be synced by this source. You can select from the dropdown or enter a file within the input.`, + preventInstanceGroupFallback: t`If enabled, the job template will prevent adding any inventory or organization instance groups to the list of preferred instances groups to run on.`, }); export default getInventoryHelpTextStrings; diff --git a/awx/ui/src/screens/Inventory/shared/InventoryForm.js b/awx/ui/src/screens/Inventory/shared/InventoryForm.js index e29ffcdfa6..30c166c73d 100644 --- a/awx/ui/src/screens/Inventory/shared/InventoryForm.js +++ b/awx/ui/src/screens/Inventory/shared/InventoryForm.js @@ -5,7 +5,10 @@ import { func, shape } from 'prop-types'; import { Form, FormGroup } from '@patternfly/react-core'; import { VariablesField } from 'components/CodeEditor'; import Popover from 'components/Popover'; -import FormField, { FormSubmitError } from 'components/FormField'; +import FormField, { + CheckboxField, + FormSubmitError, +} from 'components/FormField'; import FormActionGroup from 'components/FormActionGroup'; import { required } from 'util/validators'; import LabelSelect from 'components/LabelSelect'; @@ -71,6 +74,12 @@ function InventoryFormFields({ inventory }) { }} fieldName="instanceGroups" /> + ({ privilegeEscalation: t`If enabled, run this playbook as an administrator.`, enableWebhook: t`Enable webhook for this template.`, concurrentJobs: t`If enabled, simultaneous runs of this job template will be allowed.`, + preventInstanceGroupFallback: t`If enabled, the job template will prevent adding any inventory or organization instance groups to the list of preferred instances groups to run on.`, enableFactStorage: t`If enabled, this will store gathered facts so they can be viewed at the host level. Facts are persisted and injected into the fact cache at runtime.`, enabledOptions: ( <> @@ -38,6 +39,7 @@ const jtHelpTextStrings = () => ({

{t`Privilege escalation: If enabled, run this playbook as an administrator.`}

{t`Provisioning callbacks: Enables creation of a provisioning callback URL. Using the URL a host can contact Ansible AWX and request a configuration update using this job template.`}

{t`Webhooks: Enable webhook for this template.`}

+

{t`Prevent Instance Group Fallback: If enabled, the job template will prevent adding any inventory or organization instance groups to the list of preferred instances groups to run on.`}

), forks: ( diff --git a/awx/ui/src/screens/Template/shared/JobTemplateForm.js b/awx/ui/src/screens/Template/shared/JobTemplateForm.js index a82aaa8ad3..7621601e9e 100644 --- a/awx/ui/src/screens/Template/shared/JobTemplateForm.js +++ b/awx/ui/src/screens/Template/shared/JobTemplateForm.js @@ -597,6 +597,12 @@ function JobTemplateForm({ label={t`Enable Fact Storage`} tooltip={helpText.enableFactStorage} /> +
@@ -731,6 +737,8 @@ const FormikApp = withFormik({ limit: template.limit || '', name: template.name || '', playbook: template.playbook || '', + prevent_instance_group_fallback: + template.prevent_instance_group_fallback || false, project: summary_fields?.project || projectValues || null, scm_branch: template.scm_branch || '', skip_tags: template.skip_tags || '', diff --git a/awx/ui/src/screens/Template/shared/data.job_template.json b/awx/ui/src/screens/Template/shared/data.job_template.json index 4d4eb77af1..3c57d0143c 100644 --- a/awx/ui/src/screens/Template/shared/data.job_template.json +++ b/awx/ui/src/screens/Template/shared/data.job_template.json @@ -1,194 +1,199 @@ { - "id": 7, - "type": "job_template", - "url": "/api/v2/job_templates/7/", - "related": { - "named_url": "/api/v2/job_templates/Mike's JT/", - "created_by": "/api/v2/users/1/", - "modified_by": "/api/v2/users/1/", - "labels": "/api/v2/job_templates/7/labels/", - "inventory": "/api/v2/inventories/1/", - "project": "/api/v2/projects/6/", - "credentials": "/api/v2/job_templates/7/credentials/", - "last_job": "/api/v2/jobs/12/", - "jobs": "/api/v2/job_templates/7/jobs/", - "schedules": "/api/v2/job_templates/7/schedules/", - "activity_stream": "/api/v2/job_templates/7/activity_stream/", - "launch": "/api/v2/job_templates/7/launch/", - "notification_templates_started": "/api/v2/job_templates/7/notification_templates_started/", - "notification_templates_success": "/api/v2/job_templates/7/notification_templates_success/", - "notification_templates_error": "/api/v2/job_templates/7/notification_templates_error/", - "access_list": "/api/v2/job_templates/7/access_list/", - "survey_spec": "/api/v2/job_templates/7/survey_spec/", - "object_roles": "/api/v2/job_templates/7/object_roles/", - "instance_groups": "/api/v2/job_templates/7/instance_groups/", - "slice_workflow_jobs": "/api/v2/job_templates/7/slice_workflow_jobs/", - "copy": "/api/v2/job_templates/7/copy/", - "webhook_receiver": "/api/v2/job_templates/7/github/", - "webhook_key": "/api/v2/job_templates/7/webhook_key/" + "id": 7, + "type": "job_template", + "url": "/api/v2/job_templates/7/", + "related": { + "named_url": "/api/v2/job_templates/Mike's JT/", + "created_by": "/api/v2/users/1/", + "modified_by": "/api/v2/users/1/", + "labels": "/api/v2/job_templates/7/labels/", + "inventory": "/api/v2/inventories/1/", + "project": "/api/v2/projects/6/", + "credentials": "/api/v2/job_templates/7/credentials/", + "last_job": "/api/v2/jobs/12/", + "jobs": "/api/v2/job_templates/7/jobs/", + "schedules": "/api/v2/job_templates/7/schedules/", + "activity_stream": "/api/v2/job_templates/7/activity_stream/", + "launch": "/api/v2/job_templates/7/launch/", + "notification_templates_started": "/api/v2/job_templates/7/notification_templates_started/", + "notification_templates_success": "/api/v2/job_templates/7/notification_templates_success/", + "notification_templates_error": "/api/v2/job_templates/7/notification_templates_error/", + "access_list": "/api/v2/job_templates/7/access_list/", + "survey_spec": "/api/v2/job_templates/7/survey_spec/", + "object_roles": "/api/v2/job_templates/7/object_roles/", + "instance_groups": "/api/v2/job_templates/7/instance_groups/", + "slice_workflow_jobs": "/api/v2/job_templates/7/slice_workflow_jobs/", + "copy": "/api/v2/job_templates/7/copy/", + "webhook_receiver": "/api/v2/job_templates/7/github/", + "webhook_key": "/api/v2/job_templates/7/webhook_key/" + }, + "summary_fields": { + "inventory": { + "id": 1, + "name": "Mike's Inventory", + "description": "", + "has_active_failures": false, + "total_hosts": 1, + "hosts_with_active_failures": 0, + "total_groups": 0, + "groups_with_active_failures": 0, + "has_inventory_sources": false, + "total_inventory_sources": 0, + "inventory_sources_with_failures": 0, + "organization_id": 1, + "kind": "" }, - "summary_fields": { - "inventory": { - "id": 1, - "name": "Mike's Inventory", - "description": "", - "has_active_failures": false, - "total_hosts": 1, - "hosts_with_active_failures": 0, - "total_groups": 0, - "groups_with_active_failures": 0, - "has_inventory_sources": false, - "total_inventory_sources": 0, - "inventory_sources_with_failures": 0, - "organization_id": 1, - "kind": "" - }, - "project": { - "id": 6, - "name": "Mike's Project", - "description": "", - "status": "successful", - "scm_type": "git" - }, - "last_job": { - "id": 12, - "name": "Mike's JT", - "description": "", - "finished": "2019-10-01T14:34:35.142483Z", - "status": "successful", - "failed": false - }, - "last_update": { - "id": 12, - "name": "Mike's JT", - "description": "", - "status": "successful", - "failed": false - }, - "created_by": { - "id": 1, - "username": "admin", - "first_name": "", - "last_name": "" - }, - "modified_by": { - "id": 1, - "username": "admin", - "first_name": "", - "last_name": "" - }, - "object_roles": { - "admin_role": { - "description": "Can manage all aspects of the job template", - "name": "Admin", - "id": 24 - }, - "execute_role": { - "description": "May run the job template", - "name": "Execute", - "id": 25 - }, - "read_role": { - "description": "May view settings for the job template", - "name": "Read", - "id": 26 - } - }, - "user_capabilities": { - "edit": true, - "delete": true, - "start": true, - "schedule": true, - "copy": true - }, - "labels": { - "count": 1, - "results": [{ - "id": 91, - "name": "L_91o2" - }] - }, - "survey": { - "title": "", - "description": "" - }, - "recent_jobs": [{ - "id": 12, - "status": "successful", - "finished": "2019-10-01T14:34:35.142483Z", - "type": "job" - }], - "credentials": [{ - "id": 1, - "kind": "ssh", - "name": "Credential 1" - }, - { - "id": 2, - "kind": "awx", - "name": "Credential 2" - } - ], - "webhook_credential": { - "id": "1", - "name": "Webhook Credential" - - }, - "execution_environment": { - "id": 1, - "name": "Default EE", - "description": "", - "image": "quay.io/ansible/awx-ee" - }, - "resolved_environment": { - "id": 1, - "name": "Default EE", - "description": "", - "image": "quay.io/ansible/awx-ee" + "project": { + "id": 6, + "name": "Mike's Project", + "description": "", + "status": "successful", + "scm_type": "git" + }, + "last_job": { + "id": 12, + "name": "Mike's JT", + "description": "", + "finished": "2019-10-01T14:34:35.142483Z", + "status": "successful", + "failed": false + }, + "last_update": { + "id": 12, + "name": "Mike's JT", + "description": "", + "status": "successful", + "failed": false + }, + "created_by": { + "id": 1, + "username": "admin", + "first_name": "", + "last_name": "" + }, + "modified_by": { + "id": 1, + "username": "admin", + "first_name": "", + "last_name": "" + }, + "object_roles": { + "admin_role": { + "description": "Can manage all aspects of the job template", + "name": "Admin", + "id": 24 + }, + "execute_role": { + "description": "May run the job template", + "name": "Execute", + "id": 25 + }, + "read_role": { + "description": "May view settings for the job template", + "name": "Read", + "id": 26 + } + }, + "user_capabilities": { + "edit": true, + "delete": true, + "start": true, + "schedule": true, + "copy": true + }, + "labels": { + "count": 1, + "results": [ + { + "id": 91, + "name": "L_91o2" } + ] }, - "created": "2019-09-30T16:18:34.564820Z", - "modified": "2019-10-01T14:47:31.818431Z", - "name": "Mike's JT", - "description": "", - "job_type": "run", - "inventory": 1, - "project": 6, - "playbook": "ping.yml", - "scm_branch": "Foo branch", - "forks": 0, - "limit": "", - "verbosity": 0, - "extra_vars": "", - "job_tags": "T_100,T_200", - "force_handlers": false, - "skip_tags": "S_100,S_200", - "start_at_task": "", - "timeout": 0, - "use_fact_cache": true, - "last_job_run": "2019-10-01T14:34:35.142483Z", - "last_job_failed": false, - "next_job_run": null, - "status": "successful", - "host_config_key": "", - "ask_scm_branch_on_launch": false, - "ask_diff_mode_on_launch": false, - "ask_variables_on_launch": false, - "ask_limit_on_launch": false, - "ask_tags_on_launch": false, - "ask_skip_tags_on_launch": false, - "ask_job_type_on_launch": false, - "ask_verbosity_on_launch": false, - "ask_inventory_on_launch": false, - "ask_credential_on_launch": false, - "survey_enabled": true, - "become_enabled": false, - "diff_mode": false, - "allow_simultaneous": false, - "custom_virtualenv": null, - "job_slice_count": 1, - "webhook_credential": 1, - "webhook_key": "asertdyuhjkhgfd234567kjgfds", - "webhook_service": "github", - "execution_environment": 1 + "survey": { + "title": "", + "description": "" + }, + "recent_jobs": [ + { + "id": 12, + "status": "successful", + "finished": "2019-10-01T14:34:35.142483Z", + "type": "job" + } + ], + "credentials": [ + { + "id": 1, + "kind": "ssh", + "name": "Credential 1" + }, + { + "id": 2, + "kind": "awx", + "name": "Credential 2" + } + ], + "webhook_credential": { + "id": "1", + "name": "Webhook Credential" + }, + "execution_environment": { + "id": 1, + "name": "Default EE", + "description": "", + "image": "quay.io/ansible/awx-ee" + }, + "resolved_environment": { + "id": 1, + "name": "Default EE", + "description": "", + "image": "quay.io/ansible/awx-ee" + } + }, + "created": "2019-09-30T16:18:34.564820Z", + "modified": "2019-10-01T14:47:31.818431Z", + "name": "Mike's JT", + "description": "", + "job_type": "run", + "inventory": 1, + "project": 6, + "playbook": "ping.yml", + "scm_branch": "Foo branch", + "forks": 0, + "limit": "", + "verbosity": 0, + "extra_vars": "", + "job_tags": "T_100,T_200", + "force_handlers": false, + "skip_tags": "S_100,S_200", + "start_at_task": "", + "timeout": 0, + "use_fact_cache": true, + "last_job_run": "2019-10-01T14:34:35.142483Z", + "last_job_failed": false, + "next_job_run": null, + "status": "successful", + "host_config_key": "", + "ask_scm_branch_on_launch": false, + "ask_diff_mode_on_launch": false, + "ask_variables_on_launch": false, + "ask_limit_on_launch": false, + "ask_tags_on_launch": false, + "ask_skip_tags_on_launch": false, + "ask_job_type_on_launch": false, + "ask_verbosity_on_launch": false, + "ask_inventory_on_launch": false, + "ask_credential_on_launch": false, + "survey_enabled": true, + "become_enabled": false, + "diff_mode": false, + "allow_simultaneous": false, + "custom_virtualenv": null, + "job_slice_count": 1, + "webhook_credential": 1, + "webhook_key": "asertdyuhjkhgfd234567kjgfds", + "webhook_service": "github", + "execution_environment": 1, + "prevent_instance_group_fallback": false }