diff --git a/awx/api/serializers.py b/awx/api/serializers.py index a234240a7e..d1c1b6c9e2 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1147,7 +1147,6 @@ class InventorySerializer(BaseSerializerWithVariables): update_inventory_sources = self.reverse('api:inventory_inventory_sources_update', kwargs={'pk': obj.pk}), activity_stream = self.reverse('api:inventory_activity_stream_list', kwargs={'pk': obj.pk}), job_templates = self.reverse('api:inventory_job_template_list', kwargs={'pk': obj.pk}), - scan_job_templates = self.reverse('api:inventory_scan_job_template_list', kwargs={'pk': obj.pk}), ad_hoc_commands = self.reverse('api:inventory_ad_hoc_commands_list', kwargs={'pk': obj.pk}), access_list = self.reverse('api:inventory_access_list', kwargs={'pk': obj.pk}), object_roles = self.reverse('api:inventory_object_roles_list', kwargs={'pk': obj.pk}), @@ -2336,8 +2335,7 @@ class JobOptionsSerializer(LabelsListMixin, BaseSerializer): if 'project' in self.fields and 'playbook' in self.fields: project = attrs.get('project', self.instance and self.instance.project or None) playbook = attrs.get('playbook', self.instance and self.instance.playbook or '') - job_type = attrs.get('job_type', self.instance and self.instance.job_type or None) - if not project and job_type != PERM_INVENTORY_SCAN: + if not project: raise serializers.ValidationError({'project': _('This field is required.')}) if project and project.scm_type and playbook and force_text(playbook) not in project.playbook_files: raise serializers.ValidationError({'playbook': _('Playbook not found for project.')}) @@ -2408,26 +2406,18 @@ class JobTemplateSerializer(JobTemplateMixin, UnifiedJobTemplateSerializer, JobO def get_field_from_model_or_attrs(fd): return attrs.get(fd, self.instance and getattr(self.instance, fd) or None) - survey_enabled = get_field_from_model_or_attrs('survey_enabled') - job_type = get_field_from_model_or_attrs('job_type') inventory = get_field_from_model_or_attrs('inventory') credential = get_field_from_model_or_attrs('credential') project = get_field_from_model_or_attrs('project') prompting_error_message = _("Must either set a default value or ask to prompt on launch.") - if job_type == "scan": - if inventory is None or attrs.get('ask_inventory_on_launch', False): - raise serializers.ValidationError({'inventory': _('Scan jobs must be assigned a fixed inventory.')}) - elif project is None: + if project is None: raise serializers.ValidationError({'project': _("Job types 'run' and 'check' must have assigned a project.")}) elif credential is None and not get_field_from_model_or_attrs('ask_credential_on_launch'): raise serializers.ValidationError({'credential': prompting_error_message}) elif inventory is None and not get_field_from_model_or_attrs('ask_inventory_on_launch'): raise serializers.ValidationError({'inventory': prompting_error_message}) - if survey_enabled and job_type == PERM_INVENTORY_SCAN: - raise serializers.ValidationError({'survey_enabled': _('Survey Enabled cannot be used with scan jobs.')}) - return super(JobTemplateSerializer, self).validate(attrs) def validate_extra_vars(self, value): @@ -2570,7 +2560,7 @@ class JobRelaunchSerializer(JobSerializer): obj = self.context.get('obj') if not obj.credential: raise serializers.ValidationError(dict(credential=[_("Credential not found or deleted.")])) - if obj.job_type != PERM_INVENTORY_SCAN and obj.project is None: + if obj.project is None: raise serializers.ValidationError(dict(errors=[_("Job Template Project is missing or undefined.")])) if obj.inventory is None: raise serializers.ValidationError(dict(errors=[_("Job Template Inventory is missing or undefined.")])) diff --git a/awx/api/urls.py b/awx/api/urls.py index c42d059b82..a21d1df9ab 100644 --- a/awx/api/urls.py +++ b/awx/api/urls.py @@ -97,7 +97,6 @@ inventory_urls = patterns('awx.api.views', url(r'^(?P[0-9]+)/update_inventory_sources/$', 'inventory_inventory_sources_update'), url(r'^(?P[0-9]+)/activity_stream/$', 'inventory_activity_stream_list'), url(r'^(?P[0-9]+)/job_templates/$', 'inventory_job_template_list'), - url(r'^(?P[0-9]+)/scan_job_templates/$', 'inventory_scan_job_template_list'), url(r'^(?P[0-9]+)/ad_hoc_commands/$', 'inventory_ad_hoc_commands_list'), url(r'^(?P[0-9]+)/access_list/$', 'inventory_access_list'), url(r'^(?P[0-9]+)/object_roles/$', 'inventory_object_roles_list'), diff --git a/awx/api/views.py b/awx/api/views.py index fb5e54565b..3b6d6ee08c 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -858,10 +858,8 @@ class OrganizationDetail(RetrieveUpdateDestroyAPIView): organization__id=org_id).count() org_counts['projects'] = Project.accessible_objects(**access_kwargs).filter( organization__id=org_id).count() - org_counts['job_templates'] = JobTemplate.accessible_objects(**access_kwargs).exclude( - job_type='scan').filter(project__organization__id=org_id).count() - org_counts['job_templates'] += JobTemplate.accessible_objects(**access_kwargs).filter( - job_type='scan').filter(inventory__organization__id=org_id).count() + org_counts['job_templates'] = JobTemplate.accessible_objects(**access_kwargs).filter( + project__organization__id=org_id).count() full_context['related_field_counts'] = {} full_context['related_field_counts'][org_id] = org_counts @@ -1907,21 +1905,6 @@ class InventoryJobTemplateList(SubListAPIView): return qs.filter(inventory=parent) -class InventoryScanJobTemplateList(SubListAPIView): - - model = JobTemplate - serializer_class = JobTemplateSerializer - parent_model = Inventory - relationship = 'jobtemplates' - new_in_220 = True - - def get_queryset(self): - parent = self.get_parent_object() - self.check_parent_access(parent) - qs = self.request.user.get_queryset(self.model) - return qs.filter(job_type=PERM_INVENTORY_SCAN, inventory=parent) - - class HostList(ListCreateAPIView): always_allow_superuser = False diff --git a/awx/main/access.py b/awx/main/access.py index 0582669173..417421e5f4 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -1141,9 +1141,6 @@ class JobTemplateAccess(BaseAccess): # if reference_obj is provided, determine if it can be copied reference_obj = data.get('reference_obj', None) - if 'job_type' in data and data['job_type'] == PERM_INVENTORY_SCAN: - self.check_license(feature='system_tracking') - if 'survey_enabled' in data and data['survey_enabled']: self.check_license(feature='surveys') @@ -1175,11 +1172,6 @@ class JobTemplateAccess(BaseAccess): return False project = get_value(Project, 'project') - if 'job_type' in data and data['job_type'] == PERM_INVENTORY_SCAN: - if not inventory: - return False - elif not project: - return True # If the user has admin access to the project (as an org admin), should # be able to proceed without additional checks. if project: @@ -1194,8 +1186,6 @@ class JobTemplateAccess(BaseAccess): # Check license. if validate_license: self.check_license() - if obj.job_type == PERM_INVENTORY_SCAN: - self.check_license(feature='system_tracking') if obj.survey_enabled: self.check_license(feature='surveys') if Instance.objects.active_count() > 1: @@ -1205,12 +1195,6 @@ class JobTemplateAccess(BaseAccess): if self.user.is_superuser: return True - if obj.job_type == PERM_INVENTORY_SCAN: - # Scan job with default project, must have JT execute or be org admin - if obj.project is None and obj.inventory: - return (self.user in obj.execute_role or - self.user in obj.inventory.organization.admin_role) - return self.user in obj.execute_role def can_change(self, obj, data): @@ -1221,9 +1205,6 @@ class JobTemplateAccess(BaseAccess): data = dict(data) if self.changes_are_non_sensitive(obj, data): - if 'job_type' in data and obj.job_type != data['job_type'] and data['job_type'] == PERM_INVENTORY_SCAN: - self.check_license(feature='system_tracking') - if 'survey_enabled' in data and obj.survey_enabled != data['survey_enabled'] and data['survey_enabled']: self.check_license(feature='surveys') return True diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index a071cb48c3..3aaae69fac 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -308,10 +308,7 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour validation_errors['credential'] = [_("Job Template must provide 'credential' or allow prompting for it."),] # Job type dependent checks - if self.job_type == PERM_INVENTORY_SCAN: - if self.inventory is None or self.ask_inventory_on_launch: - validation_errors['inventory'] = [_("Scan jobs must be assigned a fixed inventory."),] - elif self.project is None: + if self.project is None: resources_needed_to_start.append('project') validation_errors['project'] = [_("Job types 'run' and 'check' must have assigned a project."),] @@ -407,12 +404,8 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour """ errors = {} if 'job_type' in data and self.ask_job_type_on_launch: - if ((self.job_type == PERM_INVENTORY_SCAN and not data['job_type'] == PERM_INVENTORY_SCAN) or - (data['job_type'] == PERM_INVENTORY_SCAN and not self.job_type == PERM_INVENTORY_SCAN)): + if data['job_type'] == PERM_INVENTORY_SCAN and not self.job_type == PERM_INVENTORY_SCAN: errors['job_type'] = _('Cannot override job_type to or from a scan job.') - if (self.job_type == PERM_INVENTORY_SCAN and ('inventory' in data) and self.ask_inventory_on_launch and - self.inventory != data['inventory']): - errors['inventory'] = _('Inventory cannot be changed at runtime for scan jobs.') return errors @property @@ -647,8 +640,6 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin): return data def _resources_sufficient_for_launch(self): - if self.job_type == PERM_INVENTORY_SCAN: - return self.inventory_id is not None return not (self.inventory_id is None or self.project_id is None) def display_artifacts(self): diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 68172a3085..8e2f5d7423 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -1057,18 +1057,13 @@ class RunJob(BaseTask): args.extend(['-e', json.dumps(extra_vars)]) # Add path to playbook (relative to project.local_path). - if job.project is None and job.job_type == PERM_INVENTORY_SCAN: - args.append("scan_facts.yml") - else: - args.append(job.playbook) + args.append(job.playbook) return args def build_safe_args(self, job, **kwargs): return self.build_args(job, display=True, **kwargs) def build_cwd(self, job, **kwargs): - if job.project is None and job.job_type == PERM_INVENTORY_SCAN: - return self.get_path_to('..', 'playbooks') cwd = job.project.get_project_path() if not cwd: root = settings.PROJECTS_ROOT diff --git a/awx/main/tests/functional/api/test_job_runtime_params.py b/awx/main/tests/functional/api/test_job_runtime_params.py index 69c82e5d28..3626ae89a7 100644 --- a/awx/main/tests/functional/api/test_job_runtime_params.py +++ b/awx/main/tests/functional/api/test_job_runtime_params.py @@ -76,14 +76,6 @@ def job_template_prompts_null(project): ) -@pytest.fixture -def bad_scan_JT(job_template_prompts): - job_template = job_template_prompts(True) - job_template.job_type = 'scan' - job_template.save() - return job_template - - # End of setup, tests start here @pytest.mark.django_db @pytest.mark.job_runtime_vars @@ -259,18 +251,6 @@ def test_job_block_scan_job_type_change(job_template_prompts, post, admin_user): assert 'job_type' in response.data -@pytest.mark.django_db -@pytest.mark.job_runtime_vars -def test_job_block_scan_job_inv_change(mocker, bad_scan_JT, runtime_data, post, admin_user): - # Assure that giving a new inventory for a scan job blocks the launch - with mocker.patch('awx.main.access.BaseAccess.check_license'): - response = post(reverse('api:job_template_launch', kwargs={'pk': bad_scan_JT.pk}), - dict(inventory=runtime_data['inventory']), admin_user, - expect=400) - - assert 'inventory' in response.data - - @pytest.mark.django_db def test_job_launch_JT_with_validation(machine_credential, deploy_jobtemplate): deploy_jobtemplate.extra_vars = '{"job_template_var": 3}' diff --git a/awx/main/tests/functional/api/test_job_template.py b/awx/main/tests/functional/api/test_job_template.py index 61892ffa82..3ef9775c13 100644 --- a/awx/main/tests/functional/api/test_job_template.py +++ b/awx/main/tests/functional/api/test_job_template.py @@ -1,7 +1,7 @@ import pytest # AWX -from awx.api.serializers import JobTemplateSerializer, JobLaunchSerializer +from awx.api.serializers import JobTemplateSerializer from awx.api.versioning import reverse from awx.main.models.jobs import Job from awx.main.migrations import _save_password_keys as save_password_keys @@ -387,7 +387,6 @@ def test_edit_nonsenstive(patch, job_template_factory, alice): 'ask_inventory_on_launch':True, 'ask_credential_on_launch': True, }, alice, expect=200) - print(res.data) assert res.data['name'] == 'updated' @@ -430,48 +429,6 @@ def test_jt_admin_copy_edit_functional(jt_copy_edit, rando, get, post): assert post_response.status_code == 403 -@pytest.mark.django_db -def test_scan_jt_no_inventory(job_template_factory): - # A user should be able to create a scan job without a project, but an inventory is required - objects = job_template_factory('jt', - credential='c', - job_type="scan", - project='p', - inventory='i', - organization='o') - serializer = JobTemplateSerializer(data={"name": "Test", "job_type": "scan", - "project": None, "inventory": objects.inventory.pk}) - assert serializer.is_valid() - serializer = JobTemplateSerializer(data={"name": "Test", "job_type": "scan", - "project": None, "inventory": None}) - assert not serializer.is_valid() - assert "inventory" in serializer.errors - serializer = JobTemplateSerializer(data={"name": "Test", "job_type": "scan", - "project": None, "inventory": None, - "ask_inventory_on_launch": True}) - assert not serializer.is_valid() - assert "inventory" in serializer.errors - - # A user shouldn't be able to launch a scan job template which is missing an inventory - obj_jt = objects.job_template - obj_jt.inventory = None - serializer = JobLaunchSerializer(instance=obj_jt, - context={'obj': obj_jt, - "data": {}}, - data={}) - assert not serializer.is_valid() - assert 'inventory' in serializer.errors - - -@pytest.mark.django_db -def test_scan_jt_surveys(inventory): - serializer = JobTemplateSerializer(data={"name": "Test", "job_type": "scan", - "project": None, "inventory": inventory.pk, - "survey_enabled": True}) - assert not serializer.is_valid() - assert "survey_enabled" in serializer.errors - - @pytest.mark.django_db def test_launch_with_pending_deletion_inventory(get, post, organization_factory, job_template_factory, machine_credential, @@ -641,9 +598,6 @@ def test_jt_without_project(inventory): serializer = JobTemplateSerializer(data=data) assert not serializer.is_valid() assert "project" in serializer.errors - data["job_type"] = "scan" - serializer = JobTemplateSerializer(data=data) - assert serializer.is_valid() @pytest.mark.django_db diff --git a/awx/main/tests/functional/api/test_organization_counts.py b/awx/main/tests/functional/api/test_organization_counts.py index 452db26dfd..9c4f536b09 100644 --- a/awx/main/tests/functional/api/test_organization_counts.py +++ b/awx/main/tests/functional/api/test_organization_counts.py @@ -163,12 +163,7 @@ def test_two_organizations(resourced_organization, organizations, user, get): @pytest.mark.django_db def test_scan_JT_counted(resourced_organization, user, get): admin_user = user('admin', True) - # Add a scan job template to the org - resourced_organization.projects.all()[0].jobtemplates.create( - job_type='scan', inventory=resourced_organization.inventories.all()[0], - name='scan-job-template') counts_dict = COUNTS_PRIMES - counts_dict['job_templates'] += 1 # Test list view list_response = get(reverse('api:organization_list'), admin_user) @@ -184,7 +179,7 @@ def test_scan_JT_counted(resourced_organization, user, get): @pytest.mark.django_db def test_JT_not_double_counted(resourced_organization, user, get): admin_user = user('admin', True) - # Add a scan job template to the org + # Add a run job template to the org resourced_organization.projects.all()[0].jobtemplates.create( job_type='run', inventory=resourced_organization.inventories.all()[0], diff --git a/awx/main/tests/unit/models/test_workflow_unit.py b/awx/main/tests/unit/models/test_workflow_unit.py index adee9b7ae7..129f18d78c 100644 --- a/awx/main/tests/unit/models/test_workflow_unit.py +++ b/awx/main/tests/unit/models/test_workflow_unit.py @@ -242,11 +242,3 @@ class TestWorkflowWarnings: assert 'credential' in job_node_with_prompts.get_prompts_warnings()['ignored'] assert len(job_node_with_prompts.get_prompts_warnings()['ignored']) == 2 - def test_warn_scan_errors_node_prompts(self, job_node_with_prompts): - job_node_with_prompts.unified_job_template.job_type = 'scan' - job_node_with_prompts.char_prompts['job_type'] = 'run' - job_node_with_prompts.inventory = Inventory(name='different-inventory', pk=23) - assert 'ignored' in job_node_with_prompts.get_prompts_warnings() - assert 'job_type' in job_node_with_prompts.get_prompts_warnings()['ignored'] - assert 'inventory' in job_node_with_prompts.get_prompts_warnings()['ignored'] - assert len(job_node_with_prompts.get_prompts_warnings()['ignored']) == 2