Élie Bouttier 7 years ago
parent
commit
000d598926

+ 20 - 0
banking/migrations/0009_auto_20180208_2022.py

@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.9 on 2018-02-08 19:22
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('banking', '0008_paymentupdate_month_day'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='paymentupdate',
+            name='payment_method',
+            field=models.IntegerField(choices=[(2, 'Prélèvement'), (3, 'Virement'), (4, 'Liquide'), (5, 'Facture'), (1, 'Gratuit'), (0, 'Arrêt')], default=2, verbose_name='Méthode de paiement'),
+        ),
+    ]

+ 59 - 2
services/admin.py

@@ -25,7 +25,9 @@ from datetime import timedelta
 from djadhere.utils import get_active_filter
 from adhesions.models import Adhesion
 from banking.models import PaymentUpdate
-from .models import Service, ServiceType, IPPrefix, IPResource, Route, Tunnel, ServiceAllocation, Antenna, AntennaAllocation, Allocation
+from .models import Service, ServiceType, IPPrefix, IPResource, Route, Tunnel, \
+                    ServiceAllocation, Antenna, AntennaAllocation, Allocation, \
+                    Switch, Port
 from .utils import notify_allocation
 from .forms import AntennaForm
 
@@ -245,6 +247,33 @@ class InactiveAntennaAllocationInline(AntennaAllocationMixin, InactiveAllocation
     pass
 
 
+class PortInline(admin.TabularInline):
+    model = Port
+
+    def has_add_permission(self, request):
+        return False
+
+    def has_delete_permission(self, request, obj=None):
+        return False
+
+
+class SwitchPortInline(PortInline):
+    fields = ('port', 'reserved', 'service', 'notes',)
+    readonly_fields = ('port',)
+    raw_id_fields = ('service',)
+
+    def get_queryset(self, request):
+        qs = super().get_queryset(request)
+        qs = qs.prefetch_related('switch', 'service', 'service__service_type')
+        return qs
+
+
+class ServicePortInline(PortInline):
+    fields = ('switch', 'port', 'notes',)
+    readonly_fields = ('switch', 'port',)
+    max_num = 0
+
+
 ### Actions
 
 def ends_resource(resource, request, queryset):
@@ -263,7 +292,6 @@ class ServiceAdmin(admin.ModelAdmin):
         ActiveServiceFilter,
         ('service_type', admin.RelatedOnlyFieldListFilter),
     )
-    inlines = (ActiveServiceAllocationInline, InactiveServiceAllocationInline,)
     search_fields = ('=id', 'service_type__name', 'label', 'notes',)
     fields = ('adhesion', 'service_type', 'label', 'notes', 'get_contribution_link', 'is_active',)
     readonly_fields = ('get_contribution_link', 'is_active',)
@@ -279,6 +307,13 @@ class ServiceAdmin(admin.ModelAdmin):
         return format_html(u'<a href="{}">{}</a>', obj.contribution.get_absolute_url(), obj.contribution)
     get_contribution_link.short_description = 'Contribution financière'
 
+    def get_inline_instances(self, request, obj=None):
+        inlines = []
+        if obj and obj.ports.exists():
+            inlines += [ServicePortInline]
+        inlines += [ActiveServiceAllocationInline, InactiveServiceAllocationInline]
+        return [inline(self.model, self.admin_site) for inline in inlines]
+
     def get_actions(self, request):
         actions = super().get_actions(request)
         if 'delete_selected' in actions:
@@ -615,6 +650,27 @@ class AntennaAdmin(admin.ModelAdmin):
         return HttpResponse(geojson, content_type='application/json')
 
 
+class SwitchAdmin(admin.ModelAdmin):
+    list_display = ('name', 'size',)
+    fields = ('name', 'size', 'notes',)
+    inlines = (SwitchPortInline,)
+
+    def get_readonly_fields(self, request, obj=None):
+        if obj:
+            return ('size',)
+        else:
+            return ()
+
+    def get_actions(self, request):
+        actions = super().get_actions(request)
+        if 'delete_selected' in actions:
+            del actions['delete_selected']
+        return actions
+
+    def has_delete_permission(self, request, obj=None):
+        return False
+
+
 admin.site.register(ServiceType, ServiceTypeAdmin)
 admin.site.register(Service, ServiceAdmin)
 admin.site.register(IPPrefix, IPPrefixAdmin)
@@ -622,3 +678,4 @@ admin.site.register(IPResource, IPResourceAdmin)
 admin.site.register(Route, RouteAdmin)
 admin.site.register(Tunnel, TunnelAdmin)
 admin.site.register(Antenna, AntennaAdmin)
+admin.site.register(Switch, SwitchAdmin)

+ 65 - 0
services/migrations/0044_auto_20180208_2310.py

@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.9 on 2018-02-08 22:10
+from __future__ import unicode_literals
+
+import django.core.validators
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('services', '0043_ipresource_checkmk_label'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Port',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('port', models.IntegerField(verbose_name='N° de port')),
+                ('reserved', models.BooleanField(default=False, verbose_name='réservé')),
+                ('notes', models.CharField(blank=True, default='', max_length=128)),
+            ],
+            options={
+                'ordering': ('switch', 'port'),
+            },
+        ),
+        migrations.CreateModel(
+            name='Switch',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=64, unique=True, verbose_name='Nom')),
+                ('size', models.IntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(64)], verbose_name='Nombre de ports')),
+                ('notes', models.TextField(blank=True, default='')),
+            ],
+            options={
+                'ordering': ('name',),
+            },
+        ),
+        migrations.AlterField(
+            model_name='service',
+            name='service_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='services', to='services.ServiceType', verbose_name='Type de service'),
+        ),
+        migrations.AlterField(
+            model_name='serviceallocation',
+            name='route',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='allocations', related_query_name='allocation', to='services.Route', verbose_name='Route'),
+        ),
+        migrations.AddField(
+            model_name='port',
+            name='service',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ports', to='services.Service'),
+        ),
+        migrations.AddField(
+            model_name='port',
+            name='switch',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ports', to='services.Switch'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='port',
+            unique_together=set([('switch', 'port')]),
+        ),
+    ]

+ 41 - 8
services/models.py

@@ -10,7 +10,7 @@ from django.urls import reverse
 from django.utils import timezone
 from django.utils.html import format_html, mark_safe, escape
 from django.core.exceptions import PermissionDenied
-from django.core.validators import RegexValidator
+from django.core.validators import RegexValidator, MinValueValidator, MaxValueValidator
 
 from ipaddress import ip_network
 
@@ -114,6 +114,7 @@ class IPResource(models.Model):
     @property
     def checkmk_url(self):
         if self.checkmk_label:
+            # TODO: put url in config
             return mark_safe('https://nagios.tetaneutral.net/check_mk/view.py?host={}&view_name=host'.format(escape(self.checkmk_label)))
         else:
             return None
@@ -140,9 +141,9 @@ class ServiceType(models.Model):
 
 
 class Service(models.Model):
-    adhesion = models.ForeignKey(Adhesion, verbose_name='Adhérent', related_name='services')
+    adhesion = models.ForeignKey(Adhesion, verbose_name='Adhérent', related_name='services', on_delete=models.CASCADE)
     service_type = models.ForeignKey(ServiceType, related_name='services',
-                                     verbose_name='Type de service')
+                                     verbose_name='Type de service', on_delete=models.PROTECT)
     label = models.CharField(blank=True, default='', max_length=128)
     notes = models.TextField(blank=True, default='')
     created = models.DateTimeField(auto_now_add=True)
@@ -296,9 +297,9 @@ class Allocation(models.Model):
 
 class ServiceAllocation(Allocation):
     resource = models.ForeignKey(IPResource, verbose_name='Ressource', related_name='service_allocations',
-                        related_query_name='service_allocation', limit_choices_to={'category': 0})
-    service = models.ForeignKey(Service, related_name='allocations', related_query_name='allocation')
-    route = models.ForeignKey(Route, verbose_name='Route', related_name='allocations', related_query_name='allocation')
+                        related_query_name='service_allocation', limit_choices_to={'category': 0}, on_delete=models.CASCADE)
+    service = models.ForeignKey(Service, related_name='allocations', related_query_name='allocation', on_delete=models.CASCADE)
+    route = models.ForeignKey(Route, verbose_name='Route', related_name='allocations', related_query_name='allocation', on_delete=models.PROTECT)
 
     class Meta:
         verbose_name = 'allocation'
@@ -307,9 +308,41 @@ class ServiceAllocation(Allocation):
 
 class AntennaAllocation(Allocation):
     resource = models.ForeignKey(IPResource, verbose_name='Ressource', related_name='antenna_allocations',
-                        related_query_name='antenna_allocation', limit_choices_to={'category': 1})
-    antenna = models.ForeignKey(Antenna, related_name='allocations', related_query_name='allocation')
+                        related_query_name='antenna_allocation', limit_choices_to={'category': 1}, on_delete=models.CASCADE)
+    antenna = models.ForeignKey(Antenna, related_name='allocations', related_query_name='allocation', on_delete=models.CASCADE)
 
     class Meta:
         verbose_name = 'allocation'
         verbose_name_plural = 'allocations'
+
+
+class Switch(models.Model):
+    name = models.CharField(max_length=64, verbose_name='Nom', unique=True)
+    size = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(64)],
+                               verbose_name='Nombre de ports')
+    notes = models.TextField(blank=True, default='')
+
+    class Meta:
+        ordering = ('name',)
+
+    def __str__(self):
+        return self.name
+
+
+class Port(models.Model):
+    switch = models.ForeignKey(Switch, related_name='ports', on_delete=models.CASCADE)
+    service = models.ForeignKey(Service, null=True, blank=True, related_name='ports', on_delete=models.SET_NULL)
+    port = models.IntegerField(verbose_name='N° de port')
+    reserved = models.BooleanField(default=False, verbose_name='réservé')
+    notes = models.CharField(max_length=128, blank=True, default='')
+
+    def clean(self):
+        if self.reserved and self.service:
+            raise ValidationError('Un port réservé ne peut avoir de service.')
+
+    class Meta:
+        unique_together = ('switch', 'port',)
+        ordering = ('switch', 'port',)
+
+    def __str__(self):
+        return '%s #%d' % (self.switch, self.port)

+ 8 - 1
services/signals.py

@@ -1,7 +1,7 @@
 from django.dispatch import receiver
 from django.db.models.signals import post_save, post_delete
 
-from .models import Service, IPPrefix, IPResource
+from .models import Service, IPPrefix, IPResource, Switch, Port
 
 from ipaddress import ip_address, ip_network
 
@@ -31,3 +31,10 @@ def ip_resource(sender, instance, created, **kwargs):
 @receiver(post_delete, sender=Service, dispatch_uid='delete_service_contribution')
 def delete_service_contribution(sender, instance, **kwargs):
     instance.contribution.delete()
+
+
+@receiver(post_save, sender=Switch, dispatch_uid='create_switch_ports')
+def create_switch_ports(sender, instance, created, **kwargs):
+    if created:
+        ports = [ Port(switch=instance, port=i) for i in range(1, instance.size + 1) ]
+        Port.objects.bulk_create(ports)