Élie Bouttier 7 years ago
parent
commit
780e5568b0

+ 1 - 0
djadhere/settings.py

@@ -53,6 +53,7 @@ INSTALLED_APPS = [
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'django.contrib.gis',
+    'django.contrib.humanize',
 ]
 
 MIDDLEWARE = [

+ 22 - 23
services/admin.py

@@ -58,21 +58,21 @@ class ResourcePingFilter(admin.SimpleListFilter):
 
     def lookups(self, request, model_admin):
         return (
-            ('up', 'Up'),
-            ('down', 'Down'),
-            ('unknown', 'Inconnu'),
+            ('up', 'UP'),
+            ('down', 'DOWN'),
+            ('down-since', 'DOWN depuis…'),
+            ('never-up', 'Jamais vu UP'),
         )
 
     def queryset(self, request, queryset):
-        known_filter = models.Q(last_time_up__isnull=False, last_check__isnull=False)
-        if self.value() == 'unknown':
-            return queryset.exclude(known_filter)
-        else:
-            queryset = queryset.filter(known_filter)
-            if self.value() == 'up':
-                return queryset.filter(last_check__lte=models.F('last_time_up'))
-            if self.value() == 'down':
-                return queryset.filter(last_check__gt=models.F('last_time_up'))
+        if self.value() == 'up':
+            return queryset.filter(up=True)
+        if self.value() == 'down':
+            return queryset.filter(models.Q(up__isnull=True) | models.Q(up=False))
+        if self.value() == 'down-since':
+            return queryset.filter(up=False)
+        if self.value() == 'never-up':
+            return queryset.filter(up__isnull=True)
 
 
 class ActiveServiceFilter(admin.SimpleListFilter):
@@ -416,14 +416,13 @@ class IPResourceAdmin(admin.ModelAdmin):
                     models.When(category=1, then=models.Max('antenna_allocation__end')),
                     default=None,
                 ))
-        qs = qs.annotate(downtime=Cast(
-                    models.Case(
-                        models.When(last_check__isnull=False, last_time_up__isnull=False, last_check__lte=models.F('last_time_up'), then=timedelta(days=0)),
-                        models.When(last_check__isnull=False, last_time_up__isnull=False, then=models.F('last_check') - models.F('last_time_up')),
-                        default=None,
-                    ),
-                    models.DurationField(),
+        qs = qs.annotate(up=models.Case(
+                    models.When(last_check__isnull=False, last_time_up__isnull=False, last_time_up=models.F('last_check'), then=True),
+                    models.When(last_check__isnull=False, last_time_up__isnull=False, then=False),
+                    default=None,
+                    output_field=models.NullBooleanField(),
                 ))
+        qs = qs.annotate(downtime=models.F('last_check') - models.F('last_time_up'))
         return qs
 
     def available_display(self, obj):
@@ -440,12 +439,12 @@ class IPResourceAdmin(admin.ModelAdmin):
     last_use.admin_order_field = 'last_use'
 
     def ping(self, obj):
-        if obj.downtime:
-            label = 'down depuis >' + str(obj.downtime)
-        elif obj.downtime == timedelta(days=0):
+        if obj.up:
             label = 'UP'
+        elif obj.downtime:
+            label = 'dernier ping : ' + naturaltime(timezone.now() - obj.downtime)
         else:
-            return None
+            label = 'DOWN'
         if obj.checkmk_url:
             return format_html('<a href="{}">{}</a>', obj.checkmk_url, label)
         else:

+ 15 - 0
services/management/commands/fastping.py

@@ -0,0 +1,15 @@
+from django.core.management.base import BaseCommand, CommandError
+
+from services.utils import fastping_update
+
+
+class Command(BaseCommand):
+    help = 'Analyse d’un fichier fastping'
+
+    def add_arguments(self, parser):
+        parser.add_argument('file', help='Fichier fastping')
+
+    def handle(self, *args, **options):
+        with open(options['file'], 'rb') as f:
+            stats = fastping_update(f)
+        self.stdout.write(stats + '\n')

+ 3 - 8
services/management/commands/fetchping.py

@@ -11,18 +11,13 @@ from djadhere.utils import from_livestatus
 
 
 class Command(BaseCommand):
-    help = 'Récupération du dernier ping depuis check_mk'
+    help = 'Récupération du label check_mk'
 
     def handle(self, *args, **options):
         paris = timezone('Europe/Paris')
-        hosts = from_livestatus('hosts', columns=['name', 'address', 'last_check', 'last_time_up'])
+        hosts = from_livestatus('hosts', columns=['name', 'address'])
         data = {}
         # quelques IP sont listées curieusement plusieurs fois, on prend la première occurence
         for address, group in groupby(sorted(hosts, key=lambda host: host.address), lambda host: host.address):
-            last_check = last_time_up = 0
             host = next(group)
-            IPResource.objects.filter(ip=address).update(
-                checkmk_label=host.name,
-                last_check=paris.localize(datetime.fromtimestamp(int(host.last_check))),
-                last_time_up=paris.localize(datetime.fromtimestamp(int(host.last_time_up))),
-            )
+            IPResource.objects.filter(ip=address).update(checkmk_label=host.name)

+ 18 - 0
services/migrations/0049_auto_20180212_2051.py

@@ -0,0 +1,18 @@
+# Generated by Django 2.0.2 on 2018-02-12 19:51
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('services', '0048_service_loan_equipment'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='ipresource',
+            name='last_check',
+            field=models.DateTimeField(blank=True, null=True, verbose_name='Dernier contrôle'),
+        ),
+    ]

+ 1 - 1
services/models.py

@@ -90,7 +90,7 @@ class IPResource(models.Model):
     notes = models.TextField(blank=True, default='')
     checkmk_label = models.CharField(max_length=128, blank=True, default='')
     last_time_up = models.DateTimeField(null=True, blank=True, verbose_name='Dernière réponse au ping')
-    last_check = models.DateTimeField(null=True, blank=True, verbose_name='Dernier contrôle CheckMK')
+    last_check = models.DateTimeField(null=True, blank=True, verbose_name='Dernier contrôle')
 
     objects = IPResourceManager()
 

+ 2 - 2
services/templates/services/service_detail.html

@@ -1,6 +1,6 @@
 {% extends 'base.html' %}
 
-{% load bootstrap3 %}
+{% load bootstrap3 humanize %}
 
 {% block usertab %}{% if service.adhesion == request.user.adhesion %} class="active"{% endif %}{% endblock %}
 {% block corptab %}{% if service.adhesion != request.user.adhesion %} active{% endif %}{% endblock %}
@@ -25,7 +25,7 @@
             IP allouée{{ service.active_allocations.count|pluralize }} :
             {% for allocation in service.active_allocations.all %}
             {% if forloop.first %}<ul>{% endif %}
-                <li>{{ allocation.resource }}{% if allocation.resource.last_check %} (dernière réponse au ping : {{ allocation.resource.last_time_up }}){% endif %}</li>
+                <li>{{ allocation.resource }}{% if allocation.resource.last_time_up %} (dernière réponse au ping : {{ allocation.resource.last_time_up|naturaltime }}){% endif %}</li>
             {% if forloop.last %}</ul>{% endif %}
             {% empty %}
             aucune IP allouée

+ 1 - 0
services/urls.py

@@ -5,4 +5,5 @@ from . import views
 
 urlpatterns = [
     url(r'^services/(?P<pk>[0-9]+)/$', views.ServiceDetail.as_view(), name='service-detail'),
+    url(r'^api/fastping/$', views.fastping, name='fastping'),
 ]

+ 54 - 0
services/utils.py

@@ -1,7 +1,13 @@
 from django.urls import reverse
 from django.conf import settings
+from django.db.models import Q, F
+from django.utils import timezone
 
 from djadhere.utils import send_notification
+from services.models import IPResource
+
+import re
+from ipaddress import IPv4Address, AddressValueError
 
 
 def notify_allocation(request, new_alloc, old_alloc=None):
@@ -41,3 +47,51 @@ def notify_allocation(request, new_alloc, old_alloc=None):
     if sujet:
         sujet += ' ADT%d' % new_alloc.service.adhesion.pk
         send_notification(sujet, message, settings.ALLOCATIONS_EMAILS, cc=[benevole])
+
+
+def fastping_update(f):
+    regex = re.compile('^(?P<ip>[0-9.]+)[ ]+: (?P<p1>([0-9]+.[0-9]+|-)) (?P<p2>([0-9]+.[0-9]+|-))'
+                       ' (?P<p3>([0-9]+.[0-9]+|-)) (?P<p4>([0-9]+.[0-9]+|-)) (?P<p5>([0-9]+.[0-9]+|-))$')
+    up_count = IPResource.objects.filter(last_check__isnull=False).filter(last_time_up=F('last_check')).count()
+    down_count = IPResource.objects.filter(last_check__isnull=False).exclude(last_time_up=F('last_check')).count()
+    unknown_count = IPResource.objects.filter(last_check__isnull=True).count()
+
+    up, down, unknown = set(), set(), set()
+    for line in f:
+        try:
+            line = line.decode('utf-8')
+        except UnicodeDecodeError:
+            continue
+        g = regex.match(line)
+        if not g:
+            continue
+        try:
+            ip = IPv4Address(g.group('ip'))
+        except AddressValueError:
+            continue
+        p = [ g.group('p%d' % i) for i in [1, 2, 3, 4, 5] ]
+        if all(map(lambda p: p == '-', p)):
+            down.add(ip.compressed)
+        else:
+            up.add(ip.compressed)
+
+    up = up - down # suppression des doublons
+
+    up = IPResource.objects.filter(ip__in=up)
+    down = IPResource.objects.filter(ip__in=down)
+    unknown = IPResource.objects.exclude(Q(pk__in=up) | Q(pk__in=down))
+
+    up.exclude(last_time_up=F('last_check')).count()
+    down.filter(last_time_up=F('last_check')).count()
+
+    now = timezone.now()
+
+    up.update(last_time_up=now, last_check=now)
+    down.update(last_check=now)
+
+    upped = len(up) - up_count
+    downed = len(down) - down_count
+    unknowned = len(unknown) - unknown_count
+
+    return "UP: %d (%+d), DOWN: %d (%+d), UNKNOWN: %d (%+d)" \
+            % (len(up), upped, len(down), downed, len(unknown), unknowned)

+ 16 - 0
services/views.py

@@ -1,10 +1,26 @@
 from django.views.generic import DetailView
 from django.contrib.auth.mixins import LoginRequiredMixin
+from django.views.decorators.http import require_POST
+from django.http import HttpResponseForbidden, HttpResponse
+from django.conf import settings
+from django.views.decorators.csrf import csrf_exempt
 
 from .models import Service
+from .utils import fastping_update
 
 
 class ServiceDetail(LoginRequiredMixin, DetailView):
     def get_queryset(self):
         adhesions = self.request.user.profile.adhesions.values_list('pk')
         return Service.objects.filter(adhesion__pk__in=adhesions).order_by('service_type')
+
+
+@csrf_exempt
+@require_POST
+def fastping(request):
+    if request.POST.get('key', None) != settings.FASTPING_KEY:
+        return HttpResponseForbidden('Invalid key.')
+    if 'fastping' not in request.FILES:
+        return HttpResponse(status=400)  # Bad Request
+    stats = fastping_update(request.FILES['fastping'])
+    return HttpResponse(stats + '\n')