diff --git a/Makefile b/Makefile index 4e76fa2422..e086036bf7 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -release = ansibleworks-1.2b1 +RELEASE = ansibleworks-1.2b2 clean: rm -rf build *.egg-info @@ -81,16 +81,15 @@ release_build: release_ball: clean make release_build (cd ../ansible-doc; make) - -(rm -rf $(release)) - mkdir -p $(release)/dist + -(rm -rf $(RELEASE)) + mkdir -p $(RELEASE)/dist cp -a dist/* $(release)/dist - mkdir -p $(release)/setup - cp -a setup/* $(release)/setup - mkdir -p $(release)/docs - cp -a ../ansible-doc/*.pdf $(release)/docs - tar -cvf $(release)-all.tar $(release) + mkdir -p $(RELEASE)/setup + cp -a setup/* $(RELEASE)/setup + mkdir -p $(RELEASE)/docs + cp -a ../ansible-doc/*.pdf $(RELEASE)/docs + tar -cvf $(RELEASE)-all.tar $(RELEASE) -clean: +release_clean: -(rm *.tar) - -(rm -rf ($release)) - + -(rm -rf ($RELEASE)) diff --git a/ansibleworks/main/access.py b/ansibleworks/main/access.py index 1b5548effb..d621b3bbec 100644 --- a/ansibleworks/main/access.py +++ b/ansibleworks/main/access.py @@ -139,18 +139,6 @@ class UserAccess(BaseAccess): return bool(self.user.is_superuser or 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): model = Organization @@ -259,6 +247,11 @@ class HostAccess(BaseAccess): # Checks for admin or change permission on inventory. 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): model = Group @@ -275,34 +268,9 @@ class GroupAccess(BaseAccess): def can_change(self, obj, data): # 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) -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): model = Credential @@ -538,12 +506,10 @@ class JobEventAccess(BaseAccess): model = JobEvent register_access(User, UserAccess) -register_access(Tag, TagAccess) register_access(Organization, OrganizationAccess) register_access(Inventory, InventoryAccess) register_access(Host, HostAccess) register_access(Group, GroupAccess) -register_access(VariableData, VariableDataAccess) register_access(Credential, CredentialAccess) register_access(Team, TeamAccess) register_access(Project, ProjectAccess) diff --git a/ansibleworks/main/admin.py b/ansibleworks/main/admin.py index 1d388d078e..9e64cf74ae 100644 --- a/ansibleworks/main/admin.py +++ b/ansibleworks/main/admin.py @@ -58,25 +58,23 @@ class OrganizationAdmin(BaseModelAdmin): (_('Members'), {'fields': ('users', 'admins',)}), (_('Projects'), {'fields': ('projects',)}), (_('Tags'), {'fields': ('tags',)}), - (_('Audit Trail'), {'fields': ('created', 'created_by', - 'audit_trail',)}), + (_('Audit'), {'fields': ('created', 'created_by',)}), ) - readonly_fields = ('created', 'created_by', 'audit_trail') - filter_horizontal = ('users', 'admins', 'projects', 'tags') + readonly_fields = ('created', 'created_by') + filter_horizontal = ('users', 'admins', 'projects') class InventoryHostInline(admin.StackedInline): model = Host extra = 0 fields = ('name', 'description', 'active', 'tags') - filter_horizontal = ('tags',) class InventoryGroupInline(admin.StackedInline): model = Group extra = 0 fields = ('name', 'description', 'active', 'parents', 'hosts', 'tags') - filter_horizontal = ('parents', 'hosts', 'tags') + filter_horizontal = ('parents', 'hosts') class InventoryAdmin(BaseModelAdmin): @@ -85,15 +83,14 @@ class InventoryAdmin(BaseModelAdmin): fieldsets = ( (None, {'fields': (('name', 'active'), 'organization', 'description',)}), (_('Tags'), {'fields': ('tags',)}), - (_('Audit Trail'), {'fields': ('created', 'created_by', 'audit_trail',)}), + (_('Audit'), {'fields': ('created', 'created_by',)}), ) - readonly_fields = ('created', 'created_by', 'audit_trail') - filter_horizontal = ('tags',) + readonly_fields = ('created', 'created_by') inlines = [InventoryHostInline, InventoryGroupInline] -class TagAdmin(BaseModelAdmin): - - list_display = ('name',) +#class TagAdmin(BaseModelAdmin): +# +# list_display = ('name',) #class AuditTrailAdmin(admin.ModelAdmin): # @@ -101,13 +98,6 @@ class TagAdmin(BaseModelAdmin): # not currently on model, so disabling for now. # 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): @@ -136,9 +126,9 @@ class JobEventInline(admin.StackedInline): class JobHostSummaryInlineForHost(JobHostSummaryInline): fields = ('job', 'changed', 'dark', 'failures', 'ok', 'processed', - 'skipped') + 'skipped', 'failed') readonly_fields = ('job', 'changed', 'dark', 'failures', 'ok', 'processed', - 'skipped') + 'skipped', 'failed') class JobEventInlineForHost(JobEventInline): @@ -149,17 +139,15 @@ class HostAdmin(BaseModelAdmin): list_display = ('name', 'inventory', 'description', 'active') list_filter = ('inventory', 'active') - #form = HostAdminForm fieldsets = ( - (None, {'fields': (('name', 'active'), 'inventory', 'description', 'variable_data', + (None, {'fields': (('name', 'active'), 'inventory', 'description', + 'variables', )}), (_('Tags'), {'fields': ('tags',)}), - (_('Audit Trail'), {'fields': ('created', 'created_by', 'audit_trail',)}), + (_('Audit'), {'fields': ('created', 'created_by',)}), ) - readonly_fields = ('created', 'created_by', 'audit_trail') - filter_horizontal = ('tags',) + readonly_fields = ('created', 'created_by') # FIXME: Edit reverse of many to many for groups. - #inlines = [VariableDataInline] inlines = [JobHostSummaryInlineForHost, JobEventInlineForHost] class GroupAdmin(BaseModelAdmin): @@ -167,18 +155,12 @@ class GroupAdmin(BaseModelAdmin): list_display = ('name', 'description', 'active') fieldsets = ( (None, {'fields': (('name', 'active'), 'inventory', 'description', - 'parents')}), + 'parents', 'variables')}), (_('Tags'), {'fields': ('tags',)}), - (_('Audit Trail'), {'fields': ('created', 'created_by', 'audit_trail',)}), + (_('Audit'), {'fields': ('created', 'created_by',)}), ) - readonly_fields = ('created', 'created_by', 'audit_trail') - filter_horizontal = ('parents', 'hosts', 'tags') - #inlines = [VariableDataInline] - -class VariableDataAdmin(BaseModelAdmin): - - list_display = ('name', 'description', 'active') - filter_horizontal = ('tags',) + readonly_fields = ('created', 'created_by') + filter_horizontal = ('parents', 'hosts') class CredentialAdmin(BaseModelAdmin): @@ -187,16 +169,15 @@ class CredentialAdmin(BaseModelAdmin): (_('Auth Info'), {'fields': (('ssh_username', 'ssh_password'), 'ssh_key_data', 'ssh_key_unlock', ('sudo_username', 'sudo_password'))}), - #(_('Tags'), {'fields': ('tags',)}), - (_('Audit Trail'), {'fields': ('created', 'created_by', 'audit_trail',)}), + (_('Tags'), {'fields': ('tags',)}), + (_('Audit'), {'fields': ('created', 'created_by',)}), ) - readonly_fields = ('created', 'created_by', 'audit_trail') - filter_horizontal = ('tags',) + readonly_fields = ('created', 'created_by') class TeamAdmin(BaseModelAdmin): list_display = ('name', 'description', 'active') - filter_horizontal = ('projects', 'users', 'tags') + filter_horizontal = ('projects', 'users') class ProjectAdmin(BaseModelAdmin): @@ -205,11 +186,9 @@ class ProjectAdmin(BaseModelAdmin): (None, {'fields': (('name', 'active'), 'description', 'local_path', 'get_playbooks_display')}), (_('Tags'), {'fields': ('tags',)}), - (_('Audit Trail'), {'fields': ('created', 'created_by', 'audit_trail',)}), + (_('Audit'), {'fields': ('created', 'created_by',)}), ) - readonly_fields = ('created', 'created_by', 'audit_trail', - 'get_playbooks_display') - filter_horizontal = ('tags',) + readonly_fields = ('created', 'created_by', 'get_playbooks_display') form = ProjectAdminForm def get_playbooks_display(self, obj): @@ -221,7 +200,6 @@ class ProjectAdmin(BaseModelAdmin): class PermissionAdmin(BaseModelAdmin): list_display = ('name', 'description', 'active') - filter_horizontal = ('tags',) class JobTemplateAdmin(BaseModelAdmin): @@ -232,17 +210,15 @@ class JobTemplateAdmin(BaseModelAdmin): 'get_create_link_display', 'get_jobs_link_display')}), (_('Job Parameters'), {'fields': ('inventory', 'project', 'playbook', 'credential', 'job_type')}), - (_('More Options'), {'fields': ('forks', 'limit', - 'verbosity', 'extra_vars'), + (_('More Options'), {'fields': ('forks', 'limit', 'verbosity', + 'extra_vars', 'job_tags', 'host_config_key'), 'classes': ('collapse',)}), - #(_('Tags'), {'fields': ('tags',)}), - (_('Audit Trail'), {'fields': ('created', 'created_by', - 'audit_trail',)}), + (_('Tags'), {'fields': ('tags',)}), + (_('Audit'), {'fields': ('created', 'created_by',)}), ) - readonly_fields = ('created', 'created_by', 'audit_trail', - 'get_create_link_display', 'get_jobs_link_display') + readonly_fields = ('created', 'created_by', 'get_create_link_display', + 'get_jobs_link_display') form = JobTemplateAdminForm - #filter_horizontal = ('tags',) def get_create_link_display(self, obj): if not obj or not obj.pk: @@ -272,6 +248,8 @@ class JobTemplateAdmin(BaseModelAdmin): create_opts['verbosity'] = obj.verbosity if 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) return format_html('{1}', create_url, 'Create Job') get_create_link_display.short_description = _('Create Job') @@ -291,9 +269,9 @@ class JobTemplateAdmin(BaseModelAdmin): class JobHostSummaryInlineForJob(JobHostSummaryInline): fields = ('host', 'changed', 'dark', 'failures', 'ok', 'processed', - 'skipped') + 'skipped', 'failed') readonly_fields = ('host', 'changed', 'dark', 'failures', 'ok', - 'processed', 'skipped') + 'processed', 'skipped', 'failed') class JobEventInlineForJob(JobEventInline): @@ -310,13 +288,12 @@ class JobAdmin(BaseModelAdmin): (_('Job Parameters'), {'fields': ('inventory', 'project', 'playbook', 'credential', 'job_type')}), (_('More Options'), {'fields': ('forks', 'limit', 'verbosity', - 'extra_vars'), + 'extra_vars', 'job_tags'), 'classes': ('collapse',)}), (_('Start Job'), {'fields': ('start_job', 'ssh_password', 'sudo_password', 'ssh_key_unlock')}), - #(_('Tags'), {'fields': ('tags',)}), - (_('Audit Trail'), {'fields': ('created', 'created_by', - 'audit_trail',)}), + (_('Tags'), {'fields': ('tags',)}), + (_('Audit'), {'fields': ('created', 'created_by',)}), (_('Job Status'), {'fields': (('status', 'failed', 'cancel_job'), 'get_result_stdout_display', 'get_result_traceback_display', @@ -325,8 +302,7 @@ class JobAdmin(BaseModelAdmin): readonly_fields = ('status', 'failed', 'get_job_template_display', 'get_result_stdout_display', 'get_result_traceback_display', 'celery_task_id', - 'created', 'created_by', 'audit_trail',) - filter_horizontal = ('tags',) + 'created', 'created_by') form = JobAdminForm inlines = [JobHostSummaryInlineForJob, JobEventInlineForJob] @@ -336,7 +312,7 @@ class JobAdmin(BaseModelAdmin): ro_fields.extend(['name', 'description', 'job_template', 'inventory', 'project', 'playbook', 'credential', 'job_type', 'forks', 'limit', - 'verbosity', 'extra_vars']) + 'verbosity', 'extra_vars', 'job_tags']) return ro_fields 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.allow_tags = True - -# FIXME: Add the rest of the models... - admin.site.register(Organization, OrganizationAdmin) admin.site.register(Inventory, InventoryAdmin) -admin.site.register(Tag, TagAdmin) +#admin.site.register(Tag, TagAdmin) #admin.site.register(AuditTrail, AuditTrailAdmin) admin.site.register(Host, HostAdmin) admin.site.register(Group, GroupAdmin) -admin.site.register(VariableData, VariableDataAdmin) +#admin.site.register(VariableData, VariableDataAdmin) admin.site.register(Team, TeamAdmin) admin.site.register(Project, ProjectAdmin) admin.site.register(Credential, CredentialAdmin) diff --git a/ansibleworks/main/base_views.py b/ansibleworks/main/base_views.py index f674b08853..ad3b703104 100644 --- a/ansibleworks/main/base_views.py +++ b/ansibleworks/main/base_views.py @@ -214,7 +214,7 @@ class BaseSubList(BaseList): class BaseDetail(generics.RetrieveUpdateDestroyAPIView): 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 def destroy(self, request, *args, **kwargs): @@ -247,73 +247,3 @@ class BaseDetail(generics.RetrieveUpdateDestroyAPIView): 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 ''' 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)) - diff --git a/ansibleworks/main/forms.py b/ansibleworks/main/forms.py index 887835641a..410a8e9f28 100644 --- a/ansibleworks/main/forms.py +++ b/ansibleworks/main/forms.py @@ -29,46 +29,6 @@ class PlaybookSelect(forms.Select): opt = opt.replace('">', '" class="project-%s">' % obj.project.pk) 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): '''Custom admin form for Projects.''' diff --git a/ansibleworks/main/management/commands/acom_callback_event.py b/ansibleworks/main/management/commands/acom_callback_event.py index be7c89f5ae..a228dea27c 100755 --- a/ansibleworks/main/management/commands/acom_callback_event.py +++ b/ansibleworks/main/management/commands/acom_callback_event.py @@ -8,6 +8,7 @@ from optparse import make_option import os import sys from django.core.management.base import NoArgsCommand, CommandError +from django.db import transaction class Command(NoArgsCommand): ''' @@ -30,12 +31,13 @@ class Command(NoArgsCommand): help='JSON-formatted callback event data'), ) + @transaction.commit_on_success def handle_noargs(self, **options): from ansibleworks.main.models import Job, JobEvent event_type = options.get('event_type', None) if not event_type: 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') event_data_file = options.get('event_data_file', None) event_data_json = options.get('event_data_json', None) diff --git a/ansibleworks/main/management/commands/acom_inventory.py b/ansibleworks/main/management/commands/acom_inventory.py index f9db23314c..a107502e71 100755 --- a/ansibleworks/main/management/commands/acom_inventory.py +++ b/ansibleworks/main/management/commands/acom_inventory.py @@ -32,8 +32,8 @@ class Command(NoArgsCommand): 'hosts': list(group.hosts.values_list('name', flat=True)), 'children': list(group.children.values_list('name', flat=True)), } - if group.variable_data is not None: - group_info['vars'] = json.loads(group.variable_data.data) + if group.variables: + group_info['vars'] = group.variables_dict group_info = dict(filter(lambda x: bool(x[1]), group_info.items())) if group_info.keys() in ([], ['hosts']): @@ -51,8 +51,8 @@ class Command(NoArgsCommand): except Host.DoesNotExist: raise CommandError('Host %s not found in the given inventory' % hostname) hostvars = {} - if host.variable_data is not None: - hostvars = json.loads(host.variable_data.data) + if host.variables: + hostvars = host.variables_dict self.stdout.write(json.dumps(hostvars, indent=indent)) def handle_noargs(self, **options): diff --git a/ansibleworks/main/migrations/0002_v12b2_changes.py b/ansibleworks/main/migrations/0002_v12b2_changes.py new file mode 100644 index 0000000000..18421fbde0 --- /dev/null +++ b/ansibleworks/main/migrations/0002_v12b2_changes.py @@ -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'] \ No newline at end of file diff --git a/ansibleworks/main/migrations/0003_v12b2_changes.py b/ansibleworks/main/migrations/0003_v12b2_changes.py new file mode 100644 index 0000000000..37ac78162d --- /dev/null +++ b/ansibleworks/main/migrations/0003_v12b2_changes.py @@ -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 diff --git a/ansibleworks/main/migrations/0004_v12b2_changes.py b/ansibleworks/main/migrations/0004_v12b2_changes.py new file mode 100644 index 0000000000..7af5157022 --- /dev/null +++ b/ansibleworks/main/migrations/0004_v12b2_changes.py @@ -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'] \ No newline at end of file diff --git a/ansibleworks/main/models/__init__.py b/ansibleworks/main/models/__init__.py index 80f2117285..46637aa533 100644 --- a/ansibleworks/main/models/__init__.py +++ b/ansibleworks/main/models/__init__.py @@ -1,6 +1,7 @@ # Copyright (c) 2013 AnsibleWorks, Inc. # All Rights Reserved. +import json import os import shlex from django.conf import settings @@ -13,6 +14,7 @@ from django.core.urlresolvers import reverse from django.contrib.auth.models import User from django.utils.timezone import now from jsonfield import JSONField +from taggit.managers import TaggableManager from djcelery.models import TaskMeta from rest_framework.authtoken.models import Token import yaml @@ -108,10 +110,10 @@ class PrimordialModel(models.Model): 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 = 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) + tags = TaggableManager(blank=True) + def __unicode__(self): return unicode("%s-%s"% (self.name, self.id)) @@ -131,39 +133,6 @@ class CommonModelNameNotUnique(PrimordialModel): 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): ''' organizations are the basic unit of multi-tenancy divisions @@ -206,7 +175,7 @@ class Host(CommonModelNameNotUnique): app_label = 'main' 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') 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+') @@ -217,13 +186,37 @@ class Host(CommonModelNameNotUnique): def get_absolute_url(self): 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_events.all() to get events affecting this host. # 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): ''' - 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: @@ -231,8 +224,9 @@ class Group(CommonModelNameNotUnique): unique_together = (("name", "inventory"),) 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) - 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) def __unicode__(self): @@ -242,12 +236,60 @@ class Group(CommonModelNameNotUnique): return reverse('main:groups_detail', args=(self.pk,)) @property - def all_hosts(self): - qs = self.hosts.distinct() - for group in self.children.exclude(pk=self.pk): - qs = qs | group.all_hosts + def variables_dict(self): + # FIXME: Add YAML support. + return json.loads(self.variables or '{}') + + 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 + @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 def job_host_summaries(self): return JobHostSummary.objects.filter(host__in=self.all_hosts) @@ -256,27 +298,9 @@ class Group(CommonModelNameNotUnique): def job_events(self): return JobEvent.objects.filter(host__in=self.all_hosts) -# FIXME: audit nullables -# FIXME: audit cascades - -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,)) + @property + def has_active_failures(self): + return bool(self.all_hosts.filter(last_job_host_summary__failed=True).count()) class Credential(CommonModelNameNotUnique): ''' @@ -537,6 +561,16 @@ class JobTemplate(CommonModel): blank=True, 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): ''' @@ -555,6 +589,7 @@ class JobTemplate(CommonModel): kwargs.setdefault('limit', self.limit) kwargs.setdefault('verbosity', self.verbosity) kwargs.setdefault('extra_vars', self.extra_vars) + kwargs.setdefault('job_tags', self.job_tags) job = Job(**kwargs) if save_job: job.save() @@ -633,6 +668,11 @@ class Job(CommonModel): blank=True, default='', ) + job_tags = models.CharField( + max_length=1024, + blank=True, + default='', + ) cancel_flag = models.BooleanField( blank=True, default=False, @@ -647,6 +687,23 @@ class Job(CommonModel): default=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( blank=True, default='', @@ -797,6 +854,7 @@ class JobHostSummary(models.Model): ok = models.PositiveIntegerField(default=0) processed = models.PositiveIntegerField(default=0) skipped = models.PositiveIntegerField(default=0) + failed = models.BooleanField(default=False) def __unicode__(self): 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,)) def save(self, *args, **kwargs): + self.failed = bool(self.dark or self.failures) super(JobHostSummary, self).save(*args, **kwargs) 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. ''' - EVENT_TYPES = [ - ('runner_on_failed', _('Runner on Failed')), - ('runner_on_ok', _('Runner on OK')), - ('runner_on_error', _('Runner on Error')), - ('runner_on_skipped', _('Runner on Skipped')), - ('runner_on_unreachable', _('Runner on Unreachable')), - ('runner_on_no_hosts', _('Runner on No Hosts')), - ('runner_on_async_poll', _('Runner on Async Poll')), - ('runner_on_async_ok', _('Runner on Async OK')), - ('runner_on_async_failed', _('Runner on Async Failed')), - ('playbook_on_start', _('Playbook on Start')), - ('playbook_on_notify', _('Playbook on Notify')), - ('playbook_on_task_start', _('Playbook on Task Start')), - ('playbook_on_vars_prompt', _('Playbook on Vars Prompt')), - ('playbook_on_setup', _('Playbook on Setup')), - ('playbook_on_import_for_host', _('Playbook on Import for Host')), - ('playbook_on_not_import_for_host', _('Playbook on Not Import for Host')), - ('playbook_on_play_start', _('Playbook on Play Start')), - ('playbook_on_stats', _('Playbook on Stats')), - ] + # Playbook events will be structured to form the following hierarchy: + # - playbook_on_start (once for each playbook file) + # - playbook_on_vars_prompt (for each play, but before play starts, we + # currently don't handle responding to these prompts) + # - playbook_on_play_start + # - playbook_on_import_for_host + # - playbook_on_not_import_for_host + # - playbook_on_no_hosts_matched + # - playbook_on_no_hosts_remaining + # - playbook_on_setup + # - runner_on* + # - playbook_on_task_start + # - runner_on_failed + # - runner_on_ok + # - runner_on_error + # - runner_on_skipped + # - runner_on_unreachable + # - runner_on_no_hosts + # - runner_on_async_poll + # - runner_on_async_ok + # - runner_on_async_failed + # - runner_on_file_diff + # - playbook_on_notify + # - playbook_on_stats - FAILED_EVENTS = [ - 'runner_on_failed', - 'runner_on_error', - 'runner_on_unreachable', - 'runner_on_async_failed', + EVENT_TYPES = [ + # (level, event, verbose name, failed) + (3, 'runner_on_failed', _('Runner on Failed'), True), + (3, 'runner_on_ok', _('Runner on OK'), False), + (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: app_label = 'main' @@ -868,7 +952,7 @@ class JobEvent(models.Model): ) event = models.CharField( max_length=100, - choices=EVENT_TYPES, + choices=EVENT_CHOICES, ) event_data = JSONField( blank=True, @@ -878,9 +962,32 @@ class JobEvent(models.Model): default=False, ) 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', related_name='job_events', 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, default=None, on_delete=models.SET_NULL, @@ -892,25 +999,72 @@ class JobEvent(models.Model): def __unicode__(self): 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): + self.failed = bool(self.event in self.FAILED_EVENTS) try: if not self.host and self.event_data.get('host', ''): self.host = self.job.inventory.hosts.get(name=self.event_data['host']) except (Host.DoesNotExist, AttributeError): 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) + self.update_hosts() self.update_host_summary_from_stats() - self.update_host_last_job() - def update_host_last_job(self): - if self.host: - update_fields = [] - if self.host.last_job != self.job: - self.host.last_job = self.job - update_fields.append('last_job') - if update_fields: - self.host.save(update_fields=update_fields) + def update_hosts(self, extra_hosts=None): + extra_hosts = extra_hosts or [] + hostnames = set() + if self.event_data.get('host', ''): + hostnames.add(self.event_data['host']) + if self.event == 'playbook_on_stats': + try: + 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): if self.event != 'playbook_on_stats': diff --git a/ansibleworks/main/rbac.py b/ansibleworks/main/rbac.py index 90f20a55e1..d9b54f2246 100644 --- a/ansibleworks/main/rbac.py +++ b/ansibleworks/main/rbac.py @@ -46,8 +46,12 @@ class CustomRbac(permissions.BasePermission): if not obj: return True # FIXME: For some reason this needs to return True # because it is first called with obj=None? - return check_user_access(request.user, view.model, 'change', obj, - request.DATA) + if getattr(view, 'is_variable_data', False): + 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): if not obj: diff --git a/ansibleworks/main/serializers.py b/ansibleworks/main/serializers.py index 9a592df2a9..1f1c5ae50c 100644 --- a/ansibleworks/main/serializers.py +++ b/ansibleworks/main/serializers.py @@ -1,6 +1,9 @@ # Copyright (c) 2013 AnsibleWorks, Inc. # All Rights Reserved. +# Python +import json + # Django from django.contrib.auth.models import User from django.core.urlresolvers import reverse @@ -126,28 +129,16 @@ class OrganizationSerializer(BaseSerializer): def get_related(self, obj): res = super(OrganizationSerializer, self).get_related(obj) 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,)), inventories = reverse('main:organizations_inventories_list', args=(obj.pk,)), users = reverse('main:organizations_users_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,)), )) 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): playbooks = serializers.Field(source='playbooks') @@ -197,9 +188,11 @@ class InventorySerializer(BaseSerializer): class HostSerializer(BaseSerializer): + has_active_failures = serializers.Field(source='has_active_failures') + class Meta: model = Host - fields = BASE_FIELDS + ('inventory',) + fields = BASE_FIELDS + ('inventory', 'variables', 'has_active_failures') def get_related(self, obj): res = super(HostSerializer, self).get_related(obj) @@ -218,9 +211,11 @@ class HostSerializer(BaseSerializer): class GroupSerializer(BaseSerializer): + has_active_failures = serializers.Field(source='has_active_failures') + class Meta: model = Group - fields = BASE_FIELDS + ('inventory',) + fields = BASE_FIELDS + ('inventory', 'variables', 'has_active_failures') def get_related(self, obj): res = super(GroupSerializer, self).get_related(obj) @@ -235,6 +230,28 @@ class GroupSerializer(BaseSerializer): )) 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 Meta: @@ -319,42 +336,13 @@ class UserSerializer(BaseSerializer): )) 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 Meta: model = JobTemplate fields = BASE_FIELDS + ('job_type', 'inventory', 'project', 'playbook', 'credential', 'forks', 'limit', 'verbosity', - 'extra_vars') + 'extra_vars', 'job_tags') def get_related(self, obj): res = super(JobTemplateSerializer, self).get_related(obj) @@ -383,7 +371,7 @@ class JobSerializer(BaseSerializer): fields = BASE_FIELDS + ('job_template', 'job_type', 'inventory', 'project', 'playbook', 'credential', 'forks', 'limit', 'verbosity', 'extra_vars', - 'status', 'failed', 'result_stdout', + 'job_tags', 'status', 'failed', 'result_stdout', 'result_traceback', 'passwords_needed_to_start') @@ -424,6 +412,7 @@ class JobSerializer(BaseSerializer): data.setdefault('limit', job_template.limit) data.setdefault('verbosity', job_template.verbosity) data.setdefault('extra_vars', job_template.extra_vars) + data.setdefault('job_tags', job_template.job_tags) return super(JobSerializer, self).from_native(data, files) class JobHostSummarySerializer(BaseSerializer): @@ -431,7 +420,8 @@ class JobHostSummarySerializer(BaseSerializer): class Meta: model = JobHostSummary 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): res = super(JobHostSummarySerializer, self).get_related(obj) diff --git a/ansibleworks/main/tasks.py b/ansibleworks/main/tasks.py index 9f50871c54..5e9841c09a 100644 --- a/ansibleworks/main/tasks.py +++ b/ansibleworks/main/tasks.py @@ -124,6 +124,8 @@ class RunJob(Task): args.append('-%s' % ('v' * min(3, job.verbosity))) if 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 ssh_key_path = kwargs.get('ssh_key_path', '') if ssh_key_path: @@ -192,6 +194,8 @@ class RunJob(Task): raise RuntimeError('project local_path %s cannot be found' % project.local_path) 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, kwargs['passwords']) except Exception: diff --git a/ansibleworks/main/tests/commands.py b/ansibleworks/main/tests/commands.py index 5016cbc7ad..6f65f5d83c 100644 --- a/ansibleworks/main/tests/commands.py +++ b/ansibleworks/main/tests/commands.py @@ -111,23 +111,23 @@ class AcomInventoryTest(BaseCommandTest): hosts = [] for x in xrange(10): if n > 0: - variable_data = VariableData.objects.create(data=json.dumps({'ho': 'hum-%d' % x})) + variables = json.dumps({'ho': 'hum-%d' % x}) else: - variable_data = None + variables = '' host = inventory.hosts.create(name='host-%02d-%02d.example.com' % (n, x), inventory=inventory, - variable_data=variable_data) + variables=variables) hosts.append(host) self.hosts.extend(hosts) groups = [] for x in xrange(5): if n > 0: - variable_data = VariableData.objects.create(data=json.dumps({'gee': 'whiz-%d' % x})) + variables = json.dumps({'gee': 'whiz-%d' % x}) else: - variable_data = None + variables = '' group = inventory.groups.create(name='group-%d' % x, inventory=inventory, - variable_data=variable_data) + variables=variables) groups.append(group) group.hosts.add(hosts[x]) group.hosts.add(hosts[x + 5]) @@ -184,9 +184,9 @@ class AcomInventoryTest(BaseCommandTest): group = inventory.groups.get(name=k) self.assertEqual(set(v.get('hosts', [])), set(group.hosts.values_list('name', flat=True))) - if group.variable_data: + if group.variables: self.assertEqual(v.get('vars', {}), - json.loads(group.variable_data.data)) + json.loads(group.variables)) if k == 'group-3': self.assertEqual(set(v.get('children', [])), set(group.children.values_list('name', flat=True))) @@ -211,7 +211,7 @@ class AcomInventoryTest(BaseCommandTest): host=host.name) self.assertEqual(result, None) 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): # Valid host, but not part of the specified inventory. diff --git a/ansibleworks/main/tests/inventory.py b/ansibleworks/main/tests/inventory.py index 2e9d486669..e3675d2f15 100644 --- a/ansibleworks/main/tests/inventory.py +++ b/ansibleworks/main/tests/inventory.py @@ -250,7 +250,7 @@ class InventoryTest(BaseTest): # 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']) 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 # 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()) # a normal user with inventory write permissions can edit variable objects - vdata_url = "/api/v1/hosts/1/variable_data/" - got = self.put(vdata_url, data=vars_b, expect=200, auth=self.get_normal_credentials()) - self.assertEquals(got, vars_b) + #vdata_url = "/api/v1/hosts/1/variable_data/" + #got = self.put(vdata_url, data=vars_b, expect=200, auth=self.get_normal_credentials()) + #self.assertEquals(got, vars_b) # 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 # be able to put to it. - backend_url = '/api/v1/variable_data/1/' - 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()) + #backend_url = '/api/v1/variable_data/1/' + #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()) ################################################### # VARIABLES -> GROUPS @@ -443,7 +443,96 @@ class InventoryTest(BaseTest): # on a group resource, I can see related resources for variables, inventories, and children # 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])) diff --git a/ansibleworks/main/tests/jobs.py b/ansibleworks/main/tests/jobs.py index 1a9520f9f0..e73710793b 100644 --- a/ansibleworks/main/tests/jobs.py +++ b/ansibleworks/main/tests/jobs.py @@ -779,7 +779,8 @@ class JobStartCancelTest(BaseJobTestMixin, django.test.TransactionTestCase): self.assertFalse(response['passwords_needed_to_start']) response = self.post(url, {}, expect=202) job = Job.objects.get(pk=job.pk) - self.assertEqual(job.status, 'successful') + self.assertEqual(job.status, 'successful', + job.result_stdout) else: self.assertFalse(response['can_start']) response = self.post(url, {}, expect=405) diff --git a/ansibleworks/main/tests/organizations.py b/ansibleworks/main/tests/organizations.py index 3c661f0251..4e891f5c3c 100644 --- a/ansibleworks/main/tests/organizations.py +++ b/ansibleworks/main/tests/organizations.py @@ -79,7 +79,7 @@ class OrganizationsTest(BaseTest): # check that the related URL functionality works 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) # 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()) 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 org1 = Organization.objects.get(pk=2) @@ -181,7 +182,8 @@ class OrganizationsTest(BaseTest): self.assertEquals(org1_tags['count'], 2) 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/' self.get(url, expect=200, auth=self.get_normal_credentials()) # 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()) 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') url = '/api/v1/organizations/2/tags/' @@ -305,7 +308,8 @@ class OrganizationsTest(BaseTest): tags = self.get(url, expect=200, auth=self.get_normal_credentials()) 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. url = '/api/v1/organizations/2/audit_trail/' self.post(url, dict(id=1), expect=405, auth=self.get_super_credentials()) diff --git a/ansibleworks/main/urls.py b/ansibleworks/main/urls.py index 73da7f1ac2..7a01659775 100644 --- a/ansibleworks/main/urls.py +++ b/ansibleworks/main/urls.py @@ -13,12 +13,10 @@ def url(regex, view, kwargs=None, name=None, prefix=''): organizations_urls = patterns('ansibleworks.main.views', url(r'^$', 'organizations_list'), url(r'^(?P[0-9]+)/$', 'organizations_detail'), - url(r'^(?P[0-9]+)/audit_trail/$', 'organizations_audit_trail_list'), url(r'^(?P[0-9]+)/users/$', 'organizations_users_list'), url(r'^(?P[0-9]+)/admins/$', 'organizations_admins_list'), url(r'^(?P[0-9]+)/inventories/$', 'organizations_inventories_list'), url(r'^(?P[0-9]+)/projects/$', 'organizations_projects_list'), - url(r'^(?P[0-9]+)/tags/$', 'organizations_tags_list'), url(r'^(?P[0-9]+)/teams/$', 'organizations_teams_list'), ) @@ -40,12 +38,6 @@ projects_urls = patterns('ansibleworks.main.views', url(r'^(?P[0-9]+)/organizations/$', 'projects_organizations_list'), ) -audit_trails_urls = patterns('ansibleworks.main.views', - #url(r'^$', 'audit_trails_list'), - #url(r'^(?P[0-9]+)/$', 'audit_trails_detail'), - # ... and ./audit_trails/ on all resources -) - teams_urls = patterns('ansibleworks.main.views', url(r'^$', 'teams_list'), url(r'^(?P[0-9]+)/$', 'teams_detail'), @@ -82,11 +74,6 @@ groups_urls = patterns('ansibleworks.main.views', url(r'^(?P[0-9]+)/job_host_summaries/$', 'group_job_host_summary_list'), ) -variable_data_urls = patterns('ansibleworks.main.views', - url(r'^(?P[0-9]+)/$', 'variable_detail'), - # See also variable_data resources on hosts/groups. -) - credentials_urls = patterns('ansibleworks.main.views', url(r'^$', 'credentials_list'), url(r'^(?P[0-9]+)/$', 'credentials_detail'), @@ -125,11 +112,6 @@ job_events_urls = patterns('ansibleworks.main.views', url(r'^(?P[0-9]+)/$', 'job_event_detail'), ) -tags_urls = patterns('ansibleworks.main.views', - url(r'^(?P[0-9]+)/$', 'tags_detail'), - # ... and tag relations on all resources -) - v1_urls = patterns('ansibleworks.main.views', url(r'^$', 'api_v1_root_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'^users/', include(users_urls)), url(r'^projects/', include(projects_urls)), - url(r'^audit_trails/', include(audit_trails_urls)), url(r'^teams/', include(teams_urls)), url(r'^inventories/', include(inventory_urls)), url(r'^hosts/', include(hosts_urls)), url(r'^groups/', include(groups_urls)), - url(r'^variable_data/', include(variable_data_urls)), url(r'^credentials/', include(credentials_urls)), url(r'^permissions/', include(permissions_urls)), url(r'^job_templates/', include(job_templates_urls)), url(r'^jobs/', include(jobs_urls)), url(r'^job_host_summaries/', include(job_host_summary_urls)), url(r'^job_events/', include(job_events_urls)), - url(r'^tags/', include(tags_urls)), ) urlpatterns = patterns('ansibleworks.main.views', diff --git a/ansibleworks/main/views.py b/ansibleworks/main/views.py index d4fb41a499..2498200957 100644 --- a/ansibleworks/main/views.py +++ b/ansibleworks/main/views.py @@ -133,23 +133,6 @@ class OrganizationsDetail(BaseDetail): serializer_class = OrganizationSerializer 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): model = Inventory @@ -221,25 +204,6 @@ class OrganizationsProjectsList(BaseSubList): raise PermissionDenied() 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): model = Team @@ -433,12 +397,6 @@ class ProjectsOrganizationsList(BaseSubList): raise PermissionDenied() return Organization.objects.filter(projects__in = [ project ]) -class TagsDetail(BaseDetail): - - model = Tag - serializer_class = TagSerializer - permission_classes = (CustomRbac,) - class UsersList(BaseList): model = User @@ -889,32 +847,19 @@ class InventoryRootGroupsList(BaseSubList): all_ids = base.values_list('id', flat=True) return base.exclude(parents__pk__in = all_ids) -class GroupsVariableDetail(VariableBaseDetail): +class HostsVariableDetail(BaseDetail): - model = VariableData - serializer_class = VariableDataSerializer + model = Host + serializer_class = HostVariableDataSerializer permission_classes = (CustomRbac,) - parent_model = Group - reverse_relationship = 'variable_data' - relationship = 'group' + is_variable_data = True # Special flag for RBAC -class HostsVariableDetail(VariableBaseDetail): +class GroupsVariableDetail(BaseDetail): - model = VariableData - serializer_class = VariableDataSerializer + model = Group + serializer_class = GroupVariableDataSerializer permission_classes = (CustomRbac,) - parent_model = Host - 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() + is_variable_data = True # Special flag for RBAC class JobTemplateList(BaseList): diff --git a/ansibleworks/plugins/callback/acom_callback.py b/ansibleworks/plugins/callback/acom_callback.py index cea57ccee1..30e880ce8f 100644 --- a/ansibleworks/plugins/callback/acom_callback.py +++ b/ansibleworks/plugins/callback/acom_callback.py @@ -39,10 +39,31 @@ class CallbackModule(object): 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): self.callback_script = os.getenv('ACOM_CALLBACK_EVENT_SCRIPT') 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) cmdline = [self.callback_script, '-e', event, '-d', event_data_json] subprocess.check_call(cmdline) @@ -79,11 +100,20 @@ class CallbackModule(object): def runner_on_async_failed(self, host, res, 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): self._log_event('playbook_on_start') 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): self._log_event('playbook_on_task_start', name=name, diff --git a/ansibleworks/settings/defaults.py b/ansibleworks/settings/defaults.py index 0f10a011c7..a19c2569b1 100644 --- a/ansibleworks/settings/defaults.py +++ b/ansibleworks/settings/defaults.py @@ -29,9 +29,9 @@ REST_FRAMEWORK = { 'PAGINATE_BY': 25, 'PAGINATE_BY_PARAM': 'page_size', 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', - 'rest_framework.authentication.TokenAuthentication', ) } @@ -137,6 +137,7 @@ INSTALLED_APPS = ( 'django_extensions', 'djcelery', 'kombu.transport.django', + 'taggit', 'ansibleworks.main', 'ansibleworks.ui', ) diff --git a/requirements.txt b/requirements.txt index c4da567330..9e86883e7b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ django-devserver django-extensions django-filter django-jsonfield +django-taggit djangorestframework ipython markdown diff --git a/setup.py b/setup.py index cc9e49448c..8e0b494b93 100755 --- a/setup.py +++ b/setup.py @@ -26,10 +26,10 @@ setup( install_requires=[ 'Django>=1.5', 'django-celery', - 'django-devserver', 'django-extensions', 'django-filter', 'django-jsonfield', + 'django-taggit', 'djangorestframework', 'pexpect', 'python-dateutil', @@ -40,10 +40,10 @@ setup( #tests_require=[ # 'Django>=1.5', # 'django-celery', - # 'django-devserver', # 'django-extensions', # 'django-filter', # 'django-jsonfield', + # 'django-taggit', # 'django-setuptest', # 'djangorestframework', # 'pexpect',