diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 582d39630d..215150365b 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1828,6 +1828,13 @@ class JobTemplateSerializer(UnifiedJobTemplateSerializer, JobOptionsSerializer): def validate(self, attrs): survey_enabled = attrs.get('survey_enabled', self.instance and self.instance.survey_enabled or False) job_type = attrs.get('job_type', self.instance and self.instance.job_type or None) + + if job_type == "scan": + if attrs['inventory'] is None or attrs.get('ask_inventory_on_launch', False): + raise serializers.ValidationError({'inventory': 'Scan jobs must be assigned a fixed inventory.'}) + elif attrs['project'] is None: + raise serializers.ValidationError({'project': "Job types 'run' and 'check' must have assigned a project."}) + if survey_enabled and job_type == PERM_INVENTORY_SCAN: raise serializers.ValidationError({'survey_enabled': 'Survey Enabled can not be used with scan jobs.'}) diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index d94f09cafc..78e6aa01b3 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -273,12 +273,6 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, ResourceMixin): return (validation_errors, resources_needed_to_start) - def clean(self): - validation_errors, resources_needed_to_start = self.resource_validation_data() - if validation_errors: - raise ValidationError(validation_errors) - return super(JobTemplate, self).clean() - @property def resources_needed_to_start(self): validation_errors, resources_needed_to_start = self.resource_validation_data() diff --git a/awx/main/tests/functional/api/test_job_template.py b/awx/main/tests/functional/api/test_job_template.py index 1b971fe0d4..2812237606 100644 --- a/awx/main/tests/functional/api/test_job_template.py +++ b/awx/main/tests/functional/api/test_job_template.py @@ -2,7 +2,7 @@ import pytest import mock # AWX -from awx.api.serializers import JobTemplateSerializer +from awx.api.serializers import JobTemplateSerializer, JobLaunchSerializer from awx.main.models.jobs import JobTemplate from awx.main.models.projects import ProjectOptions @@ -10,7 +10,6 @@ from awx.main.models.projects import ProjectOptions from django.test.client import RequestFactory from django.core.urlresolvers import reverse - @pytest.fixture def jt_copy_edit(job_template_factory, project): objects = job_template_factory( @@ -177,3 +176,58 @@ def test_jt_admin_copy_edit_functional(jt_copy_edit, rando, get, post): post_data['name'] = '%s @ 12:19:47 pm' % post_data['name'] post_response = post(reverse('api:job_template_list', args=[]), user=rando, data=post_data) 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_jt_without_project(inventory): + data = dict(name="Test", job_type="run", + inventory=inventory.pk, project=None) + serializer = JobTemplateSerializer(data=data) + assert not serializer.is_valid() + assert "project" in serializer.errors + data["job_type"] = "check" + 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()