diff --git a/orchestra/apps/lists/backends.py b/orchestra/apps/lists/backends.py
index e3d1d233..40239d8c 100644
--- a/orchestra/apps/lists/backends.py
+++ b/orchestra/apps/lists/backends.py
@@ -29,10 +29,12 @@ class MailmanTraffic(ServiceMonitor):
"echo %(object_id)s $(( ${SIZE}*${SUBSCRIBERS} ))" % context)
def get_context(self, mail_list):
+ last_date = timezone.localtime(self.get_last_date(mail_list.pk))
+ current_date = timezone.localtime(self.current_date)
return {
'mailman_log': settings.LISTS_MAILMAN_POST_LOG_PATH,
'list_name': mail_list.name,
'object_id': mail_list.pk,
- 'last_date': timezone.localtime(self.get_last_date(mail_list)).strftime("%b %d %H:%M:%S"),
- 'current_date': timezone.localtime(self.get_current_date()).strftime("%b %d %H:%M:%S"),
+ 'last_date': last_date.strftime("%b %d %H:%M:%S"),
+ 'current_date': current_date.strftime("%b %d %H:%M:%S"),
}
diff --git a/orchestra/apps/orchestration/admin.py b/orchestra/apps/orchestration/admin.py
index 72d93b1c..cf5e39ad 100644
--- a/orchestra/apps/orchestration/admin.py
+++ b/orchestra/apps/orchestration/admin.py
@@ -2,6 +2,7 @@ from django.contrib import admin
from django.core.urlresolvers import reverse
from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _
+from djcelery.humanize import naturaldate
from orchestra.admin.html import monospace_format
from orchestra.admin.utils import link
@@ -30,7 +31,7 @@ class RouteAdmin(admin.ModelAdmin):
def display_model(self, route):
try:
- return route.backend_class().model
+ return escape(route.backend_class().model)
except KeyError:
return "NOT AVAILABLE"
display_model.short_description = _("model")
@@ -61,7 +62,9 @@ class BackendOperationInline(admin.TabularInline):
try:
return link('instance')(self, operation)
except:
- return _("deleted %s %s") % (operation.content_type, operation.object_id)
+ return _("deleted {0} {1}").format(
+ escape(operation.content_type), escape(operation.object_id)
+ )
instance_link.allow_tags = True
instance_link.short_description = _("Instance")
@@ -71,8 +74,8 @@ class BackendOperationInline(admin.TabularInline):
class BackendLogAdmin(admin.ModelAdmin):
list_display = (
- 'id', 'backend', 'server_link', 'display_state', 'exit_code', 'created',
- 'execution_time',
+ 'id', 'backend', 'server_link', 'display_state', 'exit_code',
+ 'display_created', 'execution_time',
)
list_display_links = ('id', 'backend')
list_filter = ('state', 'backend')
@@ -80,14 +83,10 @@ class BackendLogAdmin(admin.ModelAdmin):
inlines = [BackendOperationInline]
fields = [
'backend', 'server', 'state', 'mono_script', 'mono_stdout', 'mono_stderr',
- 'mono_traceback', 'exit_code', 'task_id', 'created', 'last_update',
- 'execution_time'
- ]
- readonly_fields = [
- 'backend', 'server', 'state', 'mono_script', 'mono_stdout', 'mono_stderr',
- 'mono_traceback', 'exit_code', 'task_id', 'created', 'last_update',
- 'execution_time'
+ 'mono_traceback', 'exit_code', 'task_id', 'display_created',
+ 'display_last_update', 'execution_time'
]
+ readonly_fields = fields
def server_link(self, log):
url = reverse('admin:orchestration_server_change', args=(log.server.pk,))
@@ -118,6 +117,20 @@ class BackendLogAdmin(admin.ModelAdmin):
return monospace_format(escape(log.traceback))
mono_traceback.short_description = _("traceback")
+ def display_last_update(self, log):
+ return '
{1}
'.format(
+ escape(str(log.last_update)), escape(naturaldate(log.last_update)),
+ )
+ display_last_update.short_description = _("last update")
+ display_last_update.allow_tags = True
+
+ def display_created(self, log):
+ return '{1}
'.format(
+ escape(str(log.created)), escape(naturaldate(log.created)),
+ )
+ display_created.short_description = _("created")
+ display_created.allow_tags = True
+
def get_queryset(self, request):
""" Order by structured name and imporve performance """
qs = super(BackendLogAdmin, self).get_queryset(request)
diff --git a/orchestra/apps/orchestration/backends.py b/orchestra/apps/orchestration/backends.py
index 7e71fbef..b1f6de6a 100644
--- a/orchestra/apps/orchestration/backends.py
+++ b/orchestra/apps/orchestration/backends.py
@@ -31,7 +31,7 @@ class ServiceBackend(object):
def __unicode__(self):
return type(self).__name__
-
+
def __str__(self):
return unicode(self)
diff --git a/orchestra/apps/orchestration/methods.py b/orchestra/apps/orchestration/methods.py
index 2ca95b7a..fce32c95 100644
--- a/orchestra/apps/orchestration/methods.py
+++ b/orchestra/apps/orchestration/methods.py
@@ -13,14 +13,14 @@ from . import settings
def BashSSH(backend, log, server, cmds):
from .models import BackendLog
- script = '\n\n'.join(['set -e'] + cmds + ['exit 0'])
+ script = '\n\n'.join(['set -e', 'set -o pipefail'] + cmds + ['exit 0'])
script = script.replace('\r', '')
log.script = script
log.save()
try:
- # In order to avoid "Argument list too long" we while generate first a
- # script file, then scp the escript and safely execute in remote
+ # Avoid "Argument list too long" on large scripts by genereting a file
+ # and scping it to the remote server
digest = hashlib.md5(script).hexdigest()
path = os.path.join(settings.ORCHESTRATION_TEMP_SCRIPT_PATH, digest)
with open(path, 'w') as script_file:
diff --git a/orchestra/apps/resources/admin.py b/orchestra/apps/resources/admin.py
index 53a45cac..ea4aa9b8 100644
--- a/orchestra/apps/resources/admin.py
+++ b/orchestra/apps/resources/admin.py
@@ -1,7 +1,9 @@
from django.contrib import admin, messages
from django.contrib.contenttypes import generic
from django.utils.functional import cached_property
+from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _
+from djcelery.humanize import naturaldate
from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.filters import UsedContentTypeFilter
@@ -100,6 +102,8 @@ def resource_inline_factory(resources):
form = ResourceForm
formset = ResourceInlineFormSet
can_delete = False
+ fields = ('verbose_name', 'used', 'display_last_update', 'allocated',)
+ readonly_fields = ('used', 'display_last_update',)
class Media:
css = {
@@ -109,6 +113,13 @@ def resource_inline_factory(resources):
def has_add_permission(self, *args, **kwargs):
""" Hidde add another """
return False
+
+ def display_last_update(self, log):
+ return '{1}
'.format(
+ escape(str(log.last_update)), escape(naturaldate(log.last_update)),
+ )
+ display_last_update.short_description = _("last update")
+ display_last_update.allow_tags = True
return ResourceInline
diff --git a/orchestra/apps/resources/backends.py b/orchestra/apps/resources/backends.py
index 6339f316..686289dd 100644
--- a/orchestra/apps/resources/backends.py
+++ b/orchestra/apps/resources/backends.py
@@ -2,9 +2,9 @@ import datetime
from django.contrib.contenttypes.models import ContentType
from django.utils import timezone
+from django.utils.functional import cached_property
from orchestra.apps.orchestration import ServiceBackend
-from orchestra.utils.functional import cached
class ServiceMonitor(ServiceBackend):
@@ -23,18 +23,29 @@ class ServiceMonitor(ServiceBackend):
if backend != ServiceMonitor and ServiceMonitor in backend.__mro__:
yield backend
- @cached
- def get_last_date(self, obj):
+ @cached_property
+ def current_date(self):
+ return timezone.now()
+
+ @cached_property
+ def content_type(self):
+ app_label, model = self.model.split('.')
+ model = model.lower()
+ return ContentType.objects.get(app_label=app_label, model=model)
+
+ def get_last_data(self, object_id):
from .models import MonitorData
try:
- ct = ContentType.objects.get_for_model(type(obj))
- return MonitorData.objects.filter(content_type=ct, object_id=obj.pk).latest().date
+ return MonitorData.objects.filter(content_type=self.content_type,
+ object_id=object_id).latest()
except MonitorData.DoesNotExist:
- return self.get_current_date() - datetime.timedelta(days=1)
-
- @cached
- def get_current_date(self):
- return timezone.now()
+ return None
+
+ def get_last_date(self, object_id):
+ data = self.get_last_data(object_id)
+ if data is None:
+ return self.current_date - datetime.timedelta(days=1)
+ return data.date
def store(self, log):
""" object_id value """
@@ -46,7 +57,7 @@ class ServiceMonitor(ServiceBackend):
line = line.strip()
object_id, value = line.split()
MonitorData.objects.create(monitor=name, object_id=object_id,
- content_type=ct, value=value, date=self.get_current_date())
+ content_type=ct, value=value, date=self.current_date)
def execute(self, server):
log = super(ServiceMonitor, self).execute(server)
diff --git a/orchestra/apps/resources/forms.py b/orchestra/apps/resources/forms.py
index fc46c8a4..44cf9adf 100644
--- a/orchestra/apps/resources/forms.py
+++ b/orchestra/apps/resources/forms.py
@@ -1,5 +1,7 @@
from django import forms
+from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _
+from djcelery.humanize import naturaldate
from orchestra.forms.widgets import ShowTextWidget, ReadOnlyWidget
@@ -9,8 +11,6 @@ class ResourceForm(forms.ModelForm):
required=False)
used = forms.IntegerField(label=_("Used"), widget=ShowTextWidget(),
required=False)
- last_update = forms.DateTimeField(label=_("Last update"), widget=ShowTextWidget(),
- required=False)
allocated = forms.IntegerField(label=_("Allocated"))
class Meta:
diff --git a/orchestra/apps/users/backends.py b/orchestra/apps/users/backends.py
index ee957fa1..c209c9f4 100644
--- a/orchestra/apps/users/backends.py
+++ b/orchestra/apps/users/backends.py
@@ -101,10 +101,12 @@ class FTPTraffic(ServiceMonitor):
' | xargs echo %(object_id)s """ % context)
def get_context(self, user):
+ last_date = timezone.localtime(self.get_last_date(user.pk))
+ current_date = timezone.localtime(self.current_date)
return {
'log_file': settings.USERS_FTP_LOG_PATH,
- 'last_date': timezone.localtime(self.get_last_date(user)).strftime("%Y%m%d%H%M%S"),
- 'current_date': timezone.localtime(self.get_current_date()).strftime("%Y%m%d%H%M%S"),
+ 'last_date': last_date.strftime("%Y%m%d%H%M%S"),
+ 'current_date': current_date.strftime("%Y%m%d%H%M%S"),
'object_id': user.pk,
'username': user.username,
}
diff --git a/orchestra/apps/vps/backends.py b/orchestra/apps/vps/backends.py
index f38b3384..4b077bda 100644
--- a/orchestra/apps/vps/backends.py
+++ b/orchestra/apps/vps/backends.py
@@ -3,18 +3,32 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.apps.resources import ServiceMonitor
-class OpenVZDisk(ServiceMonitor):
- model = 'vps.VPS'
- resource = ServiceMonitor.DISK
-
-
-class OpenVZMemory(ServiceMonitor):
- model = 'vps.VPS'
- resource = ServiceMonitor.MEMORY
-
-
class OpenVZTraffic(ServiceMonitor):
model = 'vps.VPS'
resource = ServiceMonitor.TRAFFIC
-
-
+
+ def process(self, line, obj):
+ """ diff with last stored value """
+ object_id, value = line.split()
+ last = self.get_last_data(object_id)
+ if not last or last.value > value:
+ return object_id, value
+ return object_id, value-last.value
+
+ def monitor(self, container):
+ """ Get OpenVZ container traffic on a Proxmox +2.0 cluster """
+ context = self.get_context(container)
+ self.append(
+ "CONF=$(grep -r 'HOSTNAME=\"%(hostname)s\"' /etc/pve/nodes/*/openvz/*.conf)" % context)
+ self.append('NODE=$(echo "${CONF}" | cut -d"/" -f5)')
+ self.append('CTID=$(echo "${CONF}" | cut -d"/" -f7 | cur -d"\." -f1)')
+ self.append(
+ "ssh root@${NODE} vzctl exec ${CTID} cat /proc/net/dev \\\n"
+ " | grep venet0 \\\n"
+ " | awk -F: '{print $2}' \\\n"
+ " | awk '{print $1+$9}'")
+
+ def get_context(self, container):
+ return {
+ 'hostname': container.hostname,
+ }
diff --git a/orchestra/apps/websites/backends/apache.py b/orchestra/apps/websites/backends/apache.py
index 5c751eda..a58ce5f2 100644
--- a/orchestra/apps/websites/backends/apache.py
+++ b/orchestra/apps/websites/backends/apache.py
@@ -219,9 +219,11 @@ class Apache2Traffic(ServiceMonitor):
}' %(log_file)s || echo 0; } | xargs echo %(object_id)s """ % context)
def get_context(self, site):
+ last_date = timezone.localtime(self.get_last_date(site.pk))
+ current_date = timezone.localtime(self.current_date)
return {
'log_file': os.path.join(settings.WEBSITES_BASE_APACHE_LOGS, site.unique_name),
- 'last_date': timezone.localtime(self.get_last_date(site)).strftime("%Y%m%d%H%M%S"),
- 'current_date': timezone.localtime(self.get_current_date()).strftime("%Y%m%d%H%M%S"),
+ 'last_date': last_date.strftime("%Y%m%d%H%M%S"),
+ 'current_date': current_date.strftime("%Y%m%d%H%M%S"),
'object_id': site.pk,
}
diff --git a/orchestra/utils/time.py b/orchestra/utils/time.py
deleted file mode 100644
index f3cf5cf7..00000000
--- a/orchestra/utils/time.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from __future__ import absolute_import
-
-from datetime import datetime
-
-from django.utils.timesince import timesince as django_timesince
-from django.utils.timezone import is_aware, utc
-
-
-# TODO deprecate in favour of celery timesince
-def timesince(d, now=None, reversed=False):
- """ Hack to provide second precision under 2 minutes """
- if not now:
- now = datetime.now(utc if is_aware(d) else None)
-
- delta = (d - now) if reversed else (now - d)
- s = django_timesince(d, now=now, reversed=reversed)
-
- if len(s.split(' ')) is 2:
- count, name = s.split(' ')
- if name in ['minutes', 'minute']:
- seconds = delta.seconds % 60
- extension = '%(number)d %(type)s' % {'number': seconds, 'type': 'seconds'}
- if int(count) is 0:
- return extension
- elif int(count) < 2:
- s += ', %s' % extension
- return s
-
-
-def timeuntil(d, now=None):
- """
- Like timesince, but returns a string measuring the time until
- the given time.
- """
- return timesince(d, now, reversed=True)