mirror of
https://github.com/ansible/awx.git
synced 2026-02-15 10:10:01 -03:30
Changed project property available_playbooks to playbooks, exposed via API as a child resource of the project detail. Changed project local_path to a CharField and added helper method and API attribute to show the list of available choices for local_path.
This commit is contained in:
@@ -217,19 +217,20 @@ class ProjectAdmin(BaseModelAdmin):
|
|||||||
list_display = ('name', 'description', 'active')
|
list_display = ('name', 'description', 'active')
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {'fields': (('name', 'active'), 'description', 'local_path',
|
(None, {'fields': (('name', 'active'), 'description', 'local_path',
|
||||||
'get_available_playbooks_display')}),
|
'get_playbooks_display')}),
|
||||||
(_('Tags'), {'fields': ('tags',)}),
|
(_('Tags'), {'fields': ('tags',)}),
|
||||||
(_('Audit Trail'), {'fields': ('created', 'created_by', 'audit_trail',)}),
|
(_('Audit Trail'), {'fields': ('created', 'created_by', 'audit_trail',)}),
|
||||||
)
|
)
|
||||||
readonly_fields = ('created', 'created_by', 'audit_trail',
|
readonly_fields = ('created', 'created_by', 'audit_trail',
|
||||||
'get_available_playbooks_display')
|
'get_playbooks_display')
|
||||||
filter_horizontal = ('tags',)
|
filter_horizontal = ('tags',)
|
||||||
|
form = ProjectAdminForm
|
||||||
|
|
||||||
def get_available_playbooks_display(self, obj):
|
def get_playbooks_display(self, obj):
|
||||||
return '<br/>'.join([format_html('{0}', x) for x in
|
return '<br/>'.join([format_html('{0}', x) for x in
|
||||||
obj.available_playbooks])
|
obj.playbooks])
|
||||||
get_available_playbooks_display.short_description = _('Available playbooks')
|
get_playbooks_display.short_description = _('Playbooks')
|
||||||
get_available_playbooks_display.allow_tags = True
|
get_playbooks_display.allow_tags = True
|
||||||
|
|
||||||
class PermissionAdmin(BaseModelAdmin):
|
class PermissionAdmin(BaseModelAdmin):
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,18 @@ class GroupForm(forms.ModelForm):
|
|||||||
|
|
||||||
variable_data = JSONFormField(required=False, widget=forms.Textarea(attrs={'class': 'vLargeTextField'}))
|
variable_data = JSONFormField(required=False, widget=forms.Textarea(attrs={'class': 'vLargeTextField'}))
|
||||||
|
|
||||||
|
class ProjectAdminForm(forms.ModelForm):
|
||||||
|
'''Custom admin form for Projects.'''
|
||||||
|
|
||||||
|
local_path = forms.ChoiceField(choices=[])
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Project
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(ProjectAdminForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['local_path'].choices = [(x, x) for x in Project.get_local_path_choices()]
|
||||||
|
|
||||||
class JobTemplateAdminForm(forms.ModelForm):
|
class JobTemplateAdminForm(forms.ModelForm):
|
||||||
'''Custom admin form for creating/editing JobTemplates.'''
|
'''Custom admin form for creating/editing JobTemplates.'''
|
||||||
|
|
||||||
@@ -80,7 +92,7 @@ class JobTemplateAdminForm(forms.ModelForm):
|
|||||||
super(JobTemplateAdminForm, self).__init__(*args, **kwargs)
|
super(JobTemplateAdminForm, self).__init__(*args, **kwargs)
|
||||||
playbook_choices = []
|
playbook_choices = []
|
||||||
for project in Project.objects.all():
|
for project in Project.objects.all():
|
||||||
for playbook in project.available_playbooks:
|
for playbook in project.playbooks:
|
||||||
playbook_choices.append((playbook,
|
playbook_choices.append((playbook,
|
||||||
PlaybookOption(project, playbook)))
|
PlaybookOption(project, playbook)))
|
||||||
self.fields['playbook'].choices = [EMPTY_CHOICE] + playbook_choices
|
self.fields['playbook'].choices = [EMPTY_CHOICE] + playbook_choices
|
||||||
|
|||||||
294
lib/main/migrations/0017_changes.py
Normal file
294
lib/main/migrations/0017_changes.py
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import datetime
|
||||||
|
from south.db import db
|
||||||
|
from south.v2 import SchemaMigration
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(SchemaMigration):
|
||||||
|
|
||||||
|
def forwards(self, orm):
|
||||||
|
# Removing unique constraint on 'Project', fields ['local_path']
|
||||||
|
db.delete_unique(u'main_project', ['local_path'])
|
||||||
|
|
||||||
|
|
||||||
|
# Changing field 'Project.local_path'
|
||||||
|
db.alter_column(u'main_project', 'local_path', self.gf('django.db.models.fields.CharField')(max_length=1024))
|
||||||
|
|
||||||
|
# Change absolute paths to only the directory name instead.
|
||||||
|
if not db.dry_run:
|
||||||
|
import os
|
||||||
|
for project in orm.Project.objects.all():
|
||||||
|
project.local_path = os.path.basename(project.local_path)
|
||||||
|
project.save()
|
||||||
|
|
||||||
|
def backwards(self, orm):
|
||||||
|
|
||||||
|
# Change directory names back to aboslute paths.
|
||||||
|
if not db.dry_run:
|
||||||
|
import os
|
||||||
|
from django.conf import settings
|
||||||
|
for project in orm.Project.objects.all():
|
||||||
|
project.local_path = os.path.join(settings.PROJECTS_ROOT, os.path.basename(project.local_path))
|
||||||
|
project.save()
|
||||||
|
|
||||||
|
# Changing field 'Project.local_path'
|
||||||
|
db.alter_column(u'main_project', 'local_path', self.gf('django.db.models.fields.FilePathField')(max_length=1024, path='/Users/chris/Sandbox/ansible-commander/lib/projects', unique=True))
|
||||||
|
# Adding unique constraint on 'Project', fields ['local_path']
|
||||||
|
db.create_unique(u'main_project', ['local_path'])
|
||||||
|
|
||||||
|
|
||||||
|
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.audittrail': {
|
||||||
|
'Meta': {'object_name': 'AuditTrail'},
|
||||||
|
'comment': ('django.db.models.fields.TextField', [], {}),
|
||||||
|
'delta': ('django.db.models.fields.TextField', [], {}),
|
||||||
|
'detail': ('django.db.models.fields.TextField', [], {}),
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
|
||||||
|
'resource_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
|
||||||
|
'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Tag']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'})
|
||||||
|
},
|
||||||
|
'main.credential': {
|
||||||
|
'Meta': {'object_name': 'Credential'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'credential_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'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'}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'credential_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"}),
|
||||||
|
'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'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'group_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'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']"}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'group_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"}),
|
||||||
|
'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'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'host_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'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'}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'host_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"}),
|
||||||
|
'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'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'inventory_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'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']"}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'inventory_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"})
|
||||||
|
},
|
||||||
|
'main.job': {
|
||||||
|
'Meta': {'object_name': 'Job'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'job_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'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_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'}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'job_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"}),
|
||||||
|
'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'}),
|
||||||
|
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']"})
|
||||||
|
},
|
||||||
|
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'}),
|
||||||
|
'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'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'jobtemplate_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'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'}),
|
||||||
|
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_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']"}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'jobtemplate_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"}),
|
||||||
|
'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']"}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organization_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'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']"}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organization_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"}),
|
||||||
|
'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'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'permission_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'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']"}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'permission_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"}),
|
||||||
|
'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'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'project_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'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'}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'project_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"})
|
||||||
|
},
|
||||||
|
'main.tag': {
|
||||||
|
'Meta': {'object_name': 'Tag'},
|
||||||
|
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||||
|
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
|
||||||
|
},
|
||||||
|
'main.team': {
|
||||||
|
'Meta': {'object_name': 'Team'},
|
||||||
|
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'team_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'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']"}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'team_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"}),
|
||||||
|
'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'}),
|
||||||
|
'audit_trail': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'variabledata_by_audit_trail'", 'blank': 'True', 'to': "orm['main.AuditTrail']"}),
|
||||||
|
'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'}),
|
||||||
|
'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'variabledata_by_tag'", 'blank': 'True', 'to': "orm['main.Tag']"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
complete_apps = ['main']
|
||||||
@@ -370,13 +370,25 @@ class Project(CommonModel):
|
|||||||
# this is not part of the project, but managed with perms
|
# this is not part of the project, but managed with perms
|
||||||
# inventories = models.ManyToManyField('Inventory', blank=True, related_name='projects')
|
# inventories = models.ManyToManyField('Inventory', blank=True, related_name='projects')
|
||||||
|
|
||||||
local_path = models.FilePathField(
|
# Project files must be available on the server in folders directly
|
||||||
path=settings.PROJECTS_ROOT,
|
# beneath the path specified by settings.PROJECTS_ROOT. There is no way
|
||||||
recursive=False,
|
# via the API to upload/update a project or its playbooks; this must be
|
||||||
allow_files=False,
|
# done by other means for now.
|
||||||
allow_folders=True,
|
|
||||||
|
@classmethod
|
||||||
|
def get_local_path_choices(cls):
|
||||||
|
if os.path.exists(settings.PROJECTS_ROOT):
|
||||||
|
return [x for x in os.listdir(settings.PROJECTS_ROOT)
|
||||||
|
if os.path.isdir(os.path.join(settings.PROJECTS_ROOT, x))
|
||||||
|
and not x.startswith('.')]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
local_path = models.CharField(
|
||||||
max_length=1024,
|
max_length=1024,
|
||||||
unique=True,
|
# Not unique for now, otherwise "deletes" won't allow reusing the
|
||||||
|
# same path for another active project.
|
||||||
|
#unique=True,
|
||||||
help_text=_('Local path (relative to PROJECTS_ROOT) containing '
|
help_text=_('Local path (relative to PROJECTS_ROOT) containing '
|
||||||
'playbooks and related files for this project.')
|
'playbooks and related files for this project.')
|
||||||
)
|
)
|
||||||
@@ -386,11 +398,19 @@ class Project(CommonModel):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('main:projects_detail', args=(self.pk,))
|
return reverse('main:projects_detail', args=(self.pk,))
|
||||||
|
|
||||||
|
def get_project_path(self):
|
||||||
|
local_path = os.path.basename(self.local_path)
|
||||||
|
if local_path and not local_path.startswith('.'):
|
||||||
|
proj_path = os.path.join(settings.PROJECTS_ROOT, local_path)
|
||||||
|
if os.path.exists(proj_path):
|
||||||
|
return proj_path
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available_playbooks(self):
|
def playbooks(self):
|
||||||
playbooks = []
|
results = []
|
||||||
if self.local_path and os.path.exists(self.local_path):
|
project_path = self.get_project_path()
|
||||||
for dirpath, dirnames, filenames in os.walk(self.local_path):
|
if project_path:
|
||||||
|
for dirpath, dirnames, filenames in os.walk(project_path):
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
if os.path.splitext(filename)[-1] != '.yml':
|
if os.path.splitext(filename)[-1] != '.yml':
|
||||||
continue
|
continue
|
||||||
@@ -407,15 +427,15 @@ class Project(CommonModel):
|
|||||||
continue
|
continue
|
||||||
except (TypeError, IndexError, KeyError):
|
except (TypeError, IndexError, KeyError):
|
||||||
continue
|
continue
|
||||||
playbook = os.path.relpath(playbook, self.local_path)
|
playbook = os.path.relpath(playbook, project_path)
|
||||||
# Filter files in a roles subdirectory.
|
# Filter files in a roles subdirectory.
|
||||||
if 'roles' in playbook.split(os.sep):
|
if 'roles' in playbook.split(os.sep):
|
||||||
continue
|
continue
|
||||||
# Filter files in a tasks subdirectory.
|
# Filter files in a tasks subdirectory.
|
||||||
if 'tasks' in playbook.split(os.sep):
|
if 'tasks' in playbook.split(os.sep):
|
||||||
continue
|
continue
|
||||||
playbooks.append(playbook)
|
results.append(playbook)
|
||||||
return playbooks
|
return results
|
||||||
|
|
||||||
class Permission(CommonModelNameNotUnique):
|
class Permission(CommonModelNameNotUnique):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -163,20 +163,35 @@ class AuditTrailSerializer(BaseSerializer):
|
|||||||
|
|
||||||
class ProjectSerializer(BaseSerializer):
|
class ProjectSerializer(BaseSerializer):
|
||||||
|
|
||||||
available_playbooks = serializers.Field(source='available_playbooks')
|
playbooks = serializers.Field(source='playbooks')
|
||||||
|
local_path_choices = serializers.SerializerMethodField('get_local_path_choices')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Project
|
model = Project
|
||||||
fields = BASE_FIELDS + ('local_path', 'available_playbooks')
|
fields = BASE_FIELDS + ('local_path', 'local_path_choices')
|
||||||
# 'default_playbook', 'scm_type')
|
# 'default_playbook', 'scm_type')
|
||||||
|
|
||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
res = super(ProjectSerializer, self).get_related(obj)
|
res = super(ProjectSerializer, self).get_related(obj)
|
||||||
res.update(dict(
|
res.update(dict(
|
||||||
organizations = reverse('main:projects_organizations_list', args=(obj.pk,)),
|
organizations = reverse('main:projects_organizations_list', args=(obj.pk,)),
|
||||||
|
playbooks = reverse('main:projects_detail_playbooks', args=(obj.pk,)),
|
||||||
))
|
))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def get_local_path_choices(self, obj):
|
||||||
|
return Project.get_local_path_choices()
|
||||||
|
|
||||||
|
class ProjectPlaybooksSerializer(ProjectSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Project
|
||||||
|
fields = ('playbooks',)
|
||||||
|
|
||||||
|
def to_native(self, obj):
|
||||||
|
ret = super(ProjectPlaybooksSerializer, self).to_native(obj)
|
||||||
|
return ret.get('playbooks', [])
|
||||||
|
|
||||||
class InventorySerializer(BaseSerializer):
|
class InventorySerializer(BaseSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
@@ -201,7 +201,10 @@ class RunJob(Task):
|
|||||||
kwargs['ssh_key_path'] = self.build_ssh_key_path(job, **kwargs)
|
kwargs['ssh_key_path'] = self.build_ssh_key_path(job, **kwargs)
|
||||||
kwargs['passwords'] = self.build_passwords(job, **kwargs)
|
kwargs['passwords'] = self.build_passwords(job, **kwargs)
|
||||||
args = self.build_args(job, **kwargs)
|
args = self.build_args(job, **kwargs)
|
||||||
cwd = job.project.local_path
|
cwd = job.project.get_project_path()
|
||||||
|
if not cwd:
|
||||||
|
raise RuntimeError('project local_path %s cannot be found' %
|
||||||
|
project.local_path)
|
||||||
env = self.build_env(job, **kwargs)
|
env = self.build_env(job, **kwargs)
|
||||||
status, stdout = self.run_pexpect(job_pk, args, cwd, env,
|
status, stdout = self.run_pexpect(job_pk, args, cwd, env,
|
||||||
kwargs['passwords'])
|
kwargs['passwords'])
|
||||||
|
|||||||
@@ -95,8 +95,8 @@ class BaseTestMixin(object):
|
|||||||
test_playbook_file.write(playbook_content)
|
test_playbook_file.write(playbook_content)
|
||||||
test_playbook_file.close()
|
test_playbook_file.close()
|
||||||
return Project.objects.create(
|
return Project.objects.create(
|
||||||
name=name, description=description, local_path=project_dir,
|
name=name, description=description,
|
||||||
created_by=created_by,
|
local_path=os.path.basename(project_dir), created_by=created_by,
|
||||||
#scm_type='git', default_playbook='foo.yml',
|
#scm_type='git', default_playbook='foo.yml',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ class BaseJobTest(BaseTest):
|
|||||||
job_type='check',
|
job_type='check',
|
||||||
inventory= self.inv_eng,
|
inventory= self.inv_eng,
|
||||||
project=self.proj_dev,
|
project=self.proj_dev,
|
||||||
playbook=self.proj_dev.available_playbooks[0],
|
playbook=self.proj_dev.playbooks[0],
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
self.jt_eng_run = JobTemplate.objects.create(
|
self.jt_eng_run = JobTemplate.objects.create(
|
||||||
@@ -323,7 +323,7 @@ class BaseJobTest(BaseTest):
|
|||||||
job_type='run',
|
job_type='run',
|
||||||
inventory= self.inv_eng,
|
inventory= self.inv_eng,
|
||||||
project=self.proj_dev,
|
project=self.proj_dev,
|
||||||
playbook=self.proj_dev.available_playbooks[0],
|
playbook=self.proj_dev.playbooks[0],
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -334,7 +334,7 @@ class BaseJobTest(BaseTest):
|
|||||||
job_type='check',
|
job_type='check',
|
||||||
inventory= self.inv_sup,
|
inventory= self.inv_sup,
|
||||||
project=self.proj_test,
|
project=self.proj_test,
|
||||||
playbook=self.proj_test.available_playbooks[0],
|
playbook=self.proj_test.playbooks[0],
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
self.jt_sup_run = JobTemplate.objects.create(
|
self.jt_sup_run = JobTemplate.objects.create(
|
||||||
@@ -342,7 +342,7 @@ class BaseJobTest(BaseTest):
|
|||||||
job_type='run',
|
job_type='run',
|
||||||
inventory= self.inv_sup,
|
inventory= self.inv_sup,
|
||||||
project=self.proj_test,
|
project=self.proj_test,
|
||||||
playbook=self.proj_test.available_playbooks[0],
|
playbook=self.proj_test.playbooks[0],
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -353,7 +353,7 @@ class BaseJobTest(BaseTest):
|
|||||||
job_type='check',
|
job_type='check',
|
||||||
inventory= self.inv_ops_east,
|
inventory= self.inv_ops_east,
|
||||||
project=self.proj_prod,
|
project=self.proj_prod,
|
||||||
playbook=self.proj_prod.available_playbooks[0],
|
playbook=self.proj_prod.playbooks[0],
|
||||||
credential=self.cred_ops_east,
|
credential=self.cred_ops_east,
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
@@ -362,7 +362,7 @@ class BaseJobTest(BaseTest):
|
|||||||
job_type='run',
|
job_type='run',
|
||||||
inventory= self.inv_ops_east,
|
inventory= self.inv_ops_east,
|
||||||
project=self.proj_prod,
|
project=self.proj_prod,
|
||||||
playbook=self.proj_prod.available_playbooks[0],
|
playbook=self.proj_prod.playbooks[0],
|
||||||
credential=self.cred_ops_east,
|
credential=self.cred_ops_east,
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
@@ -371,7 +371,7 @@ class BaseJobTest(BaseTest):
|
|||||||
job_type='check',
|
job_type='check',
|
||||||
inventory= self.inv_ops_west,
|
inventory= self.inv_ops_west,
|
||||||
project=self.proj_prod,
|
project=self.proj_prod,
|
||||||
playbook=self.proj_prod.available_playbooks[0],
|
playbook=self.proj_prod.playbooks[0],
|
||||||
credential=self.cred_ops_west,
|
credential=self.cred_ops_west,
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
@@ -380,7 +380,7 @@ class BaseJobTest(BaseTest):
|
|||||||
job_type='run',
|
job_type='run',
|
||||||
inventory= self.inv_ops_west,
|
inventory= self.inv_ops_west,
|
||||||
project=self.proj_prod,
|
project=self.proj_prod,
|
||||||
playbook=self.proj_prod.available_playbooks[0],
|
playbook=self.proj_prod.playbooks[0],
|
||||||
credential=self.cred_ops_west,
|
credential=self.cred_ops_west,
|
||||||
created_by=self.user_sue,
|
created_by=self.user_sue,
|
||||||
)
|
)
|
||||||
@@ -454,7 +454,7 @@ class JobTemplateTest(BaseJobTest):
|
|||||||
inventory = self.inventory.pk,
|
inventory = self.inventory.pk,
|
||||||
project = self.project.pk,
|
project = self.project.pk,
|
||||||
job_type = PERM_INVENTORY_DEPLOY,
|
job_type = PERM_INVENTORY_DEPLOY,
|
||||||
playbook = self.project.available_playbooks[0],
|
playbook = self.project.playbooks[0],
|
||||||
)
|
)
|
||||||
with self.current_user(self.normal_django_user):
|
with self.current_user(self.normal_django_user):
|
||||||
response = self.post(url, data, expect=201)
|
response = self.post(url, data, expect=201)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class ProjectsTest(BaseTest):
|
|||||||
self.setup_users()
|
self.setup_users()
|
||||||
|
|
||||||
self.organizations = self.make_organizations(self.super_django_user, 10)
|
self.organizations = self.make_organizations(self.super_django_user, 10)
|
||||||
self.projects = self.make_projects(self.normal_django_user, 10)
|
self.projects = self.make_projects(self.normal_django_user, 10, TEST_PLAYBOOK)
|
||||||
|
|
||||||
# add projects to organizations in a more or less arbitrary way
|
# add projects to organizations in a more or less arbitrary way
|
||||||
for project in self.projects[0:2]:
|
for project in self.projects[0:2]:
|
||||||
@@ -100,50 +100,56 @@ class ProjectsTest(BaseTest):
|
|||||||
# here is a user without any permissions...
|
# here is a user without any permissions...
|
||||||
return ('nobody', 'nobody')
|
return ('nobody', 'nobody')
|
||||||
|
|
||||||
def test_available_playbooks(self):
|
def test_playbooks(self):
|
||||||
def write_test_file(project, name, content):
|
def write_test_file(project, name, content):
|
||||||
full_path = os.path.join(project.local_path, name)
|
full_path = os.path.join(project.get_project_path(), name)
|
||||||
if not os.path.exists(os.path.dirname(full_path)):
|
if not os.path.exists(os.path.dirname(full_path)):
|
||||||
os.makedirs(os.path.dirname(full_path))
|
os.makedirs(os.path.dirname(full_path))
|
||||||
f = file(os.path.join(project.local_path, name), 'wb')
|
f = file(full_path, 'wb')
|
||||||
f.write(content)
|
f.write(content)
|
||||||
f.close()
|
f.close()
|
||||||
# Invalid local_path
|
# Invalid local_path
|
||||||
project = self.projects[0]
|
project = self.projects[0]
|
||||||
project.local_path = os.path.join(project.local_path,
|
project.local_path = 'path_does_not_exist'
|
||||||
'does_not_exist')
|
|
||||||
project.save()
|
project.save()
|
||||||
self.assertEqual(len(project.available_playbooks), 0)
|
self.assertFalse(project.get_project_path())
|
||||||
|
self.assertEqual(len(project.playbooks), 0)
|
||||||
# Simple playbook
|
# Simple playbook
|
||||||
project = self.projects[1]
|
project = self.projects[1]
|
||||||
|
self.assertEqual(len(project.playbooks), 1)
|
||||||
write_test_file(project, 'foo.yml', TEST_PLAYBOOK)
|
write_test_file(project, 'foo.yml', TEST_PLAYBOOK)
|
||||||
self.assertEqual(len(project.available_playbooks), 1)
|
self.assertEqual(len(project.playbooks), 2)
|
||||||
# Other files
|
# Other files
|
||||||
project = self.projects[2]
|
project = self.projects[2]
|
||||||
|
self.assertEqual(len(project.playbooks), 1)
|
||||||
write_test_file(project, 'foo.txt', 'not a playbook')
|
write_test_file(project, 'foo.txt', 'not a playbook')
|
||||||
self.assertEqual(len(project.available_playbooks), 0)
|
self.assertEqual(len(project.playbooks), 1)
|
||||||
# Empty playbook
|
# Empty playbook
|
||||||
project = self.projects[3]
|
project = self.projects[3]
|
||||||
|
self.assertEqual(len(project.playbooks), 1)
|
||||||
write_test_file(project, 'blah.yml', '')
|
write_test_file(project, 'blah.yml', '')
|
||||||
self.assertEqual(len(project.available_playbooks), 0)
|
self.assertEqual(len(project.playbooks), 1)
|
||||||
# Invalid YAML
|
# Invalid YAML
|
||||||
project = self.projects[4]
|
project = self.projects[4]
|
||||||
|
self.assertEqual(len(project.playbooks), 1)
|
||||||
write_test_file(project, 'blah.yml', TEST_PLAYBOOK + '----')
|
write_test_file(project, 'blah.yml', TEST_PLAYBOOK + '----')
|
||||||
self.assertEqual(len(project.available_playbooks), 0)
|
self.assertEqual(len(project.playbooks), 1)
|
||||||
# No hosts or includes
|
# No hosts or includes
|
||||||
project = self.projects[5]
|
project = self.projects[5]
|
||||||
|
self.assertEqual(len(project.playbooks), 1)
|
||||||
playbook_content = TEST_PLAYBOOK.replace('hosts', 'hoists')
|
playbook_content = TEST_PLAYBOOK.replace('hosts', 'hoists')
|
||||||
write_test_file(project, 'blah.yml', playbook_content)
|
write_test_file(project, 'blah.yml', playbook_content)
|
||||||
self.assertEqual(len(project.available_playbooks), 0)
|
self.assertEqual(len(project.playbooks), 1)
|
||||||
# Playbook in roles folder
|
# Playbook in roles folder
|
||||||
project = self.projects[6]
|
project = self.projects[6]
|
||||||
|
self.assertEqual(len(project.playbooks), 1)
|
||||||
write_test_file(project, 'roles/blah.yml', TEST_PLAYBOOK)
|
write_test_file(project, 'roles/blah.yml', TEST_PLAYBOOK)
|
||||||
self.assertEqual(len(project.available_playbooks), 0)
|
self.assertEqual(len(project.playbooks), 1)
|
||||||
# Playbook in tasks folder
|
# Playbook in tasks folder
|
||||||
project = self.projects[7]
|
project = self.projects[7]
|
||||||
|
self.assertEqual(len(project.playbooks), 1)
|
||||||
write_test_file(project, 'tasks/blah.yml', TEST_PLAYBOOK)
|
write_test_file(project, 'tasks/blah.yml', TEST_PLAYBOOK)
|
||||||
self.assertEqual(len(project.available_playbooks), 0)
|
self.assertEqual(len(project.playbooks), 1)
|
||||||
|
|
||||||
|
|
||||||
def test_mainline(self):
|
def test_mainline(self):
|
||||||
|
|
||||||
@@ -180,6 +186,11 @@ class ProjectsTest(BaseTest):
|
|||||||
self.delete(project, expect=204, auth=self.get_normal_credentials())
|
self.delete(project, expect=204, auth=self.get_normal_credentials())
|
||||||
self.get(project, expect=404, auth=self.get_normal_credentials())
|
self.get(project, expect=404, auth=self.get_normal_credentials())
|
||||||
|
|
||||||
|
# can list playbooks for projects
|
||||||
|
proj_playbooks = '/api/v1/projects/%d/playbooks/' % self.projects[2].pk
|
||||||
|
got = self.get(proj_playbooks, expect=200, auth=self.get_super_credentials())
|
||||||
|
self.assertEqual(got, self.projects[2].playbooks)
|
||||||
|
|
||||||
# can list member organizations for projects
|
# can list member organizations for projects
|
||||||
proj_orgs = '/api/v1/projects/1/organizations/'
|
proj_orgs = '/api/v1/projects/1/organizations/'
|
||||||
# only usable as superuser
|
# only usable as superuser
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ class RunJobTest(BaseCeleryTest):
|
|||||||
'credential': self.credential,
|
'credential': self.credential,
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
opts['playbook'] = self.project.available_playbooks[0]
|
opts['playbook'] = self.project.playbooks[0]
|
||||||
except (AttributeError, IndexError):
|
except (AttributeError, IndexError):
|
||||||
pass
|
pass
|
||||||
opts.update(kwargs)
|
opts.update(kwargs)
|
||||||
@@ -196,7 +196,7 @@ class RunJobTest(BaseCeleryTest):
|
|||||||
'credential': self.credential,
|
'credential': self.credential,
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
opts['playbook'] = self.project.available_playbooks[0]
|
opts['playbook'] = self.project.playbooks[0]
|
||||||
except (AttributeError, IndexError):
|
except (AttributeError, IndexError):
|
||||||
pass
|
pass
|
||||||
opts.update(kwargs)
|
opts.update(kwargs)
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ users_urls = patterns('lib.main.views',
|
|||||||
projects_urls = patterns('lib.main.views',
|
projects_urls = patterns('lib.main.views',
|
||||||
url(r'^$', 'projects_list'),
|
url(r'^$', 'projects_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/$', 'projects_detail'),
|
url(r'^(?P<pk>[0-9]+)/$', 'projects_detail'),
|
||||||
|
url(r'^(?P<pk>[0-9]+)/playbooks/$', 'projects_detail_playbooks'),
|
||||||
url(r'^(?P<pk>[0-9]+)/organizations/$', 'projects_organizations_list'),
|
url(r'^(?P<pk>[0-9]+)/organizations/$', 'projects_organizations_list'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -403,6 +403,12 @@ class ProjectsDetail(BaseDetail):
|
|||||||
serializer_class = ProjectSerializer
|
serializer_class = ProjectSerializer
|
||||||
permission_classes = (CustomRbac,)
|
permission_classes = (CustomRbac,)
|
||||||
|
|
||||||
|
class ProjectsDetailPlaybooks(generics.RetrieveAPIView):
|
||||||
|
|
||||||
|
model = Project
|
||||||
|
serializer_class = ProjectPlaybooksSerializer
|
||||||
|
permission_classes = (CustomRbac,)
|
||||||
|
|
||||||
class ProjectsOrganizationsList(BaseSubList):
|
class ProjectsOrganizationsList(BaseSubList):
|
||||||
|
|
||||||
model = Organization
|
model = Organization
|
||||||
|
|||||||
Reference in New Issue
Block a user