mirror of
https://github.com/ansible/awx.git
synced 2024-11-02 18:21:12 +03:00
AC-382, AC-352. Added status and last_updated fields for projects API. Various other updates to support projects using SCM.
This commit is contained in:
parent
15fbf95c2a
commit
72d87fb908
@ -569,7 +569,7 @@ class ProjectAccess(BaseAccess):
|
||||
|
||||
def get_queryset(self):
|
||||
qs = Project.objects.filter(active=True).distinct()
|
||||
qs = qs.select_related('created_by')
|
||||
qs = qs.select_related('created_by', 'current_update', 'last_update')
|
||||
if self.user.is_superuser:
|
||||
return qs
|
||||
allowed = [PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK]
|
||||
|
@ -14,21 +14,27 @@ from django.utils.dateparse import parse_datetime
|
||||
from django.utils.timezone import now, is_aware, make_aware
|
||||
|
||||
# AWX
|
||||
from awx.main.models import Job
|
||||
from awx.main.models import ProjectUpdate, Job
|
||||
|
||||
class Command(NoArgsCommand):
|
||||
'''
|
||||
Management command to cleanup old jobs.
|
||||
Management command to cleanup old jobs and project updates.
|
||||
'''
|
||||
|
||||
help = 'Remove old jobs and events from the database.'
|
||||
help = 'Remove old jobs and project updates from the database.'
|
||||
|
||||
option_list = NoArgsCommand.option_list + (
|
||||
make_option('--days', dest='days', type='int', default=90, metavar='N',
|
||||
help='Remove jobs executed more than N days ago'),
|
||||
help='Remove jobs/updates executed more than N days ago'),
|
||||
make_option('--dry-run', dest='dry_run', action='store_true',
|
||||
default=False, help='Dry run mode (show items that would '
|
||||
'be removed)'),
|
||||
make_option('--jobs', dest='only_jobs', action='store_true',
|
||||
default=False,
|
||||
help='Only remove jobs (leave project updates alone)'),
|
||||
make_option('--project-updates', dest='only_project_updates',
|
||||
action='store_true', default=False,
|
||||
help='Only remove project updates (leave jobs alone)'),
|
||||
)
|
||||
|
||||
def cleanup_jobs(self):
|
||||
@ -38,7 +44,7 @@ class Command(NoArgsCommand):
|
||||
job_display = '"%s" (started %s, %d host summaries, %d events)' % \
|
||||
(unicode(job), unicode(job.created),
|
||||
job.job_host_summaries.count(), job.job_events.count())
|
||||
if job.status in ('pending', 'running'):
|
||||
if job.status in ('pending', 'waiting', 'running'):
|
||||
action_text = 'would skip' if self.dry_run else 'skipping'
|
||||
self.logger.debug('%s %s job %s', action_text, job.status, job_display)
|
||||
elif job.created >= self.cutoff:
|
||||
@ -50,6 +56,24 @@ class Command(NoArgsCommand):
|
||||
if not self.dry_run:
|
||||
job.delete()
|
||||
|
||||
def cleanup_project_updates(self):
|
||||
for pu in ProjectUpdate.objects.all():
|
||||
pu_display = '"%s" (started %s)' % (unicode(pu), unicode(pu.created))
|
||||
if pu.status in ('pending', 'waiting', 'running'):
|
||||
action_text = 'would skip' if self.dry_run else 'skipping'
|
||||
self.logger.debug('%s %s project update %s', action_text, pu.status, pu_display)
|
||||
if pu in (pu.project.current_update, pu.project.last_update) and pu.project.scm_type:
|
||||
action_text = 'would skip' if self.dry_run else 'skipping'
|
||||
self.logger.debug('%s %s', action_text, pu_display)
|
||||
elif pu.created >= self.cutoff:
|
||||
action_text = 'would skip' if self.dry_run else 'skipping'
|
||||
self.logger.debug('%s %s', action_text, pu_display)
|
||||
else:
|
||||
action_text = 'would delete' if self.dry_run else 'deleting'
|
||||
self.logger.info('%s %s', action_text, pu_display)
|
||||
if not self.dry_run:
|
||||
pu.delete()
|
||||
|
||||
def init_logging(self):
|
||||
log_levels = dict(enumerate([logging.ERROR, logging.INFO,
|
||||
logging.DEBUG, 0]))
|
||||
@ -67,4 +91,9 @@ class Command(NoArgsCommand):
|
||||
self.days = int(options.get('days', 90))
|
||||
self.dry_run = bool(options.get('dry_run', False))
|
||||
self.cutoff = now() - datetime.timedelta(days=self.days)
|
||||
self.only_jobs = bool(options.get('only_jobs', False))
|
||||
self.only_project_updates = bool(options.get('only_project_updates', False))
|
||||
if self.only_jobs or (not self.only_jobs and not self.only_project_updates):
|
||||
self.cleanup_jobs()
|
||||
if self.only_project_updates or (not self.only_jobs and not self.only_project_updates):
|
||||
self.cleanup_project_updates()
|
||||
|
521
awx/main/migrations/0010_v13_changes.py
Normal file
521
awx/main/migrations/0010_v13_changes.py
Normal file
@ -0,0 +1,521 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding field 'ProjectUpdate.modified'
|
||||
db.add_column(u'main_projectupdate', 'modified',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'ProjectUpdate.modified_by'
|
||||
db.add_column(u'main_projectupdate', 'modified_by',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name="{'class': 'projectupdate', 'app_label': 'main'}(class)s_modified+", null=True, on_delete=models.SET_NULL, to=orm['auth.User']),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Job.modified'
|
||||
db.add_column(u'main_job', 'modified',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Job.modified_by'
|
||||
db.add_column(u'main_job', 'modified_by',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name="{'class': 'job', 'app_label': 'main'}(class)s_modified+", null=True, on_delete=models.SET_NULL, to=orm['auth.User']),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Inventory.modified'
|
||||
db.add_column(u'main_inventory', 'modified',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Inventory.modified_by'
|
||||
db.add_column(u'main_inventory', 'modified_by',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name="{'class': 'inventory', 'app_label': 'main'}(class)s_modified+", null=True, on_delete=models.SET_NULL, to=orm['auth.User']),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Host.modified'
|
||||
db.add_column(u'main_host', 'modified',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Host.modified_by'
|
||||
db.add_column(u'main_host', 'modified_by',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name="{'class': 'host', 'app_label': 'main'}(class)s_modified+", null=True, on_delete=models.SET_NULL, to=orm['auth.User']),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'JobHostSummary.created'
|
||||
db.add_column(u'main_jobhostsummary', 'created',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now_add=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'JobHostSummary.modified'
|
||||
db.add_column(u'main_jobhostsummary', 'modified',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Group.modified'
|
||||
db.add_column(u'main_group', 'modified',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Group.modified_by'
|
||||
db.add_column(u'main_group', 'modified_by',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name="{'class': 'group', 'app_label': 'main'}(class)s_modified+", null=True, on_delete=models.SET_NULL, to=orm['auth.User']),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Credential.modified'
|
||||
db.add_column(u'main_credential', 'modified',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Credential.modified_by'
|
||||
db.add_column(u'main_credential', 'modified_by',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name="{'class': 'credential', 'app_label': 'main'}(class)s_modified+", null=True, on_delete=models.SET_NULL, to=orm['auth.User']),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'JobTemplate.modified'
|
||||
db.add_column(u'main_jobtemplate', 'modified',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'JobTemplate.modified_by'
|
||||
db.add_column(u'main_jobtemplate', 'modified_by',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name="{'class': 'jobtemplate', 'app_label': 'main'}(class)s_modified+", null=True, on_delete=models.SET_NULL, to=orm['auth.User']),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Team.modified'
|
||||
db.add_column(u'main_team', 'modified',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Team.modified_by'
|
||||
db.add_column(u'main_team', 'modified_by',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name="{'class': 'team', 'app_label': 'main'}(class)s_modified+", null=True, on_delete=models.SET_NULL, to=orm['auth.User']),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'JobEvent.modified'
|
||||
db.add_column(u'main_jobevent', 'modified',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Project.modified'
|
||||
db.add_column(u'main_project', 'modified',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Project.modified_by'
|
||||
db.add_column(u'main_project', 'modified_by',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name="{'class': 'project', 'app_label': u'main'}(class)s_modified+", null=True, on_delete=models.SET_NULL, to=orm['auth.User']),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Project.current_update'
|
||||
db.add_column(u'main_project', 'current_update',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='project_as_current_update+', null=True, to=orm['main.ProjectUpdate']),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
# Changing field 'Project.scm_url'
|
||||
db.alter_column(u'main_project', 'scm_url', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True))
|
||||
# Adding field 'Organization.modified'
|
||||
db.add_column(u'main_organization', 'modified',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Organization.modified_by'
|
||||
db.add_column(u'main_organization', 'modified_by',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name="{'class': 'organization', 'app_label': 'main'}(class)s_modified+", null=True, on_delete=models.SET_NULL, to=orm['auth.User']),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Permission.modified'
|
||||
db.add_column(u'main_permission', 'modified',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=now, auto_now=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Permission.modified_by'
|
||||
db.add_column(u'main_permission', 'modified_by',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name="{'class': 'permission', 'app_label': 'main'}(class)s_modified+", null=True, on_delete=models.SET_NULL, to=orm['auth.User']),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'ProjectUpdate.modified'
|
||||
db.delete_column(u'main_projectupdate', 'modified')
|
||||
|
||||
# Deleting field 'ProjectUpdate.modified_by'
|
||||
db.delete_column(u'main_projectupdate', 'modified_by_id')
|
||||
|
||||
# Deleting field 'Job.modified'
|
||||
db.delete_column(u'main_job', 'modified')
|
||||
|
||||
# Deleting field 'Job.modified_by'
|
||||
db.delete_column(u'main_job', 'modified_by_id')
|
||||
|
||||
# Deleting field 'Inventory.modified'
|
||||
db.delete_column(u'main_inventory', 'modified')
|
||||
|
||||
# Deleting field 'Inventory.modified_by'
|
||||
db.delete_column(u'main_inventory', 'modified_by_id')
|
||||
|
||||
# Deleting field 'Host.modified'
|
||||
db.delete_column(u'main_host', 'modified')
|
||||
|
||||
# Deleting field 'Host.modified_by'
|
||||
db.delete_column(u'main_host', 'modified_by_id')
|
||||
|
||||
# Deleting field 'JobHostSummary.created'
|
||||
db.delete_column(u'main_jobhostsummary', 'created')
|
||||
|
||||
# Deleting field 'JobHostSummary.modified'
|
||||
db.delete_column(u'main_jobhostsummary', 'modified')
|
||||
|
||||
# Deleting field 'Group.modified'
|
||||
db.delete_column(u'main_group', 'modified')
|
||||
|
||||
# Deleting field 'Group.modified_by'
|
||||
db.delete_column(u'main_group', 'modified_by_id')
|
||||
|
||||
# Deleting field 'Credential.modified'
|
||||
db.delete_column(u'main_credential', 'modified')
|
||||
|
||||
# Deleting field 'Credential.modified_by'
|
||||
db.delete_column(u'main_credential', 'modified_by_id')
|
||||
|
||||
# Deleting field 'JobTemplate.modified'
|
||||
db.delete_column(u'main_jobtemplate', 'modified')
|
||||
|
||||
# Deleting field 'JobTemplate.modified_by'
|
||||
db.delete_column(u'main_jobtemplate', 'modified_by_id')
|
||||
|
||||
# Deleting field 'Team.modified'
|
||||
db.delete_column(u'main_team', 'modified')
|
||||
|
||||
# Deleting field 'Team.modified_by'
|
||||
db.delete_column(u'main_team', 'modified_by_id')
|
||||
|
||||
# Deleting field 'JobEvent.modified'
|
||||
db.delete_column(u'main_jobevent', 'modified')
|
||||
|
||||
# Deleting field 'Project.modified'
|
||||
db.delete_column(u'main_project', 'modified')
|
||||
|
||||
# Deleting field 'Project.modified_by'
|
||||
db.delete_column(u'main_project', 'modified_by_id')
|
||||
|
||||
# Deleting field 'Project.current_update'
|
||||
db.delete_column(u'main_project', 'current_update_id')
|
||||
|
||||
|
||||
# Changing field 'Project.scm_url'
|
||||
db.alter_column(u'main_project', 'scm_url', self.gf('django.db.models.fields.URLField')(max_length=1024, null=True))
|
||||
# Deleting field 'Organization.modified'
|
||||
db.delete_column(u'main_organization', 'modified')
|
||||
|
||||
# Deleting field 'Organization.modified_by'
|
||||
db.delete_column(u'main_organization', 'modified_by_id')
|
||||
|
||||
# Deleting field 'Permission.modified'
|
||||
db.delete_column(u'main_permission', 'modified')
|
||||
|
||||
# Deleting field 'Permission.modified_by'
|
||||
db.delete_column(u'main_permission', 'modified_by_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', [], {'default': 'None', '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'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'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', [], {'default': 'None', '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'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'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']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'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', [], {'default': 'None', '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'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
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'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'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', [], {'default': 'None', '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'}),
|
||||
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'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']"}),
|
||||
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'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', [], {'default': 'None', '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'}),
|
||||
'launch_type': ('django.db.models.fields.CharField', [], {'default': "'manual'", 'max_length': '20'}),
|
||||
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'job\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'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'},
|
||||
'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', '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']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
|
||||
'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'}),
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'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']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
|
||||
'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', [], {'default': 'None', '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'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'jobtemplate\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'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', [], {'default': 'None', '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'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'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', [], {'default': 'None', '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']"}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'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', [], {'default': 'None', 'related_name': '"{\'class\': \'project\', \'app_label\': u\'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'current_update': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'project_as_current_update+'", 'null': 'True', 'to': "orm['main.ProjectUpdate']"}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'last_update': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'project_as_last_update+'", 'null': 'True', 'to': "orm['main.ProjectUpdate']"}),
|
||||
'last_update_failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'project\', \'app_label\': u\'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
|
||||
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'null': 'True', 'blank': 'True'}),
|
||||
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_next_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_key_data': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
|
||||
'scm_key_unlock': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
|
||||
'scm_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
|
||||
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'null': 'True', 'blank': 'True'}),
|
||||
'scm_update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'null': 'True', 'blank': 'True'}),
|
||||
'scm_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'main.projectupdate': {
|
||||
'Meta': {'object_name': 'ProjectUpdate'},
|
||||
'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', [], {'default': 'None', 'related_name': '"{\'class\': \'projectupdate\', \'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'}),
|
||||
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'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'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'projectupdate\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'project_updates'", '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'})
|
||||
},
|
||||
'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', [], {'default': 'None', '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'}),
|
||||
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
|
||||
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
|
||||
'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']
|
@ -2,6 +2,7 @@
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import datetime
|
||||
import hmac
|
||||
import json
|
||||
import logging
|
||||
@ -19,7 +20,7 @@ from django.db.models import CASCADE, SET_NULL, PROTECT
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.timezone import now
|
||||
from django.utils.timezone import now, make_aware, get_default_timezone
|
||||
|
||||
# Django-JSONField
|
||||
from jsonfield import JSONField
|
||||
@ -67,6 +68,7 @@ PERMISSION_TYPE_CHOICES = [
|
||||
JOB_STATUS_CHOICES = [
|
||||
('new', _('New')), # Job has been created, but not started.
|
||||
('pending', _('Pending')), # Job has been queued, but is not yet running.
|
||||
('waiting', _('Waiting')), # Job is waiting on an update/dependency.
|
||||
('running', _('Running')), # Job is currently running.
|
||||
('successful', _('Successful')), # Job completed successfully.
|
||||
('failed', _('Failed')), # Job completed, but with failures.
|
||||
@ -85,11 +87,16 @@ class PrimordialModel(models.Model):
|
||||
abstract = True
|
||||
|
||||
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)
|
||||
modified = models.DateTimeField(auto_now=True, default=now)
|
||||
created_by = models.ForeignKey('auth.User',
|
||||
related_name='%s(class)s_created+',
|
||||
default=None, null=True, editable=False,
|
||||
on_delete=models.SET_NULL)
|
||||
modified_by = models.ForeignKey('auth.User',
|
||||
related_name='%s(class)s_modified+',
|
||||
default=None, null=True, editable=False,
|
||||
on_delete=models.SET_NULL)
|
||||
active = models.BooleanField(default=True)
|
||||
|
||||
tags = TaggableManager(blank=True)
|
||||
@ -522,9 +529,6 @@ class Project(CommonModel):
|
||||
|
||||
local_path = models.CharField(
|
||||
max_length=1024,
|
||||
# Not unique for now, otherwise "deletes" won't allow reusing the
|
||||
# same path for another active project.
|
||||
#unique=True,
|
||||
blank=True,
|
||||
help_text=_('Local path (relative to PROJECTS_ROOT) containing '
|
||||
'playbooks and related files for this project.')
|
||||
@ -538,7 +542,7 @@ class Project(CommonModel):
|
||||
default='',
|
||||
verbose_name=_('SCM Type'),
|
||||
)
|
||||
scm_url = models.URLField(
|
||||
scm_url = models.CharField(
|
||||
max_length=1024,
|
||||
blank=True,
|
||||
null=True,
|
||||
@ -598,6 +602,13 @@ class Project(CommonModel):
|
||||
help_text=_('Passphrase to unlock SSH private key if encrypted (or '
|
||||
'"ASK" to prompt the user).'),
|
||||
)
|
||||
current_update = models.ForeignKey(
|
||||
'ProjectUpdate',
|
||||
null=True,
|
||||
default=None,
|
||||
editable=False,
|
||||
related_name='project_as_current_update+',
|
||||
)
|
||||
last_update = models.ForeignKey(
|
||||
'ProjectUpdate',
|
||||
null=True,
|
||||
@ -611,12 +622,8 @@ class Project(CommonModel):
|
||||
)
|
||||
|
||||
# FIXME: Still need to implement:
|
||||
# - some scm_url validation
|
||||
# - scm_update_on_launch
|
||||
# - prompt for passwords for project update
|
||||
# - prompt for passwords when running job when scm_update_on_launch set
|
||||
# - prevent simultaneous updates of project and running jobs using project
|
||||
# - prevent manually setting local path when scm_type is set
|
||||
# - masking passwords in project update args/stdout
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
@ -650,8 +657,45 @@ class Project(CommonModel):
|
||||
needed.append(field)
|
||||
return needed
|
||||
|
||||
def update(self, **kwargs):
|
||||
@property
|
||||
def status(self):
|
||||
# FIXME: Update status values!
|
||||
if self.scm_type:
|
||||
if self.current_update:
|
||||
return 'updating'
|
||||
elif not self.last_update:
|
||||
return 'never updated'
|
||||
elif self.last_update_failed:
|
||||
return 'failed'
|
||||
elif not self.get_project_path():
|
||||
return 'missing'
|
||||
else:
|
||||
return 'successsful'
|
||||
elif not self.get_project_path():
|
||||
return 'missing'
|
||||
else:
|
||||
return 'ok'
|
||||
|
||||
@property
|
||||
def last_updated(self):
|
||||
if self.scm_type and self.last_update:
|
||||
return self.last_update.modified
|
||||
else:
|
||||
project_path = self.get_project_path()
|
||||
if project_path:
|
||||
try:
|
||||
mtime = os.path.getmtime(project_path)
|
||||
dt = datetime.datetime.fromtimestamp(mtime)
|
||||
return make_aware(dt, get_default_timezone())
|
||||
except os.error:
|
||||
pass
|
||||
|
||||
@property
|
||||
def can_update(self):
|
||||
return bool(self.scm_type and not self.current_update)
|
||||
|
||||
def update(self, **kwargs):
|
||||
if self.can_update:
|
||||
needed = self.scm_passwords_needed
|
||||
opts = dict([(field, kwargs.get(field, '')) for field in needed])
|
||||
if not all(opts.values()):
|
||||
@ -660,10 +704,6 @@ class Project(CommonModel):
|
||||
project_update.start(**opts)
|
||||
return project_update
|
||||
|
||||
@property
|
||||
def active_updates(self):
|
||||
return self.project_updates.filter(active=True, status__in=('new', 'pending', 'running'))
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('main:project_detail', args=(self.pk,))
|
||||
|
||||
@ -779,15 +819,24 @@ class ProjectUpdate(PrimordialModel):
|
||||
status_before = project_update_before.status
|
||||
self.failed = bool(self.status in ('failed', 'error', 'canceled'))
|
||||
super(ProjectUpdate, self).save(*args, **kwargs)
|
||||
# If status changed, and update has completed, update project.
|
||||
# If status changed, update project.
|
||||
if self.status != status_before:
|
||||
if self.status in ('successful', 'failed', 'error', 'canceled'):
|
||||
if self.status in ('pending', 'waiting', 'running'):
|
||||
project = self.project
|
||||
if project.current_update != self:
|
||||
project.current_update = self
|
||||
project.save(update_fields=['current_update'])
|
||||
elif self.status in ('successful', 'failed', 'error', 'canceled'):
|
||||
project = self.project
|
||||
if project.current_update == self:
|
||||
project.current_update = None
|
||||
project.last_update = self
|
||||
project.last_update_failed = self.failed
|
||||
if not self.failed and project.scm_delete_on_next_update:
|
||||
project.scm_delete_on_next_update = False
|
||||
project.save()
|
||||
project.save(update_fields=['current_update', 'last_update',
|
||||
'last_update_failed',
|
||||
'scm_delete_on_next_update'])
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('main:project_update_detail', args=(self.pk,))
|
||||
@ -800,10 +849,6 @@ class ProjectUpdate(PrimordialModel):
|
||||
except TaskMeta.DoesNotExist:
|
||||
pass
|
||||
|
||||
def get_passwords_needed_to_start(self):
|
||||
'''Return list of password field names needed to start the job.'''
|
||||
return (self.credential and self.credential.passwords_needed) or []
|
||||
|
||||
@property
|
||||
def can_start(self):
|
||||
return bool(self.status == 'new')
|
||||
@ -830,7 +875,7 @@ class ProjectUpdate(PrimordialModel):
|
||||
|
||||
@property
|
||||
def can_cancel(self):
|
||||
return bool(self.status in ('pending', 'running'))
|
||||
return bool(self.status in ('pending', 'waiting', 'running'))
|
||||
|
||||
def cancel(self):
|
||||
if self.can_cancel:
|
||||
@ -1151,9 +1196,15 @@ class Job(CommonModelNameNotUnique):
|
||||
h = hmac.new(settings.SECRET_KEY, self.created.isoformat())
|
||||
return '%d-%s' % (self.pk, h.hexdigest())
|
||||
|
||||
def get_passwords_needed_to_start(self):
|
||||
@property
|
||||
def passwords_needed_to_start(self):
|
||||
'''Return list of password field names needed to start the job.'''
|
||||
return (self.credential and self.credential.passwords_needed) or []
|
||||
needed = []
|
||||
if self.credential:
|
||||
needed.extend(self.credential.passwords_needed)
|
||||
if self.project.scm_update_on_launch:
|
||||
needed.extend(self.project.scm_passwords_needed)
|
||||
return needed
|
||||
|
||||
@property
|
||||
def can_start(self):
|
||||
@ -1163,7 +1214,7 @@ class Job(CommonModelNameNotUnique):
|
||||
from awx.main.tasks import RunJob
|
||||
if not self.can_start:
|
||||
return False
|
||||
needed = self.get_passwords_needed_to_start()
|
||||
needed = self.passwords_needed_to_start
|
||||
opts = dict([(field, kwargs.get(field, '')) for field in needed])
|
||||
if not all(opts.values()):
|
||||
return False
|
||||
@ -1181,7 +1232,7 @@ class Job(CommonModelNameNotUnique):
|
||||
|
||||
@property
|
||||
def can_cancel(self):
|
||||
return bool(self.status in ('pending', 'running'))
|
||||
return bool(self.status in ('pending', 'waiting', 'running'))
|
||||
|
||||
def cancel(self):
|
||||
if self.can_cancel:
|
||||
@ -1245,6 +1296,14 @@ class JobHostSummary(models.Model):
|
||||
on_delete=models.CASCADE,
|
||||
editable=False,
|
||||
)
|
||||
created = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
default=now,
|
||||
)
|
||||
modified = models.DateTimeField(
|
||||
auto_now=True,
|
||||
default=now,
|
||||
)
|
||||
|
||||
changed = models.PositiveIntegerField(default=0, editable=False)
|
||||
dark = models.PositiveIntegerField(default=0, editable=False)
|
||||
@ -1353,6 +1412,11 @@ class JobEvent(models.Model):
|
||||
)
|
||||
created = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
default=now,
|
||||
)
|
||||
modified = models.DateTimeField(
|
||||
auto_now=True,
|
||||
default=now,
|
||||
)
|
||||
event = models.CharField(
|
||||
max_length=100,
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
# Python
|
||||
import json
|
||||
import urlparse
|
||||
|
||||
# PyYAML
|
||||
import yaml
|
||||
@ -21,8 +22,8 @@ from rest_framework import serializers
|
||||
# AWX
|
||||
from awx.main.models import *
|
||||
|
||||
BASE_FIELDS = ('id', 'url', 'related', 'summary_fields', 'created', 'name',
|
||||
'description')
|
||||
BASE_FIELDS = ('id', 'url', 'related', 'summary_fields', 'created', 'modified',
|
||||
'name', 'description')
|
||||
|
||||
# objects that if found we should add summary info for them
|
||||
SUMMARIZABLE_FKS = (
|
||||
@ -43,6 +44,7 @@ class BaseSerializer(serializers.ModelSerializer):
|
||||
|
||||
# make certain fields read only
|
||||
created = serializers.SerializerMethodField('get_created')
|
||||
modified = serializers.SerializerMethodField('get_modified')
|
||||
active = serializers.SerializerMethodField('get_active')
|
||||
|
||||
def get_fields(self):
|
||||
@ -63,6 +65,9 @@ class BaseSerializer(serializers.ModelSerializer):
|
||||
elif key == 'created':
|
||||
field.help_text = 'Timestamp when this %s was created.' % unicode(opts.verbose_name)
|
||||
field.type_label = 'datetime'
|
||||
elif key == 'modified':
|
||||
field.help_text = 'Timestamp when this %s was last modified.' % unicode(opts.verbose_name)
|
||||
field.type_label = 'datetime'
|
||||
return ret
|
||||
|
||||
def get_url(self, obj):
|
||||
@ -101,6 +106,12 @@ class BaseSerializer(serializers.ModelSerializer):
|
||||
else:
|
||||
return obj.created
|
||||
|
||||
def get_modified(self, obj):
|
||||
if isinstance(obj, User):
|
||||
return obj.last_login # Not actually exposed for User.
|
||||
else:
|
||||
return obj.modified
|
||||
|
||||
def get_active(self, obj):
|
||||
if isinstance(obj, User):
|
||||
return obj.is_active
|
||||
@ -182,6 +193,8 @@ class ProjectSerializer(BaseSerializer):
|
||||
|
||||
playbooks = serializers.Field(source='playbooks', help_text='Array of playbooks available within this project.')
|
||||
scm_delete_on_next_update = serializers.Field(source='scm_delete_on_next_update')
|
||||
status = serializers.Field(source='status')
|
||||
last_updated = serializers.Field(source='last_updated')
|
||||
|
||||
class Meta:
|
||||
model = Project
|
||||
@ -190,7 +203,7 @@ class ProjectSerializer(BaseSerializer):
|
||||
'scm_delete_on_update', 'scm_delete_on_next_update',
|
||||
'scm_update_on_launch',
|
||||
'scm_username', 'scm_password', 'scm_key_data',
|
||||
'scm_key_unlock', 'last_update_failed')
|
||||
'scm_key_unlock', 'last_update_failed', 'status', 'last_updated')
|
||||
|
||||
def get_related(self, obj):
|
||||
res = super(ProjectSerializer, self).get_related(obj)
|
||||
@ -201,6 +214,9 @@ class ProjectSerializer(BaseSerializer):
|
||||
update = reverse('main:project_update_view', args=(obj.pk,)),
|
||||
project_updates = reverse('main:project_updates_list', args=(obj.pk,)),
|
||||
))
|
||||
if obj.current_update:
|
||||
res['current_update'] = reverse('main:project_update_detail',
|
||||
args=(obj.current_update.pk,))
|
||||
if obj.last_update:
|
||||
res['last_update'] = reverse('main:project_update_detail',
|
||||
args=(obj.last_update.pk,))
|
||||
@ -208,13 +224,32 @@ class ProjectSerializer(BaseSerializer):
|
||||
|
||||
def validate_local_path(self, attrs, source):
|
||||
# Don't allow assigning a local_path used by another project.
|
||||
# Don't allow assigning a local_path when scm_type is set.
|
||||
print attrs, source, self.object
|
||||
valid_local_paths = Project.get_local_path_choices()
|
||||
if self.object:
|
||||
scm_type = attrs.get('scm_type', self.object.scm_type)
|
||||
if not scm_type:
|
||||
valid_local_paths.append(self.object.local_path)
|
||||
else:
|
||||
scm_type = attrs.get('scm_type', '')
|
||||
if scm_type:
|
||||
attrs.pop(source, None)
|
||||
if source in attrs and attrs[source] not in valid_local_paths:
|
||||
raise serializers.ValidationError('Invalid path choice')
|
||||
return attrs
|
||||
|
||||
def validate_scm_url(self, attrs, source):
|
||||
if self.object:
|
||||
scm_type = attrs.get('scm_type', self.object.scm_type)
|
||||
else:
|
||||
scm_type = attrs.get('scm_type', '')
|
||||
scm_url = unicode(attrs.get(source, None) or '')
|
||||
scm_url_parts = urlparse.urlsplit(scm_url)
|
||||
if scm_type and not any(scm_url_parts):
|
||||
raise serializers.ValidationError('SCM URL must be provided')
|
||||
return attrs
|
||||
|
||||
class ProjectPlaybooksSerializer(ProjectSerializer):
|
||||
|
||||
class Meta:
|
||||
@ -230,7 +265,7 @@ class ProjectUpdateSerializer(BaseSerializer):
|
||||
class Meta:
|
||||
model = ProjectUpdate
|
||||
fields = ('id', 'url', 'related', 'summary_fields', 'created',
|
||||
'project', 'status', 'failed', 'result_stdout',
|
||||
'modified', 'project', 'status', 'failed', 'result_stdout',
|
||||
'result_traceback', 'job_args', 'job_cwd', 'job_env')
|
||||
|
||||
def get_related(self, obj):
|
||||
@ -530,9 +565,9 @@ class JobHostSummarySerializer(BaseSerializer):
|
||||
|
||||
class Meta:
|
||||
model = JobHostSummary
|
||||
fields = ('id', 'url', 'job', 'host', 'summary_fields', 'related',
|
||||
'changed', 'dark', 'failures', 'ok', 'processed', 'skipped',
|
||||
'failed')
|
||||
fields = ('id', 'url', 'job', 'host', 'created', 'modified',
|
||||
'summary_fields', 'related', 'changed', 'dark', 'failures',
|
||||
'ok', 'processed', 'skipped', 'failed')
|
||||
|
||||
def get_related(self, obj):
|
||||
res = super(JobHostSummarySerializer, self).get_related(obj)
|
||||
@ -549,9 +584,10 @@ class JobEventSerializer(BaseSerializer):
|
||||
|
||||
class Meta:
|
||||
model = JobEvent
|
||||
fields = ('id', 'url', 'created', 'job', 'event', 'event_display',
|
||||
'event_data', 'event_level', 'failed', 'changed', 'host',
|
||||
'related', 'summary_fields', 'parent', 'play', 'task')
|
||||
fields = ('id', 'url', 'created', 'modified', 'job', 'event',
|
||||
'event_display', 'event_data', 'event_level', 'failed',
|
||||
'changed', 'host', 'related', 'summary_fields', 'parent',
|
||||
'play', 'task')
|
||||
|
||||
def get_related(self, obj):
|
||||
res = super(JobEventSerializer, self).get_related(obj)
|
||||
|
@ -30,10 +30,13 @@ __all__ = ['RunJob', 'RunProjectUpdate']
|
||||
|
||||
logger = logging.getLogger('awx.main.tasks')
|
||||
|
||||
# FIXME: Cleanly cancel task when celery worker is stopped.
|
||||
|
||||
class BaseTask(Task):
|
||||
|
||||
name = None
|
||||
model = None
|
||||
idle_timeout = None
|
||||
|
||||
def update_model(self, pk, **updates):
|
||||
'''
|
||||
@ -132,10 +135,10 @@ class BaseTask(Task):
|
||||
expect_passwords[n] = passwords.get(item[1], '') or ''
|
||||
expect_list.extend([pexpect.TIMEOUT, pexpect.EOF])
|
||||
while child.isalive():
|
||||
result_id = child.expect(expect_list, timeout=2)
|
||||
result_id = child.expect(expect_list, timeout=5)
|
||||
if result_id in expect_passwords:
|
||||
child.sendline(expect_passwords[result_id])
|
||||
updates = {}
|
||||
updates = {'status': 'running'}
|
||||
if logfile_pos != logfile.tell():
|
||||
logfile_pos = logfile.tell()
|
||||
updates['result_stdout'] = logfile.getvalue()
|
||||
@ -144,11 +147,11 @@ class BaseTask(Task):
|
||||
if instance.cancel_flag:
|
||||
child.close(True)
|
||||
canceled = True
|
||||
elif (time.time() - last_stdout_update) > 30: # FIXME: Configurable idle timeout?
|
||||
print 'no updates...'
|
||||
# print 'canceling...'
|
||||
# child.close(True)
|
||||
# canceled = True
|
||||
# FIXME: Configurable idle timeout? Find a way to determine if task
|
||||
# is hung waiting at a prompt.
|
||||
if self.idle_timeout and (time.time() - last_stdout_update) > self.idle_timeout:
|
||||
child.close(True)
|
||||
canceled = True
|
||||
if canceled:
|
||||
status = 'canceled'
|
||||
elif child.exitstatus == 0:
|
||||
@ -166,10 +169,17 @@ class BaseTask(Task):
|
||||
return False
|
||||
return True
|
||||
|
||||
def post_run_hook(self, instance):
|
||||
'''
|
||||
Hook for actions after job/task has completed.
|
||||
'''
|
||||
|
||||
def run(self, pk, **kwargs):
|
||||
'''
|
||||
Run the job/task using ansible-playbook and capture its output.
|
||||
'''
|
||||
self.pk = pk
|
||||
self.kwargs = dict(kwargs.items())
|
||||
instance = self.update_model(pk)
|
||||
if not self.pre_run_check(instance, **kwargs):
|
||||
return
|
||||
@ -193,8 +203,9 @@ class BaseTask(Task):
|
||||
os.remove(kwargs['ssh_key_path'])
|
||||
except IOError:
|
||||
pass
|
||||
self.update_model(pk, status=status, result_stdout=stdout,
|
||||
instance = self.update_model(pk, status=status, result_stdout=stdout,
|
||||
result_traceback=tb)
|
||||
self.post_run_hook(instance)
|
||||
|
||||
class RunJob(BaseTask):
|
||||
'''
|
||||
@ -313,12 +324,21 @@ class RunJob(BaseTask):
|
||||
if not super(RunJob, self).pre_run_check(job, **kwargs):
|
||||
return False
|
||||
# FIXME: Check if job is waiting on any projects that are being updated.
|
||||
if job.project.has_active_updates:
|
||||
pass
|
||||
return True
|
||||
|
||||
def post_run_hook(self, job):
|
||||
'''
|
||||
Hook for actions after job has completed.
|
||||
'''
|
||||
# Start any project updates that were blocked waiting for the job.
|
||||
|
||||
class RunProjectUpdate(BaseTask):
|
||||
|
||||
name = 'run_project_update'
|
||||
model = ProjectUpdate
|
||||
idle_timeout = 30
|
||||
|
||||
def build_passwords(self, project_update, **kwargs):
|
||||
'''
|
||||
@ -359,6 +379,8 @@ class RunProjectUpdate(BaseTask):
|
||||
optionally using ssh-agent for public/private key authentication.
|
||||
'''
|
||||
args = ['ansible-playbook', '-i', 'localhost,']
|
||||
# Since we specify -vvv and tasks use async polling, we should get some
|
||||
# output regularly...
|
||||
args.append('-%s' % ('v' * 3))
|
||||
project = project_update.project
|
||||
scm_url = project.scm_url
|
||||
@ -375,8 +397,6 @@ class RunProjectUpdate(BaseTask):
|
||||
'scm_url': scm_url,
|
||||
'scm_branch': scm_branch,
|
||||
'scm_clean': project.scm_clean,
|
||||
#'scm_username': project.scm_username,
|
||||
#'scm_password': project.scm_password,
|
||||
'scm_delete_on_update': scm_delete_on_update,
|
||||
}
|
||||
args.extend(['-e', json.dumps(extra_vars)])
|
||||
@ -408,4 +428,13 @@ class RunProjectUpdate(BaseTask):
|
||||
if not super(RunProjectUpdate, self).pre_run_check(project_update, **kwargs):
|
||||
return False
|
||||
# FIXME: Check if project update is blocked by any jobs that are being run.
|
||||
project = project_update.project
|
||||
if project.jobs.filter(status__in=('pending', 'waiting', 'running')):
|
||||
pass
|
||||
return True
|
||||
|
||||
def post_run_hook(self, project_update):
|
||||
'''
|
||||
Hook for actions after project_update has completed.
|
||||
'''
|
||||
# Start any jobs waiting on this update to finish.
|
||||
|
@ -778,6 +778,8 @@ class JobStartCancelTest(BaseJobTestMixin, django.test.LiveServerTestCase):
|
||||
# Sue can start a job (when passwords are already saved) as long as the
|
||||
# status is new. Reverse list so "new" will be last.
|
||||
for status in reversed([x[0] for x in JOB_STATUS_CHOICES]):
|
||||
if status == 'waiting':
|
||||
continue
|
||||
job.status = status
|
||||
job.save()
|
||||
with self.current_user(self.user_sue):
|
||||
@ -864,6 +866,8 @@ class JobStartCancelTest(BaseJobTestMixin, django.test.LiveServerTestCase):
|
||||
|
||||
# sue can cancel the job, but only when it is pending or running.
|
||||
for status in [x[0] for x in JOB_STATUS_CHOICES]:
|
||||
if status == 'waiting':
|
||||
continue
|
||||
job.status = status
|
||||
job.save()
|
||||
with self.current_user(self.user_sue):
|
||||
|
@ -268,7 +268,7 @@ class ProjectUpdateView(GenericAPIView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
obj = self.get_object()
|
||||
data = dict(
|
||||
can_update=bool(obj.scm_type),
|
||||
can_update=obj.can_update,
|
||||
)
|
||||
if obj.scm_type:
|
||||
data['passwords_needed_to_update'] = obj.scm_passwords_needed
|
||||
@ -276,13 +276,14 @@ class ProjectUpdateView(GenericAPIView):
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
obj = self.get_object()
|
||||
if bool(obj.scm_type):
|
||||
if obj.can_update:
|
||||
project_update = obj.update(**request.DATA)
|
||||
if not project_update:
|
||||
data = dict(passwords_needed_to_update=obj.scm_passwords_needed)
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
else:
|
||||
return Response(status=status.HTTP_202_ACCEPTED)
|
||||
headers = {'Location': project_update.get_absolute_url()}
|
||||
return Response(status=status.HTTP_202_ACCEPTED, headers=headers)
|
||||
else:
|
||||
return self.http_method_not_allowed(request, *args, **kwargs)
|
||||
|
||||
@ -796,7 +797,7 @@ class JobStart(GenericAPIView):
|
||||
can_start=obj.can_start,
|
||||
)
|
||||
if obj.can_start:
|
||||
data['passwords_needed_to_start'] = obj.get_passwords_needed_to_start()
|
||||
data['passwords_needed_to_start'] = obj.asswords_needed_to_start
|
||||
return Response(data)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
@ -804,7 +805,7 @@ class JobStart(GenericAPIView):
|
||||
if obj.can_start:
|
||||
result = obj.start(**request.DATA)
|
||||
if not result:
|
||||
data = dict(passwords_needed_to_start=obj.get_passwords_needed_to_start())
|
||||
data = dict(passwords_needed_to_start=obj.passwords_needed_to_start)
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
else:
|
||||
return Response(status=status.HTTP_202_ACCEPTED)
|
||||
|
@ -20,17 +20,11 @@
|
||||
- name: update project using git
|
||||
git: dest={{project_path}} repo={{scm_url}} version={{scm_branch}} force={{scm_clean}}
|
||||
when: scm_type == 'git'
|
||||
async: 0
|
||||
poll: 5
|
||||
|
||||
- name: update project using hg
|
||||
hg: dest={{project_path}} repo={{scm_url}} revision={{scm_branch}} force={{scm_clean}}
|
||||
when: scm_type == 'hg'
|
||||
async: 0
|
||||
poll: 5
|
||||
|
||||
- name: update project using svn
|
||||
subversion: dest={{project_path}} repo={{scm_url}} revision={{scm_branch}} force={{scm_clean}}
|
||||
when: scm_type == 'svn'
|
||||
async: 0
|
||||
poll: 5
|
||||
|
@ -245,8 +245,8 @@ BROKER_URL = 'django://'
|
||||
CELERY_TASK_SERIALIZER = 'json'
|
||||
CELERY_RESULT_SERIALIZER = 'json'
|
||||
CELERY_TRACK_STARTED = True
|
||||
CELERYD_TASK_TIME_LIMIT = 3600
|
||||
CELERYD_TASK_SOFT_TIME_LIMIT = 3540
|
||||
CELERYD_TASK_TIME_LIMIT = None
|
||||
CELERYD_TASK_SOFT_TIME_LIMIT = None
|
||||
CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler'
|
||||
CELERYBEAT_MAX_LOOP_INTERVAL = 60
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user