mirror of
https://github.com/ansible/awx.git
synced 2026-02-23 05:55:59 -03:30
More job template tests, enable creating a new job by posting to the job template job list.
This commit is contained in:
@@ -511,7 +511,7 @@ class JobTemplateAccess(BaseAccess):
|
|||||||
def can_change(self, obj, data):
|
def can_change(self, obj, data):
|
||||||
'''
|
'''
|
||||||
'''
|
'''
|
||||||
return False # FIXME
|
return self.user.is_superuser # FIXME
|
||||||
|
|
||||||
class JobAccess(BaseAccess):
|
class JobAccess(BaseAccess):
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import shlex
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models, DatabaseError
|
from django.db import models, DatabaseError
|
||||||
from django.db.models import CASCADE, SET_NULL, PROTECT
|
from django.db.models import CASCADE, SET_NULL, PROTECT
|
||||||
@@ -25,7 +26,6 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
import exceptions
|
|
||||||
from jsonfield import JSONField
|
from jsonfield import JSONField
|
||||||
from djcelery.models import TaskMeta
|
from djcelery.models import TaskMeta
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
@@ -679,6 +679,13 @@ class Job(CommonModel):
|
|||||||
@property
|
@property
|
||||||
def extra_vars_dict(self):
|
def extra_vars_dict(self):
|
||||||
'''Return extra_vars key=value pairs as a dictionary.'''
|
'''Return extra_vars key=value pairs as a dictionary.'''
|
||||||
|
d = {}
|
||||||
|
extra_vars = self.extra_vars.encode('utf-8')
|
||||||
|
for kv in [x.decode('utf-8') for x in shlex.split(extra_vars, posix=True)]:
|
||||||
|
if '=' in kv:
|
||||||
|
k, v = kv.split('=', 1)
|
||||||
|
d[k] = v
|
||||||
|
return d
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def celery_task(self):
|
def celery_task(self):
|
||||||
|
|||||||
@@ -376,6 +376,13 @@ class JobTemplateSerializer(BaseSerializer):
|
|||||||
res['credential'] = reverse('main:credentials_detail', args=(obj.credential.pk,))
|
res['credential'] = reverse('main:credentials_detail', args=(obj.credential.pk,))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def validate_playbook(self, attrs, source):
|
||||||
|
project = attrs.get('project', None)
|
||||||
|
playbook = attrs.get('playbook', '')
|
||||||
|
if project and playbook and playbook not in project.playbooks:
|
||||||
|
raise serializers.ValidationError('Playbook not found for project')
|
||||||
|
return attrs
|
||||||
|
|
||||||
class JobSerializer(BaseSerializer):
|
class JobSerializer(BaseSerializer):
|
||||||
|
|
||||||
passwords_needed_to_start = serializers.Field(source='get_passwords_needed_to_start')
|
passwords_needed_to_start = serializers.Field(source='get_passwords_needed_to_start')
|
||||||
@@ -402,6 +409,28 @@ class JobSerializer(BaseSerializer):
|
|||||||
res['job_template'] = reverse('main:job_template_detail', args=(obj.job_template.pk,))
|
res['job_template'] = reverse('main:job_template_detail', args=(obj.job_template.pk,))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def from_native(self, data, files):
|
||||||
|
# When creating a new job and a job template is specified, populate any
|
||||||
|
# fields not provided in data from the job template.
|
||||||
|
if not self.object and isinstance(data, dict) and 'job_template' in data:
|
||||||
|
try:
|
||||||
|
job_template = JobTemplate.objects.get(pk=data['job_template'])
|
||||||
|
except JobTemplate.DoesNotExist:
|
||||||
|
self._errors = {'job_template': 'Invalid job template'}
|
||||||
|
return
|
||||||
|
# Don't auto-populate name or description.
|
||||||
|
data.setdefault('job_type', job_template.job_type)
|
||||||
|
data.setdefault('inventory', job_template.inventory.pk)
|
||||||
|
data.setdefault('project', job_template.project.pk)
|
||||||
|
data.setdefault('playbook', job_template.playbook)
|
||||||
|
if job_template.credential:
|
||||||
|
data.setdefault('credential', job_template.credential.pk)
|
||||||
|
data.setdefault('forks', job_template.forks)
|
||||||
|
data.setdefault('limit', job_template.limit)
|
||||||
|
data.setdefault('verbosity', job_template.verbosity)
|
||||||
|
data.setdefault('extra_vars', job_template.extra_vars)
|
||||||
|
return super(JobSerializer, self).from_native(data, files)
|
||||||
|
|
||||||
class JobHostSummarySerializer(BaseSerializer):
|
class JobHostSummarySerializer(BaseSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
@@ -55,7 +55,10 @@ class BaseTestMixin(object):
|
|||||||
username = user_or_username
|
username = user_or_username
|
||||||
password = password or self._user_passwords.get(username)
|
password = password or self._user_passwords.get(username)
|
||||||
previous_auth = self._current_auth
|
previous_auth = self._current_auth
|
||||||
self._current_auth = (username, password)
|
if username is None:
|
||||||
|
self._current_auth = None
|
||||||
|
else:
|
||||||
|
self._current_auth = (username, password)
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
self._current_auth = previous_auth
|
self._current_auth = previous_auth
|
||||||
@@ -179,8 +182,8 @@ class BaseTestMixin(object):
|
|||||||
assert response.status_code == expect, "expected status %s, got %s for url=%s as auth=%s: %s" % (expect, response.status_code, url, auth, response.content)
|
assert response.status_code == expect, "expected status %s, got %s for url=%s as auth=%s: %s" % (expect, response.status_code, url, auth, response.content)
|
||||||
if method_name == 'head':
|
if method_name == 'head':
|
||||||
self.assertFalse(response.content)
|
self.assertFalse(response.content)
|
||||||
if response.status_code not in [ 202, 204, 400, 405, 409 ] and method_name != 'head':
|
if response.status_code not in [ 202, 204, 405, 409 ] and method_name != 'head' and response.content:
|
||||||
# no JSON responses in these at least for now, 400/409 should probably return some (FIXME)
|
# no JSON responses in these at least for now, 409 should probably return some (FIXME)
|
||||||
return json.loads(response.content)
|
return json.loads(response.content)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -317,7 +317,11 @@ class BaseJobTest(BaseTest):
|
|||||||
project=self.proj_dev,
|
project=self.proj_dev,
|
||||||
playbook=self.proj_dev.playbooks[0],
|
playbook=self.proj_dev.playbooks[0],
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
|
self.job_eng_check = self.jt_eng_check.create_job(
|
||||||
|
created_by=self.user_sue,
|
||||||
|
credential=self.cred_doug,
|
||||||
|
)
|
||||||
self.jt_eng_run = JobTemplate.objects.create(
|
self.jt_eng_run = JobTemplate.objects.create(
|
||||||
name='eng-dev-run',
|
name='eng-dev-run',
|
||||||
job_type='run',
|
job_type='run',
|
||||||
@@ -326,6 +330,10 @@ class BaseJobTest(BaseTest):
|
|||||||
playbook=self.proj_dev.playbooks[0],
|
playbook=self.proj_dev.playbooks[0],
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
|
self.job_eng_run = self.jt_eng_run.create_job(
|
||||||
|
created_by=self.user_sue,
|
||||||
|
credential=self.cred_chuck,
|
||||||
|
)
|
||||||
|
|
||||||
# Support has job templates to check/run the test project onto
|
# Support has job templates to check/run the test project onto
|
||||||
# their own inventory.
|
# their own inventory.
|
||||||
@@ -336,7 +344,11 @@ class BaseJobTest(BaseTest):
|
|||||||
project=self.proj_test,
|
project=self.proj_test,
|
||||||
playbook=self.proj_test.playbooks[0],
|
playbook=self.proj_test.playbooks[0],
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
|
self.job_sup_check = self.jt_sup_check.create_job(
|
||||||
|
created_by=self.user_sue,
|
||||||
|
credential=self.cred_frank,
|
||||||
|
)
|
||||||
self.jt_sup_run = JobTemplate.objects.create(
|
self.jt_sup_run = JobTemplate.objects.create(
|
||||||
name='sup-test-run',
|
name='sup-test-run',
|
||||||
job_type='run',
|
job_type='run',
|
||||||
@@ -345,6 +357,10 @@ class BaseJobTest(BaseTest):
|
|||||||
playbook=self.proj_test.playbooks[0],
|
playbook=self.proj_test.playbooks[0],
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
|
self.job_sup_run = self.jt_sup_run.create_job(
|
||||||
|
created_by=self.user_sue,
|
||||||
|
credential=self.cred_eve,
|
||||||
|
)
|
||||||
|
|
||||||
# Operations has job templates to check/run the prod project onto
|
# Operations has job templates to check/run the prod project onto
|
||||||
# both east and west inventories, by default using the team credential.
|
# both east and west inventories, by default using the team credential.
|
||||||
@@ -356,7 +372,10 @@ class BaseJobTest(BaseTest):
|
|||||||
playbook=self.proj_prod.playbooks[0],
|
playbook=self.proj_prod.playbooks[0],
|
||||||
credential=self.cred_ops_east,
|
credential=self.cred_ops_east,
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
|
self.job_ops_east_check = self.jt_ops_east_check.create_job(
|
||||||
|
created_by=self.user_sue,
|
||||||
|
)
|
||||||
self.jt_ops_east_run = JobTemplate.objects.create(
|
self.jt_ops_east_run = JobTemplate.objects.create(
|
||||||
name='ops-east-prod-run',
|
name='ops-east-prod-run',
|
||||||
job_type='run',
|
job_type='run',
|
||||||
@@ -366,6 +385,9 @@ class BaseJobTest(BaseTest):
|
|||||||
credential=self.cred_ops_east,
|
credential=self.cred_ops_east,
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
|
self.job_ops_east_run = self.jt_ops_east_run.create_job(
|
||||||
|
created_by=self.user_sue,
|
||||||
|
)
|
||||||
self.jt_ops_west_check = JobTemplate.objects.create(
|
self.jt_ops_west_check = JobTemplate.objects.create(
|
||||||
name='ops-west-prod-check',
|
name='ops-west-prod-check',
|
||||||
job_type='check',
|
job_type='check',
|
||||||
@@ -374,7 +396,10 @@ class BaseJobTest(BaseTest):
|
|||||||
playbook=self.proj_prod.playbooks[0],
|
playbook=self.proj_prod.playbooks[0],
|
||||||
credential=self.cred_ops_west,
|
credential=self.cred_ops_west,
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
|
self.job_ops_west_check = self.jt_ops_west_check.create_job(
|
||||||
|
created_by=self.user_sue,
|
||||||
|
)
|
||||||
self.jt_ops_west_run = JobTemplate.objects.create(
|
self.jt_ops_west_run = JobTemplate.objects.create(
|
||||||
name='ops-west-prod-run',
|
name='ops-west-prod-run',
|
||||||
job_type='run',
|
job_type='run',
|
||||||
@@ -384,6 +409,9 @@ class BaseJobTest(BaseTest):
|
|||||||
credential=self.cred_ops_west,
|
credential=self.cred_ops_west,
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
|
self.job_ops_west_run = self.jt_ops_west_run.create_job(
|
||||||
|
created_by=self.user_sue,
|
||||||
|
)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(BaseJobTest, self).setUp()
|
super(BaseJobTest, self).setUp()
|
||||||
@@ -395,19 +423,23 @@ class JobTemplateTest(BaseJobTest):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(JobTemplateTest, self).setUp()
|
super(JobTemplateTest, self).setUp()
|
||||||
|
|
||||||
|
def _test_invalid_creds(self, url, data=None, methods=None):
|
||||||
|
data = data or {}
|
||||||
|
methods = methods or ('options', 'head', 'get')
|
||||||
|
for auth in [(None,), ('invalid', 'password')]:
|
||||||
|
with self.current_user(*auth):
|
||||||
|
for method in methods:
|
||||||
|
f = getattr(self, method)
|
||||||
|
if method in ('post', 'put'):
|
||||||
|
f(url, data, expect=401)
|
||||||
|
else:
|
||||||
|
f(url, expect=401)
|
||||||
|
|
||||||
def test_get_job_template_list(self):
|
def test_get_job_template_list(self):
|
||||||
url = reverse('main:job_template_list')
|
url = reverse('main:job_template_list')
|
||||||
|
|
||||||
# no credentials == 401
|
# Test with no auth and with invalid login.
|
||||||
self.options(url, expect=401)
|
self._test_invalid_creds(url)
|
||||||
self.head(url, expect=401)
|
|
||||||
self.get(url, expect=401)
|
|
||||||
|
|
||||||
# wrong credentials == 401
|
|
||||||
with self.current_user('invalid', 'password'):
|
|
||||||
self.options(url, expect=401)
|
|
||||||
self.head(url, expect=401)
|
|
||||||
self.get(url, expect=401)
|
|
||||||
|
|
||||||
# sue's credentials (superuser) == 200, full list
|
# sue's credentials (superuser) == 200, full list
|
||||||
with self.current_user(self.user_sue):
|
with self.current_user(self.user_sue):
|
||||||
@@ -418,7 +450,7 @@ class JobTemplateTest(BaseJobTest):
|
|||||||
self.check_pagination_and_size(response, qs.count())
|
self.check_pagination_and_size(response, qs.count())
|
||||||
self.check_list_ids(response, qs)
|
self.check_list_ids(response, qs)
|
||||||
|
|
||||||
# FIXME: Check individual job template result.
|
# FIXME: Check individual job template result fields.
|
||||||
|
|
||||||
# alex's credentials (admin of all orgs) == 200, full list
|
# alex's credentials (admin of all orgs) == 200, full list
|
||||||
with self.current_user(self.user_alex):
|
with self.current_user(self.user_alex):
|
||||||
@@ -441,80 +473,132 @@ class JobTemplateTest(BaseJobTest):
|
|||||||
#self.check_pagination_and_size(response, qs.count())
|
#self.check_pagination_and_size(response, qs.count())
|
||||||
#self.check_list_ids(response, qs)
|
#self.check_list_ids(response, qs)
|
||||||
|
|
||||||
|
# FIXME: Check with other credentials.
|
||||||
|
|
||||||
def test_post_job_template_list(self):
|
def test_post_job_template_list(self):
|
||||||
url = reverse('main:job_template_list')
|
url = reverse('main:job_template_list')
|
||||||
|
|
||||||
return # FIXME
|
|
||||||
|
|
||||||
# org admin can add job template
|
|
||||||
data = dict(
|
data = dict(
|
||||||
name = 'job-foo',
|
name = 'new job template',
|
||||||
credential = self.user_credential.pk,
|
|
||||||
inventory = self.inventory.pk,
|
|
||||||
project = self.project.pk,
|
|
||||||
job_type = PERM_INVENTORY_DEPLOY,
|
job_type = PERM_INVENTORY_DEPLOY,
|
||||||
playbook = self.project.playbooks[0],
|
inventory = self.inv_eng.pk,
|
||||||
|
project = self.proj_dev.pk,
|
||||||
|
playbook = self.proj_dev.playbooks[0],
|
||||||
)
|
)
|
||||||
with self.current_user(self.normal_django_user):
|
|
||||||
|
# Test with no auth and with invalid login.
|
||||||
|
self._test_invalid_creds(url, data, methods=('post',))
|
||||||
|
|
||||||
|
# sue can always add job templates.
|
||||||
|
with self.current_user(self.user_sue):
|
||||||
response = self.post(url, data, expect=201)
|
response = self.post(url, data, expect=201)
|
||||||
detail_url = reverse('main:job_template_detail',
|
detail_url = reverse('main:job_template_detail',
|
||||||
args=(response['id'],))
|
args=(response['id'],))
|
||||||
self.assertEquals(response['url'], detail_url)
|
self.assertEquals(response['url'], detail_url)
|
||||||
|
|
||||||
# other_django_user is on a team that can deploy, so can create both
|
# Check that all fields provided were set.
|
||||||
# deploy and check type job templates
|
jt = JobTemplate.objects.get(pk=response['id'])
|
||||||
with self.current_user(self.other_django_user):
|
self.assertEqual(jt.name, data['name'])
|
||||||
data['name'] = 'job-foo2'
|
self.assertEqual(jt.job_type, data['job_type'])
|
||||||
response = self.post(url, data, expect=201)
|
self.assertEqual(jt.inventory.pk, data['inventory'])
|
||||||
data['name'] = 'job-foo3'
|
self.assertEqual(jt.credential, None)
|
||||||
data['job_type'] = PERM_INVENTORY_CHECK
|
self.assertEqual(jt.project.pk, data['project'])
|
||||||
response = self.post(url, data, expect=201)
|
self.assertEqual(jt.playbook, data['playbook'])
|
||||||
|
|
||||||
# other2_django_user has individual permissions to run check mode,
|
# Test that all required fields are really required.
|
||||||
# but not deploy
|
data['name'] = 'another new job template'
|
||||||
with self.current_user(self.other2_django_user):
|
for field in ('name', 'job_type', 'inventory', 'project', 'playbook'):
|
||||||
data['name'] = 'job-foo4'
|
with self.current_user(self.user_sue):
|
||||||
#data['credential'] = self.user_credential.pk
|
d = dict(data.items())
|
||||||
#response = self.post(url, data, expect=201)
|
d.pop(field)
|
||||||
data['name'] = 'job-foo5'
|
response = self.post(url, d, expect=400)
|
||||||
data['job_type'] = PERM_INVENTORY_DEPLOY
|
self.assertTrue(field in response,
|
||||||
response = self.post(url, data, expect=403)
|
'no error for field "%s" in response' % field)
|
||||||
|
|
||||||
# nobody user can't even run check mode
|
# Test invalid value for job_type.
|
||||||
with self.current_user(self.nobody_django_user):
|
with self.current_user(self.user_sue):
|
||||||
data['name'] = 'job-foo5'
|
d = dict(data.items())
|
||||||
data['job_type'] = PERM_INVENTORY_CHECK
|
d['job_type'] = 'world domination'
|
||||||
response = self.post(url, data, expect=403)
|
response = self.post(url, d, expect=400)
|
||||||
data['job_type'] = PERM_INVENTORY_DEPLOY
|
self.assertTrue('job_type' in response)
|
||||||
response = self.post(url, data, expect=403)
|
|
||||||
|
# Test playbook not in list of project playbooks.
|
||||||
|
with self.current_user(self.user_sue):
|
||||||
|
d = dict(data.items())
|
||||||
|
d['playbook'] = 'no_playbook_here.yml'
|
||||||
|
response = self.post(url, d, expect=400)
|
||||||
|
self.assertTrue('playbook' in response)
|
||||||
|
|
||||||
|
# FIXME: Check other credentials and optional fields.
|
||||||
|
|
||||||
def test_get_job_template_detail(self):
|
def test_get_job_template_detail(self):
|
||||||
|
jt = self.jt_eng_run
|
||||||
return # FIXME
|
url = reverse('main:job_template_detail', args=(jt.pk,))
|
||||||
|
|
||||||
url = reverse('main:job_template_detail', args=(self.job_template1.pk,))
|
# Test with no auth and with invalid login.
|
||||||
|
self._test_invalid_creds(url)
|
||||||
# verify we can also get the job template record
|
|
||||||
with self.current_user(self.other2_django_user):
|
# sue can read the job template detail.
|
||||||
|
with self.current_user(self.user_sue):
|
||||||
self.options(url)
|
self.options(url)
|
||||||
self.head(url)
|
self.head(url)
|
||||||
response = self.get(url)
|
response = self.get(url)
|
||||||
self.assertEqual(response['url'], url)
|
self.assertEqual(response['url'], url)
|
||||||
|
|
||||||
|
# FIXME: Check other credentials and optional fields.
|
||||||
|
|
||||||
# TODO: add more tests that show
|
# TODO: add more tests that show
|
||||||
# the method used to START a JobTemplate follow the exact same permissions as those to create it ...
|
# the method used to START a JobTemplate follow the exact same permissions as those to create it ...
|
||||||
# and that jobs come back nicely serialized with related resources and so on ...
|
# and that jobs come back nicely serialized with related resources and so on ...
|
||||||
# that we can drill all the way down and can get at host failure lists, etc ...
|
# that we can drill all the way down and can get at host failure lists, etc ...
|
||||||
|
|
||||||
def test_put_job_template_detail(self):
|
def test_put_job_template_detail(self):
|
||||||
pass
|
jt = self.jt_eng_run
|
||||||
|
url = reverse('main:job_template_detail', args=(jt.pk,))
|
||||||
|
|
||||||
|
# Test with no auth and with invalid login.
|
||||||
|
self._test_invalid_creds(url, methods=('put',))
|
||||||
|
|
||||||
|
# sue can update the job template detail.
|
||||||
|
with self.current_user(self.user_sue):
|
||||||
|
data = self.get(url)
|
||||||
|
response = self.put(url, data)
|
||||||
|
|
||||||
|
# FIXME: Check other credentials and optional fields.
|
||||||
|
|
||||||
def test_get_job_template_job_list(self):
|
def test_get_job_template_job_list(self):
|
||||||
pass
|
jt = self.jt_eng_run
|
||||||
|
url = reverse('main:job_template_job_list', args=(jt.pk,))
|
||||||
|
|
||||||
|
# Test with no auth and with invalid login.
|
||||||
|
self._test_invalid_creds(url)
|
||||||
|
|
||||||
|
# sue can read the job template job list.
|
||||||
|
with self.current_user(self.user_sue):
|
||||||
|
self.options(url)
|
||||||
|
self.head(url)
|
||||||
|
response = self.get(url)
|
||||||
|
qs = jt.jobs.all()
|
||||||
|
self.check_pagination_and_size(response, qs.count())
|
||||||
|
self.check_list_ids(response, qs)
|
||||||
|
|
||||||
|
# FIXME: Check other credentials and optional fields.
|
||||||
|
|
||||||
def test_post_job_template_job_list(self):
|
def test_post_job_template_job_list(self):
|
||||||
pass
|
jt = self.jt_eng_run
|
||||||
|
url = reverse('main:job_template_job_list', args=(jt.pk,))
|
||||||
|
data = dict(
|
||||||
|
name='new job from template',
|
||||||
|
credential=self.cred_bob.pk,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test with no auth and with invalid login.
|
||||||
|
self._test_invalid_creds(url, data, methods=('post',))
|
||||||
|
|
||||||
|
# sue can create a new job from the template.
|
||||||
|
with self.current_user(self.user_sue):
|
||||||
|
response = self.post(url, data, expect=201)
|
||||||
|
|
||||||
|
# FIXME: Check other credentials and optional fields.
|
||||||
|
|
||||||
class JobTest(BaseJobTest):
|
class JobTest(BaseJobTest):
|
||||||
|
|
||||||
@@ -533,7 +617,16 @@ class JobTest(BaseJobTest):
|
|||||||
def test_put_job_detail(self):
|
def test_put_job_detail(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_post_job_detail(self):
|
def test_get_job_start(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_post_job_start(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_get_job_cancel(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_post_job_cancel(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_get_job_host_list(self):
|
def test_get_job_host_list(self):
|
||||||
|
|||||||
@@ -116,6 +116,8 @@ job_templates_urls = patterns('lib.main.views',
|
|||||||
jobs_urls = patterns('lib.main.views',
|
jobs_urls = patterns('lib.main.views',
|
||||||
url(r'^$', 'job_list'),
|
url(r'^$', 'job_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/$', 'job_detail'),
|
url(r'^(?P<pk>[0-9]+)/$', 'job_detail'),
|
||||||
|
url(r'^(?P<pk>[0-9]+)/start/$', 'job_start'),
|
||||||
|
url(r'^(?P<pk>[0-9]+)/cancel/$', 'job_cancel'),
|
||||||
url(r'^(?P<pk>[0-9]+)/hosts/$', 'job_hosts_list'),
|
url(r'^(?P<pk>[0-9]+)/hosts/$', 'job_hosts_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/successful_hosts/$', 'jobs_successful_hosts_list'),
|
url(r'^(?P<pk>[0-9]+)/successful_hosts/$', 'jobs_successful_hosts_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/changed_hosts/$', 'jobs_changed_hosts_list'),
|
url(r'^(?P<pk>[0-9]+)/changed_hosts/$', 'jobs_changed_hosts_list'),
|
||||||
|
|||||||
@@ -913,7 +913,6 @@ class JobTemplateList(BaseList):
|
|||||||
def _get_queryset(self):
|
def _get_queryset(self):
|
||||||
return get_user_queryset(self.request.user, self.model)
|
return get_user_queryset(self.request.user, self.model)
|
||||||
|
|
||||||
|
|
||||||
class JobTemplateDetail(BaseDetail):
|
class JobTemplateDetail(BaseDetail):
|
||||||
|
|
||||||
model = JobTemplate
|
model = JobTemplate
|
||||||
@@ -955,6 +954,13 @@ class JobDetail(BaseDetail):
|
|||||||
serializer_class = JobSerializer
|
serializer_class = JobSerializer
|
||||||
permission_classes = (CustomRbac,)
|
permission_classes = (CustomRbac,)
|
||||||
|
|
||||||
|
class JobStart(BaseDetail):
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
pass # FIXME
|
||||||
|
|
||||||
|
class JobCancel(BaseDetail):
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
pass # FIXME
|
pass # FIXME
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user