from django.core import meta, validators
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.utils.hostinfo import LazyHostname
PING_INTERVAL=30 # seconds
PING_TIMEOUT=90 # consider the daemon crashed
class DaemonHost(models.Model):
"""
A DaemonHost is a host running a daemon instance.
You can specify which jobs should run on which machine.
"""
hostname = models.CharField(_("hostname"), maxlength=100)
ping = models.DateTimeField(_('ping'), default=models.LazyDate(), editable=False)
def __str__(self):
return self.hostname
class JobPing(models.Model):
"""A JobPing is regulary emitted (updated) by a running daemon.
This way the other daemons detect crashed daemons and free their taken jobs.
"""
host = models.ForeignKey(DaemonHost, editable=false)
queue = models.ForeignKey(Queue, editable=false)
ping = models.DateTimeField(_('ping'), default=models.LazyDate(), editable=False)
def __str__(self):
return "%s@%s : %s" %(self.queue, self.host, self.ping)
class Queue(models.Model):
"""
A queue is a collection of jobs processed by one daemon at a time.
All jobs in the queue will be processed according their priority.
"""
slug = models.SlugField(_('slug'), unique=True)
title = models.CharField(_('title'), maxlength=100)
running = models.BooleanField(_('running'), default=False)
pid = models.IntegerField(_('pid'), default=0, editable=False)
host = models.CharField(_('running on'), default=None, editable=False)
allowedhosts = models.ManyToMany(DaemonHost)
maxinstances = models.IntegerField(_('maximum parallel instances'), default=1)
created = models.DateTimeField(_('created'), auto_create_now=True, editable=False)
pings = models.ManyToMany(JobPing)
def __str__(self):
return self.title
def age(self):
from django.core.template.defaultfilters import timesince
return timesince(self.created)
age.short_description = _('age')
def runningJobs(self):
return str(self.get_job_count(done__exact=False, enabled__exact=True))
runningJobs.short_description = _('running jobs')
def getNextJob(self, assign=True, last_job=None):
"""Return a next free job in the queue"""
pass
class Meta:
pass
# verbose_name = _('queue')
# verbose_name_plural = _('queues')
# ordering = ('slug',)
# admin = models.Admin(
# fields = (
# (None, {'fields': ('slug', 'title', 'running')}),
# ),
# list_display = ('slug', 'title', 'running', 'running_jobs', 'pid', 'age'),
# search_fields = ('slug', 'title'),
# )
class Admin:
pass
class Job(models.Model):
"""
Jobs are background processes waiting to happen. They can be scheduled
by time and ordered by priority and grouped into classes. There will
be one process per queue of jobs, and jobs in that queue will be executed
ordered by priority and execution_time. The function to be executed
will be given in python dotted notation (like in the urlpatterns).
"""
queue = models.ForeignKey(Queue)
slug = models.SlugField(_('slug'), unique=True)
handler = models.CharField(_('handler'), maxlength=200)
title = models.CharField(_('title'), maxlength=100)
parm = models.TextField(_('parameter'), blank=True)
priority = models.IntegerField(_('priority'))
execution_time = models.DateTimeField(_('execution time'), default=models.LazyDate())
enabled = models.BooleanField(_('enabled'), default=True, db_index=True)
done = models.BooleanField(_('done'), default=False, db_index=True)
runcount = models.IntegerField(_('run count'), default=0)
class Meta:
pass
# verbose_name = _('job')
# verbose_name_plural = _('jobs')
# ordering = ('-execution_time',)
# admin = models.Admin(
# fields = (
# (None, {'fields': ('queue', 'handler', 'title', 'parm', 'priority', 'execution_time', 'done', 'enabled', 'runcount')}),
# ),
# list_display = ('title', 'priority', 'execution_time', 'handler', 'queue', 'done', 'runcount'),
# list_filter = ('execution_time', 'queue', 'done'),
# search_fields = ('handler','title'),
# date_hierarchy = ('execution_time',),
# )
def __str__(self):
return self.title
def _post_save(self):
self.get_queue().trigger()
def _module_fetch_next_job(queue):
"""
This function returns the next job from the given job queue and removes
it from that job queue. It returns both the job that is taken from
the job queue and the handler to be run.
"""
""" import datetime
l = queue.get_job_list(done__exact=False, enabled__exact=True, execution_time__lte=datetime.datetime.now(),
limit=1, order_by=('priority', 'execution_time'))
if l:
j = l[0]
j.execution_time = datetime.datetime.now()
j.done = True
j.runcount += 1
j.save()
p = j.handler.rfind('.')
m = __import__(j.handler[:p], {}, {}, [j.handler[p+1:]])
h = getattr(m, j.handler[p+1:])
return (j, h)
else:
return None
"""
def _module_run_job_at(queuename, handlername, priority, title, parm='', when=None):
"""
This function will create a new job for a given job queue. You give the
queue name, handler name, priority and title and optionally an
execution time. If the queue doesn't already exist, a new queue will
be automatically created, but disabled. The queue is returned for
further processing.
"""
import datetime
#from djazz.jobcontrol.models import jobs, queues
if when is None: when = datetime.datetime.now()
q = Queues.objects.get_or_create(slug__exact=queuename, defaults={
"slug":queuename,
"title": queuename,
}
q.save()
j = jobs.Job(queue=q, handler=handlername, priority=priority, title=title, parm=parm, execution_time=when)
j.save()