90 lines
2.8 KiB
Python
90 lines
2.8 KiB
Python
from __future__ import absolute_import
|
|
|
|
import os
|
|
import datetime
|
|
import platform
|
|
|
|
import psutil
|
|
|
|
from django.db import models, IntegrityError, transaction
|
|
from django.db import close_connection
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from django.utils.translation import ugettext
|
|
|
|
from common.models import Singleton
|
|
|
|
DEFAULT_NODE_TTL = 5
|
|
DEFAULT_NODE_HEARTBEAT_INTERVAL = 1
|
|
|
|
|
|
class NodeManager(models.Manager):
|
|
def myself(self):
|
|
node, created = self.model.objects.get_or_create(hostname=platform.node())
|
|
node.refresh()
|
|
if created:
|
|
# Store the refresh data because is a new instance
|
|
node.save()
|
|
return node
|
|
|
|
|
|
class Node(models.Model):
|
|
hostname = models.CharField(max_length=255, verbose_name=_(u'hostname'))
|
|
cpuload = models.FloatField(blank=True, default=0.0, verbose_name=_(u'cpu load'))
|
|
heartbeat = models.DateTimeField(blank=True, default=datetime.datetime.now(), verbose_name=_(u'last heartbeat check'))
|
|
memory_usage = models.FloatField(blank=True, default=0.0, verbose_name=_(u'memory usage'))
|
|
|
|
objects = NodeManager()
|
|
|
|
@classmethod
|
|
def platform_info(cls):
|
|
return {
|
|
'cpuload': psutil.cpu_percent(),
|
|
'memory_usage': psutil.phymem_usage().percent
|
|
}
|
|
|
|
def __unicode__(self):
|
|
return self.hostname
|
|
|
|
def refresh(self):
|
|
if self.hostname == platform.node():
|
|
# Make we can only update ourselves
|
|
info = Node.platform_info()
|
|
self.cpuload = info['cpuload']
|
|
self.memory_usage = info['memory_usage']
|
|
|
|
def save(self, *args, **kwargs):
|
|
self.heartbeat = datetime.datetime.now()
|
|
return super(Node, self).save(*args, **kwargs)
|
|
|
|
class Meta:
|
|
verbose_name = _(u'node')
|
|
verbose_name_plural = _(u'nodes')
|
|
|
|
|
|
class ClusteringConfigManager(models.Manager):
|
|
def dead_nodes(self):
|
|
return Node.objects.filter(heartbeat__lt=datetime.datetime.now() - datetime.timedelta(seconds=self.model.get().node_time_to_live))
|
|
|
|
def delete_dead_nodes(self):
|
|
self.dead_nodes().delete()
|
|
|
|
def zombiest_node(self):
|
|
try:
|
|
return self.dead_nodes().order_by('-heartbeat')[0]
|
|
except IndexError:
|
|
return None
|
|
|
|
|
|
class ClusteringConfig(Singleton):
|
|
node_time_to_live = models.PositiveIntegerField(verbose_name=(u'time to live (in seconds)'), default=DEFAULT_NODE_TTL) # After this time a worker is considered dead
|
|
node_heartbeat_interval = models.PositiveIntegerField(verbose_name=(u'heartbeat interval'), default=DEFAULT_NODE_HEARTBEAT_INTERVAL)
|
|
# TODO: add validation, interval cannot be greater than TTL
|
|
|
|
objects = ClusteringConfigManager()
|
|
|
|
def __unicode__(self):
|
|
return ugettext('clustering config')
|
|
|
|
class Meta:
|
|
verbose_name = verbose_name_plural = _(u'clustering config')
|