Numerous model-related updates and supporing changes, including:

- Add variables field on Host/Group models and remove separate VariableData model.
- Add data migrations for existing variable data.
- Update views, serializers and tests to keep roughly the same API interface for variable data.
- Add has_active_failures properties on Group/Host models to provide indication of last job status.
- Add job_tags field on JobTemplate/Job models to specify tags to ansible-playbook.
- Add host_config_key field to JobTemplate model for use by empheral hosts.
- Add job_args, job_cwd and job_env fields to Job model to capture more info from running the job.
- Add failed flag on JobHostSummary model.
- Add play/task fields on JobEvent model to capture new context variables from callback.
- Add parent field on JobEvent model to capture hierarchy of job events.
- Add hosts field on JobEvent model to capture all hosts associated with the event (especially useful for parent events in the hierarchy).
- Removed existing Tag model, replace with django-taggit instead.
- Removed existing AuditLog model, replacement TBD.
This commit is contained in:
Chris Church
2013-06-10 17:21:04 -04:00
parent 7b0bbff376
commit cba55a061a
24 changed files with 1924 additions and 498 deletions

View File

@@ -1,4 +1,4 @@
release = ansibleworks-1.2b1 RELEASE = ansibleworks-1.2b2
clean: clean:
rm -rf build *.egg-info rm -rf build *.egg-info
@@ -81,16 +81,15 @@ release_build:
release_ball: clean release_ball: clean
make release_build make release_build
(cd ../ansible-doc; make) (cd ../ansible-doc; make)
-(rm -rf $(release)) -(rm -rf $(RELEASE))
mkdir -p $(release)/dist mkdir -p $(RELEASE)/dist
cp -a dist/* $(release)/dist cp -a dist/* $(release)/dist
mkdir -p $(release)/setup mkdir -p $(RELEASE)/setup
cp -a setup/* $(release)/setup cp -a setup/* $(RELEASE)/setup
mkdir -p $(release)/docs mkdir -p $(RELEASE)/docs
cp -a ../ansible-doc/*.pdf $(release)/docs cp -a ../ansible-doc/*.pdf $(RELEASE)/docs
tar -cvf $(release)-all.tar $(release) tar -cvf $(RELEASE)-all.tar $(RELEASE)
clean: release_clean:
-(rm *.tar) -(rm *.tar)
-(rm -rf ($release)) -(rm -rf ($RELEASE))

View File

@@ -139,18 +139,6 @@ class UserAccess(BaseAccess):
return bool(self.user.is_superuser or return bool(self.user.is_superuser or
obj.organizations.filter(admins__in=[self.user]).count()) obj.organizations.filter(admins__in=[self.user]).count())
class TagAccess(BaseAccess):
model = Tag
def can_read(self, obj):
# anybody can read tags, we won't show much detail other than the names
return True
def can_add(self, data):
# anybody can make up tags
return True
class OrganizationAccess(BaseAccess): class OrganizationAccess(BaseAccess):
model = Organization model = Organization
@@ -259,6 +247,11 @@ class HostAccess(BaseAccess):
# Checks for admin or change permission on inventory. # Checks for admin or change permission on inventory.
return check_user_access(self.user, Inventory, 'change', inventory, None) return check_user_access(self.user, Inventory, 'change', inventory, None)
def can_change(self, obj, data):
# Checks for admin or change permission on inventory, controls whether
# the user can edit variable data.
return check_user_access(self.user, Inventory, 'change', obj.inventory, None)
class GroupAccess(BaseAccess): class GroupAccess(BaseAccess):
model = Group model = Group
@@ -275,34 +268,9 @@ class GroupAccess(BaseAccess):
def can_change(self, obj, data): def can_change(self, obj, data):
# Checks for admin or change permission on inventory, controls whether # Checks for admin or change permission on inventory, controls whether
# the user can attach subgroups # the user can attach subgroups or edit variable data.
return check_user_access(self.user, Inventory, 'change', obj.inventory, None) return check_user_access(self.user, Inventory, 'change', obj.inventory, None)
class VariableDataAccess(BaseAccess):
model = VariableData
def can_read(self, obj):
if obj.host:
inventory = obj.host.inventory
elif obj.group:
inventory = obj.group.inventory
else:
return False
return check_user_access(self.user, Inventory, 'read', inventory)
def can_change(self, obj, data):
if obj.host:
inventory = obj.host.inventory
elif obj.group:
inventory = obj.group.inventory
else:
return False
return check_user_access(self.user, Inventory, 'change', inventory)
def can_delete(self, obj):
return False
class CredentialAccess(BaseAccess): class CredentialAccess(BaseAccess):
model = Credential model = Credential
@@ -538,12 +506,10 @@ class JobEventAccess(BaseAccess):
model = JobEvent model = JobEvent
register_access(User, UserAccess) register_access(User, UserAccess)
register_access(Tag, TagAccess)
register_access(Organization, OrganizationAccess) register_access(Organization, OrganizationAccess)
register_access(Inventory, InventoryAccess) register_access(Inventory, InventoryAccess)
register_access(Host, HostAccess) register_access(Host, HostAccess)
register_access(Group, GroupAccess) register_access(Group, GroupAccess)
register_access(VariableData, VariableDataAccess)
register_access(Credential, CredentialAccess) register_access(Credential, CredentialAccess)
register_access(Team, TeamAccess) register_access(Team, TeamAccess)
register_access(Project, ProjectAccess) register_access(Project, ProjectAccess)

View File

@@ -58,25 +58,23 @@ class OrganizationAdmin(BaseModelAdmin):
(_('Members'), {'fields': ('users', 'admins',)}), (_('Members'), {'fields': ('users', 'admins',)}),
(_('Projects'), {'fields': ('projects',)}), (_('Projects'), {'fields': ('projects',)}),
(_('Tags'), {'fields': ('tags',)}), (_('Tags'), {'fields': ('tags',)}),
(_('Audit Trail'), {'fields': ('created', 'created_by', (_('Audit'), {'fields': ('created', 'created_by',)}),
'audit_trail',)}),
) )
readonly_fields = ('created', 'created_by', 'audit_trail') readonly_fields = ('created', 'created_by')
filter_horizontal = ('users', 'admins', 'projects', 'tags') filter_horizontal = ('users', 'admins', 'projects')
class InventoryHostInline(admin.StackedInline): class InventoryHostInline(admin.StackedInline):
model = Host model = Host
extra = 0 extra = 0
fields = ('name', 'description', 'active', 'tags') fields = ('name', 'description', 'active', 'tags')
filter_horizontal = ('tags',)
class InventoryGroupInline(admin.StackedInline): class InventoryGroupInline(admin.StackedInline):
model = Group model = Group
extra = 0 extra = 0
fields = ('name', 'description', 'active', 'parents', 'hosts', 'tags') fields = ('name', 'description', 'active', 'parents', 'hosts', 'tags')
filter_horizontal = ('parents', 'hosts', 'tags') filter_horizontal = ('parents', 'hosts')
class InventoryAdmin(BaseModelAdmin): class InventoryAdmin(BaseModelAdmin):
@@ -85,15 +83,14 @@ class InventoryAdmin(BaseModelAdmin):
fieldsets = ( fieldsets = (
(None, {'fields': (('name', 'active'), 'organization', 'description',)}), (None, {'fields': (('name', 'active'), 'organization', 'description',)}),
(_('Tags'), {'fields': ('tags',)}), (_('Tags'), {'fields': ('tags',)}),
(_('Audit Trail'), {'fields': ('created', 'created_by', 'audit_trail',)}), (_('Audit'), {'fields': ('created', 'created_by',)}),
) )
readonly_fields = ('created', 'created_by', 'audit_trail') readonly_fields = ('created', 'created_by')
filter_horizontal = ('tags',)
inlines = [InventoryHostInline, InventoryGroupInline] inlines = [InventoryHostInline, InventoryGroupInline]
class TagAdmin(BaseModelAdmin): #class TagAdmin(BaseModelAdmin):
#
list_display = ('name',) # list_display = ('name',)
#class AuditTrailAdmin(admin.ModelAdmin): #class AuditTrailAdmin(admin.ModelAdmin):
# #
@@ -101,13 +98,6 @@ class TagAdmin(BaseModelAdmin):
# not currently on model, so disabling for now. # not currently on model, so disabling for now.
# filter_horizontal = ('tags',) # filter_horizontal = ('tags',)
class VariableDataInline(admin.StackedInline):
model = VariableData
extra = 0
max_num = 1
# FIXME: Doesn't yet work as inline due to the way the OneToOne field is
# defined.
class JobHostSummaryInline(admin.TabularInline): class JobHostSummaryInline(admin.TabularInline):
@@ -136,9 +126,9 @@ class JobEventInline(admin.StackedInline):
class JobHostSummaryInlineForHost(JobHostSummaryInline): class JobHostSummaryInlineForHost(JobHostSummaryInline):
fields = ('job', 'changed', 'dark', 'failures', 'ok', 'processed', fields = ('job', 'changed', 'dark', 'failures', 'ok', 'processed',
'skipped') 'skipped', 'failed')
readonly_fields = ('job', 'changed', 'dark', 'failures', 'ok', 'processed', readonly_fields = ('job', 'changed', 'dark', 'failures', 'ok', 'processed',
'skipped') 'skipped', 'failed')
class JobEventInlineForHost(JobEventInline): class JobEventInlineForHost(JobEventInline):
@@ -149,17 +139,15 @@ class HostAdmin(BaseModelAdmin):
list_display = ('name', 'inventory', 'description', 'active') list_display = ('name', 'inventory', 'description', 'active')
list_filter = ('inventory', 'active') list_filter = ('inventory', 'active')
#form = HostAdminForm
fieldsets = ( fieldsets = (
(None, {'fields': (('name', 'active'), 'inventory', 'description', 'variable_data', (None, {'fields': (('name', 'active'), 'inventory', 'description',
'variables',
)}), )}),
(_('Tags'), {'fields': ('tags',)}), (_('Tags'), {'fields': ('tags',)}),
(_('Audit Trail'), {'fields': ('created', 'created_by', 'audit_trail',)}), (_('Audit'), {'fields': ('created', 'created_by',)}),
) )
readonly_fields = ('created', 'created_by', 'audit_trail') readonly_fields = ('created', 'created_by')
filter_horizontal = ('tags',)
# FIXME: Edit reverse of many to many for groups. # FIXME: Edit reverse of many to many for groups.
#inlines = [VariableDataInline]
inlines = [JobHostSummaryInlineForHost, JobEventInlineForHost] inlines = [JobHostSummaryInlineForHost, JobEventInlineForHost]
class GroupAdmin(BaseModelAdmin): class GroupAdmin(BaseModelAdmin):
@@ -167,18 +155,12 @@ class GroupAdmin(BaseModelAdmin):
list_display = ('name', 'description', 'active') list_display = ('name', 'description', 'active')
fieldsets = ( fieldsets = (
(None, {'fields': (('name', 'active'), 'inventory', 'description', (None, {'fields': (('name', 'active'), 'inventory', 'description',
'parents')}), 'parents', 'variables')}),
(_('Tags'), {'fields': ('tags',)}), (_('Tags'), {'fields': ('tags',)}),
(_('Audit Trail'), {'fields': ('created', 'created_by', 'audit_trail',)}), (_('Audit'), {'fields': ('created', 'created_by',)}),
) )
readonly_fields = ('created', 'created_by', 'audit_trail') readonly_fields = ('created', 'created_by')
filter_horizontal = ('parents', 'hosts', 'tags') filter_horizontal = ('parents', 'hosts')
#inlines = [VariableDataInline]
class VariableDataAdmin(BaseModelAdmin):
list_display = ('name', 'description', 'active')
filter_horizontal = ('tags',)
class CredentialAdmin(BaseModelAdmin): class CredentialAdmin(BaseModelAdmin):
@@ -187,16 +169,15 @@ class CredentialAdmin(BaseModelAdmin):
(_('Auth Info'), {'fields': (('ssh_username', 'ssh_password'), (_('Auth Info'), {'fields': (('ssh_username', 'ssh_password'),
'ssh_key_data', 'ssh_key_unlock', 'ssh_key_data', 'ssh_key_unlock',
('sudo_username', 'sudo_password'))}), ('sudo_username', 'sudo_password'))}),
#(_('Tags'), {'fields': ('tags',)}), (_('Tags'), {'fields': ('tags',)}),
(_('Audit Trail'), {'fields': ('created', 'created_by', 'audit_trail',)}), (_('Audit'), {'fields': ('created', 'created_by',)}),
) )
readonly_fields = ('created', 'created_by', 'audit_trail') readonly_fields = ('created', 'created_by')
filter_horizontal = ('tags',)
class TeamAdmin(BaseModelAdmin): class TeamAdmin(BaseModelAdmin):
list_display = ('name', 'description', 'active') list_display = ('name', 'description', 'active')
filter_horizontal = ('projects', 'users', 'tags') filter_horizontal = ('projects', 'users')
class ProjectAdmin(BaseModelAdmin): class ProjectAdmin(BaseModelAdmin):
@@ -205,11 +186,9 @@ class ProjectAdmin(BaseModelAdmin):
(None, {'fields': (('name', 'active'), 'description', 'local_path', (None, {'fields': (('name', 'active'), 'description', 'local_path',
'get_playbooks_display')}), 'get_playbooks_display')}),
(_('Tags'), {'fields': ('tags',)}), (_('Tags'), {'fields': ('tags',)}),
(_('Audit Trail'), {'fields': ('created', 'created_by', 'audit_trail',)}), (_('Audit'), {'fields': ('created', 'created_by',)}),
) )
readonly_fields = ('created', 'created_by', 'audit_trail', readonly_fields = ('created', 'created_by', 'get_playbooks_display')
'get_playbooks_display')
filter_horizontal = ('tags',)
form = ProjectAdminForm form = ProjectAdminForm
def get_playbooks_display(self, obj): def get_playbooks_display(self, obj):
@@ -221,7 +200,6 @@ class ProjectAdmin(BaseModelAdmin):
class PermissionAdmin(BaseModelAdmin): class PermissionAdmin(BaseModelAdmin):
list_display = ('name', 'description', 'active') list_display = ('name', 'description', 'active')
filter_horizontal = ('tags',)
class JobTemplateAdmin(BaseModelAdmin): class JobTemplateAdmin(BaseModelAdmin):
@@ -232,17 +210,15 @@ class JobTemplateAdmin(BaseModelAdmin):
'get_create_link_display', 'get_jobs_link_display')}), 'get_create_link_display', 'get_jobs_link_display')}),
(_('Job Parameters'), {'fields': ('inventory', 'project', 'playbook', (_('Job Parameters'), {'fields': ('inventory', 'project', 'playbook',
'credential', 'job_type')}), 'credential', 'job_type')}),
(_('More Options'), {'fields': ('forks', 'limit', (_('More Options'), {'fields': ('forks', 'limit', 'verbosity',
'verbosity', 'extra_vars'), 'extra_vars', 'job_tags', 'host_config_key'),
'classes': ('collapse',)}), 'classes': ('collapse',)}),
#(_('Tags'), {'fields': ('tags',)}), (_('Tags'), {'fields': ('tags',)}),
(_('Audit Trail'), {'fields': ('created', 'created_by', (_('Audit'), {'fields': ('created', 'created_by',)}),
'audit_trail',)}),
) )
readonly_fields = ('created', 'created_by', 'audit_trail', readonly_fields = ('created', 'created_by', 'get_create_link_display',
'get_create_link_display', 'get_jobs_link_display') 'get_jobs_link_display')
form = JobTemplateAdminForm form = JobTemplateAdminForm
#filter_horizontal = ('tags',)
def get_create_link_display(self, obj): def get_create_link_display(self, obj):
if not obj or not obj.pk: if not obj or not obj.pk:
@@ -272,6 +248,8 @@ class JobTemplateAdmin(BaseModelAdmin):
create_opts['verbosity'] = obj.verbosity create_opts['verbosity'] = obj.verbosity
if obj.extra_vars: if obj.extra_vars:
create_opts['extra_vars'] = obj.extra_vars create_opts['extra_vars'] = obj.extra_vars
if obj.job_tags:
create_opts['job_tags'] = obj.job_tags
create_url += '?%s' % urllib.urlencode(create_opts) create_url += '?%s' % urllib.urlencode(create_opts)
return format_html('<a href="{0}">{1}</a>', create_url, 'Create Job') return format_html('<a href="{0}">{1}</a>', create_url, 'Create Job')
get_create_link_display.short_description = _('Create Job') get_create_link_display.short_description = _('Create Job')
@@ -291,9 +269,9 @@ class JobTemplateAdmin(BaseModelAdmin):
class JobHostSummaryInlineForJob(JobHostSummaryInline): class JobHostSummaryInlineForJob(JobHostSummaryInline):
fields = ('host', 'changed', 'dark', 'failures', 'ok', 'processed', fields = ('host', 'changed', 'dark', 'failures', 'ok', 'processed',
'skipped') 'skipped', 'failed')
readonly_fields = ('host', 'changed', 'dark', 'failures', 'ok', readonly_fields = ('host', 'changed', 'dark', 'failures', 'ok',
'processed', 'skipped') 'processed', 'skipped', 'failed')
class JobEventInlineForJob(JobEventInline): class JobEventInlineForJob(JobEventInline):
@@ -310,13 +288,12 @@ class JobAdmin(BaseModelAdmin):
(_('Job Parameters'), {'fields': ('inventory', 'project', 'playbook', (_('Job Parameters'), {'fields': ('inventory', 'project', 'playbook',
'credential', 'job_type')}), 'credential', 'job_type')}),
(_('More Options'), {'fields': ('forks', 'limit', 'verbosity', (_('More Options'), {'fields': ('forks', 'limit', 'verbosity',
'extra_vars'), 'extra_vars', 'job_tags'),
'classes': ('collapse',)}), 'classes': ('collapse',)}),
(_('Start Job'), {'fields': ('start_job', 'ssh_password', (_('Start Job'), {'fields': ('start_job', 'ssh_password',
'sudo_password', 'ssh_key_unlock')}), 'sudo_password', 'ssh_key_unlock')}),
#(_('Tags'), {'fields': ('tags',)}), (_('Tags'), {'fields': ('tags',)}),
(_('Audit Trail'), {'fields': ('created', 'created_by', (_('Audit'), {'fields': ('created', 'created_by',)}),
'audit_trail',)}),
(_('Job Status'), {'fields': (('status', 'failed', 'cancel_job'), (_('Job Status'), {'fields': (('status', 'failed', 'cancel_job'),
'get_result_stdout_display', 'get_result_stdout_display',
'get_result_traceback_display', 'get_result_traceback_display',
@@ -325,8 +302,7 @@ class JobAdmin(BaseModelAdmin):
readonly_fields = ('status', 'failed', 'get_job_template_display', readonly_fields = ('status', 'failed', 'get_job_template_display',
'get_result_stdout_display', 'get_result_stdout_display',
'get_result_traceback_display', 'celery_task_id', 'get_result_traceback_display', 'celery_task_id',
'created', 'created_by', 'audit_trail',) 'created', 'created_by')
filter_horizontal = ('tags',)
form = JobAdminForm form = JobAdminForm
inlines = [JobHostSummaryInlineForJob, JobEventInlineForJob] inlines = [JobHostSummaryInlineForJob, JobEventInlineForJob]
@@ -336,7 +312,7 @@ class JobAdmin(BaseModelAdmin):
ro_fields.extend(['name', 'description', 'job_template', ro_fields.extend(['name', 'description', 'job_template',
'inventory', 'project', 'playbook', 'credential', 'inventory', 'project', 'playbook', 'credential',
'job_type', 'forks', 'limit', 'job_type', 'forks', 'limit',
'verbosity', 'extra_vars']) 'verbosity', 'extra_vars', 'job_tags'])
return ro_fields return ro_fields
def get_fieldsets(self, request, obj=None): def get_fieldsets(self, request, obj=None):
@@ -386,16 +362,13 @@ class JobAdmin(BaseModelAdmin):
get_result_traceback_display.short_description = _('Traceback') get_result_traceback_display.short_description = _('Traceback')
get_result_traceback_display.allow_tags = True get_result_traceback_display.allow_tags = True
# FIXME: Add the rest of the models...
admin.site.register(Organization, OrganizationAdmin) admin.site.register(Organization, OrganizationAdmin)
admin.site.register(Inventory, InventoryAdmin) admin.site.register(Inventory, InventoryAdmin)
admin.site.register(Tag, TagAdmin) #admin.site.register(Tag, TagAdmin)
#admin.site.register(AuditTrail, AuditTrailAdmin) #admin.site.register(AuditTrail, AuditTrailAdmin)
admin.site.register(Host, HostAdmin) admin.site.register(Host, HostAdmin)
admin.site.register(Group, GroupAdmin) admin.site.register(Group, GroupAdmin)
admin.site.register(VariableData, VariableDataAdmin) #admin.site.register(VariableData, VariableDataAdmin)
admin.site.register(Team, TeamAdmin) admin.site.register(Team, TeamAdmin)
admin.site.register(Project, ProjectAdmin) admin.site.register(Project, ProjectAdmin)
admin.site.register(Credential, CredentialAdmin) admin.site.register(Credential, CredentialAdmin)

View File

@@ -214,7 +214,7 @@ class BaseSubList(BaseList):
class BaseDetail(generics.RetrieveUpdateDestroyAPIView): class BaseDetail(generics.RetrieveUpdateDestroyAPIView):
def pre_save(self, obj): def pre_save(self, obj):
if type(obj) not in [ User, Tag, AuditTrail ]: if type(obj) not in [ User ]:
obj.created_by = self.request.user obj.created_by = self.request.user
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):
@@ -247,73 +247,3 @@ class BaseDetail(generics.RetrieveUpdateDestroyAPIView):
def put_filter(self, request, *args, **kwargs): def put_filter(self, request, *args, **kwargs):
''' scrub any fields the user cannot/should not put, based on user context. This runs after read-only serialization filtering ''' ''' scrub any fields the user cannot/should not put, based on user context. This runs after read-only serialization filtering '''
pass pass
class VariableBaseDetail(BaseDetail):
'''
an object that is always 1 to 1 with the foreign key of another object
and does not have it's own key, such as HostVariableDetail
'''
def destroy(self, request, *args, **kwargs):
raise PermissionDenied()
def put(self, request, *args, **kwargs):
# FIXME: lots of overlap between put and get here, need to refactor
through_obj = self.__class__.parent_model.objects.get(pk=kwargs['pk'])
#has_permission = Inventory._has_permission_types(request.user, through_obj.inventory, PERMISSION_TYPES_ALLOWING_INVENTORY_WRITE)
#if not has_permission:
# raise PermissionDenied()
if not check_user_access(request.user, Inventory, 'change', through_obj.inventory, None):
raise PermissionDenied
this_object = None
if hasattr(request.DATA, 'dict'):
data = request.DATA.dict()
else:
data = request.DATA
try:
this_object = getattr(through_obj, self.__class__.reverse_relationship, None)
except:
pass
if this_object is None:
this_object = self.__class__.model.objects.create(data=python_json.dumps(data))
else:
this_object.data = python_json.dumps(data)
this_object.save()
setattr(through_obj, self.__class__.reverse_relationship, this_object)
through_obj.save()
return Response(status=status.HTTP_200_OK, data=python_json.loads(this_object.data))
def get(self, request, *args, **kwargs):
# if null, recreate a blank object
through_obj = self.__class__.parent_model.objects.get(pk=kwargs['pk'])
this_object = None
try:
this_object = getattr(through_obj, self.__class__.reverse_relationship, None)
except Exception, e:
pass
if this_object is None:
new_args = {}
new_args['data'] = python_json.dumps(dict())
this_object = self.__class__.model.objects.create(**new_args)
setattr(through_obj, self.__class__.reverse_relationship, this_object)
through_obj.save()
#has_permission = Inventory._has_permission_types(request.user, through_obj.inventory, PERMISSION_TYPES_ALLOWING_INVENTORY_WRITE)
#if not has_permission:
# raise PermissionDenied()
if not check_user_access(request.user, Inventory, 'read', through_obj.inventory):
raise PermissionDenied
return Response(status=status.HTTP_200_OK, data=python_json.loads(this_object.data))

View File

@@ -29,46 +29,6 @@ class PlaybookSelect(forms.Select):
opt = opt.replace('">', '" class="project-%s">' % obj.project.pk) opt = opt.replace('">', '" class="project-%s">' % obj.project.pk)
return opt return opt
class HostAdminForm(forms.ModelForm):
class Meta:
model = Host
vdata = JSONFormField(label=_('Variable data'), required=False, widget=forms.Textarea(attrs={'class': 'vLargeTextField'}))
def __init__(self, *args, **kwargs):
super(HostAdminForm, self).__init__(*args, **kwargs)
if self.instance.variable_data:
print repr(self.instance.variable_data.data)
self.initial['vdata'] = self.instance.variable_data.data
def save(self, commit=True):
instance = super(HostAdminForm, self).save(commit=commit)
save_m2m = getattr(self, 'save_m2m', lambda: None)
vdata = self.cleaned_data.get('vdata', '')
def new_save_m2m():
save_m2m()
if not instance.variable_data:
instance.variable_data = VariableData.objects.create(data=vdata)
instance.save()
else:
variable_data = instance.variable_data
# FIXME!!!
#variable_data.data = vdata
#variable_data.save()
if commit:
new_save_m2m()
else:
self.save_m2m = new_save_m2m
return instance
class GroupForm(forms.ModelForm):
class Meta:
model = Host
variable_data = JSONFormField(required=False, widget=forms.Textarea(attrs={'class': 'vLargeTextField'}))
class ProjectAdminForm(forms.ModelForm): class ProjectAdminForm(forms.ModelForm):
'''Custom admin form for Projects.''' '''Custom admin form for Projects.'''

View File

@@ -8,6 +8,7 @@ from optparse import make_option
import os import os
import sys import sys
from django.core.management.base import NoArgsCommand, CommandError from django.core.management.base import NoArgsCommand, CommandError
from django.db import transaction
class Command(NoArgsCommand): class Command(NoArgsCommand):
''' '''
@@ -30,12 +31,13 @@ class Command(NoArgsCommand):
help='JSON-formatted callback event data'), help='JSON-formatted callback event data'),
) )
@transaction.commit_on_success
def handle_noargs(self, **options): def handle_noargs(self, **options):
from ansibleworks.main.models import Job, JobEvent from ansibleworks.main.models import Job, JobEvent
event_type = options.get('event_type', None) event_type = options.get('event_type', None)
if not event_type: if not event_type:
raise CommandError('No event specified') raise CommandError('No event specified')
if event_type not in [x[0] for x in JobEvent.EVENT_TYPES]: if event_type not in [x[0] for x in JobEvent.EVENT_CHOICES]:
raise CommandError('Unsupported event') raise CommandError('Unsupported event')
event_data_file = options.get('event_data_file', None) event_data_file = options.get('event_data_file', None)
event_data_json = options.get('event_data_json', None) event_data_json = options.get('event_data_json', None)

View File

@@ -32,8 +32,8 @@ class Command(NoArgsCommand):
'hosts': list(group.hosts.values_list('name', flat=True)), 'hosts': list(group.hosts.values_list('name', flat=True)),
'children': list(group.children.values_list('name', flat=True)), 'children': list(group.children.values_list('name', flat=True)),
} }
if group.variable_data is not None: if group.variables:
group_info['vars'] = json.loads(group.variable_data.data) group_info['vars'] = group.variables_dict
group_info = dict(filter(lambda x: bool(x[1]), group_info.items())) group_info = dict(filter(lambda x: bool(x[1]), group_info.items()))
if group_info.keys() in ([], ['hosts']): if group_info.keys() in ([], ['hosts']):
@@ -51,8 +51,8 @@ class Command(NoArgsCommand):
except Host.DoesNotExist: except Host.DoesNotExist:
raise CommandError('Host %s not found in the given inventory' % hostname) raise CommandError('Host %s not found in the given inventory' % hostname)
hostvars = {} hostvars = {}
if host.variable_data is not None: if host.variables:
hostvars = json.loads(host.variable_data.data) hostvars = host.variables_dict
self.stdout.write(json.dumps(hostvars, indent=indent)) self.stdout.write(json.dumps(hostvars, indent=indent))
def handle_noargs(self, **options): def handle_noargs(self, **options):

View File

@@ -0,0 +1,664 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
'''
Schema migration for AnsibleWorks 1.2-b2 release.
- Adds variables field on Host and Group models.
- Adds job_tags and host_config_key fields on JobTemplate.
- Adds job_tags, job_args, job_cwd, job_env fields on Job.
- Adds failed field on JobHostSummary.
- Adds play, task, parent and hosts fields on JobEvent.
NOTE: This migration has been manually edited!
'''
def forwards(self, orm):
# Adding field 'Host.variables'
db.add_column(u'main_host', 'variables',
self.gf('django.db.models.fields.TextField')(default='', blank=True, null=True),
keep_default=False)
# Adding field 'Group.variables'
db.add_column(u'main_group', 'variables',
self.gf('django.db.models.fields.TextField')(default='', blank=True, null=True),
keep_default=False)
# Adding field 'JobTemplate.job_tags'
db.add_column(u'main_jobtemplate', 'job_tags',
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, null=True, blank=True),
keep_default=False)
# Adding field 'JobTemplate.host_config_key'
db.add_column(u'main_jobtemplate', 'host_config_key',
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, blank=True, null=True),
keep_default=False)
# Adding field 'Job.job_tags'
db.add_column(u'main_job', 'job_tags',
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, null=True, blank=True),
keep_default=False)
# Adding field 'Job.job_args'
db.add_column(u'main_job', 'job_args',
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, null=True, blank=True),
keep_default=False)
# Adding field 'Job.job_cwd'
db.add_column(u'main_job', 'job_cwd',
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, null=True, blank=True),
keep_default=False)
# Adding field 'Job.job_env'
db.add_column(u'main_job', 'job_env',
self.gf('jsonfield.fields.JSONField')(default={}, null=True, blank=True),
keep_default=False)
# Adding field 'JobHostSummary.failed'
db.add_column(u'main_jobhostsummary', 'failed',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
# Adding field 'JobEvent.play'
db.add_column(u'main_jobevent', 'play',
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, blank=True, null=True),
keep_default=False)
# Adding field 'JobEvent.task'
db.add_column(u'main_jobevent', 'task',
self.gf('django.db.models.fields.CharField')(default='', max_length=1024, blank=True, null=True),
keep_default=False)
# Adding field 'JobEvent.parent'
db.add_column(u'main_jobevent', 'parent',
self.gf('django.db.models.fields.related.ForeignKey')(related_name='children', on_delete=models.SET_NULL, default=None, to=orm['main.JobEvent'], blank=True, null=True),
keep_default=False)
# Adding M2M table for field hosts on 'JobEvent'
m2m_table_name = db.shorten_name(u'main_jobevent_hosts')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('jobevent', models.ForeignKey(orm['main.jobevent'], null=False)),
('host', models.ForeignKey(orm['main.host'], null=False))
))
db.create_unique(m2m_table_name, ['jobevent_id', 'host_id'])
# Removing M2M table for field tags on 'Job'
db.delete_table(db.shorten_name(u'main_job_tags'))
# Removing M2M table for field audit_trail on 'Job'
db.delete_table(db.shorten_name(u'main_job_audit_trail'))
# Removing M2M table for field tags on 'Inventory'
db.delete_table(db.shorten_name(u'main_inventory_tags'))
# Removing M2M table for field audit_trail on 'Inventory'
db.delete_table(db.shorten_name(u'main_inventory_audit_trail'))
# Removing M2M table for field tags on 'Host'
db.delete_table(db.shorten_name(u'main_host_tags'))
# Removing M2M table for field audit_trail on 'Host'
db.delete_table(db.shorten_name(u'main_host_audit_trail'))
# Removing M2M table for field tags on 'Group'
db.delete_table(db.shorten_name(u'main_group_tags'))
# Removing M2M table for field audit_trail on 'Group'
db.delete_table(db.shorten_name(u'main_group_audit_trail'))
# Removing M2M table for field audit_trail on 'Credential'
db.delete_table(db.shorten_name(u'main_credential_audit_trail'))
# Removing M2M table for field tags on 'Credential'
db.delete_table(db.shorten_name(u'main_credential_tags'))
# Removing M2M table for field tags on 'JobTemplate'
db.delete_table(db.shorten_name(u'main_jobtemplate_tags'))
# Removing M2M table for field audit_trail on 'JobTemplate'
db.delete_table(db.shorten_name(u'main_jobtemplate_audit_trail'))
# Removing M2M table for field tags on 'Team'
db.delete_table(db.shorten_name(u'main_team_tags'))
# Removing M2M table for field audit_trail on 'Team'
db.delete_table(db.shorten_name(u'main_team_audit_trail'))
# Removing M2M table for field tags on 'Project'
db.delete_table(db.shorten_name(u'main_project_tags'))
# Removing M2M table for field audit_trail on 'Project'
db.delete_table(db.shorten_name(u'main_project_audit_trail'))
# Removing M2M table for field tags on 'Permission'
db.delete_table(db.shorten_name(u'main_permission_tags'))
# Removing M2M table for field audit_trail on 'Permission'
db.delete_table(db.shorten_name(u'main_permission_audit_trail'))
# Removing M2M table for field tags on 'VariableData'
db.delete_table(db.shorten_name(u'main_variabledata_tags'))
# Removing M2M table for field audit_trail on 'VariableData'
db.delete_table(db.shorten_name(u'main_variabledata_audit_trail'))
# Removing M2M table for field tags on 'Organization'
db.delete_table(db.shorten_name(u'main_organization_tags'))
# Removing M2M table for field audit_trail on 'Organization'
db.delete_table(db.shorten_name(u'main_organization_audit_trail'))
# Deleting model 'Tag'
db.delete_table(u'main_tag')
# Deleting model 'AuditTrail'
db.delete_table(u'main_audittrail')
def backwards(self, orm):
# Deleting field 'Host.variables'
db.delete_column(u'main_host', 'variables')
# Deleting field 'Group.variables'
db.delete_column(u'main_group', 'variables')
# Deleting field 'JobTemplate.job_tags'
db.delete_column(u'main_jobtemplate', 'job_tags')
# Deleting field 'JobTemplate.host_config_key'
db.delete_column(u'main_jobtemplate', 'host_config_key')
# Deleting field 'Job.job_tags'
db.delete_column(u'main_job', 'job_tags')
# Deleting field 'Job.job_args'
db.delete_column(u'main_job', 'job_args')
# Deleting field 'Job.job_cwd'
db.delete_column(u'main_job', 'job_cwd')
# Deleting field 'Job.job_env'
db.delete_column(u'main_job', 'job_env')
# Deleting field 'JobHostSummary.failed'
db.delete_column(u'main_jobhostsummary', 'failed')
# Deleting field 'JobEvent.play'
db.delete_column(u'main_jobevent', 'play')
# Deleting field 'JobEvent.task'
db.delete_column(u'main_jobevent', 'task')
# Deleting field 'JobEvent.parent'
db.delete_column(u'main_jobevent', 'parent_id')
# Removing M2M table for field hosts on 'JobEvent'
db.delete_table(db.shorten_name(u'main_jobevent_hosts'))
# Adding model 'AuditTrail'
db.create_table(u'main_audittrail', (
('comment', self.gf('django.db.models.fields.TextField')()),
('modified_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.SET_NULL, blank=True)),
('delta', self.gf('django.db.models.fields.TextField')()),
('tag', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.Tag'], null=True, on_delete=models.SET_NULL, blank=True)),
('detail', self.gf('django.db.models.fields.TextField')()),
('resource_type', self.gf('django.db.models.fields.CharField')(max_length=64)),
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
))
db.send_create_signal('main', ['AuditTrail'])
# Adding model 'Tag'
db.create_table(u'main_tag', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(max_length=512)),
))
db.send_create_signal('main', ['Tag'])
# Adding M2M table for field tags on 'Job'
m2m_table_name = db.shorten_name(u'main_job_tags')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('job', models.ForeignKey(orm['main.job'], null=False)),
('tag', models.ForeignKey(orm['main.tag'], null=False))
))
db.create_unique(m2m_table_name, ['job_id', 'tag_id'])
# Adding M2M table for field audit_trail on 'Job'
m2m_table_name = db.shorten_name(u'main_job_audit_trail')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('job', models.ForeignKey(orm['main.job'], null=False)),
('audittrail', models.ForeignKey(orm['main.audittrail'], null=False))
))
db.create_unique(m2m_table_name, ['job_id', 'audittrail_id'])
# Adding M2M table for field tags on 'Inventory'
m2m_table_name = db.shorten_name(u'main_inventory_tags')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('inventory', models.ForeignKey(orm['main.inventory'], null=False)),
('tag', models.ForeignKey(orm['main.tag'], null=False))
))
db.create_unique(m2m_table_name, ['inventory_id', 'tag_id'])
# Adding M2M table for field audit_trail on 'Inventory'
m2m_table_name = db.shorten_name(u'main_inventory_audit_trail')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('inventory', models.ForeignKey(orm['main.inventory'], null=False)),
('audittrail', models.ForeignKey(orm['main.audittrail'], null=False))
))
db.create_unique(m2m_table_name, ['inventory_id', 'audittrail_id'])
# Adding M2M table for field tags on 'Host'
m2m_table_name = db.shorten_name(u'main_host_tags')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('host', models.ForeignKey(orm['main.host'], null=False)),
('tag', models.ForeignKey(orm['main.tag'], null=False))
))
db.create_unique(m2m_table_name, ['host_id', 'tag_id'])
# Adding M2M table for field audit_trail on 'Host'
m2m_table_name = db.shorten_name(u'main_host_audit_trail')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('host', models.ForeignKey(orm['main.host'], null=False)),
('audittrail', models.ForeignKey(orm['main.audittrail'], null=False))
))
db.create_unique(m2m_table_name, ['host_id', 'audittrail_id'])
# Adding M2M table for field tags on 'Group'
m2m_table_name = db.shorten_name(u'main_group_tags')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('group', models.ForeignKey(orm['main.group'], null=False)),
('tag', models.ForeignKey(orm['main.tag'], null=False))
))
db.create_unique(m2m_table_name, ['group_id', 'tag_id'])
# Adding M2M table for field audit_trail on 'Group'
m2m_table_name = db.shorten_name(u'main_group_audit_trail')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('group', models.ForeignKey(orm['main.group'], null=False)),
('audittrail', models.ForeignKey(orm['main.audittrail'], null=False))
))
db.create_unique(m2m_table_name, ['group_id', 'audittrail_id'])
# Adding M2M table for field audit_trail on 'Credential'
m2m_table_name = db.shorten_name(u'main_credential_audit_trail')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('credential', models.ForeignKey(orm['main.credential'], null=False)),
('audittrail', models.ForeignKey(orm['main.audittrail'], null=False))
))
db.create_unique(m2m_table_name, ['credential_id', 'audittrail_id'])
# Adding M2M table for field tags on 'Credential'
m2m_table_name = db.shorten_name(u'main_credential_tags')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('credential', models.ForeignKey(orm['main.credential'], null=False)),
('tag', models.ForeignKey(orm['main.tag'], null=False))
))
db.create_unique(m2m_table_name, ['credential_id', 'tag_id'])
# Adding M2M table for field tags on 'JobTemplate'
m2m_table_name = db.shorten_name(u'main_jobtemplate_tags')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('jobtemplate', models.ForeignKey(orm['main.jobtemplate'], null=False)),
('tag', models.ForeignKey(orm['main.tag'], null=False))
))
db.create_unique(m2m_table_name, ['jobtemplate_id', 'tag_id'])
# Adding M2M table for field audit_trail on 'JobTemplate'
m2m_table_name = db.shorten_name(u'main_jobtemplate_audit_trail')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('jobtemplate', models.ForeignKey(orm['main.jobtemplate'], null=False)),
('audittrail', models.ForeignKey(orm['main.audittrail'], null=False))
))
db.create_unique(m2m_table_name, ['jobtemplate_id', 'audittrail_id'])
# Adding M2M table for field tags on 'Team'
m2m_table_name = db.shorten_name(u'main_team_tags')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('team', models.ForeignKey(orm['main.team'], null=False)),
('tag', models.ForeignKey(orm['main.tag'], null=False))
))
db.create_unique(m2m_table_name, ['team_id', 'tag_id'])
# Adding M2M table for field audit_trail on 'Team'
m2m_table_name = db.shorten_name(u'main_team_audit_trail')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('team', models.ForeignKey(orm['main.team'], null=False)),
('audittrail', models.ForeignKey(orm['main.audittrail'], null=False))
))
db.create_unique(m2m_table_name, ['team_id', 'audittrail_id'])
# Adding M2M table for field tags on 'Project'
m2m_table_name = db.shorten_name(u'main_project_tags')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('project', models.ForeignKey(orm[u'main.project'], null=False)),
('tag', models.ForeignKey(orm['main.tag'], null=False))
))
db.create_unique(m2m_table_name, ['project_id', 'tag_id'])
# Adding M2M table for field audit_trail on 'Project'
m2m_table_name = db.shorten_name(u'main_project_audit_trail')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('project', models.ForeignKey(orm[u'main.project'], null=False)),
('audittrail', models.ForeignKey(orm['main.audittrail'], null=False))
))
db.create_unique(m2m_table_name, ['project_id', 'audittrail_id'])
# Adding M2M table for field tags on 'Permission'
m2m_table_name = db.shorten_name(u'main_permission_tags')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('permission', models.ForeignKey(orm['main.permission'], null=False)),
('tag', models.ForeignKey(orm['main.tag'], null=False))
))
db.create_unique(m2m_table_name, ['permission_id', 'tag_id'])
# Adding M2M table for field audit_trail on 'Permission'
m2m_table_name = db.shorten_name(u'main_permission_audit_trail')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('permission', models.ForeignKey(orm['main.permission'], null=False)),
('audittrail', models.ForeignKey(orm['main.audittrail'], null=False))
))
db.create_unique(m2m_table_name, ['permission_id', 'audittrail_id'])
# Adding M2M table for field tags on 'VariableData'
m2m_table_name = db.shorten_name(u'main_variabledata_tags')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('variabledata', models.ForeignKey(orm['main.variabledata'], null=False)),
('tag', models.ForeignKey(orm['main.tag'], null=False))
))
db.create_unique(m2m_table_name, ['variabledata_id', 'tag_id'])
# Adding M2M table for field audit_trail on 'VariableData'
m2m_table_name = db.shorten_name(u'main_variabledata_audit_trail')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('variabledata', models.ForeignKey(orm['main.variabledata'], null=False)),
('audittrail', models.ForeignKey(orm['main.audittrail'], null=False))
))
db.create_unique(m2m_table_name, ['variabledata_id', 'audittrail_id'])
# Adding M2M table for field tags on 'Organization'
m2m_table_name = db.shorten_name(u'main_organization_tags')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('organization', models.ForeignKey(orm['main.organization'], null=False)),
('tag', models.ForeignKey(orm['main.tag'], null=False))
))
db.create_unique(m2m_table_name, ['organization_id', 'tag_id'])
# Adding M2M table for field audit_trail on 'Organization'
m2m_table_name = db.shorten_name(u'main_organization_audit_trail')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('organization', models.ForeignKey(orm['main.organization'], null=False)),
('audittrail', models.ForeignKey(orm['main.audittrail'], null=False))
))
db.create_unique(m2m_table_name, ['organization_id', 'audittrail_id'])
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'main.credential': {
'Meta': {'object_name': 'Credential'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'ssh_key_data': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'ssh_key_unlock': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'ssh_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'ssh_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'sudo_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'sudo_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'team': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credentials'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Team']", 'blank': 'True', 'null': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credentials'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['auth.User']", 'blank': 'True', 'null': 'True'})
},
'main.group': {
'Meta': {'unique_together': "(('name', 'inventory'),)", 'object_name': 'Group'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'groups'", 'blank': 'True', 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['main.Inventory']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'children'", 'blank': 'True', 'to': "orm['main.Group']"}),
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True', 'null': 'True'}),
'variable_data': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'group'", 'unique': 'True', 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.VariableData']", 'blank': 'True', 'null': 'True'})
},
'main.host': {
'Meta': {'unique_together': "(('name', 'inventory'),)", 'object_name': 'Host'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts'", 'to': "orm['main.Inventory']"}),
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Job']", 'blank': 'True', 'null': 'True'}),
'last_job_host_summary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job_summary+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['main.JobHostSummary']", 'blank': 'True', 'null': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True', 'null': 'True'}),
'variable_data': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'host'", 'unique': 'True', 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.VariableData']", 'blank': 'True', 'null': 'True'})
},
'main.inventory': {
'Meta': {'unique_together': "(('name', 'organization'),)", 'object_name': 'Inventory'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventories'", 'to': "orm['main.Organization']"})
},
'main.job': {
'Meta': {'object_name': 'Job'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'cancel_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'celery_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'job\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Credential']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'jobs'", 'blank': 'True', 'through': u"orm['main.JobHostSummary']", 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'job_args': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'job_cwd': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'job_env': ('jsonfield.fields.JSONField', [], {'default': '{}', 'null': 'True', 'blank': 'True'}),
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobTemplate']", 'blank': 'True', 'null': 'True'}),
'job_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'playbook': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['main.Project']"}),
'result_stdout': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'result_traceback': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '20'}),
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
},
'main.jobevent': {
'Meta': {'ordering': "('pk',)", 'object_name': 'JobEvent'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Host']", 'blank': 'True', 'null': 'True'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'job_events'", 'blank': 'True', 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events'", 'to': "orm['main.Job']"}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobEvent']", 'blank': 'True', 'null': 'True'}),
'play': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True', 'null': 'True'}),
'task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True', 'null': 'True'})
},
u'main.jobhostsummary': {
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('job', 'host')]", 'object_name': 'JobHostSummary'},
'changed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'dark': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Job']"}),
'ok': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'processed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'skipped': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
},
'main.jobtemplate': {
'Meta': {'object_name': 'JobTemplate'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'jobtemplate\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_templates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
'host_config_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True', 'null': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_templates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'job_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_templates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['main.Project']"}),
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
},
'main.organization': {
'Meta': {'object_name': 'Organization'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'admins': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'admin_of_organizations'", 'blank': 'True', 'to': u"orm['auth.User']"}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['main.Project']"}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['auth.User']"})
},
'main.permission': {
'Meta': {'object_name': 'Permission'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'permission_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['main.Project']"}),
'team': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Team']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
},
u'main.project': {
'Meta': {'object_name': 'Project'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'project\', \'app_label\': u\'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
},
'main.team': {
'Meta': {'object_name': 'Team'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'teams'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Organization']"}),
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['main.Project']"}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['auth.User']"})
},
'main.variabledata': {
'Meta': {'object_name': 'VariableData'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'variabledata\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'data': ('django.db.models.fields.TextField', [], {'default': "''"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
},
u'taggit.tag': {
'Meta': {'object_name': 'Tag'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
},
u'taggit.taggeditem': {
'Meta': {'object_name': 'TaggedItem'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_tagged_items'", 'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_items'", 'to': u"orm['taggit.Tag']"})
}
}
complete_apps = ['main']

View File

@@ -0,0 +1,387 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
'''
Data migration for AnsibleWorks 1.2-b2 release.
- Update variables from VariableData.data for Host and Group models.
- Update new char/text field values to be empty string if they are null.
- Update failed flag for existing JobHostSummary models.
- Update parent field for existing JobEvent models.
- Update hosts for existing JobEvent models.
'''
def forwards(self, orm):
for host in orm.Host.objects.all():
if host.variable_data:
host.variables = host.variable_data.data
else:
host.variables = ''
host.save()
for group in orm.Group.objects.all():
if group.variable_data:
group.variables = group.variable_data.data
else:
group.variables = ''
group.save()
for job_template in orm.JobTemplate.objects.all():
changed = False
if job_template.host_config_key is None:
job_template.host_config_key = ''
changed = True
if job_template.job_tags is None:
job_template.job_tags = ''
changed = True
if changed:
job_template.save()
for job in orm.Job.objects.all():
changed = False
if job.job_tags is None:
job.job_tags = ''
changed = True
if job.job_args is None:
job.job_args = ''
changed = True
if job.job_cwd is None:
job.job_cwd = ''
changed = True
if job.job_env is None:
job.job_env = ''
changed = True
if changed:
job.save()
for job_host_summary in orm.JobHostSummary.objects.all():
if job_host_summary.failures or job_host_summary.dark:
job_host_summary.failed = True
job_host_summary.save()
for job_event in orm.JobEvent.objects.all():
job_event.play = job_event.event_data.get('play', '')
job_event.task = job_event.event_data.get('task', '')
job_event.parent = None
parent_events = set()
if job_event.event in ('playbook_on_play_start',
'playbook_on_stats',
'playbook_on_vars_prompt'):
parent_events.add('playbook_on_start')
elif job_event.event in ('playbook_on_notify', 'playbook_on_setup',
'playbook_on_task_start',
'playbook_on_no_hosts_matched',
'playbook_on_no_hosts_remaining',
'playbook_on_import_for_host',
'playbook_on_not_import_for_host'):
parent_events.add('playbook_on_play_start')
elif job_event.event.startswith('runner_on_'):
parent_events.add('playbook_on_setup')
parent_events.add('playbook_on_task_start')
if parent_events:
try:
qs = job_event.job.job_events.all()
qs = qs.filter(pk__lt=job_event.pk,
event__in=parent_events)
job_event.parent = qs.order_by('-pk')[0]
except IndexError:
pass
job_event.save()
def update_job_event_hosts(orm, job_event, extra_hosts=None):
extra_hosts = extra_hosts or []
hostnames = set()
if job_event.event_data.get('host', ''):
hostnames.add(job_event.event_data['host'])
if job_event.event == 'playbook_on_stats':
try:
for v in job_event.event_data.values():
hostnames.update(v.keys())
except AttributeError:
pass
if job_event.host:
job_event.hosts.add(job_event.host)
for hostname in hostnames:
try:
host = job_event.job.inventory.hosts.get(name=hostname)
except orm.Host.DoesNotExist:
continue
job_event.hosts.add(host)
for host in extra_hosts:
job_event.hosts.add(host)
if job_event.parent:
update_job_event_hosts(orm, job_event.parent,
job_event.hosts.all())
for job_event in orm.JobEvent.objects.all():
update_job_event_hosts(orm, job_event)
def backwards(self, orm):
for host in orm.Host.objects.all():
if host.variable_data:
variable_data = host.variable_data
variable_data.data = host.variables
variable_data.save()
else:
host.variable_data = orm.VariableData.objects.create(data=host.variables)
host.save()
for group in orm.Group.objects.all():
if group.variable_data:
variable_data = group.variable_data
variable_data.data = group.variables
variable_data.save()
else:
group.variable_data = orm.VariableData.objects.create(data=group.variables)
group.save()
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'main.credential': {
'Meta': {'object_name': 'Credential'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'ssh_key_data': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'ssh_key_unlock': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'ssh_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'ssh_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'sudo_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'sudo_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'team': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credentials'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Team']", 'blank': 'True', 'null': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credentials'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['auth.User']", 'blank': 'True', 'null': 'True'})
},
'main.group': {
'Meta': {'unique_together': "(('name', 'inventory'),)", 'object_name': 'Group'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'groups'", 'blank': 'True', 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['main.Inventory']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'children'", 'blank': 'True', 'to': "orm['main.Group']"}),
'variable_data': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'group'", 'unique': 'True', 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.VariableData']", 'blank': 'True', 'null': 'True'}),
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
},
'main.host': {
'Meta': {'unique_together': "(('name', 'inventory'),)", 'object_name': 'Host'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts'", 'to': "orm['main.Inventory']"}),
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Job']", 'blank': 'True', 'null': 'True'}),
'last_job_host_summary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job_summary+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['main.JobHostSummary']", 'blank': 'True', 'null': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'variable_data': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'host'", 'unique': 'True', 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.VariableData']", 'blank': 'True', 'null': 'True'}),
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
},
'main.inventory': {
'Meta': {'unique_together': "(('name', 'organization'),)", 'object_name': 'Inventory'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventories'", 'to': "orm['main.Organization']"})
},
'main.job': {
'Meta': {'object_name': 'Job'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'cancel_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'celery_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'job\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Credential']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'jobs'", 'blank': 'True', 'through': u"orm['main.JobHostSummary']", 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'job_args': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'job_cwd': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'job_env': ('jsonfield.fields.JSONField', [], {'default': '{}', 'null': 'True', 'blank': 'True'}),
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobTemplate']", 'blank': 'True', 'null': 'True'}),
'job_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'playbook': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['main.Project']"}),
'result_stdout': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'result_traceback': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '20'}),
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
},
'main.jobevent': {
'Meta': {'ordering': "('pk',)", 'object_name': 'JobEvent'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Host']", 'blank': 'True', 'null': 'True'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'job_events'", 'blank': 'True', 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events'", 'to': "orm['main.Job']"}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobEvent']", 'blank': 'True', 'null': 'True'}),
'play': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'})
},
u'main.jobhostsummary': {
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('job', 'host')]", 'object_name': 'JobHostSummary'},
'changed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'dark': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Job']"}),
'ok': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'processed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'skipped': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
},
'main.jobtemplate': {
'Meta': {'object_name': 'JobTemplate'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'jobtemplate\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_templates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
'host_config_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_templates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'job_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_templates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['main.Project']"}),
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
},
'main.organization': {
'Meta': {'object_name': 'Organization'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'admins': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'admin_of_organizations'", 'blank': 'True', 'to': u"orm['auth.User']"}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['main.Project']"}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['auth.User']"})
},
'main.permission': {
'Meta': {'object_name': 'Permission'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'permission_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['main.Project']"}),
'team': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Team']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
},
u'main.project': {
'Meta': {'object_name': 'Project'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'project\', \'app_label\': u\'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
},
'main.team': {
'Meta': {'object_name': 'Team'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'teams'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Organization']"}),
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['main.Project']"}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['auth.User']"})
},
'main.variabledata': {
'Meta': {'object_name': 'VariableData'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'variabledata\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'data': ('django.db.models.fields.TextField', [], {'default': "''"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
},
u'taggit.tag': {
'Meta': {'object_name': 'Tag'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
},
u'taggit.taggeditem': {
'Meta': {'object_name': 'TaggedItem'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_tagged_items'", 'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_items'", 'to': u"orm['taggit.Tag']"})
}
}
complete_apps = ['main']
symmetrical = True

View File

@@ -0,0 +1,343 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
'''
Schema migration for AnsibleWorks 1.2-b2 release.
- Remove variable_data field on Host and Group models.
- Remove VariableData model.
- Remove null=True on new char fields previously added.
NOTE: This migration has been manually edited!
'''
def forwards(self, orm):
# Changing field 'Job.job_cwd'
db.alter_column(u'main_job', 'job_cwd', self.gf('django.db.models.fields.CharField')(max_length=1024))
# Changing field 'Job.job_tags'
db.alter_column(u'main_job', 'job_tags', self.gf('django.db.models.fields.CharField')(max_length=1024))
# Changing field 'Job.job_env'
db.alter_column(u'main_job', 'job_env', self.gf('jsonfield.fields.JSONField')())
# Changing field 'Job.job_args'
db.alter_column(u'main_job', 'job_args', self.gf('django.db.models.fields.CharField')(max_length=1024))
# Deleting field 'Host.variable_data'
db.delete_column(u'main_host', 'variable_data_id')
# Changing field 'Host.variables'
db.alter_column(u'main_host', 'variables', self.gf('django.db.models.fields.TextField')())
# Deleting field 'Group.variable_data'
db.delete_column(u'main_group', 'variable_data_id')
# Changing field 'Group.variables'
db.alter_column(u'main_group', 'variables', self.gf('django.db.models.fields.TextField')())
# Changing field 'JobTemplate.job_tags'
db.alter_column(u'main_jobtemplate', 'job_tags', self.gf('django.db.models.fields.CharField')(max_length=1024))
# Changing field 'JobTemplate.host_config_key'
db.alter_column(u'main_jobtemplate', 'host_config_key', self.gf('django.db.models.fields.CharField')(max_length=1024))
# Changing field 'JobEvent.play'
db.alter_column(u'main_jobevent', 'play', self.gf('django.db.models.fields.CharField')(max_length=1024))
# Changing field 'JobEvent.task'
db.alter_column(u'main_jobevent', 'task', self.gf('django.db.models.fields.CharField')(max_length=1024))
# Deleting model 'VariableData'
db.delete_table(u'main_variabledata')
def backwards(self, orm):
# Adding model 'VariableData'
db.create_table(u'main_variabledata', (
('description', self.gf('django.db.models.fields.TextField')(default='', blank=True)),
('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('active', self.gf('django.db.models.fields.BooleanField')(default=True)),
('data', self.gf('django.db.models.fields.TextField')(default='')),
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('created_by', self.gf('django.db.models.fields.related.ForeignKey')(related_name="{'class': 'variabledata', 'app_label': 'main'}(class)s_created", null=True, on_delete=models.SET_NULL, to=orm['auth.User'])),
('name', self.gf('django.db.models.fields.CharField')(max_length=512)),
))
db.send_create_signal('main', ['VariableData'])
# Changing field 'Job.job_cwd'
db.alter_column(u'main_job', 'job_cwd', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True))
# Changing field 'Job.job_tags'
db.alter_column(u'main_job', 'job_tags', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True))
# Changing field 'Job.job_env'
db.alter_column(u'main_job', 'job_env', self.gf('jsonfield.fields.JSONField')(null=True))
# Changing field 'Job.job_args'
db.alter_column(u'main_job', 'job_args', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True))
# Adding field 'Host.variable_data'
db.add_column(u'main_host', 'variable_data',
self.gf('django.db.models.fields.related.OneToOneField')(related_name='host', null=True, on_delete=models.SET_NULL, default=None, to=orm['main.VariableData'], blank=True, unique=True),
keep_default=False)
# Changing field 'Host.variables'
db.alter_column(u'main_host', 'variables', self.gf('django.db.models.fields.TextField')(null=True))
# Adding field 'Group.variable_data'
db.add_column(u'main_group', 'variable_data',
self.gf('django.db.models.fields.related.OneToOneField')(related_name='group', null=True, on_delete=models.SET_NULL, default=None, to=orm['main.VariableData'], blank=True, unique=True),
keep_default=False)
# Changing field 'Group.variables'
db.alter_column(u'main_group', 'variables', self.gf('django.db.models.fields.TextField')(null=True))
# Changing field 'JobTemplate.job_tags'
db.alter_column(u'main_jobtemplate', 'job_tags', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True))
# Changing field 'JobTemplate.host_config_key'
db.alter_column(u'main_jobtemplate', 'host_config_key', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True))
# Changing field 'JobEvent.play'
db.alter_column(u'main_jobevent', 'play', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True))
# Changing field 'JobEvent.task'
db.alter_column(u'main_jobevent', 'task', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True))
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'main.credential': {
'Meta': {'object_name': 'Credential'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'ssh_key_data': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'ssh_key_unlock': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'ssh_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'ssh_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'sudo_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'sudo_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'team': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credentials'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Team']", 'blank': 'True', 'null': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credentials'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['auth.User']", 'blank': 'True', 'null': 'True'})
},
'main.group': {
'Meta': {'unique_together': "(('name', 'inventory'),)", 'object_name': 'Group'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'groups'", 'blank': 'True', 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['main.Inventory']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'children'", 'blank': 'True', 'to': "orm['main.Group']"}),
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
},
'main.host': {
'Meta': {'unique_together': "(('name', 'inventory'),)", 'object_name': 'Host'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts'", 'to': "orm['main.Inventory']"}),
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Job']", 'blank': 'True', 'null': 'True'}),
'last_job_host_summary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job_summary+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': u"orm['main.JobHostSummary']", 'blank': 'True', 'null': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
},
'main.inventory': {
'Meta': {'unique_together': "(('name', 'organization'),)", 'object_name': 'Inventory'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventories'", 'to': "orm['main.Organization']"})
},
'main.job': {
'Meta': {'object_name': 'Job'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'cancel_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'celery_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'job\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Credential']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'jobs'", 'blank': 'True', 'through': u"orm['main.JobHostSummary']", 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'job_args': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'job_cwd': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'job_env': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobTemplate']", 'blank': 'True', 'null': 'True'}),
'job_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'playbook': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['main.Project']"}),
'result_stdout': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'result_traceback': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '20'}),
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
},
'main.jobevent': {
'Meta': {'ordering': "('pk',)", 'object_name': 'JobEvent'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events_as_primary_host'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Host']", 'blank': 'True', 'null': 'True'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'job_events'", 'blank': 'True', 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events'", 'to': "orm['main.Job']"}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobEvent']", 'blank': 'True', 'null': 'True'}),
'play': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'})
},
u'main.jobhostsummary': {
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('job', 'host')]", 'object_name': 'JobHostSummary'},
'changed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'dark': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Job']"}),
'ok': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'processed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'skipped': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
},
'main.jobtemplate': {
'Meta': {'object_name': 'JobTemplate'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'jobtemplate\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_templates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
'host_config_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_templates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'job_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_templates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['main.Project']"}),
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
},
'main.organization': {
'Meta': {'object_name': 'Organization'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'admins': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'admin_of_organizations'", 'blank': 'True', 'to': u"orm['auth.User']"}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['main.Project']"}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['auth.User']"})
},
'main.permission': {
'Meta': {'object_name': 'Permission'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'permission_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['main.Project']"}),
'team': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Team']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
},
u'main.project': {
'Meta': {'object_name': 'Project'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'project\', \'app_label\': u\'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
},
'main.team': {
'Meta': {'object_name': 'Team'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_created"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'teams'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Organization']"}),
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['main.Project']"}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['auth.User']"})
},
u'taggit.tag': {
'Meta': {'object_name': 'Tag'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
},
u'taggit.taggeditem': {
'Meta': {'object_name': 'TaggedItem'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_tagged_items'", 'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_items'", 'to': u"orm['taggit.Tag']"})
}
}
complete_apps = ['main']

View File

@@ -1,6 +1,7 @@
# Copyright (c) 2013 AnsibleWorks, Inc. # Copyright (c) 2013 AnsibleWorks, Inc.
# All Rights Reserved. # All Rights Reserved.
import json
import os import os
import shlex import shlex
from django.conf import settings from django.conf import settings
@@ -13,6 +14,7 @@ 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
from jsonfield import JSONField from jsonfield import JSONField
from taggit.managers import TaggableManager
from djcelery.models import TaskMeta from djcelery.models import TaskMeta
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
import yaml import yaml
@@ -108,10 +110,10 @@ class PrimordialModel(models.Model):
description = models.TextField(blank=True, default='') description = models.TextField(blank=True, default='')
created_by = models.ForeignKey('auth.User', on_delete=SET_NULL, null=True, related_name='%s(class)s_created', editable=False) # not blank=False on purpose for admin! created_by = models.ForeignKey('auth.User', on_delete=SET_NULL, null=True, related_name='%s(class)s_created', editable=False) # not blank=False on purpose for admin!
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
tags = models.ManyToManyField('Tag', related_name='%(class)s_by_tag', blank=True)
audit_trail = models.ManyToManyField('AuditTrail', related_name='%(class)s_by_audit_trail', blank=True)
active = models.BooleanField(default=True) active = models.BooleanField(default=True)
tags = TaggableManager(blank=True)
def __unicode__(self): def __unicode__(self):
return unicode("%s-%s"% (self.name, self.id)) return unicode("%s-%s"% (self.name, self.id))
@@ -131,39 +133,6 @@ class CommonModelNameNotUnique(PrimordialModel):
name = models.CharField(max_length=512, unique=False) name = models.CharField(max_length=512, unique=False)
class Tag(models.Model):
'''
any type of object can be given a search tag
'''
class Meta:
app_label = 'main'
name = models.CharField(max_length=512)
def __unicode__(self):
return unicode(self.name)
def get_absolute_url(self):
return reverse('main:tags_detail', args=(self.pk,))
class AuditTrail(models.Model):
'''
changing any object records the change
'''
class Meta:
app_label = 'main'
resource_type = models.CharField(max_length=64)
modified_by = models.ForeignKey('auth.User', on_delete=SET_NULL, null=True, blank=True)
delta = models.TextField() # FIXME: switch to JSONField
detail = models.TextField()
comment = models.TextField()
# FIXME: this looks like this should be a ManyToMany
tag = models.ForeignKey('Tag', on_delete=SET_NULL, null=True, blank=True)
class Organization(CommonModel): class Organization(CommonModel):
''' '''
organizations are the basic unit of multi-tenancy divisions organizations are the basic unit of multi-tenancy divisions
@@ -206,7 +175,7 @@ class Host(CommonModelNameNotUnique):
app_label = 'main' app_label = 'main'
unique_together = (("name", "inventory"),) unique_together = (("name", "inventory"),)
variable_data = models.OneToOneField('VariableData', null=True, default=None, blank=True, on_delete=SET_NULL, related_name='host') variables = models.TextField(blank=True, default='')
inventory = models.ForeignKey('Inventory', null=False, related_name='hosts') inventory = models.ForeignKey('Inventory', null=False, related_name='hosts')
last_job = models.ForeignKey('Job', blank=True, null=True, default=None, on_delete=models.SET_NULL, related_name='hosts_as_last_job+') last_job = models.ForeignKey('Job', blank=True, null=True, default=None, on_delete=models.SET_NULL, related_name='hosts_as_last_job+')
last_job_host_summary = models.ForeignKey('JobHostSummary', blank=True, null=True, default=None, on_delete=models.SET_NULL, related_name='hosts_as_last_job_summary+') last_job_host_summary = models.ForeignKey('JobHostSummary', blank=True, null=True, default=None, on_delete=models.SET_NULL, related_name='hosts_as_last_job_summary+')
@@ -217,13 +186,37 @@ class Host(CommonModelNameNotUnique):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('main:hosts_detail', args=(self.pk,)) return reverse('main:hosts_detail', args=(self.pk,))
@property
def variables_dict(self):
# FIXME: Add YAML support.
return json.loads(self.variables or '{}')
@property
def all_groups(self):
'''
Return all groups of which this host is a member, avoiding infinite
recursion in the case of cyclical group relations.
'''
qs = self.groups.distinct()
for group in self.groups.all():
qs = qs | group.all_parents
return qs
@property
def has_active_failures(self):
return self.last_job_host_summary and self.last_job_host_summary.failed
# Use .job_host_summaries.all() to get jobs affecting this host. # Use .job_host_summaries.all() to get jobs affecting this host.
# Use .job_events.all() to get events affecting this host. # Use .job_events.all() to get events affecting this host.
# Use .job_host_summaries.order_by('-pk')[0] to get the last result. # Use .job_host_summaries.order_by('-pk')[0] to get the last result.
# To get all hosts with active failures:
# Host.objects.filter(last_job_host_summary__failed=True)
class Group(CommonModelNameNotUnique): class Group(CommonModelNameNotUnique):
''' '''
A group of managed nodes. May belong to multiple groups A group containing managed hosts. A group or host may belong to multiple
groups.
''' '''
class Meta: class Meta:
@@ -231,8 +224,9 @@ class Group(CommonModelNameNotUnique):
unique_together = (("name", "inventory"),) unique_together = (("name", "inventory"),)
inventory = models.ForeignKey('Inventory', null=False, related_name='groups') inventory = models.ForeignKey('Inventory', null=False, related_name='groups')
# Can also be thought of as: parents == member_of, children == members
parents = models.ManyToManyField('self', symmetrical=False, related_name='children', blank=True) parents = models.ManyToManyField('self', symmetrical=False, related_name='children', blank=True)
variable_data = models.OneToOneField('VariableData', null=True, default=None, blank=True, on_delete=SET_NULL, related_name='group') variables = models.TextField(blank=True, default='')
hosts = models.ManyToManyField('Host', related_name='groups', blank=True) hosts = models.ManyToManyField('Host', related_name='groups', blank=True)
def __unicode__(self): def __unicode__(self):
@@ -242,12 +236,60 @@ class Group(CommonModelNameNotUnique):
return reverse('main:groups_detail', args=(self.pk,)) return reverse('main:groups_detail', args=(self.pk,))
@property @property
def all_hosts(self): def variables_dict(self):
qs = self.hosts.distinct() # FIXME: Add YAML support.
for group in self.children.exclude(pk=self.pk): return json.loads(self.variables or '{}')
qs = qs | group.all_hosts
def get_all_parents(self, except_pks=None):
'''
Return all parents of this group recursively, avoiding infinite
recursion in the case of cyclical relations. The group itself will be
excluded unless there is a cycle leading back to it.
'''
except_pks = except_pks or set()
except_pks.add(self.pk)
qs = self.parents.distinct()
for group in self.parents.exclude(pk__in=except_pks):
qs = qs | group.get_all_parents(except_pks)
return qs return qs
@property
def all_parents(self):
return self.get_all_parents()
def get_all_children(self, except_pks=None):
'''
Return all children of this group recursively, avoiding infinite
recursion in the case of cyclical relations. The group itself will be
excluded unless there is a cycle leading back to it.
'''
except_pks = except_pks or set()
except_pks.add(self.pk)
qs = self.children.distinct()
for group in self.children.exclude(pk__in=except_pks):
qs = qs | group.get_all_children(except_pks)
return qs
@property
def all_children(self):
return self.get_all_children()
def get_all_hosts(self, except_group_pks=None):
'''
Return all hosts associated with this group or any of its children,
avoiding infinite recursion in the case of cyclical group relations.
'''
except_group_pks = except_group_pks or set()
except_group_pks.add(self.pk)
qs = self.hosts.distinct()
for group in self.children.exclude(pk__in=except_group_pks):
qs = qs | group.get_all_hosts(except_group_pks)
return qs
@property
def all_hosts(self):
return self.get_all_hosts()
@property @property
def job_host_summaries(self): def job_host_summaries(self):
return JobHostSummary.objects.filter(host__in=self.all_hosts) return JobHostSummary.objects.filter(host__in=self.all_hosts)
@@ -256,27 +298,9 @@ class Group(CommonModelNameNotUnique):
def job_events(self): def job_events(self):
return JobEvent.objects.filter(host__in=self.all_hosts) return JobEvent.objects.filter(host__in=self.all_hosts)
# FIXME: audit nullables @property
# FIXME: audit cascades def has_active_failures(self):
return bool(self.all_hosts.filter(last_job_host_summary__failed=True).count())
class VariableData(CommonModelNameNotUnique):
'''
A set of host or group variables
'''
class Meta:
app_label = 'main'
verbose_name_plural = _('variable data')
#host = models.OneToOneField('Host', null=True, default=None, blank=True, on_delete=SET_NULL, related_name='variable_data')
#group = models.OneToOneField('Group', null=True, default=None, blank=True, on_delete=SET_NULL, related_name='variable_data')
data = models.TextField(default='')
def __unicode__(self):
return '%s = %s' % (self.name, self.data)
def get_absolute_url(self):
return reverse('main:variable_detail', args=(self.pk,))
class Credential(CommonModelNameNotUnique): class Credential(CommonModelNameNotUnique):
''' '''
@@ -537,6 +561,16 @@ class JobTemplate(CommonModel):
blank=True, blank=True,
default='', default='',
) )
job_tags = models.CharField(
max_length=1024,
blank=True,
default='',
)
host_config_key = models.CharField(
max_length=1024,
blank=True,
default='',
)
def create_job(self, **kwargs): def create_job(self, **kwargs):
''' '''
@@ -555,6 +589,7 @@ class JobTemplate(CommonModel):
kwargs.setdefault('limit', self.limit) kwargs.setdefault('limit', self.limit)
kwargs.setdefault('verbosity', self.verbosity) kwargs.setdefault('verbosity', self.verbosity)
kwargs.setdefault('extra_vars', self.extra_vars) kwargs.setdefault('extra_vars', self.extra_vars)
kwargs.setdefault('job_tags', self.job_tags)
job = Job(**kwargs) job = Job(**kwargs)
if save_job: if save_job:
job.save() job.save()
@@ -633,6 +668,11 @@ class Job(CommonModel):
blank=True, blank=True,
default='', default='',
) )
job_tags = models.CharField(
max_length=1024,
blank=True,
default='',
)
cancel_flag = models.BooleanField( cancel_flag = models.BooleanField(
blank=True, blank=True,
default=False, default=False,
@@ -647,6 +687,23 @@ class Job(CommonModel):
default=False, default=False,
editable=False, editable=False,
) )
job_args = models.CharField(
max_length=1024,
blank=True,
default='',
editable=False,
)
job_cwd = models.CharField(
max_length=1024,
blank=True,
default='',
editable=False,
)
job_env = JSONField(
blank=True,
default={},
editable=False,
)
result_stdout = models.TextField( result_stdout = models.TextField(
blank=True, blank=True,
default='', default='',
@@ -797,6 +854,7 @@ class JobHostSummary(models.Model):
ok = models.PositiveIntegerField(default=0) ok = models.PositiveIntegerField(default=0)
processed = models.PositiveIntegerField(default=0) processed = models.PositiveIntegerField(default=0)
skipped = models.PositiveIntegerField(default=0) skipped = models.PositiveIntegerField(default=0)
failed = models.BooleanField(default=False)
def __unicode__(self): def __unicode__(self):
return '%s changed=%d dark=%d failures=%d ok=%d processed=%d skipped=%s' % \ return '%s changed=%d dark=%d failures=%d ok=%d processed=%d skipped=%s' % \
@@ -807,6 +865,7 @@ class JobHostSummary(models.Model):
return reverse('main:job_host_summary_detail', args=(self.pk,)) return reverse('main:job_host_summary_detail', args=(self.pk,))
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.failed = bool(self.dark or self.failures)
super(JobHostSummary, self).save(*args, **kwargs) super(JobHostSummary, self).save(*args, **kwargs)
self.update_host_last_job_summary() self.update_host_last_job_summary()
@@ -826,33 +885,58 @@ class JobEvent(models.Model):
An event/message logged from the callback when running a job. An event/message logged from the callback when running a job.
''' '''
EVENT_TYPES = [ # Playbook events will be structured to form the following hierarchy:
('runner_on_failed', _('Runner on Failed')), # - playbook_on_start (once for each playbook file)
('runner_on_ok', _('Runner on OK')), # - playbook_on_vars_prompt (for each play, but before play starts, we
('runner_on_error', _('Runner on Error')), # currently don't handle responding to these prompts)
('runner_on_skipped', _('Runner on Skipped')), # - playbook_on_play_start
('runner_on_unreachable', _('Runner on Unreachable')), # - playbook_on_import_for_host
('runner_on_no_hosts', _('Runner on No Hosts')), # - playbook_on_not_import_for_host
('runner_on_async_poll', _('Runner on Async Poll')), # - playbook_on_no_hosts_matched
('runner_on_async_ok', _('Runner on Async OK')), # - playbook_on_no_hosts_remaining
('runner_on_async_failed', _('Runner on Async Failed')), # - playbook_on_setup
('playbook_on_start', _('Playbook on Start')), # - runner_on*
('playbook_on_notify', _('Playbook on Notify')), # - playbook_on_task_start
('playbook_on_task_start', _('Playbook on Task Start')), # - runner_on_failed
('playbook_on_vars_prompt', _('Playbook on Vars Prompt')), # - runner_on_ok
('playbook_on_setup', _('Playbook on Setup')), # - runner_on_error
('playbook_on_import_for_host', _('Playbook on Import for Host')), # - runner_on_skipped
('playbook_on_not_import_for_host', _('Playbook on Not Import for Host')), # - runner_on_unreachable
('playbook_on_play_start', _('Playbook on Play Start')), # - runner_on_no_hosts
('playbook_on_stats', _('Playbook on Stats')), # - runner_on_async_poll
] # - runner_on_async_ok
# - runner_on_async_failed
# - runner_on_file_diff
# - playbook_on_notify
# - playbook_on_stats
FAILED_EVENTS = [ EVENT_TYPES = [
'runner_on_failed', # (level, event, verbose name, failed)
'runner_on_error', (3, 'runner_on_failed', _('Runner on Failed'), True),
'runner_on_unreachable', (3, 'runner_on_ok', _('Runner on OK'), False),
'runner_on_async_failed', (3, 'runner_on_error', _('Runner on Error'), True),
(3, 'runner_on_skipped', _('Runner on Skipped'), False),
(3, 'runner_on_unreachable', _('Runner on Unreachable'), True),
(3, 'runner_on_no_hosts', _('Runner on No Hosts'), False),
(3, 'runner_on_async_poll', _('Runner on Async Poll'), False),
(3, 'runner_on_async_ok', _('Runner on Async OK'), False),
(3, 'runner_on_async_failed', _('Runner on Async Failed'), True),
(3, 'runner_on_file_diff', _('Runner on File Diff'), False),
(0, 'playbook_on_start', _('Playbook on Start'), False),
(2, 'playbook_on_notify', _('Playbook on Notify'), False),
(2, 'playbook_on_no_hosts_matched', _('Playbook on No Hosts Matched'), False),
(2, 'playbook_on_no_hosts_remaining', _('Playbook on No Hosts Remaining'), False),
(2, 'playbook_on_task_start', _('Playbook on Task Start'), False),
(1, 'playbook_on_vars_prompt', _('Playbook on Vars Prompt'), False),
(2, 'playbook_on_setup', _('Playbook on Setup'), False),
(2, 'playbook_on_import_for_host', _('Playbook on Import for Host'), False),
(2, 'playbook_on_not_import_for_host', _('Playbook on Not Import for Host'), False),
(1, 'playbook_on_play_start', _('Playbook on Play Start'), False),
(1, 'playbook_on_stats', _('Playbook on Stats'), False),
] ]
FAILED_EVENTS = [x[1] for x in EVENT_TYPES if x[3]]
EVENT_CHOICES = [(x[1], x[2]) for x in EVENT_TYPES]
LEVEL_FOR_EVENT = dict([(x[1], x[0]) for x in EVENT_TYPES])
class Meta: class Meta:
app_label = 'main' app_label = 'main'
@@ -868,7 +952,7 @@ class JobEvent(models.Model):
) )
event = models.CharField( event = models.CharField(
max_length=100, max_length=100,
choices=EVENT_TYPES, choices=EVENT_CHOICES,
) )
event_data = JSONField( event_data = JSONField(
blank=True, blank=True,
@@ -878,9 +962,32 @@ class JobEvent(models.Model):
default=False, default=False,
) )
host = models.ForeignKey( host = models.ForeignKey(
'Host',
related_name='job_events_as_primary_host',
blank=True,
null=True,
default=None,
on_delete=models.SET_NULL,
)
hosts = models.ManyToManyField(
'Host', 'Host',
related_name='job_events', related_name='job_events',
blank=True, blank=True,
)
play = models.CharField(
max_length=1024,
blank=True,
default='',
)
task = models.CharField(
max_length=1024,
blank=True,
default='',
)
parent = models.ForeignKey(
'self',
related_name='children',
blank=True,
null=True, null=True,
default=None, default=None,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
@@ -892,25 +999,72 @@ class JobEvent(models.Model):
def __unicode__(self): def __unicode__(self):
return u'%s @ %s' % (self.get_event_display(), self.created.isoformat()) return u'%s @ %s' % (self.get_event_display(), self.created.isoformat())
@property
def event_level(self):
return self.LEVEL_FOR_EVENT.get(self.event, 0)
def _find_parent(self):
parent_events = set()
if self.event in ('playbook_on_play_start', 'playbook_on_stats',
'playbook_on_vars_prompt'):
parent_events.add('playbook_on_start')
elif self.event in ('playbook_on_notify', 'playbook_on_setup',
'playbook_on_task_start',
'playbook_on_no_hosts_matched',
'playbook_on_no_hosts_remaining',
'playbook_on_import_for_host',
'playbook_on_not_import_for_host'):
parent_events.add('playbook_on_play_start')
elif self.event.startswith('runner_on_'):
parent_events.add('playbook_on_setup')
parent_events.add('playbook_on_task_start')
if parent_events:
try:
qs = self.job.job_events.all()
if self.pk:
qs = qs.filter(pk__lt=self.pk, event__in=parent_events)
else:
qs = qs.filter(event__in=parent_events)
return qs.order_by('-pk')[0]
except IndexError:
pass
return None
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.failed = bool(self.event in self.FAILED_EVENTS)
try: try:
if not self.host and self.event_data.get('host', ''): if not self.host and self.event_data.get('host', ''):
self.host = self.job.inventory.hosts.get(name=self.event_data['host']) self.host = self.job.inventory.hosts.get(name=self.event_data['host'])
except (Host.DoesNotExist, AttributeError): except (Host.DoesNotExist, AttributeError):
pass pass
self.failed = bool(self.event in self.FAILED_EVENTS) self.play = self.event_data.get('play', '')
self.task = self.event_data.get('task', '')
self.parent = self._find_parent()
super(JobEvent, self).save(*args, **kwargs) super(JobEvent, self).save(*args, **kwargs)
self.update_hosts()
self.update_host_summary_from_stats() self.update_host_summary_from_stats()
self.update_host_last_job()
def update_host_last_job(self): def update_hosts(self, extra_hosts=None):
if self.host: extra_hosts = extra_hosts or []
update_fields = [] hostnames = set()
if self.host.last_job != self.job: if self.event_data.get('host', ''):
self.host.last_job = self.job hostnames.add(self.event_data['host'])
update_fields.append('last_job') if self.event == 'playbook_on_stats':
if update_fields: try:
self.host.save(update_fields=update_fields) for v in self.event_data.values():
hostnames.update(v.keys())
except AttributeError: # In case event_data or v isn't a dict.
pass
for hostname in hostnames:
try:
host = self.job.inventory.hosts.get(name=hostname)
except Host.DoesNotExist:
continue
self.hosts.add(host)
for host in extra_hosts:
self.hosts.add(host)
if self.parent:
self.parent.update_hosts(self.hosts.all())
def update_host_summary_from_stats(self): def update_host_summary_from_stats(self):
if self.event != 'playbook_on_stats': if self.event != 'playbook_on_stats':

View File

@@ -46,8 +46,12 @@ class CustomRbac(permissions.BasePermission):
if not obj: if not obj:
return True # FIXME: For some reason this needs to return True return True # FIXME: For some reason this needs to return True
# because it is first called with obj=None? # because it is first called with obj=None?
return check_user_access(request.user, view.model, 'change', obj, if getattr(view, 'is_variable_data', False):
request.DATA) return check_user_access(request.user, view.model, 'change', obj,
{'variables': request.DATA})
else:
return check_user_access(request.user, view.model, 'change', obj,
request.DATA)
def _check_delete_permissions(self, request, view, obj=None): def _check_delete_permissions(self, request, view, obj=None):
if not obj: if not obj:

View File

@@ -1,6 +1,9 @@
# Copyright (c) 2013 AnsibleWorks, Inc. # Copyright (c) 2013 AnsibleWorks, Inc.
# All Rights Reserved. # All Rights Reserved.
# Python
import json
# Django # Django
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
@@ -126,28 +129,16 @@ class OrganizationSerializer(BaseSerializer):
def get_related(self, obj): def get_related(self, obj):
res = super(OrganizationSerializer, self).get_related(obj) res = super(OrganizationSerializer, self).get_related(obj)
res.update(dict( res.update(dict(
audit_trail = reverse('main:organizations_audit_trail_list', args=(obj.pk,)), #audit_trail = reverse('main:organizations_audit_trail_list', args=(obj.pk,)),
projects = reverse('main:organizations_projects_list', args=(obj.pk,)), projects = reverse('main:organizations_projects_list', args=(obj.pk,)),
inventories = reverse('main:organizations_inventories_list', args=(obj.pk,)), inventories = reverse('main:organizations_inventories_list', args=(obj.pk,)),
users = reverse('main:organizations_users_list', args=(obj.pk,)), users = reverse('main:organizations_users_list', args=(obj.pk,)),
admins = reverse('main:organizations_admins_list', args=(obj.pk,)), admins = reverse('main:organizations_admins_list', args=(obj.pk,)),
tags = reverse('main:organizations_tags_list', args=(obj.pk,)), #tags = reverse('main:organizations_tags_list', args=(obj.pk,)),
teams = reverse('main:organizations_teams_list', args=(obj.pk,)), teams = reverse('main:organizations_teams_list', args=(obj.pk,)),
)) ))
return res return res
class AuditTrailSerializer(BaseSerializer):
class Meta:
model = AuditTrail
fields = ('url', 'id', 'modified_by', 'delta', 'detail', 'comment')
def get_related(self, obj):
res = super(AuditTrailSerializer, self).get_related(obj)
if obj.modified_by:
res['modified_by'] = reverse('main:users_detail', args=(obj.modified_by.pk,))
return res
class ProjectSerializer(BaseSerializer): class ProjectSerializer(BaseSerializer):
playbooks = serializers.Field(source='playbooks') playbooks = serializers.Field(source='playbooks')
@@ -197,9 +188,11 @@ class InventorySerializer(BaseSerializer):
class HostSerializer(BaseSerializer): class HostSerializer(BaseSerializer):
has_active_failures = serializers.Field(source='has_active_failures')
class Meta: class Meta:
model = Host model = Host
fields = BASE_FIELDS + ('inventory',) fields = BASE_FIELDS + ('inventory', 'variables', 'has_active_failures')
def get_related(self, obj): def get_related(self, obj):
res = super(HostSerializer, self).get_related(obj) res = super(HostSerializer, self).get_related(obj)
@@ -218,9 +211,11 @@ class HostSerializer(BaseSerializer):
class GroupSerializer(BaseSerializer): class GroupSerializer(BaseSerializer):
has_active_failures = serializers.Field(source='has_active_failures')
class Meta: class Meta:
model = Group model = Group
fields = BASE_FIELDS + ('inventory',) fields = BASE_FIELDS + ('inventory', 'variables', 'has_active_failures')
def get_related(self, obj): def get_related(self, obj):
res = super(GroupSerializer, self).get_related(obj) res = super(GroupSerializer, self).get_related(obj)
@@ -235,6 +230,28 @@ class GroupSerializer(BaseSerializer):
)) ))
return res return res
class BaseVariableDataSerializer(BaseSerializer):
def to_native(self, obj):
ret = super(BaseVariableDataSerializer, self).to_native(obj)
return json.loads(ret.get('variables', '') or '{}')
def from_native(self, data, files):
data = {'variables': json.dumps(data)}
return super(BaseVariableDataSerializer, self).from_native(data, files)
class HostVariableDataSerializer(BaseVariableDataSerializer):
class Meta:
model = Host
fields = ('variables',)
class GroupVariableDataSerializer(BaseVariableDataSerializer):
class Meta:
model = Group
fields = ('variables',)
class TeamSerializer(BaseSerializer): class TeamSerializer(BaseSerializer):
class Meta: class Meta:
@@ -319,42 +336,13 @@ class UserSerializer(BaseSerializer):
)) ))
return res return res
class TagSerializer(BaseSerializer):
class Meta:
model = Tag
fields = ('id', 'url', 'name')
def get_related(self, obj):
res = super(TagSerializer, self).get_related(obj)
res.pop('created_by', None)
return res
class VariableDataSerializer(BaseSerializer):
class Meta:
model = VariableData
fields = BASE_FIELDS + ('data',)
def get_related(self, obj):
res = super(VariableDataSerializer, self).get_related(obj)
try:
res['host'] = reverse('main:hosts_detail', args=(obj.host.pk,))
except Host.DoesNotExist:
pass
try:
res['group'] = reverse('main:groups_detail', args=(obj.group.pk,))
except Group.DoesNotExist:
pass
return res
class JobTemplateSerializer(BaseSerializer): class JobTemplateSerializer(BaseSerializer):
class Meta: class Meta:
model = JobTemplate model = JobTemplate
fields = BASE_FIELDS + ('job_type', 'inventory', 'project', 'playbook', fields = BASE_FIELDS + ('job_type', 'inventory', 'project', 'playbook',
'credential', 'forks', 'limit', 'verbosity', 'credential', 'forks', 'limit', 'verbosity',
'extra_vars') 'extra_vars', 'job_tags')
def get_related(self, obj): def get_related(self, obj):
res = super(JobTemplateSerializer, self).get_related(obj) res = super(JobTemplateSerializer, self).get_related(obj)
@@ -383,7 +371,7 @@ class JobSerializer(BaseSerializer):
fields = BASE_FIELDS + ('job_template', 'job_type', 'inventory', fields = BASE_FIELDS + ('job_template', 'job_type', 'inventory',
'project', 'playbook', 'credential', 'project', 'playbook', 'credential',
'forks', 'limit', 'verbosity', 'extra_vars', 'forks', 'limit', 'verbosity', 'extra_vars',
'status', 'failed', 'result_stdout', 'job_tags', 'status', 'failed', 'result_stdout',
'result_traceback', 'result_traceback',
'passwords_needed_to_start') 'passwords_needed_to_start')
@@ -424,6 +412,7 @@ class JobSerializer(BaseSerializer):
data.setdefault('limit', job_template.limit) data.setdefault('limit', job_template.limit)
data.setdefault('verbosity', job_template.verbosity) data.setdefault('verbosity', job_template.verbosity)
data.setdefault('extra_vars', job_template.extra_vars) data.setdefault('extra_vars', job_template.extra_vars)
data.setdefault('job_tags', job_template.job_tags)
return super(JobSerializer, self).from_native(data, files) return super(JobSerializer, self).from_native(data, files)
class JobHostSummarySerializer(BaseSerializer): class JobHostSummarySerializer(BaseSerializer):
@@ -431,7 +420,8 @@ class JobHostSummarySerializer(BaseSerializer):
class Meta: class Meta:
model = JobHostSummary model = JobHostSummary
fields = ('id', 'url', 'job', 'host', 'summary_fields', 'related', fields = ('id', 'url', 'job', 'host', 'summary_fields', 'related',
'changed', 'dark', 'failures', 'ok', 'processed', 'skipped') 'changed', 'dark', 'failures', 'ok', 'processed', 'skipped',
'failed')
def get_related(self, obj): def get_related(self, obj):
res = super(JobHostSummarySerializer, self).get_related(obj) res = super(JobHostSummarySerializer, self).get_related(obj)

View File

@@ -124,6 +124,8 @@ class RunJob(Task):
args.append('-%s' % ('v' * min(3, job.verbosity))) args.append('-%s' % ('v' * min(3, job.verbosity)))
if job.extra_vars: if job.extra_vars:
args.extend(['-e', job.extra_vars]) args.extend(['-e', job.extra_vars])
if job.job_tags:
args.extend(['-t', job.job_tags])
args.append(job.playbook) # relative path to project.local_path args.append(job.playbook) # relative path to project.local_path
ssh_key_path = kwargs.get('ssh_key_path', '') ssh_key_path = kwargs.get('ssh_key_path', '')
if ssh_key_path: if ssh_key_path:
@@ -192,6 +194,8 @@ class RunJob(Task):
raise RuntimeError('project local_path %s cannot be found' % raise RuntimeError('project local_path %s cannot be found' %
project.local_path) project.local_path)
env = self.build_env(job, **kwargs) env = self.build_env(job, **kwargs)
job = self.update_job(job_pk, job_args=args, job_cwd=cwd,
job_env=env)
status, stdout = self.run_pexpect(job_pk, args, cwd, env, status, stdout = self.run_pexpect(job_pk, args, cwd, env,
kwargs['passwords']) kwargs['passwords'])
except Exception: except Exception:

View File

@@ -111,23 +111,23 @@ class AcomInventoryTest(BaseCommandTest):
hosts = [] hosts = []
for x in xrange(10): for x in xrange(10):
if n > 0: if n > 0:
variable_data = VariableData.objects.create(data=json.dumps({'ho': 'hum-%d' % x})) variables = json.dumps({'ho': 'hum-%d' % x})
else: else:
variable_data = None variables = ''
host = inventory.hosts.create(name='host-%02d-%02d.example.com' % (n, x), host = inventory.hosts.create(name='host-%02d-%02d.example.com' % (n, x),
inventory=inventory, inventory=inventory,
variable_data=variable_data) variables=variables)
hosts.append(host) hosts.append(host)
self.hosts.extend(hosts) self.hosts.extend(hosts)
groups = [] groups = []
for x in xrange(5): for x in xrange(5):
if n > 0: if n > 0:
variable_data = VariableData.objects.create(data=json.dumps({'gee': 'whiz-%d' % x})) variables = json.dumps({'gee': 'whiz-%d' % x})
else: else:
variable_data = None variables = ''
group = inventory.groups.create(name='group-%d' % x, group = inventory.groups.create(name='group-%d' % x,
inventory=inventory, inventory=inventory,
variable_data=variable_data) variables=variables)
groups.append(group) groups.append(group)
group.hosts.add(hosts[x]) group.hosts.add(hosts[x])
group.hosts.add(hosts[x + 5]) group.hosts.add(hosts[x + 5])
@@ -184,9 +184,9 @@ class AcomInventoryTest(BaseCommandTest):
group = inventory.groups.get(name=k) group = inventory.groups.get(name=k)
self.assertEqual(set(v.get('hosts', [])), self.assertEqual(set(v.get('hosts', [])),
set(group.hosts.values_list('name', flat=True))) set(group.hosts.values_list('name', flat=True)))
if group.variable_data: if group.variables:
self.assertEqual(v.get('vars', {}), self.assertEqual(v.get('vars', {}),
json.loads(group.variable_data.data)) json.loads(group.variables))
if k == 'group-3': if k == 'group-3':
self.assertEqual(set(v.get('children', [])), self.assertEqual(set(v.get('children', [])),
set(group.children.values_list('name', flat=True))) set(group.children.values_list('name', flat=True)))
@@ -211,7 +211,7 @@ class AcomInventoryTest(BaseCommandTest):
host=host.name) host=host.name)
self.assertEqual(result, None) self.assertEqual(result, None)
data = json.loads(stdout) data = json.loads(stdout)
self.assertEqual(data, json.loads(host.variable_data.data)) self.assertEqual(data, json.loads(host.variables))
def test_invalid_host(self): def test_invalid_host(self):
# Valid host, but not part of the specified inventory. # Valid host, but not part of the specified inventory.

View File

@@ -250,7 +250,7 @@ class InventoryTest(BaseTest):
# attempting to get a variable object creates it, even though it does not already exist # attempting to get a variable object creates it, even though it does not already exist
vdata_url = "/api/v1/hosts/%s/variable_data/" % (added_by_collection_a['id']) vdata_url = "/api/v1/hosts/%s/variable_data/" % (added_by_collection_a['id'])
got = self.get(vdata_url, expect=200, auth=self.get_super_credentials()) got = self.get(vdata_url, expect=200, auth=self.get_super_credentials())
self.assertEquals(got, dict()) self.assertEquals(got, {})
# super user can create variable objects # super user can create variable objects
# an org admin can create variable objects (defers to inventory permissions) # an org admin can create variable objects (defers to inventory permissions)
@@ -267,16 +267,16 @@ class InventoryTest(BaseTest):
self.put(vdata_url, data=vars_a, expect=403, auth=self.get_nobody_credentials()) self.put(vdata_url, data=vars_a, expect=403, auth=self.get_nobody_credentials())
# a normal user with inventory write permissions can edit variable objects # a normal user with inventory write permissions can edit variable objects
vdata_url = "/api/v1/hosts/1/variable_data/" #vdata_url = "/api/v1/hosts/1/variable_data/"
got = self.put(vdata_url, data=vars_b, expect=200, auth=self.get_normal_credentials()) #got = self.put(vdata_url, data=vars_b, expect=200, auth=self.get_normal_credentials())
self.assertEquals(got, vars_b) #self.assertEquals(got, vars_b)
# this URL is not one end users will use, but is what you get back from a put # this URL is not one end users will use, but is what you get back from a put
# as a result, it also needs to be access controlled and working. You will not # as a result, it also needs to be access controlled and working. You will not
# be able to put to it. # be able to put to it.
backend_url = '/api/v1/variable_data/1/' #backend_url = '/api/v1/variable_data/1/'
got = self.get(backend_url, expect=200, auth=self.get_normal_credentials()) #got = self.get(backend_url, expect=200, auth=self.get_normal_credentials())
got = self.put(backend_url, data=dict(), expect=403, auth=self.get_super_credentials()) #got = self.put(backend_url, data=dict(), expect=403, auth=self.get_super_credentials())
################################################### ###################################################
# VARIABLES -> GROUPS # VARIABLES -> GROUPS
@@ -443,7 +443,96 @@ class InventoryTest(BaseTest):
# on a group resource, I can see related resources for variables, inventories, and children # on a group resource, I can see related resources for variables, inventories, and children
# and these work # and these work
def test_group_parents_and_children(self):
# Test for various levels of group parent/child relations, with hosts,
# to verify that helper properties return the correct querysets.
# Group A is parent of B, B is parent of C, C is parent of D. Group E
# is part of the inventory, but outside of the ABCD tree.
g_a = self.inventory_a.groups.create(name='A')
g_b = self.inventory_a.groups.create(name='B')
g_b.parents.add(g_a)
g_c = self.inventory_a.groups.create(name='C')
g_c.parents.add(g_b)
g_d = self.inventory_a.groups.create(name='D')
g_d.parents.add(g_c)
g_e = self.inventory_a.groups.create(name='E')
# Each group "X" contains one host "x".
h_a = self.inventory_a.hosts.create(name='a')
h_a.groups.add(g_a)
h_b = self.inventory_a.hosts.create(name='b')
h_b.groups.add(g_b)
h_c = self.inventory_a.hosts.create(name='c')
h_c.groups.add(g_c)
h_d = self.inventory_a.hosts.create(name='d')
h_d.groups.add(g_d)
h_e = self.inventory_a.hosts.create(name='e')
h_e.groups.add(g_e)
# Test all_children property on groups.
self.assertEqual(set(g_a.all_children.values_list('pk', flat=True)),
set([g_b.pk, g_c.pk, g_d.pk]))
self.assertEqual(set(g_b.all_children.values_list('pk', flat=True)),
set([g_c.pk, g_d.pk]))
self.assertEqual(set(g_c.all_children.values_list('pk', flat=True)),
set([g_d.pk]))
self.assertEqual(set(g_d.all_children.values_list('pk', flat=True)),
set([]))
self.assertEqual(set(g_e.all_children.values_list('pk', flat=True)),
set([]))
# Test all_parents property on groups.
self.assertEqual(set(g_a.all_parents.values_list('pk', flat=True)),
set([]))
self.assertEqual(set(g_b.all_parents.values_list('pk', flat=True)),
set([g_a.pk]))
self.assertEqual(set(g_c.all_parents.values_list('pk', flat=True)),
set([g_a.pk, g_b.pk]))
self.assertEqual(set(g_d.all_parents.values_list('pk', flat=True)),
set([g_a.pk, g_b.pk, g_c.pk]))
self.assertEqual(set(g_e.all_parents.values_list('pk', flat=True)),
set([]))
# Test all_hosts property on groups.
self.assertEqual(set(g_a.all_hosts.values_list('pk', flat=True)),
set([h_a.pk, h_b.pk, h_c.pk, h_d.pk]))
self.assertEqual(set(g_b.all_hosts.values_list('pk', flat=True)),
set([h_b.pk, h_c.pk, h_d.pk]))
self.assertEqual(set(g_c.all_hosts.values_list('pk', flat=True)),
set([h_c.pk, h_d.pk]))
self.assertEqual(set(g_d.all_hosts.values_list('pk', flat=True)),
set([h_d.pk]))
self.assertEqual(set(g_e.all_hosts.values_list('pk', flat=True)),
set([h_e.pk]))
# Test all_groups property on hosts.
self.assertEqual(set(h_a.all_groups.values_list('pk', flat=True)),
set([g_a.pk]))
self.assertEqual(set(h_b.all_groups.values_list('pk', flat=True)),
set([g_a.pk, g_b.pk]))
self.assertEqual(set(h_c.all_groups.values_list('pk', flat=True)),
set([g_a.pk, g_b.pk, g_c.pk]))
self.assertEqual(set(h_d.all_groups.values_list('pk', flat=True)),
set([g_a.pk, g_b.pk, g_c.pk, g_d.pk]))
self.assertEqual(set(h_e.all_groups.values_list('pk', flat=True)),
set([g_e.pk]))
# Now create a circular relationship from D back to A.
g_a.parents.add(g_d)
# All groups "ABCD" should be parents of each other, and children of
# each other, and contain all hosts "abcd".
for g in [g_a, g_b, g_c, g_d]:
self.assertEqual(set(g.all_children.values_list('pk', flat=True)),
set([g_a.pk, g_b.pk, g_c.pk, g_d.pk]))
self.assertEqual(set(g.all_parents.values_list('pk', flat=True)),
set([g_a.pk, g_b.pk, g_c.pk, g_d.pk]))
self.assertEqual(set(g.all_hosts.values_list('pk', flat=True)),
set([h_a.pk, h_b.pk, h_c.pk, h_d.pk]))
# All hosts "abcd" should be members of all groups "ABCD".
for h in [h_a, h_b, h_c, h_d]:
self.assertEqual(set(h.all_groups.values_list('pk', flat=True)),
set([g_a.pk, g_b.pk, g_c.pk, g_d.pk]))
# Group E and host e should not be affected.
self.assertEqual(set(g_e.all_children.values_list('pk', flat=True)),
set([]))
self.assertEqual(set(g_e.all_parents.values_list('pk', flat=True)),
set([]))
self.assertEqual(set(g_e.all_hosts.values_list('pk', flat=True)),
set([h_e.pk]))
self.assertEqual(set(h_e.all_groups.values_list('pk', flat=True)),
set([g_e.pk]))

View File

@@ -779,7 +779,8 @@ class JobStartCancelTest(BaseJobTestMixin, django.test.TransactionTestCase):
self.assertFalse(response['passwords_needed_to_start']) self.assertFalse(response['passwords_needed_to_start'])
response = self.post(url, {}, expect=202) response = self.post(url, {}, expect=202)
job = Job.objects.get(pk=job.pk) job = Job.objects.get(pk=job.pk)
self.assertEqual(job.status, 'successful') self.assertEqual(job.status, 'successful',
job.result_stdout)
else: else:
self.assertFalse(response['can_start']) self.assertFalse(response['can_start'])
response = self.post(url, {}, expect=405) response = self.post(url, {}, expect=405)

View File

@@ -79,7 +79,7 @@ class OrganizationsTest(BaseTest):
# check that the related URL functionality works # check that the related URL functionality works
related = response['results'][0]['related'] related = response['results'][0]['related']
for x in [ 'audit_trail', 'projects', 'users', 'admins', 'tags' ]: for x in ['projects', 'users', 'admins']:
self.assertTrue(x in related and related[x].endswith("/%s/" % x), "looking for %s in related" % x) self.assertTrue(x in related and related[x].endswith("/%s/" % x), "looking for %s in related" % x)
# normal credentials == 200, get only organizations of which user is a member # normal credentials == 200, get only organizations of which user is a member
@@ -163,7 +163,8 @@ class OrganizationsTest(BaseTest):
org1_users = self.get(org1_users_url, expect=200, auth=self.get_super_credentials()) org1_users = self.get(org1_users_url, expect=200, auth=self.get_super_credentials())
self.assertEquals(org1_users['count'], 1) self.assertEquals(org1_users['count'], 1)
def test_get_item_subobjects_tags(self): def _test_get_item_subobjects_tags(self):
# FIXME: Update to support taggit!
# put some tags on the org # put some tags on the org
org1 = Organization.objects.get(pk=2) org1 = Organization.objects.get(pk=2)
@@ -181,7 +182,8 @@ class OrganizationsTest(BaseTest):
self.assertEquals(org1_tags['count'], 2) self.assertEquals(org1_tags['count'], 2)
org1_tags = self.get(org1_tags_url, expect=403, auth=self.get_other_credentials()) org1_tags = self.get(org1_tags_url, expect=403, auth=self.get_other_credentials())
def test_get_item_subobjects_audit_trail(self): def _test_get_item_subobjects_audit_trail(self):
# FIXME: Update to support whatever audit trail framework is used.
url = '/api/v1/organizations/2/audit_trail/' url = '/api/v1/organizations/2/audit_trail/'
self.get(url, expect=200, auth=self.get_normal_credentials()) self.get(url, expect=200, auth=self.get_normal_credentials())
# FIXME: verify that some audit trail records are auto-created on save AND post # FIXME: verify that some audit trail records are auto-created on save AND post
@@ -291,7 +293,8 @@ class OrganizationsTest(BaseTest):
admins = self.get(url, expect=200, auth=self.get_normal_credentials()) admins = self.get(url, expect=200, auth=self.get_normal_credentials())
self.assertEqual(admins['count'], 1) self.assertEqual(admins['count'], 1)
def test_post_item_subobjects_tags(self): def _test_post_item_subobjects_tags(self):
# FIXME: Update to support taggit!
tag = Tag.objects.create(name='blippy') tag = Tag.objects.create(name='blippy')
url = '/api/v1/organizations/2/tags/' url = '/api/v1/organizations/2/tags/'
@@ -305,7 +308,8 @@ class OrganizationsTest(BaseTest):
tags = self.get(url, expect=200, auth=self.get_normal_credentials()) tags = self.get(url, expect=200, auth=self.get_normal_credentials())
self.assertEqual(tags['count'], 0) self.assertEqual(tags['count'], 0)
def test_post_item_subobjects_audit_trail(self): def _test_post_item_subobjects_audit_trail(self):
# FIXME: Update to support whatever audit trail framework is used.
# audit trails are system things, and no user can post to them. # audit trails are system things, and no user can post to them.
url = '/api/v1/organizations/2/audit_trail/' url = '/api/v1/organizations/2/audit_trail/'
self.post(url, dict(id=1), expect=405, auth=self.get_super_credentials()) self.post(url, dict(id=1), expect=405, auth=self.get_super_credentials())

View File

@@ -13,12 +13,10 @@ def url(regex, view, kwargs=None, name=None, prefix=''):
organizations_urls = patterns('ansibleworks.main.views', organizations_urls = patterns('ansibleworks.main.views',
url(r'^$', 'organizations_list'), url(r'^$', 'organizations_list'),
url(r'^(?P<pk>[0-9]+)/$', 'organizations_detail'), url(r'^(?P<pk>[0-9]+)/$', 'organizations_detail'),
url(r'^(?P<pk>[0-9]+)/audit_trail/$', 'organizations_audit_trail_list'),
url(r'^(?P<pk>[0-9]+)/users/$', 'organizations_users_list'), url(r'^(?P<pk>[0-9]+)/users/$', 'organizations_users_list'),
url(r'^(?P<pk>[0-9]+)/admins/$', 'organizations_admins_list'), url(r'^(?P<pk>[0-9]+)/admins/$', 'organizations_admins_list'),
url(r'^(?P<pk>[0-9]+)/inventories/$', 'organizations_inventories_list'), url(r'^(?P<pk>[0-9]+)/inventories/$', 'organizations_inventories_list'),
url(r'^(?P<pk>[0-9]+)/projects/$', 'organizations_projects_list'), url(r'^(?P<pk>[0-9]+)/projects/$', 'organizations_projects_list'),
url(r'^(?P<pk>[0-9]+)/tags/$', 'organizations_tags_list'),
url(r'^(?P<pk>[0-9]+)/teams/$', 'organizations_teams_list'), url(r'^(?P<pk>[0-9]+)/teams/$', 'organizations_teams_list'),
) )
@@ -40,12 +38,6 @@ projects_urls = patterns('ansibleworks.main.views',
url(r'^(?P<pk>[0-9]+)/organizations/$', 'projects_organizations_list'), url(r'^(?P<pk>[0-9]+)/organizations/$', 'projects_organizations_list'),
) )
audit_trails_urls = patterns('ansibleworks.main.views',
#url(r'^$', 'audit_trails_list'),
#url(r'^(?P<pk>[0-9]+)/$', 'audit_trails_detail'),
# ... and ./audit_trails/ on all resources
)
teams_urls = patterns('ansibleworks.main.views', teams_urls = patterns('ansibleworks.main.views',
url(r'^$', 'teams_list'), url(r'^$', 'teams_list'),
url(r'^(?P<pk>[0-9]+)/$', 'teams_detail'), url(r'^(?P<pk>[0-9]+)/$', 'teams_detail'),
@@ -82,11 +74,6 @@ groups_urls = patterns('ansibleworks.main.views',
url(r'^(?P<pk>[0-9]+)/job_host_summaries/$', 'group_job_host_summary_list'), url(r'^(?P<pk>[0-9]+)/job_host_summaries/$', 'group_job_host_summary_list'),
) )
variable_data_urls = patterns('ansibleworks.main.views',
url(r'^(?P<pk>[0-9]+)/$', 'variable_detail'),
# See also variable_data resources on hosts/groups.
)
credentials_urls = patterns('ansibleworks.main.views', credentials_urls = patterns('ansibleworks.main.views',
url(r'^$', 'credentials_list'), url(r'^$', 'credentials_list'),
url(r'^(?P<pk>[0-9]+)/$', 'credentials_detail'), url(r'^(?P<pk>[0-9]+)/$', 'credentials_detail'),
@@ -125,11 +112,6 @@ job_events_urls = patterns('ansibleworks.main.views',
url(r'^(?P<pk>[0-9]+)/$', 'job_event_detail'), url(r'^(?P<pk>[0-9]+)/$', 'job_event_detail'),
) )
tags_urls = patterns('ansibleworks.main.views',
url(r'^(?P<pk>[0-9]+)/$', 'tags_detail'),
# ... and tag relations on all resources
)
v1_urls = patterns('ansibleworks.main.views', v1_urls = patterns('ansibleworks.main.views',
url(r'^$', 'api_v1_root_view'), url(r'^$', 'api_v1_root_view'),
url(r'^authtoken/$', 'auth_token_view'), url(r'^authtoken/$', 'auth_token_view'),
@@ -137,19 +119,16 @@ v1_urls = patterns('ansibleworks.main.views',
url(r'^organizations/', include(organizations_urls)), url(r'^organizations/', include(organizations_urls)),
url(r'^users/', include(users_urls)), url(r'^users/', include(users_urls)),
url(r'^projects/', include(projects_urls)), url(r'^projects/', include(projects_urls)),
url(r'^audit_trails/', include(audit_trails_urls)),
url(r'^teams/', include(teams_urls)), url(r'^teams/', include(teams_urls)),
url(r'^inventories/', include(inventory_urls)), url(r'^inventories/', include(inventory_urls)),
url(r'^hosts/', include(hosts_urls)), url(r'^hosts/', include(hosts_urls)),
url(r'^groups/', include(groups_urls)), url(r'^groups/', include(groups_urls)),
url(r'^variable_data/', include(variable_data_urls)),
url(r'^credentials/', include(credentials_urls)), url(r'^credentials/', include(credentials_urls)),
url(r'^permissions/', include(permissions_urls)), url(r'^permissions/', include(permissions_urls)),
url(r'^job_templates/', include(job_templates_urls)), url(r'^job_templates/', include(job_templates_urls)),
url(r'^jobs/', include(jobs_urls)), url(r'^jobs/', include(jobs_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_events_urls)), url(r'^job_events/', include(job_events_urls)),
url(r'^tags/', include(tags_urls)),
) )
urlpatterns = patterns('ansibleworks.main.views', urlpatterns = patterns('ansibleworks.main.views',

View File

@@ -133,23 +133,6 @@ class OrganizationsDetail(BaseDetail):
serializer_class = OrganizationSerializer serializer_class = OrganizationSerializer
permission_classes = (CustomRbac,) permission_classes = (CustomRbac,)
class OrganizationsAuditTrailList(BaseSubList):
model = AuditTrail
serializer_class = AuditTrailSerializer
permission_classes = (CustomRbac,)
parent_model = Organization
relationship = 'audit_trail'
postable = False
def get_queryset(self):
''' to list tags in the organization, I must be a superuser or org admin '''
organization = Organization.objects.get(pk=self.kwargs['pk'])
if not (self.request.user.is_superuser or self.request.user in organization.admins.all()):
# FIXME: use: organization.can_user_administrate(...) ?
raise PermissionDenied()
return AuditTrail.objects.filter(organization_by_audit_trail__in = [ organization ])
class OrganizationsInventoriesList(BaseSubList): class OrganizationsInventoriesList(BaseSubList):
model = Inventory model = Inventory
@@ -221,25 +204,6 @@ class OrganizationsProjectsList(BaseSubList):
raise PermissionDenied() raise PermissionDenied()
return Project.objects.filter(organizations__in = [ organization ]) return Project.objects.filter(organizations__in = [ organization ])
class OrganizationsTagsList(BaseSubList):
model = Tag
serializer_class = TagSerializer
permission_classes = (CustomRbac,)
parent_model = Organization # for sub list
relationship = 'tags' # " "
postable = True
inject_primary_key_on_post_as = 'organization'
filter_fields = ('name',)
def get_queryset(self):
''' to list tags in the organization, I must be a superuser or org admin '''
organization = Organization.objects.get(pk=self.kwargs['pk'])
if not (self.request.user.is_superuser or self.request.user in organization.admins.all()):
# FIXME: use: organization.can_user_administrate(...) ?
raise PermissionDenied()
return Tag.objects.filter(organization_by_tag__in = [ organization ])
class OrganizationsTeamsList(BaseSubList): class OrganizationsTeamsList(BaseSubList):
model = Team model = Team
@@ -433,12 +397,6 @@ class ProjectsOrganizationsList(BaseSubList):
raise PermissionDenied() raise PermissionDenied()
return Organization.objects.filter(projects__in = [ project ]) return Organization.objects.filter(projects__in = [ project ])
class TagsDetail(BaseDetail):
model = Tag
serializer_class = TagSerializer
permission_classes = (CustomRbac,)
class UsersList(BaseList): class UsersList(BaseList):
model = User model = User
@@ -889,32 +847,19 @@ class InventoryRootGroupsList(BaseSubList):
all_ids = base.values_list('id', flat=True) all_ids = base.values_list('id', flat=True)
return base.exclude(parents__pk__in = all_ids) return base.exclude(parents__pk__in = all_ids)
class GroupsVariableDetail(VariableBaseDetail): class HostsVariableDetail(BaseDetail):
model = VariableData model = Host
serializer_class = VariableDataSerializer serializer_class = HostVariableDataSerializer
permission_classes = (CustomRbac,) permission_classes = (CustomRbac,)
parent_model = Group is_variable_data = True # Special flag for RBAC
reverse_relationship = 'variable_data'
relationship = 'group'
class HostsVariableDetail(VariableBaseDetail): class GroupsVariableDetail(BaseDetail):
model = VariableData model = Group
serializer_class = VariableDataSerializer serializer_class = GroupVariableDataSerializer
permission_classes = (CustomRbac,) permission_classes = (CustomRbac,)
parent_model = Host is_variable_data = True # Special flag for RBAC
reverse_relationship = 'variable_data'
relationship = 'host'
class VariableDetail(BaseDetail):
model = VariableData
serializer_class = VariableDataSerializer
permission_classes = (CustomRbac,)
def put(self, request, *args, **kwargs):
raise PermissionDenied()
class JobTemplateList(BaseList): class JobTemplateList(BaseList):

View File

@@ -39,10 +39,31 @@ class CallbackModule(object):
Callback module for logging ansible-playbook events to the database. Callback module for logging ansible-playbook events to the database.
''' '''
# These events should never have an associated play.
EVENTS_WITHOUT_PLAY = [
'playbook_on_start',
'playbook_on_stats',
]
# These events should never have an associated task.
EVENTS_WITHOUT_TASK = EVENTS_WITHOUT_PLAY + [
'playbook_on_setup',
'playbook_on_notify',
'playbook_on_import_for_host',
'playbook_on_not_import_for_host',
'playbook_on_no_hosts_matched',
'playbook_on_no_hosts_remaining',
]
def __init__(self): def __init__(self):
self.callback_script = os.getenv('ACOM_CALLBACK_EVENT_SCRIPT') self.callback_script = os.getenv('ACOM_CALLBACK_EVENT_SCRIPT')
def _log_event(self, event, **event_data): def _log_event(self, event, **event_data):
play = getattr(getattr(self, 'play', None), 'name', '')
if play and event not in self.EVENTS_WITHOUT_PLAY:
event_data['play'] = play
task = getattr(getattr(self, 'task', None), 'name', '')
if task and event not in self.EVENTS_WITHOUT_TASK:
event_data['task'] = task
event_data_json = json.dumps(event_data) event_data_json = json.dumps(event_data)
cmdline = [self.callback_script, '-e', event, '-d', event_data_json] cmdline = [self.callback_script, '-e', event, '-d', event_data_json]
subprocess.check_call(cmdline) subprocess.check_call(cmdline)
@@ -79,11 +100,20 @@ class CallbackModule(object):
def runner_on_async_failed(self, host, res, jid): def runner_on_async_failed(self, host, res, jid):
self._log_event('runner_on_async_failed', host=host, res=res, jid=jid) self._log_event('runner_on_async_failed', host=host, res=res, jid=jid)
def runner_on_file_diff(self, host, diff):
self._log_event('runner_on_file_diff', host=host, diff=diff)
def playbook_on_start(self): def playbook_on_start(self):
self._log_event('playbook_on_start') self._log_event('playbook_on_start')
def playbook_on_notify(self, host, handler): def playbook_on_notify(self, host, handler):
self._log_event('playbook_on_notify') self._log_event('playbook_on_notify', host=host, handler=handler)
def playbook_on_no_hosts_matched(self):
self._log_event('playbook_on_no_hosts_matched')
def playbook_on_no_hosts_remaining(self):
self._log_event('playbook_on_no_hosts_remaining')
def playbook_on_task_start(self, name, is_conditional): def playbook_on_task_start(self, name, is_conditional):
self._log_event('playbook_on_task_start', name=name, self._log_event('playbook_on_task_start', name=name,

View File

@@ -29,9 +29,9 @@ REST_FRAMEWORK = {
'PAGINATE_BY': 25, 'PAGINATE_BY': 25,
'PAGINATE_BY_PARAM': 'page_size', 'PAGINATE_BY_PARAM': 'page_size',
'DEFAULT_AUTHENTICATION_CLASSES': ( 'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
) )
} }
@@ -137,6 +137,7 @@ INSTALLED_APPS = (
'django_extensions', 'django_extensions',
'djcelery', 'djcelery',
'kombu.transport.django', 'kombu.transport.django',
'taggit',
'ansibleworks.main', 'ansibleworks.main',
'ansibleworks.ui', 'ansibleworks.ui',
) )

View File

@@ -7,6 +7,7 @@ django-devserver
django-extensions django-extensions
django-filter django-filter
django-jsonfield django-jsonfield
django-taggit
djangorestframework djangorestframework
ipython ipython
markdown markdown

View File

@@ -26,10 +26,10 @@ setup(
install_requires=[ install_requires=[
'Django>=1.5', 'Django>=1.5',
'django-celery', 'django-celery',
'django-devserver',
'django-extensions', 'django-extensions',
'django-filter', 'django-filter',
'django-jsonfield', 'django-jsonfield',
'django-taggit',
'djangorestframework', 'djangorestframework',
'pexpect', 'pexpect',
'python-dateutil', 'python-dateutil',
@@ -40,10 +40,10 @@ setup(
#tests_require=[ #tests_require=[
# 'Django>=1.5', # 'Django>=1.5',
# 'django-celery', # 'django-celery',
# 'django-devserver',
# 'django-extensions', # 'django-extensions',
# 'django-filter', # 'django-filter',
# 'django-jsonfield', # 'django-jsonfield',
# 'django-taggit',
# 'django-setuptest', # 'django-setuptest',
# 'djangorestframework', # 'djangorestframework',
# 'pexpect', # 'pexpect',