mirror of
https://github.com/ansible/awx.git
synced 2024-11-02 01:21:21 +03:00
AC-1040 Unified job template and unified job views.
This commit is contained in:
parent
c1d314ed6f
commit
52ab418abb
@ -10,11 +10,15 @@ from django.db import models
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.db.models.related import RelatedObject
|
from django.db.models.related import RelatedObject
|
||||||
from django.db.models.fields import FieldDoesNotExist
|
from django.db.models.fields import FieldDoesNotExist
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
# Django REST Framework
|
# Django REST Framework
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
from rest_framework.filters import BaseFilterBackend
|
from rest_framework.filters import BaseFilterBackend
|
||||||
|
|
||||||
|
# Ansible Tower
|
||||||
|
from awx.main.utils import camelcase_to_underscore
|
||||||
|
|
||||||
class ActiveOnlyBackend(BaseFilterBackend):
|
class ActiveOnlyBackend(BaseFilterBackend):
|
||||||
'''
|
'''
|
||||||
Filter to show only objects where is_active/active is True.
|
Filter to show only objects where is_active/active is True.
|
||||||
@ -28,13 +32,49 @@ class ActiveOnlyBackend(BaseFilterBackend):
|
|||||||
queryset = queryset.filter(active=True)
|
queryset = queryset.filter(active=True)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
class TypeFilterBackend(BaseFilterBackend):
|
||||||
|
'''
|
||||||
|
Filter on type field now returned with all objects.
|
||||||
|
'''
|
||||||
|
|
||||||
|
def filter_queryset(self, request, queryset, view):
|
||||||
|
try:
|
||||||
|
types = None
|
||||||
|
for key, value in request.QUERY_PARAMS.items():
|
||||||
|
if key == 'type':
|
||||||
|
if ',' in value:
|
||||||
|
types = value.split(',')
|
||||||
|
else:
|
||||||
|
types = (value,)
|
||||||
|
if types:
|
||||||
|
types_map = {}
|
||||||
|
for ct in ContentType.objects.filter(Q(app_label='main') | Q(app_label='auth', model='user')):
|
||||||
|
ct_model = ct.model_class()
|
||||||
|
if not ct_model:
|
||||||
|
continue
|
||||||
|
ct_type = camelcase_to_underscore(ct_model._meta.object_name)
|
||||||
|
types_map[ct_type] = ct.pk
|
||||||
|
model = queryset.model
|
||||||
|
model_type = camelcase_to_underscore(model._meta.object_name)
|
||||||
|
if 'polymorphic_ctype' in model._meta.get_all_field_names():
|
||||||
|
types_pks = set([v for k,v in types_map.items() if k in types])
|
||||||
|
queryset = queryset.filter(polymorphic_ctype_id__in=types_pks)
|
||||||
|
elif model_type in types:
|
||||||
|
queryset = queryset
|
||||||
|
else:
|
||||||
|
queryset = queryset.none()
|
||||||
|
return queryset
|
||||||
|
except FieldError, e:
|
||||||
|
# Return a 400 for invalid field names.
|
||||||
|
raise ParseError(*e.args)
|
||||||
|
|
||||||
class FieldLookupBackend(BaseFilterBackend):
|
class FieldLookupBackend(BaseFilterBackend):
|
||||||
'''
|
'''
|
||||||
Filter using field lookups provided via query string parameters.
|
Filter using field lookups provided via query string parameters.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
RESERVED_NAMES = ('page', 'page_size', 'format', 'order', 'order_by',
|
RESERVED_NAMES = ('page', 'page_size', 'format', 'order', 'order_by',
|
||||||
'search')
|
'search', 'type')
|
||||||
|
|
||||||
SUPPORTED_LOOKUPS = ('exact', 'iexact', 'contains', 'icontains',
|
SUPPORTED_LOOKUPS = ('exact', 'iexact', 'contains', 'icontains',
|
||||||
'startswith', 'istartswith', 'endswith', 'iendswith',
|
'startswith', 'istartswith', 'endswith', 'iendswith',
|
||||||
|
@ -197,7 +197,7 @@ class GenericAPIView(generics.GenericAPIView, APIView):
|
|||||||
# appropriate metadata about the fields that should be supplied.
|
# appropriate metadata about the fields that should be supplied.
|
||||||
serializer = self.get_serializer()
|
serializer = self.get_serializer()
|
||||||
actions['GET'] = serializer.metadata()
|
actions['GET'] = serializer.metadata()
|
||||||
ret['types'] = [serializer.get_type(None)] # FIXME: Support multiple types?
|
ret['types'] = serializer.get_types()
|
||||||
if actions:
|
if actions:
|
||||||
ret['actions'] = actions
|
ret['actions'] = actions
|
||||||
if getattr(self, 'search_fields', None):
|
if getattr(self, 'search_fields', None):
|
||||||
|
@ -148,9 +148,9 @@ class BaseSerializerMetaclass(serializers.SerializerMetaclass):
|
|||||||
|
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
meta = type('Meta', (object,), {})
|
meta = type('Meta', (object,), {})
|
||||||
for base in bases:
|
for base in bases[::-1]:
|
||||||
cls._update_meta(base, meta, getattr(base, 'Meta', None))
|
cls._update_meta(base, meta, getattr(base, 'Meta', None))
|
||||||
cls._update_meta(None, meta, attrs.get('Meta', None))
|
cls._update_meta(None, meta, attrs.get('Meta', meta))
|
||||||
attrs['Meta'] = meta
|
attrs['Meta'] = meta
|
||||||
return super(BaseSerializerMetaclass, cls).__new__(cls, name, bases, attrs)
|
return super(BaseSerializerMetaclass, cls).__new__(cls, name, bases, attrs)
|
||||||
|
|
||||||
@ -215,6 +215,9 @@ class BaseSerializer(serializers.ModelSerializer):
|
|||||||
opts = get_concrete_model(self.opts.model)._meta
|
opts = get_concrete_model(self.opts.model)._meta
|
||||||
return camelcase_to_underscore(opts.object_name)
|
return camelcase_to_underscore(opts.object_name)
|
||||||
|
|
||||||
|
def get_types(self):
|
||||||
|
return [self.get_type(None)]
|
||||||
|
|
||||||
def get_url(self, obj):
|
def get_url(self, obj):
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return ''
|
return ''
|
||||||
@ -304,6 +307,27 @@ class UnifiedJobTemplateSerializer(BaseSerializer):
|
|||||||
res['next_schedule'] = obj.next_schedule.get_absolute_url()
|
res['next_schedule'] = obj.next_schedule.get_absolute_url()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def get_types(self):
|
||||||
|
if type(self) is UnifiedJobTemplateSerializer:
|
||||||
|
return ['project', 'inventory_source', 'job_template']
|
||||||
|
else:
|
||||||
|
return super(UnifiedJobTemplateSerializer, self).get_types()
|
||||||
|
|
||||||
|
def to_native(self, obj):
|
||||||
|
serializer_class = None
|
||||||
|
if type(self) is UnifiedJobTemplateSerializer:
|
||||||
|
if isinstance(obj, Project):
|
||||||
|
serializer_class = ProjectSerializer
|
||||||
|
elif isinstance(obj, InventorySource):
|
||||||
|
serializer_class = InventorySourceSerializer
|
||||||
|
elif isinstance(obj, JobTemplate):
|
||||||
|
serializer_class = JobTemplateSerializer
|
||||||
|
if serializer_class:
|
||||||
|
serializer = serializer_class(instance=obj)
|
||||||
|
return serializer.to_native(obj)
|
||||||
|
else:
|
||||||
|
return super(UnifiedJobTemplateSerializer, self).to_native(obj)
|
||||||
|
|
||||||
|
|
||||||
class UnifiedJobSerializer(BaseSerializer):
|
class UnifiedJobSerializer(BaseSerializer):
|
||||||
|
|
||||||
@ -315,6 +339,12 @@ class UnifiedJobSerializer(BaseSerializer):
|
|||||||
'failed', 'started', 'finished', 'elapsed', 'job_args',
|
'failed', 'started', 'finished', 'elapsed', 'job_args',
|
||||||
'job_cwd', 'job_env', 'result_stdout', 'result_traceback')
|
'job_cwd', 'job_env', 'result_stdout', 'result_traceback')
|
||||||
|
|
||||||
|
def get_types(self):
|
||||||
|
if type(self) is UnifiedJobSerializer:
|
||||||
|
return ['project_update', 'inventory_update', 'job']
|
||||||
|
else:
|
||||||
|
return super(UnifiedJobSerializer, self).get_types()
|
||||||
|
|
||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
res = super(UnifiedJobSerializer, self).get_related(obj)
|
res = super(UnifiedJobSerializer, self).get_related(obj)
|
||||||
if obj.unified_job_template and obj.unified_job_template.active:
|
if obj.unified_job_template and obj.unified_job_template.active:
|
||||||
@ -324,7 +354,50 @@ class UnifiedJobSerializer(BaseSerializer):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
def to_native(self, obj):
|
def to_native(self, obj):
|
||||||
ret = super(UnifiedJobSerializer, self).to_native(obj)
|
serializer_class = None
|
||||||
|
if type(self) is UnifiedJobSerializer:
|
||||||
|
if isinstance(obj, ProjectUpdate):
|
||||||
|
serializer_class = ProjectUpdateSerializer
|
||||||
|
elif isinstance(obj, InventoryUpdate):
|
||||||
|
serializer_class = InventoryUpdateSerializer
|
||||||
|
elif isinstance(obj, Job):
|
||||||
|
serializer_class = JobSerializer
|
||||||
|
if serializer_class:
|
||||||
|
serializer = serializer_class(instance=obj)
|
||||||
|
ret = serializer.to_native(obj)
|
||||||
|
else:
|
||||||
|
ret = super(UnifiedJobSerializer, self).to_native(obj)
|
||||||
|
if 'elapsed' in ret:
|
||||||
|
ret['elapsed'] = float(ret['elapsed'])
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
class UnifiedJobListSerializer(UnifiedJobSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
exclude = ('*', 'job_args', 'job_cwd', 'job_env', 'result_traceback',
|
||||||
|
'result_stdout')
|
||||||
|
|
||||||
|
def get_types(self):
|
||||||
|
if type(self) is UnifiedJobListSerializer:
|
||||||
|
return ['project_update', 'inventory_update', 'job']
|
||||||
|
else:
|
||||||
|
return super(UnifiedJobListSerializer, self).get_types()
|
||||||
|
|
||||||
|
def to_native(self, obj):
|
||||||
|
serializer_class = None
|
||||||
|
if type(self) is UnifiedJobListSerializer:
|
||||||
|
if isinstance(obj, ProjectUpdate):
|
||||||
|
serializer_class = ProjectUpdateListSerializer
|
||||||
|
elif isinstance(obj, InventoryUpdate):
|
||||||
|
serializer_class = InventoryUpdateListSerializer
|
||||||
|
elif isinstance(obj, Job):
|
||||||
|
serializer_class = JobListSerializer
|
||||||
|
if serializer_class:
|
||||||
|
serializer = serializer_class(instance=obj)
|
||||||
|
ret = serializer.to_native(obj)
|
||||||
|
else:
|
||||||
|
ret = super(UnifiedJobListSerializer, self).to_native(obj)
|
||||||
if 'elapsed' in ret:
|
if 'elapsed' in ret:
|
||||||
ret['elapsed'] = float(ret['elapsed'])
|
ret['elapsed'] = float(ret['elapsed'])
|
||||||
return ret
|
return ret
|
||||||
@ -543,6 +616,11 @@ class ProjectUpdateSerializer(UnifiedJobSerializer, ProjectOptionsSerializer):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectUpdateListSerializer(ProjectUpdateSerializer, UnifiedJobListSerializer):
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BaseSerializerWithVariables(BaseSerializer):
|
class BaseSerializerWithVariables(BaseSerializer):
|
||||||
|
|
||||||
def validate_variables(self, attrs, source):
|
def validate_variables(self, attrs, source):
|
||||||
@ -907,6 +985,11 @@ class InventoryUpdateSerializer(UnifiedJobSerializer, InventorySourceOptionsSeri
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryUpdateListSerializer(InventoryUpdateSerializer, UnifiedJobListSerializer):
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TeamSerializer(BaseSerializer):
|
class TeamSerializer(BaseSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -1151,12 +1234,9 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer):
|
|||||||
ret['job_template'] = None
|
ret['job_template'] = None
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
class JobListSerializer(JobSerializer, UnifiedJobListSerializer):
|
||||||
|
|
||||||
class JobListSerializer(JobSerializer):
|
pass
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Job
|
|
||||||
fields = ('*', '-result_stdout')
|
|
||||||
|
|
||||||
|
|
||||||
class JobHostSummarySerializer(BaseSerializer):
|
class JobHostSummarySerializer(BaseSerializer):
|
||||||
@ -1225,8 +1305,7 @@ class ScheduleSerializer(BaseSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Schedule
|
model = Schedule
|
||||||
fields = ('*', 'unified_job_template', 'enabled', 'dtstart', 'dtend',
|
fields = ('*', 'enabled', 'dtstart', 'dtend', 'rrule', 'next_run')
|
||||||
'rrule', 'next_run')
|
|
||||||
|
|
||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
res = super(ScheduleSerializer, self).get_related(obj)
|
res = super(ScheduleSerializer, self).get_related(obj)
|
||||||
|
@ -172,7 +172,6 @@ v1_urls = patterns('awx.api.views',
|
|||||||
url(r'^me/$', 'user_me_list'),
|
url(r'^me/$', 'user_me_list'),
|
||||||
url(r'^dashboard/$', 'dashboard_view'),
|
url(r'^dashboard/$', 'dashboard_view'),
|
||||||
url(r'^schedules/$', include(schedule_urls)),
|
url(r'^schedules/$', include(schedule_urls)),
|
||||||
url(r'^unified_jobs/$','unified_jobs_list'),
|
|
||||||
url(r'^organizations/', include(organization_urls)),
|
url(r'^organizations/', include(organization_urls)),
|
||||||
url(r'^users/', include(user_urls)),
|
url(r'^users/', include(user_urls)),
|
||||||
url(r'^projects/', include(project_urls)),
|
url(r'^projects/', include(project_urls)),
|
||||||
@ -189,6 +188,8 @@ v1_urls = patterns('awx.api.views',
|
|||||||
url(r'^jobs/', include(job_urls)),
|
url(r'^jobs/', include(job_urls)),
|
||||||
url(r'^job_host_summaries/', include(job_host_summary_urls)),
|
url(r'^job_host_summaries/', include(job_host_summary_urls)),
|
||||||
url(r'^job_events/', include(job_event_urls)),
|
url(r'^job_events/', include(job_event_urls)),
|
||||||
|
url(r'^unified_job_templates/$', 'unified_job_template_list'),
|
||||||
|
url(r'^unified_jobs/$', 'unified_job_list'),
|
||||||
url(r'^activity_stream/', include(activity_stream_urls)),
|
url(r'^activity_stream/', include(activity_stream_urls)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
216
awx/api/views.py
216
awx/api/views.py
@ -93,7 +93,8 @@ class ApiV1RootView(APIView):
|
|||||||
data['job_templates'] = reverse('api:job_template_list')
|
data['job_templates'] = reverse('api:job_template_list')
|
||||||
data['jobs'] = reverse('api:job_list')
|
data['jobs'] = reverse('api:job_list')
|
||||||
data['schedules'] = reverse('api:schedule_list')
|
data['schedules'] = reverse('api:schedule_list')
|
||||||
data['unified_jobs'] = reverse('api:unified_jobs_list')
|
data['unified_job_templates'] = reverse('api:unified_job_template_list')
|
||||||
|
data['unified_jobs'] = reverse('api:unified_job_list')
|
||||||
data['activity_stream'] = reverse('api:activity_stream_list')
|
data['activity_stream'] = reverse('api:activity_stream_list')
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
@ -240,215 +241,16 @@ class DashboardView(APIView):
|
|||||||
class ScheduleList(ListCreateAPIView):
|
class ScheduleList(ListCreateAPIView):
|
||||||
|
|
||||||
view_name = "Schedules"
|
view_name = "Schedules"
|
||||||
new_in_148 = True
|
|
||||||
|
|
||||||
model = Schedule
|
model = Schedule
|
||||||
serializer_class = ScheduleSerializer
|
serializer_class = ScheduleSerializer
|
||||||
|
new_in_148 = True
|
||||||
|
|
||||||
class ScheduleDetail(RetrieveUpdateDestroyAPIView):
|
class ScheduleDetail(RetrieveUpdateDestroyAPIView):
|
||||||
|
|
||||||
new_in_148 = True
|
|
||||||
model = Schedule
|
model = Schedule
|
||||||
serializer_class = ScheduleSerializer
|
serializer_class = ScheduleSerializer
|
||||||
|
|
||||||
class UnifiedJobsList(APIView):
|
|
||||||
|
|
||||||
view_name = "Unified Job Templates"
|
|
||||||
new_in_148 = True
|
new_in_148 = True
|
||||||
|
|
||||||
def get(self, request, format=None):
|
|
||||||
data = {
|
|
||||||
'count': 3,
|
|
||||||
'next': None,
|
|
||||||
'previous': None,
|
|
||||||
'results': [
|
|
||||||
|
|
||||||
{"id": 39,
|
|
||||||
"url": "/api/v1/inventory_updates/39/",
|
|
||||||
"related": {
|
|
||||||
"cancel": "/api/v1/inventory_updates/39/cancel/",
|
|
||||||
"inventory_source": "/api/v1/inventory_sources/11/"
|
|
||||||
},
|
|
||||||
"summary_fields": {
|
|
||||||
"inventory_source": {
|
|
||||||
"source": "ec2",
|
|
||||||
"last_updated": "2014-03-19T17:44:30.726Z",
|
|
||||||
"status": "successful"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"created": "2014-03-19T17:44:13.894Z",
|
|
||||||
"modified": "2014-03-19T17:44:30.726Z",
|
|
||||||
"inventory_source": 11,
|
|
||||||
"status": "successful",
|
|
||||||
"failed": False,
|
|
||||||
"type": "inventory_sync",
|
|
||||||
"result_stdout": "there will be stdout here",
|
|
||||||
"result_traceback": "",
|
|
||||||
"job_args": "[\"awx-manage\", \"inventory_import\", \"--inventory-id\", \"6\", \"--source\", \"/home/ubuntu/projects/tower/awx/plugins/inventory/ec2.py\", \"--enabled-var\", \"ec2_state\", \"--enabled-value\", \"running\", \"-v1\", \"--traceback\"]",
|
|
||||||
"job_cwd": "/home/ubuntu/projects/tower/awx/plugins/inventory",
|
|
||||||
"job_env": {
|
|
||||||
"CELERY_LOG_REDIRECT_LEVEL": "WARNING",
|
|
||||||
"ANSIBLE_PARAMIKO_RECORD_HOST_KEYS": "False",
|
|
||||||
"DJANGO_LIVE_TEST_SERVER_ADDRESS": "localhost:9013-9199",
|
|
||||||
"LESSOPEN": "| /usr/bin/lesspipe %s",
|
|
||||||
"_MP_FORK_LOGFILE_": "",
|
|
||||||
"SSH_CLIENT": "65.190.173.93 52056 22",
|
|
||||||
"CELERY_LOG_REDIRECT": "1",
|
|
||||||
"LOGNAME": "ubuntu",
|
|
||||||
"USER": "ubuntu",
|
|
||||||
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games",
|
|
||||||
"HOME": "/home/ubuntu",
|
|
||||||
"MAKEFLAGS": "w",
|
|
||||||
"LANG": "en_US.UTF-8",
|
|
||||||
"TERM": "screen",
|
|
||||||
"SHELL": "/bin/bash",
|
|
||||||
"TZ": "America/New_York",
|
|
||||||
"_MP_FORK_LOGFORMAT_": "[%(asctime)s: %(levelname)s/%(processName)s] %(message)s",
|
|
||||||
"SHLVL": "1",
|
|
||||||
"CELERY_LOG_FILE": "",
|
|
||||||
"DJANGO_PROJECT_DIR": "/home/ubuntu/projects/tower",
|
|
||||||
"MFLAGS": "-w",
|
|
||||||
"ANSIBLE_HOST_KEY_CHECKING": "False",
|
|
||||||
"PYTHONPATH": "/home/ubuntu/projects/tower/awx/lib/site-packages:",
|
|
||||||
"COMP_WORDBREAKS": " \t\n\"'><;|&(:",
|
|
||||||
"TMUX": "/tmp/tmux-1000/default,13862,0",
|
|
||||||
"CELERY_LOADER": "djcelery.loaders.DjangoLoader",
|
|
||||||
"_MP_FORK_LOGLEVEL_": "10",
|
|
||||||
"ANSIBLE_NOCOLOR": "1",
|
|
||||||
"AWS_ACCESS_KEY_ID": "AKIAJZFZXKDVUHDDCZSA",
|
|
||||||
"_": "/usr/bin/make",
|
|
||||||
"SSH_CONNECTION": "65.190.173.93 52056 10.157.37.183 22",
|
|
||||||
"LESSCLOSE": "/usr/bin/lesspipe %s %s",
|
|
||||||
"EC2_INI_PATH": "/tmp/tmpdDHKKH",
|
|
||||||
"SSH_TTY": "/dev/pts/0",
|
|
||||||
"OLDPWD": "/home/ubuntu",
|
|
||||||
"MAKELEVEL": "2",
|
|
||||||
"PWD": "/home/ubuntu/projects/tower",
|
|
||||||
"DJANGO_SETTINGS_MODULE": "awx.settings.development",
|
|
||||||
"AWS_SECRET_ACCESS_KEY": "****************************************",
|
|
||||||
"INVENTORY_SOURCE_ID": "11",
|
|
||||||
"MAIL": "/var/mail/ubuntu",
|
|
||||||
"LS_COLORS": "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:",
|
|
||||||
"CELERY_LOG_LEVEL": "10",
|
|
||||||
"TMUX_PANE": "%1"
|
|
||||||
},
|
|
||||||
"license_error": False},
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"url": "/api/v1/jobs/1/",
|
|
||||||
"related": {
|
|
||||||
"job_host_summaries": "/api/v1/jobs/1/job_host_summaries/",
|
|
||||||
"activity_stream": "/api/v1/jobs/1/activity_stream/",
|
|
||||||
"job_events": "/api/v1/jobs/1/job_events/",
|
|
||||||
"job_template": "/api/v1/job_templates/1/",
|
|
||||||
"inventory": "/api/v1/inventories/1/",
|
|
||||||
"project": "/api/v1/projects/1/",
|
|
||||||
"credential": "/api/v1/credentials/1/",
|
|
||||||
"start": "/api/v1/jobs/1/start/",
|
|
||||||
"cancel": "/api/v1/jobs/1/cancel/"
|
|
||||||
},
|
|
||||||
"summary_fields": {
|
|
||||||
"credential": {
|
|
||||||
"name": "testcred",
|
|
||||||
"description": "",
|
|
||||||
"kind": "ssh",
|
|
||||||
"cloud": False
|
|
||||||
},
|
|
||||||
"job_template": {
|
|
||||||
"name": "ping1",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"project": {
|
|
||||||
"name": "Test Projects",
|
|
||||||
"description": "",
|
|
||||||
"status": "ok"
|
|
||||||
},
|
|
||||||
"inventory": {
|
|
||||||
"name": "Test Inventory",
|
|
||||||
"description": "",
|
|
||||||
"has_active_failures": False,
|
|
||||||
"total_hosts": 1000,
|
|
||||||
"hosts_with_active_failures": 0,
|
|
||||||
"total_groups": 1,
|
|
||||||
"groups_with_active_failures": 0,
|
|
||||||
"has_inventory_sources": False,
|
|
||||||
"total_inventory_sources": 0,
|
|
||||||
"inventory_sources_with_failures": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"created": "2014-02-10T19:28:43.886Z",
|
|
||||||
"modified": "2014-03-14T16:48:26.222Z",
|
|
||||||
"job_template": 1,
|
|
||||||
"job_type": "run",
|
|
||||||
"type": "playbook_run",
|
|
||||||
"inventory": 1,
|
|
||||||
"project": 1,
|
|
||||||
"playbook": "ping.yml",
|
|
||||||
"credential": 1,
|
|
||||||
"cloud_credential": None,
|
|
||||||
"forks": 0,
|
|
||||||
"limit": "host-1",
|
|
||||||
"verbosity": 0,
|
|
||||||
"extra_vars": "{\n\t\"ansible_connection\": \"local\"\n}",
|
|
||||||
"job_tags": "",
|
|
||||||
"launch_type": "manual",
|
|
||||||
"status": "successful",
|
|
||||||
"failed": False,
|
|
||||||
"result_traceback": "",
|
|
||||||
"passwords_needed_to_start": [],
|
|
||||||
"job_args": "[\"ansible-playbook\", \"-i\", \"/home/ubuntu/projects/tower/awx/plugins/inventory/awxrest.py\", \"-u\", \"admin\", \"--ask-pass\", \"-l\", \"host-1\", \"-e\", \"{\\\"ansible_connection\\\": \\\"local\\\"}\", \"ping.yml\"]",
|
|
||||||
"job_cwd": "/home/ubuntu/projects/tower/awx/projects/test",
|
|
||||||
"job_env": {
|
|
||||||
"CELERY_LOG_REDIRECT_LEVEL": "WARNING",
|
|
||||||
"ANSIBLE_PARAMIKO_RECORD_HOST_KEYS": "False",
|
|
||||||
"DJANGO_LIVE_TEST_SERVER_ADDRESS": "localhost:9013-9199",
|
|
||||||
"LESSOPEN": "| /usr/bin/lesspipe %s",
|
|
||||||
"_MP_FORK_LOGFILE_": "",
|
|
||||||
"SSH_CLIENT": "65.190.173.93 38699 22",
|
|
||||||
"CELERY_LOG_REDIRECT": "1",
|
|
||||||
"LOGNAME": "ubuntu",
|
|
||||||
"USER": "ubuntu",
|
|
||||||
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games",
|
|
||||||
"HOME": "/home/ubuntu",
|
|
||||||
"REST_API_TOKEN": "**********************************",
|
|
||||||
"CALLBACK_CONSUMER_PORT": "tcp://127.0.0.1:5557",
|
|
||||||
"MAKEFLAGS": "",
|
|
||||||
"ANSIBLE_CALLBACK_PLUGINS": "/home/ubuntu/projects/tower/awx/plugins/callback",
|
|
||||||
"LANG": "en_US.UTF-8",
|
|
||||||
"TERM": "screen",
|
|
||||||
"SHELL": "/bin/bash",
|
|
||||||
"TZ": "America/New_York",
|
|
||||||
"_MP_FORK_LOGFORMAT_": "[%(asctime)s: %(levelname)s/%(processName)s] %(message)s",
|
|
||||||
"SHLVL": "2",
|
|
||||||
"CELERY_LOG_FILE": "",
|
|
||||||
"DJANGO_PROJECT_DIR": "/home/ubuntu/projects/tower",
|
|
||||||
"MFLAGS": "",
|
|
||||||
"ANSIBLE_HOST_KEY_CHECKING": "False",
|
|
||||||
"JOB_ID": "1",
|
|
||||||
"PYTHONPATH": "/home/ubuntu/projects/tower/awx/lib/site-packages:",
|
|
||||||
"COMP_WORDBREAKS": " \t\n\"'><;|&(:",
|
|
||||||
"TMUX": "/tmp/tmux-1000/default,1205,0",
|
|
||||||
"CELERY_LOADER": "djcelery.loaders.DjangoLoader",
|
|
||||||
"_MP_FORK_LOGLEVEL_": "10",
|
|
||||||
"ANSIBLE_NOCOLOR": "1",
|
|
||||||
"JOB_CALLBACK_DEBUG": "1",
|
|
||||||
"REST_API_URL": "http://127.0.0.1:8013",
|
|
||||||
"_": "/usr/bin/make",
|
|
||||||
"SSH_CONNECTION": "65.190.173.93 38699 10.157.37.183 22",
|
|
||||||
"LESSCLOSE": "/usr/bin/lesspipe %s %s",
|
|
||||||
"INVENTORY_HOSTVARS": "True",
|
|
||||||
"SSH_TTY": "/dev/pts/0",
|
|
||||||
"CELERY_LOG_LEVEL": "10",
|
|
||||||
"INVENTORY_ID": "1",
|
|
||||||
"MAKELEVEL": "1",
|
|
||||||
"PWD": "/home/ubuntu/projects/tower",
|
|
||||||
"DJANGO_SETTINGS_MODULE": "awx.settings.development",
|
|
||||||
"MAIL": "/var/mail/ubuntu",
|
|
||||||
"LS_COLORS": "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:",
|
|
||||||
"TMUX_PANE": "%1"
|
|
||||||
}},]}
|
|
||||||
return Response(data)
|
|
||||||
|
|
||||||
class AuthTokenView(APIView):
|
class AuthTokenView(APIView):
|
||||||
|
|
||||||
authentication_classes = []
|
authentication_classes = []
|
||||||
@ -1554,6 +1356,18 @@ class JobJobEventsList(BaseJobEventsList):
|
|||||||
headers=headers)
|
headers=headers)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
class UnifiedJobTemplateList(ListAPIView):
|
||||||
|
|
||||||
|
model = UnifiedJobTemplate
|
||||||
|
serializer_class = UnifiedJobTemplateSerializer
|
||||||
|
new_in_148 = True
|
||||||
|
|
||||||
|
class UnifiedJobList(ListAPIView):
|
||||||
|
|
||||||
|
model = UnifiedJob
|
||||||
|
serializer_class = UnifiedJobListSerializer
|
||||||
|
new_in_148 = True
|
||||||
|
|
||||||
class ActivityStreamList(SimpleListAPIView):
|
class ActivityStreamList(SimpleListAPIView):
|
||||||
|
|
||||||
model = ActivityStream
|
model = ActivityStream
|
||||||
|
@ -1043,6 +1043,46 @@ class JobEventAccess(BaseAccess):
|
|||||||
def can_delete(self, obj):
|
def can_delete(self, obj):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
class UnifiedJobTemplateAccess(BaseAccess):
|
||||||
|
'''
|
||||||
|
I can see a unified job template whenever I can see the same project,
|
||||||
|
inventory source or job template. Unified job templates do not include
|
||||||
|
projects without SCM configured or inventory sources without a cloud
|
||||||
|
source.
|
||||||
|
'''
|
||||||
|
|
||||||
|
model = UnifiedJobTemplate
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
qs = self.model.objects.filter(active=True).distinct()
|
||||||
|
project_qs = self.user.get_queryset(Project).filter(scm_type__in=('',))
|
||||||
|
inventory_source_qs = self.user.get_queryset(InventorySource).filter(source__in=CLOUD_INVENTORY_SOURCES)
|
||||||
|
job_template_qs = self.user.get_queryset(JobTemplate)
|
||||||
|
qs = qs.filter(Q(Project___in=project_qs) |
|
||||||
|
Q(InventorySource___in=inventory_source_qs) |
|
||||||
|
Q(JobTemplate___in=job_template_qs))
|
||||||
|
# FIXME: select/prefetch to optimize!
|
||||||
|
return qs
|
||||||
|
|
||||||
|
class UnifiedJobAccess(BaseAccess):
|
||||||
|
'''
|
||||||
|
I can see a unified job whenever I can see the same project update,
|
||||||
|
inventory update or job.
|
||||||
|
'''
|
||||||
|
|
||||||
|
model = UnifiedJob
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
qs = self.model.objects.filter(active=True).distinct()
|
||||||
|
project_update_qs = self.user.get_queryset(ProjectUpdate)
|
||||||
|
inventory_update_qs = self.user.get_queryset(InventoryUpdate).filter(source__in=CLOUD_INVENTORY_SOURCES)
|
||||||
|
job_qs = self.user.get_queryset(Job)
|
||||||
|
qs = qs.filter(Q(ProjectUpdate___in=project_update_qs) |
|
||||||
|
Q(InventoryUpdate___in=inventory_update_qs) |
|
||||||
|
Q(Job___in=job_qs))
|
||||||
|
# FIXME: select/prefetch to optimize!
|
||||||
|
return qs
|
||||||
|
|
||||||
class ScheduleAccess(BaseAccess):
|
class ScheduleAccess(BaseAccess):
|
||||||
'''
|
'''
|
||||||
I can see a schedule if I can see it's related unified job, I can create them or update them if I have write access
|
I can see a schedule if I can see it's related unified job, I can create them or update them if I have write access
|
||||||
@ -1230,4 +1270,6 @@ register_access(Job, JobAccess)
|
|||||||
register_access(JobHostSummary, JobHostSummaryAccess)
|
register_access(JobHostSummary, JobHostSummaryAccess)
|
||||||
register_access(JobEvent, JobEventAccess)
|
register_access(JobEvent, JobEventAccess)
|
||||||
register_access(Schedule, ScheduleAccess)
|
register_access(Schedule, ScheduleAccess)
|
||||||
|
register_access(UnifiedJobTemplate, UnifiedJobTemplateAccess)
|
||||||
|
register_access(UnifiedJob, UnifiedJobAccess)
|
||||||
register_access(ActivityStream, ActivityStreamAccess)
|
register_access(ActivityStream, ActivityStreamAccess)
|
||||||
|
@ -160,6 +160,7 @@ REST_FRAMEWORK = {
|
|||||||
),
|
),
|
||||||
'DEFAULT_FILTER_BACKENDS': (
|
'DEFAULT_FILTER_BACKENDS': (
|
||||||
'awx.api.filters.ActiveOnlyBackend',
|
'awx.api.filters.ActiveOnlyBackend',
|
||||||
|
'awx.api.filters.TypeFilterBackend',
|
||||||
'awx.api.filters.FieldLookupBackend',
|
'awx.api.filters.FieldLookupBackend',
|
||||||
'rest_framework.filters.SearchFilter',
|
'rest_framework.filters.SearchFilter',
|
||||||
'awx.api.filters.OrderByBackend',
|
'awx.api.filters.OrderByBackend',
|
||||||
|
Loading…
Reference in New Issue
Block a user