From 7ab0d656c937d4ccd88df00ca694479ebc24fb64 Mon Sep 17 00:00:00 2001 From: Laouen Fernet Date: Sun, 27 May 2018 00:36:25 +0200 Subject: [PATCH 01/12] add model PortProfile --- re2o/templatetags/acl.py | 1 + re2o/views.py | 1 + topologie/admin.py | 7 +- topologie/forms.py | 58 ++++++++++++++++ topologie/migrations/0061_portprofile.py | 36 ++++++++++ .../migrations/0062_auto_20180609_1151.py | 66 +++++++++++++++++++ .../migrations/0063_auto_20180609_1158.py | 20 ++++++ .../migrations/0064_auto_20180609_1220.py | 20 ++++++ topologie/models.py | 55 ++++++++++++++++ .../templates/topologie/aff_port_profile.html | 46 +++++++++++++ topologie/templates/topologie/index.html | 10 +++ topologie/urls.py | 9 +++ topologie/views.py | 65 +++++++++++++++++- 13 files changed, 391 insertions(+), 3 deletions(-) create mode 100644 topologie/migrations/0061_portprofile.py create mode 100644 topologie/migrations/0062_auto_20180609_1151.py create mode 100644 topologie/migrations/0063_auto_20180609_1158.py create mode 100644 topologie/migrations/0064_auto_20180609_1220.py create mode 100644 topologie/templates/topologie/aff_port_profile.html diff --git a/re2o/templatetags/acl.py b/re2o/templatetags/acl.py index fccbea1d..11399633 100644 --- a/re2o/templatetags/acl.py +++ b/re2o/templatetags/acl.py @@ -129,6 +129,7 @@ MODEL_NAME = { 'Room': topologie.models.Room, 'Building': topologie.models.Building, 'SwitchBay': topologie.models.SwitchBay, + 'PortProfile': topologie.models.PortProfile, # users 'User': users.models.User, 'Adherent': users.models.Adherent, diff --git a/re2o/views.py b/re2o/views.py index 3c5cde09..d803505e 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -111,6 +111,7 @@ HISTORY_BIND = { 'accesspoint': topologie.models.AccessPoint, 'switchbay': topologie.models.SwitchBay, 'building': topologie.models.Building, + 'portprofile': topologie.models.PortProfile, }, 'machines': { 'machine': machines.models.Machine, diff --git a/topologie/admin.py b/topologie/admin.py index 62ffd6c4..d2a25461 100644 --- a/topologie/admin.py +++ b/topologie/admin.py @@ -38,7 +38,8 @@ from .models import ( ConstructorSwitch, AccessPoint, SwitchBay, - Building + Building, + PortProfile, ) @@ -86,6 +87,9 @@ class BuildingAdmin(VersionAdmin): """Administration d'un batiment""" pass +class PortProfileAdmin(VersionAdmin): + """Administration of a port profile""" + pass admin.site.register(Port, PortAdmin) admin.site.register(AccessPoint, AccessPointAdmin) @@ -96,3 +100,4 @@ admin.site.register(ModelSwitch, ModelSwitchAdmin) admin.site.register(ConstructorSwitch, ConstructorSwitchAdmin) admin.site.register(Building, BuildingAdmin) admin.site.register(SwitchBay, SwitchBayAdmin) +admin.site.register(PortProfile, PortProfileAdmin) diff --git a/topologie/forms.py b/topologie/forms.py index 18831217..377a39b3 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -35,6 +35,7 @@ from __future__ import unicode_literals from django import forms from django.forms import ModelForm from django.db.models import Prefetch +from django.utils.translation import ugettext_lazy as _ from machines.models import Interface from machines.forms import ( @@ -53,6 +54,7 @@ from .models import ( AccessPoint, SwitchBay, Building, + PortProfile, ) @@ -262,3 +264,59 @@ class EditBuildingForm(FormRevMixin, ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(EditBuildingForm, self).__init__(*args, prefix=prefix, **kwargs) + + +class NewPortProfileForm(FormRevMixin, ModelForm): + """Form to create a port profile""" + class Meta: + model = PortProfile + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(NewPortProfileForm, self).__init__(*args, + prefix=prefix, + **kwargs) + + def clean(self): + cleaned_data = super(NewPortProfileForm, self).clean() + radius_type = cleaned_data.get('radius_type') + radius_mode = cleaned_data.get('radius_mode') + + if radius_type == 'NO' and radius_mode: + raise forms.ValidationError(_("You can't specify a RADIUS mode" + " with RADIUS type NO")) + elif radius_type != 'NO' and not radius_mode: + raise forms.ValidationError(_("You have to specify a RADIUS" + " mode")) + + return cleaned_data + + +class EditPortProfileForm(FormRevMixin, ModelForm): + """Form to edit a port profile""" + class Meta: + model = PortProfile + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(EditPortProfileForm, self).__init__(*args, + prefix=prefix, + **kwargs) + + def clean(self): + cleaned_data = super(EditPortProfileForm, self).clean() + radius_type = cleaned_data.get('radius_type') + radius_mode = cleaned_data.get('radius_mode') + + if radius_type == 'NO' and radius_mode: + raise forms.ValidationError(_("You can't specify a RADIUS mode" + " with RADIUS type NO")) + elif radius_type != 'NO' and not radius_mode: + raise forms.ValidationError(_("You have to specify a RADIUS" + " mode")) + + return cleaned_data + + diff --git a/topologie/migrations/0061_portprofile.py b/topologie/migrations/0061_portprofile.py new file mode 100644 index 00000000..7b6af2ed --- /dev/null +++ b/topologie/migrations/0061_portprofile.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-05-26 22:26 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import re2o.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0081_auto_20180521_1413'), + ('topologie', '0060_server'), + ] + + operations = [ + migrations.CreateModel( + name='PortProfile', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('room_default', models.BooleanField()), + ('hotspot_default', models.BooleanField()), + ('uplink_default', models.BooleanField()), + ('orga_machine_default', models.BooleanField()), + ('radius_type', models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], max_length=32)), + ('radius_mode', models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], max_length=32)), + ('vlan_tagged', models.ManyToManyField(related_name='vlan_tagged', to='machines.Vlan')), + ('vlan_untagged', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vlan_untagged', to='machines.Vlan')), + ], + options={ + 'permissions': (('view_port_profile', 'Can view a port profile object'),), + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + ] diff --git a/topologie/migrations/0062_auto_20180609_1151.py b/topologie/migrations/0062_auto_20180609_1151.py new file mode 100644 index 00000000..3f074f7c --- /dev/null +++ b/topologie/migrations/0062_auto_20180609_1151.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-09 16:51 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0061_portprofile'), + ] + + operations = [ + migrations.AlterModelOptions( + name='portprofile', + options={'permissions': (('view_port_profile', 'Can view a port profile object'),), 'verbose_name': 'Port profile', 'verbose_name_plural': 'Port profiles'}, + ), + migrations.AddField( + model_name='portprofile', + name='name', + field=models.CharField(default='Sans nom', max_length=255, verbose_name='Name'), + preserve_default=False, + ), + migrations.AlterField( + model_name='portprofile', + name='hotspot_default', + field=models.BooleanField(verbose_name='Hotspot default'), + ), + migrations.AlterField( + model_name='portprofile', + name='orga_machine_default', + field=models.BooleanField(verbose_name='Organisation machine default'), + ), + migrations.AlterField( + model_name='portprofile', + name='radius_mode', + field=models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], max_length=32, verbose_name='RADIUS mode'), + ), + migrations.AlterField( + model_name='portprofile', + name='radius_type', + field=models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], max_length=32, verbose_name='RADIUS type'), + ), + migrations.AlterField( + model_name='portprofile', + name='room_default', + field=models.BooleanField(verbose_name='Room default'), + ), + migrations.AlterField( + model_name='portprofile', + name='uplink_default', + field=models.BooleanField(verbose_name='Uplink default'), + ), + migrations.AlterField( + model_name='portprofile', + name='vlan_tagged', + field=models.ManyToManyField(related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged'), + ), + migrations.AlterField( + model_name='portprofile', + name='vlan_untagged', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vlan_untagged', to='machines.Vlan', verbose_name='VLAN untagged'), + ), + ] diff --git a/topologie/migrations/0063_auto_20180609_1158.py b/topologie/migrations/0063_auto_20180609_1158.py new file mode 100644 index 00000000..59ea8732 --- /dev/null +++ b/topologie/migrations/0063_auto_20180609_1158.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-09 16:58 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0062_auto_20180609_1151'), + ] + + operations = [ + migrations.AlterField( + model_name='portprofile', + name='vlan_tagged', + field=models.ManyToManyField(blank=True, related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged'), + ), + ] diff --git a/topologie/migrations/0064_auto_20180609_1220.py b/topologie/migrations/0064_auto_20180609_1220.py new file mode 100644 index 00000000..f657a612 --- /dev/null +++ b/topologie/migrations/0064_auto_20180609_1220.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-09 17:20 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0063_auto_20180609_1158'), + ] + + operations = [ + migrations.AlterField( + model_name='portprofile', + name='radius_mode', + field=models.CharField(blank=True, choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], max_length=32, null=True, verbose_name='RADIUS mode'), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index e1c945c7..cdf30787 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -46,6 +46,7 @@ from django.dispatch import receiver from django.core.exceptions import ValidationError from django.db import IntegrityError from django.db import transaction +from django.utils.translation import ugettext_lazy as _ from reversion import revisions as reversion from machines.models import Machine, regen @@ -488,6 +489,60 @@ class Room(AclMixin, RevMixin, models.Model): return self.name +class PortProfile(AclMixin, models.Model): + """Contains the information of the ports' configuration for a switch""" + TYPES = ( + ('NO', 'NO'), + ('802.1X', '802.1X'), + ('MAC-radius', 'MAC-radius'), + ) + MODES = ( + ('STRICT', 'STRICT'), + ('COMMON', 'COMMON'), + ) + name = models.CharField(max_length=255, verbose_name=_("Name")) + room_default = models.BooleanField(verbose_name=_("Room default")) + hotspot_default = models.BooleanField(_("Hotspot default")) + uplink_default = models.BooleanField(_("Uplink default")) + orga_machine_default = models.BooleanField(_("Organisation machine" + " default")) + vlan_untagged = models.ForeignKey( + 'machines.Vlan', + related_name='vlan_untagged', + on_delete=models.SET_NULL, + blank=True, + null=True, + verbose_name=_("VLAN untagged") + ) + vlan_tagged = models.ManyToManyField( + 'machines.Vlan', + related_name='vlan_tagged', + blank=True, + verbose_name=_("VLAN(s) tagged")) + radius_type = models.CharField( + max_length=32, + choices=TYPES, + verbose_name=_("RADIUS type") + ) + radius_mode = models.CharField( + max_length=32, + choices=MODES, + blank=True, + null=True, + verbose_name=_("RADIUS mode") + ) + + class Meta: + permissions = ( + ("view_port_profile", _("Can view a port profile object")), + ) + verbose_name = _("Port profile") + verbose_name_plural = _("Port profiles") + + def __str__(self): + return self.name + + @receiver(post_save, sender=AccessPoint) def ap_post_save(**_kwargs): """Regeneration des noms des bornes vers le controleur""" diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html new file mode 100644 index 00000000..b4b936c5 --- /dev/null +++ b/topologie/templates/topologie/aff_port_profile.html @@ -0,0 +1,46 @@ +{% load acl %} +{% load i18n %} + +
+ +{% if port_profile_list.paginator %} +{% include "pagination.html" with list=port_profile_list %} +{% endif %} + + + + + + + + + + + + {% for port_profile in port_profile_list %} + + + + + + + + + {% endfor %} +
{% trans "Name" %}{% trans "VLAN untagged" %}{% trans "VLAN(s) tagged" %}{% trans "RADIUS type" %}{% trans "RADIUS mode" %}
{{port_profile.name}}{{port_profile.vlan_untagged}} + {{port_profile.vlan_tagged.all|join:", "}} + {{port_profile.radius_type}}{{port_profile.radius_mode}} + {% include 'buttons/history.html' with href='topologie:history' name='portprofile' id=port_profile.pk %} + {% can_edit port_profile %} + {% include 'buttons/edit.html' with href='topologie:edit-port-profile' id=port_profile.pk %} + {% acl_end %} + {% can_delete port_profile %} + {% include 'buttons/suppr.html' with href='topologie:del-port-profile' id=port_profile.pk %} + {% acl_end %} +
+ +{% if port_profile_list.paginator %} +{% include "pagination.html" with list=port_profile_list %} +{% endif %} + +
diff --git a/topologie/templates/topologie/index.html b/topologie/templates/topologie/index.html index a7c4bb51..7902f4a3 100644 --- a/topologie/templates/topologie/index.html +++ b/topologie/templates/topologie/index.html @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load acl %} +{% load i18n %} {% block title %}Switchs{% endblock %} @@ -72,5 +73,14 @@ Topologie des Switchs

+

{% trans "Port profiles" %}

+{% can_create PortProfile %} +{% trans " Add a port profile" %} +
+{% acl_end %} + {% include "topologie/aff_port_profile.html" with port_profile_list=port_profile_list %} +
+
+
{% endblock %} diff --git a/topologie/urls.py b/topologie/urls.py index af3327b7..7cfc5714 100644 --- a/topologie/urls.py +++ b/topologie/urls.py @@ -113,4 +113,13 @@ urlpatterns = [ url(r'^del_building/(?P[0-9]+)$', views.del_building, name='del-building'), + url(r'^new_port_profile/$', + views.new_port_profile, + name='new-port-profile'), + url(r'^edit_port_profile/(?P[0-9]+)$', + views.edit_port_profile, + name='edit-port-profile'), + url(r'^del_port_profile/(?P[0-9]+)$', + views.del_port_profile, + name='del-port-profile'), ] diff --git a/topologie/views.py b/topologie/views.py index 652ef475..f1f6a09f 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -47,6 +47,7 @@ from django.template.loader import get_template from django.template import Context, Template, loader from django.db.models.signals import post_save from django.dispatch import receiver +from django.utils.translation import ugettext as _ import tempfile @@ -80,6 +81,7 @@ from .models import ( SwitchBay, Building, Server, + PortProfile, ) from .forms import ( EditPortForm, @@ -94,7 +96,9 @@ from .forms import ( AddAccessPointForm, EditAccessPointForm, EditSwitchBayForm, - EditBuildingForm + EditBuildingForm, + NewPortProfileForm, + EditPortProfileForm, ) from subprocess import ( @@ -124,8 +128,12 @@ def index(request): request.GET.get('order'), SortTable.TOPOLOGIE_INDEX ) + + port_profile_list = PortProfile.objects.all() + pagination_number = GeneralOption.get_cached_value('pagination_number') switch_list = re2o_paginator(request, switch_list, pagination_number) + port_profile_list = re2o_paginator(request, port_profile_list, pagination_number) if any(service_link.need_regen() for service_link in Service_link.objects.filter(service__service_type='graph_topo')): make_machine_graph() @@ -137,7 +145,7 @@ def index(request): return render( request, 'topologie/index.html', - {'switch_list': switch_list} + {'switch_list': switch_list, 'port_profile_list': port_profile_list} ) @@ -955,6 +963,59 @@ def del_constructor_switch(request, constructor_switch, **_kwargs): }, 'topologie/delete.html', request) +@login_required +@can_create(PortProfile) +def new_port_profile(request): + """Create a new port profile""" + port_profile = NewPortProfileForm(request.POST or None) + if port_profile.is_valid(): + port_profile.save() + messages.success(request, _("Port profile created")) + return redirect(reverse('topologie:index')) + return form( + {'topoform': port_profile, 'action_name': _("Create")}, + 'topologie/topo.html', + request + ) + + +@login_required +@can_edit(PortProfile) +def edit_port_profile(request, port_profile, **_kwargs): + """Edit a port profile""" + port_profile = EditPortProfileForm(request.POST or None, instance=port_profile) + if port_profile.is_valid(): + if port_profile.changed_data: + port_profile.save() + messages.success(request, _("Port profile modified")) + return redirect(reverse('topologie:index')) + return form( + {'topoform': port_profile, 'action_name': _("Edit")}, + 'topologie/topo.html', + request + ) + + + +@login_required +@can_delete(PortProfile) +def del_port_profile(request, port_profile, **_kwargs): + """Delete a port profile""" + if request.method == 'POST': + try: + port_profile.delete() + messages.success(request, _("The port profile was successfully" + " deleted")) + except ProtectedError: + messages.success(request, _("Impossible to delete the port" + " profile")) + return redirect(reverse('topologie:index')) + return form( + {'objet': port_profile, 'objet_name': _("Port profile")}, + 'topologie/delete.html', + request + ) + def make_machine_graph(): """ Create the graph of switchs, machines and access points. From 983b5620aade099f321aab7e4dd4223f5f64f55d Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 26 Jun 2018 16:49:19 +0000 Subject: [PATCH 02/12] Refactor port_profil --- topologie/migrations/0061_portprofile.py | 30 +++-- .../migrations/0062_auto_20180609_1151.py | 66 ----------- .../migrations/0063_auto_20180609_1158.py | 20 ---- .../migrations/0064_auto_20180609_1220.py | 20 ---- topologie/models.py | 111 ++++++++++++++---- .../templates/topologie/aff_port_profile.html | 32 ++++- 6 files changed, 137 insertions(+), 142 deletions(-) delete mode 100644 topologie/migrations/0062_auto_20180609_1151.py delete mode 100644 topologie/migrations/0063_auto_20180609_1158.py delete mode 100644 topologie/migrations/0064_auto_20180609_1220.py diff --git a/topologie/migrations/0061_portprofile.py b/topologie/migrations/0061_portprofile.py index 7b6af2ed..2b326f28 100644 --- a/topologie/migrations/0061_portprofile.py +++ b/topologie/migrations/0061_portprofile.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-05-26 22:26 +# Generated by Django 1.10.7 on 2018-06-26 16:37 from __future__ import unicode_literals from django.db import migrations, models @@ -10,7 +10,7 @@ import re2o.mixins class Migration(migrations.Migration): dependencies = [ - ('machines', '0081_auto_20180521_1413'), + ('machines', '0082_auto_20180525_2209'), ('topologie', '0060_server'), ] @@ -19,18 +19,26 @@ class Migration(migrations.Migration): name='PortProfile', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('room_default', models.BooleanField()), - ('hotspot_default', models.BooleanField()), - ('uplink_default', models.BooleanField()), - ('orga_machine_default', models.BooleanField()), - ('radius_type', models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], max_length=32)), - ('radius_mode', models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], max_length=32)), - ('vlan_tagged', models.ManyToManyField(related_name='vlan_tagged', to='machines.Vlan')), - ('vlan_untagged', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vlan_untagged', to='machines.Vlan')), + ('name', models.CharField(max_length=255, verbose_name='Name')), + ('profil_default', models.CharField(blank=True, choices=[('room', 'room'), ('accespoint', 'accesspoint'), ('uplink', 'uplink'), ('asso_machine', 'asso_machine')], max_length=32, null=True, unique=True, verbose_name='profil default')), + ('radius_type', models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], max_length=32, verbose_name='RADIUS type')), + ('radius_mode', models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], default='COMMON', max_length=32, verbose_name='RADIUS mode')), + ('speed', models.CharField(choices=[('10-half', '10-half'), ('100-half', '100-half'), ('10-full', '10-full'), ('100-full', '100-full'), ('1000-full', '1000-full'), ('auto', 'auto'), ('auto-10', 'auto-10'), ('auto-100', 'auto-100')], default='auto', help_text='Mode de transmission et vitesse du port', max_length=32, verbose_name='Speed')), + ('mac_limit', models.IntegerField(blank=True, help_text='Limit du nombre de mac sur le port', null=True, verbose_name='Mac limit')), + ('flow_control', models.BooleanField(default=False, help_text='Gestion des débits', verbose_name='Flow control')), + ('dhcp_snooping', models.BooleanField(default=False, help_text='Protection dhcp pirate', verbose_name='Dhcp snooping')), + ('dhcpv6_snooping', models.BooleanField(default=False, help_text='Protection dhcpv6 pirate', verbose_name='Dhcpv6 snooping')), + ('arp_protect', models.BooleanField(default=False, help_text="Verification assignation de l'IP par dhcp", verbose_name='Arp protect')), + ('ra_guard', models.BooleanField(default=False, help_text='Protection contre ra pirate', verbose_name='Ra guard')), + ('loop_protect', models.BooleanField(default=False, help_text='Protection contre les boucles', verbose_name='Loop Protect')), + ('vlan_tagged', models.ManyToManyField(blank=True, null=True, related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged')), + ('vlan_untagged', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vlan_untagged', to='machines.Vlan', verbose_name='VLAN untagged')), ], options={ + 'verbose_name': 'Port profile', 'permissions': (('view_port_profile', 'Can view a port profile object'),), + 'verbose_name_plural': 'Port profiles', }, - bases=(re2o.mixins.AclMixin, models.Model), + bases=(re2o.mixins.AclMixin, re2o.mixins.RevMixin, models.Model), ), ] diff --git a/topologie/migrations/0062_auto_20180609_1151.py b/topologie/migrations/0062_auto_20180609_1151.py deleted file mode 100644 index 3f074f7c..00000000 --- a/topologie/migrations/0062_auto_20180609_1151.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-06-09 16:51 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('topologie', '0061_portprofile'), - ] - - operations = [ - migrations.AlterModelOptions( - name='portprofile', - options={'permissions': (('view_port_profile', 'Can view a port profile object'),), 'verbose_name': 'Port profile', 'verbose_name_plural': 'Port profiles'}, - ), - migrations.AddField( - model_name='portprofile', - name='name', - field=models.CharField(default='Sans nom', max_length=255, verbose_name='Name'), - preserve_default=False, - ), - migrations.AlterField( - model_name='portprofile', - name='hotspot_default', - field=models.BooleanField(verbose_name='Hotspot default'), - ), - migrations.AlterField( - model_name='portprofile', - name='orga_machine_default', - field=models.BooleanField(verbose_name='Organisation machine default'), - ), - migrations.AlterField( - model_name='portprofile', - name='radius_mode', - field=models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], max_length=32, verbose_name='RADIUS mode'), - ), - migrations.AlterField( - model_name='portprofile', - name='radius_type', - field=models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], max_length=32, verbose_name='RADIUS type'), - ), - migrations.AlterField( - model_name='portprofile', - name='room_default', - field=models.BooleanField(verbose_name='Room default'), - ), - migrations.AlterField( - model_name='portprofile', - name='uplink_default', - field=models.BooleanField(verbose_name='Uplink default'), - ), - migrations.AlterField( - model_name='portprofile', - name='vlan_tagged', - field=models.ManyToManyField(related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged'), - ), - migrations.AlterField( - model_name='portprofile', - name='vlan_untagged', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vlan_untagged', to='machines.Vlan', verbose_name='VLAN untagged'), - ), - ] diff --git a/topologie/migrations/0063_auto_20180609_1158.py b/topologie/migrations/0063_auto_20180609_1158.py deleted file mode 100644 index 59ea8732..00000000 --- a/topologie/migrations/0063_auto_20180609_1158.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-06-09 16:58 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('topologie', '0062_auto_20180609_1151'), - ] - - operations = [ - migrations.AlterField( - model_name='portprofile', - name='vlan_tagged', - field=models.ManyToManyField(blank=True, related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged'), - ), - ] diff --git a/topologie/migrations/0064_auto_20180609_1220.py b/topologie/migrations/0064_auto_20180609_1220.py deleted file mode 100644 index f657a612..00000000 --- a/topologie/migrations/0064_auto_20180609_1220.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-06-09 17:20 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('topologie', '0063_auto_20180609_1158'), - ] - - operations = [ - migrations.AlterField( - model_name='portprofile', - name='radius_mode', - field=models.CharField(blank=True, choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], max_length=32, null=True, verbose_name='RADIUS mode'), - ), - ] diff --git a/topologie/models.py b/topologie/models.py index cdf30787..c7f0e28f 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -489,7 +489,7 @@ class Room(AclMixin, RevMixin, models.Model): return self.name -class PortProfile(AclMixin, models.Model): +class PortProfile(AclMixin, RevMixin, models.Model): """Contains the information of the ports' configuration for a switch""" TYPES = ( ('NO', 'NO'), @@ -500,37 +500,100 @@ class PortProfile(AclMixin, models.Model): ('STRICT', 'STRICT'), ('COMMON', 'COMMON'), ) + SPEED = ( + ('10-half', '10-half'), + ('100-half', '100-half'), + ('10-full', '10-full'), + ('100-full', '100-full'), + ('1000-full', '1000-full'), + ('auto', 'auto'), + ('auto-10', 'auto-10'), + ('auto-100', 'auto-100'), + ) + PROFIL_DEFAULT= ( + ('room', 'room'), + ('accespoint', 'accesspoint'), + ('uplink', 'uplink'), + ('asso_machine', 'asso_machine'), + ) name = models.CharField(max_length=255, verbose_name=_("Name")) - room_default = models.BooleanField(verbose_name=_("Room default")) - hotspot_default = models.BooleanField(_("Hotspot default")) - uplink_default = models.BooleanField(_("Uplink default")) - orga_machine_default = models.BooleanField(_("Organisation machine" - " default")) + profil_default = models.CharField( + max_length=32, + choices=PROFIL_DEFAULT, + blank=True, + null=True, + unique=True, + verbose_name=_("profil default") + ) vlan_untagged = models.ForeignKey( - 'machines.Vlan', - related_name='vlan_untagged', - on_delete=models.SET_NULL, - blank=True, - null=True, - verbose_name=_("VLAN untagged") + 'machines.Vlan', + related_name='vlan_untagged', + on_delete=models.SET_NULL, + blank=True, + null=True, + verbose_name=_("VLAN untagged") ) vlan_tagged = models.ManyToManyField( - 'machines.Vlan', - related_name='vlan_tagged', - blank=True, - verbose_name=_("VLAN(s) tagged")) + 'machines.Vlan', + related_name='vlan_tagged', + blank=True, + null=True, + verbose_name=_("VLAN(s) tagged") + ) radius_type = models.CharField( - max_length=32, - choices=TYPES, - verbose_name=_("RADIUS type") + max_length=32, + choices=TYPES, + verbose_name=_("RADIUS type") ) radius_mode = models.CharField( - max_length=32, - choices=MODES, - blank=True, - null=True, - verbose_name=_("RADIUS mode") + max_length=32, + choices=MODES, + default='COMMON', + verbose_name=_("RADIUS mode") ) + speed = models.CharField( + max_length=32, + choices=SPEED, + default='auto', + help_text='Mode de transmission et vitesse du port', + verbose_name=_("Speed") + ) + mac_limit = models.IntegerField( + null=True, + blank=True, + help_text='Limit du nombre de mac sur le port', + verbose_name=_("Mac limit") + ) + flow_control = models.BooleanField( + default=False, + help_text='Gestion des débits', + verbose_name=_("Flow control") + ) + dhcp_snooping = models.BooleanField( + default=False, + help_text='Protection dhcp pirate', + verbose_name=_("Dhcp snooping") + ) + dhcpv6_snooping = models.BooleanField( + default=False, + help_text='Protection dhcpv6 pirate', + verbose_name=_("Dhcpv6 snooping") + ) + arp_protect = models.BooleanField( + default=False, + help_text='Verification assignation de l\'IP par dhcp', + verbose_name=_("Arp protect") + ) + ra_guard = models.BooleanField( + default=False, + help_text='Protection contre ra pirate', + verbose_name=_("Ra guard") + ) + loop_protect = models.BooleanField( + default=False, + help_text='Protection contre les boucles', + verbose_name=_("Loop Protect") + ) class Meta: permissions = ( diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html index b4b936c5..0c1ca622 100644 --- a/topologie/templates/topologie/aff_port_profile.html +++ b/topologie/templates/topologie/aff_port_profile.html @@ -8,13 +8,43 @@ {% endif %} - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% for port_profile in port_profile_list %} From e7b49bd5fa6985423f88549d7724ef273bdda8a1 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 26 Jun 2018 17:12:52 +0000 Subject: [PATCH 03/12] Pas de null sur manytomany --- topologie/migrations/0061_portprofile.py | 2 +- topologie/models.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/topologie/migrations/0061_portprofile.py b/topologie/migrations/0061_portprofile.py index 2b326f28..d0e84021 100644 --- a/topologie/migrations/0061_portprofile.py +++ b/topologie/migrations/0061_portprofile.py @@ -31,7 +31,7 @@ class Migration(migrations.Migration): ('arp_protect', models.BooleanField(default=False, help_text="Verification assignation de l'IP par dhcp", verbose_name='Arp protect')), ('ra_guard', models.BooleanField(default=False, help_text='Protection contre ra pirate', verbose_name='Ra guard')), ('loop_protect', models.BooleanField(default=False, help_text='Protection contre les boucles', verbose_name='Loop Protect')), - ('vlan_tagged', models.ManyToManyField(blank=True, null=True, related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged')), + ('vlan_tagged', models.ManyToManyField(blank=True, related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged')), ('vlan_untagged', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vlan_untagged', to='machines.Vlan', verbose_name='VLAN untagged')), ], options={ diff --git a/topologie/models.py b/topologie/models.py index c7f0e28f..86beed71 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -537,7 +537,6 @@ class PortProfile(AclMixin, RevMixin, models.Model): 'machines.Vlan', related_name='vlan_tagged', blank=True, - null=True, verbose_name=_("VLAN(s) tagged") ) radius_type = models.CharField( From 92f30fbe19cea3d14f5830b68595209949b12f41 Mon Sep 17 00:00:00 2001 From: chirac Date: Tue, 26 Jun 2018 23:29:40 +0000 Subject: [PATCH 04/12] =?UTF-8?q?Ajout=20reglages=20s=C3=A9curit=C3=A9=20+?= =?UTF-8?q?=20frontend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- topologie/forms.py | 43 -------------- .../migrations/0062_auto_20180627_0123.py | 25 ++++++++ topologie/models.py | 8 +++ .../templates/topologie/aff_port_profile.html | 59 +++++++------------ topologie/views.py | 5 +- 5 files changed, 55 insertions(+), 85 deletions(-) create mode 100644 topologie/migrations/0062_auto_20180627_0123.py diff --git a/topologie/forms.py b/topologie/forms.py index 377a39b3..3ed9e8b3 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -265,34 +265,6 @@ class EditBuildingForm(FormRevMixin, ModelForm): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(EditBuildingForm, self).__init__(*args, prefix=prefix, **kwargs) - -class NewPortProfileForm(FormRevMixin, ModelForm): - """Form to create a port profile""" - class Meta: - model = PortProfile - fields = '__all__' - - def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(NewPortProfileForm, self).__init__(*args, - prefix=prefix, - **kwargs) - - def clean(self): - cleaned_data = super(NewPortProfileForm, self).clean() - radius_type = cleaned_data.get('radius_type') - radius_mode = cleaned_data.get('radius_mode') - - if radius_type == 'NO' and radius_mode: - raise forms.ValidationError(_("You can't specify a RADIUS mode" - " with RADIUS type NO")) - elif radius_type != 'NO' and not radius_mode: - raise forms.ValidationError(_("You have to specify a RADIUS" - " mode")) - - return cleaned_data - - class EditPortProfileForm(FormRevMixin, ModelForm): """Form to edit a port profile""" class Meta: @@ -305,18 +277,3 @@ class EditPortProfileForm(FormRevMixin, ModelForm): prefix=prefix, **kwargs) - def clean(self): - cleaned_data = super(EditPortProfileForm, self).clean() - radius_type = cleaned_data.get('radius_type') - radius_mode = cleaned_data.get('radius_mode') - - if radius_type == 'NO' and radius_mode: - raise forms.ValidationError(_("You can't specify a RADIUS mode" - " with RADIUS type NO")) - elif radius_type != 'NO' and not radius_mode: - raise forms.ValidationError(_("You have to specify a RADIUS" - " mode")) - - return cleaned_data - - diff --git a/topologie/migrations/0062_auto_20180627_0123.py b/topologie/migrations/0062_auto_20180627_0123.py new file mode 100644 index 00000000..b8135de8 --- /dev/null +++ b/topologie/migrations/0062_auto_20180627_0123.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-26 23:23 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0061_portprofile'), + ] + + operations = [ + migrations.AlterField( + model_name='portprofile', + name='radius_mode', + field=models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], default='COMMON', help_text="En cas d'auth par mac, auth common ou strcit sur le port", max_length=32, verbose_name='RADIUS mode'), + ), + migrations.AlterField( + model_name='portprofile', + name='radius_type', + field=models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], help_text="Choix du type d'authentification radius : non actif, mac ou 802.1X", max_length=32, verbose_name='RADIUS type'), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index 86beed71..d3400e41 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -542,12 +542,14 @@ class PortProfile(AclMixin, RevMixin, models.Model): radius_type = models.CharField( max_length=32, choices=TYPES, + help_text="Choix du type d'authentification radius : non actif, mac ou 802.1X", verbose_name=_("RADIUS type") ) radius_mode = models.CharField( max_length=32, choices=MODES, default='COMMON', + help_text="En cas d'auth par mac, auth common ou strcit sur le port", verbose_name=_("RADIUS mode") ) speed = models.CharField( @@ -601,6 +603,12 @@ class PortProfile(AclMixin, RevMixin, models.Model): verbose_name = _("Port profile") verbose_name_plural = _("Port profiles") + security_parameters_fields = ['loop_protect', 'ra_guard', 'arp_protect', 'dhcpv6_snooping', 'dhcp_snooping', 'flow_control'] + + @cached_property + def security_parameters_enabled(self): + return [parameter for parameter in self.security_parameters_fields if getattr(self, parameter)] + def __str__(self): return self.name diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html index 0c1ca622..7e044c0a 100644 --- a/topologie/templates/topologie/aff_port_profile.html +++ b/topologie/templates/topologie/aff_port_profile.html @@ -9,53 +9,34 @@
{% trans "Name" %} {% trans "VLAN untagged" %} {% trans "VLAN(s) tagged" %}
{% trans "RADIUS type" %} {% trans "RADIUS mode" %}{% trans "RADIUS type" %}
{% trans "speed" %}{% trans "Mac limit" %}{% trans "Flow control" %}
{% trans "dhcp snooping" %}{% trans "dhcpv6 snooping" %}{% trans "arp protect" %}
{% trans "ra guard" %}{% trans "loop protect" %}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + {% for port_profile in port_profile_list %} - + - - + + {% endif %} + + + - - + @@ -66,8 +65,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} {% endif %} - - + - + + @@ -65,6 +66,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} {% endif %} + - + @@ -67,7 +67,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} - + diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html index 7e044c0a..577ac2a5 100644 --- a/topologie/templates/topologie/aff_port_profile.html +++ b/topologie/templates/topologie/aff_port_profile.html @@ -1,3 +1,25 @@ +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2018 Gabriel Détraz + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + {% load acl %} {% load i18n %} @@ -7,45 +29,51 @@ {% include "pagination.html" with list=port_profile_list %} {% endif %} + +
{% trans "Name" %} - {% trans "VLAN untagged" %}{% trans "VLAN(s) tagged" %}
{% trans "RADIUS type" %}{% trans "RADIUS mode" %}{% trans "RADIUS type" %}
{% trans "speed" %}{% trans "Mac limit" %}{% trans "Flow control" %}
{% trans "dhcp snooping" %}{% trans "dhcpv6 snooping" %}{% trans "arp protect" %}
{% trans "ra guard" %}{% trans "loop protect" %}{% trans "Nom" %}{% trans "Default pour" %}{% trans "VLANs" %}{% trans "Réglages RADIUS" %}{% trans "Vitesse" %}{% trans "Mac address limit" %}{% trans "Sécurité" %}
{{port_profile.name}}{{port_profile.vlan_untagged}}{{port_profile.profil_default}} - {{port_profile.vlan_tagged.all|join:", "}} + Untagged : {{port_profile.vlan_untagged}} +
+ Tagged : {{port_profile.vlan_tagged.all|join:", "}}
{{port_profile.radius_type}}{{port_profile.radius_mode}} + Type : {{port_profile.radius_type}} + {% if port_profile.radius_type == "MAC-radius" %} +
+ Mode : {{port_profile.radius_mode}}
{{port_profile.speed}}{{port_profile.mac_limit}}{{port_profile.security_parameters_enabled|join:"
"}}
{% include 'buttons/history.html' with href='topologie:history' name='portprofile' id=port_profile.pk %} {% can_edit port_profile %} diff --git a/topologie/views.py b/topologie/views.py index eb86ce9c..4b21c651 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -97,7 +97,6 @@ from .forms import ( EditAccessPointForm, EditSwitchBayForm, EditBuildingForm, - NewPortProfileForm, EditPortProfileForm, ) @@ -135,7 +134,7 @@ def index(request): switch_list = re2o_paginator(request, switch_list, pagination_number) port_profile_list = re2o_paginator(request, port_profile_list, pagination_number) - if any(service_link.need_regen() for service_link in Service_link.objects.filter(service__service_type='graph_topo')): + if any(service_link.need_regen for service_link in Service_link.objects.filter(service__service_type='graph_topo')): make_machine_graph() for service_link in Service_link.objects.filter(service__service_type='graph_topo'): service_link.done_regen() @@ -967,7 +966,7 @@ def del_constructor_switch(request, constructor_switch, **_kwargs): @can_create(PortProfile) def new_port_profile(request): """Create a new port profile""" - port_profile = NewPortProfileForm(request.POST or None) + port_profile = EditPortProfileForm(request.POST or None) if port_profile.is_valid(): port_profile.save() messages.success(request, _("Port profile created")) From d123ce920d7231f1e3e18a769e9783d2314ec258 Mon Sep 17 00:00:00 2001 From: chirac Date: Wed, 27 Jun 2018 20:28:54 +0000 Subject: [PATCH 05/12] =?UTF-8?q?Menu=20s=C3=A9par=C3=A9=20pour=20les=20pr?= =?UTF-8?q?ofils=20de=20ports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- topologie/templates/topologie/index.html | 10 ----- .../topologie/index_portprofile.html | 44 +++++++++++++++++++ topologie/templates/topologie/sidebar.html | 6 ++- topologie/urls.py | 3 ++ topologie/views.py | 17 +++++-- 5 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 topologie/templates/topologie/index_portprofile.html diff --git a/topologie/templates/topologie/index.html b/topologie/templates/topologie/index.html index 7902f4a3..7949f412 100644 --- a/topologie/templates/topologie/index.html +++ b/topologie/templates/topologie/index.html @@ -73,14 +73,4 @@ Topologie des Switchs

-

{% trans "Port profiles" %}

-{% can_create PortProfile %} -{% trans " Add a port profile" %} -
-{% acl_end %} - {% include "topologie/aff_port_profile.html" with port_profile_list=port_profile_list %} -
-
-
- {% endblock %} diff --git a/topologie/templates/topologie/index_portprofile.html b/topologie/templates/topologie/index_portprofile.html new file mode 100644 index 00000000..a4287da6 --- /dev/null +++ b/topologie/templates/topologie/index_portprofile.html @@ -0,0 +1,44 @@ +{% extends "topologie/sidebar.html" %} +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2017 Gabriel Détraz +Copyright © 2017 Goulven Kermarec +Copyright © 2017 Augustin Lemesle + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + +{% load bootstrap3 %} +{% load acl %} +{% load i18n %} + +{% block title %}Switchs{% endblock %} + +{% block content %} + +

{% trans "Port profiles" %}

+{% can_create PortProfile %} +{% trans " Add a port profile" %} +
+{% acl_end %} + {% include "topologie/aff_port_profile.html" with port_profile_list=port_profile_list %} +
+
+
+ +{% endblock %} diff --git a/topologie/templates/topologie/sidebar.html b/topologie/templates/topologie/sidebar.html index ce7b4114..c7ea6337 100644 --- a/topologie/templates/topologie/sidebar.html +++ b/topologie/templates/topologie/sidebar.html @@ -33,7 +33,11 @@ with this program; if not, write to the Free Software Foundation, Inc., Switchs - + + + Profil des ports switchs + + Bornes WiFi diff --git a/topologie/urls.py b/topologie/urls.py index 7cfc5714..a827acf2 100644 --- a/topologie/urls.py +++ b/topologie/urls.py @@ -113,6 +113,9 @@ urlpatterns = [ url(r'^del_building/(?P[0-9]+)$', views.del_building, name='del-building'), + url(r'^index_port_profile/$', + views.index_port_profile, + name='index-port-profile'), url(r'^new_port_profile/$', views.new_port_profile, name='new-port-profile'), diff --git a/topologie/views.py b/topologie/views.py index 4b21c651..b7761e0c 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -128,11 +128,9 @@ def index(request): SortTable.TOPOLOGIE_INDEX ) - port_profile_list = PortProfile.objects.all() pagination_number = GeneralOption.get_cached_value('pagination_number') switch_list = re2o_paginator(request, switch_list, pagination_number) - port_profile_list = re2o_paginator(request, port_profile_list, pagination_number) if any(service_link.need_regen for service_link in Service_link.objects.filter(service__service_type='graph_topo')): make_machine_graph() @@ -144,7 +142,20 @@ def index(request): return render( request, 'topologie/index.html', - {'switch_list': switch_list, 'port_profile_list': port_profile_list} + {'switch_list': switch_list} + ) + + +@login_required +@can_view_all(PortProfile) +def index_port_profile(request): + pagination_number = GeneralOption.get_cached_value('pagination_number') + port_profile_list = PortProfile.objects.all().select_related('vlan_untagged') + port_profile_list = re2o_paginator(request, port_profile_list, pagination_number) + return render( + request, + 'topologie/index_portprofile.html', + {'port_profile_list': port_profile_list} ) From 447919a2afc0e8aa43621e0ce26914e3705f7444 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 30 Jun 2018 15:29:00 +0000 Subject: [PATCH 06/12] =?UTF-8?q?Ajout=20et=20transfert=20des=20anciennes?= =?UTF-8?q?=20donn=C3=A9es=20vers=20le=20nouveau=20syst=C3=A8me=20de=20pro?= =?UTF-8?q?fil=20de=20ports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- topologie/forms.py | 7 +-- topologie/migrations/0061_portprofile.py | 2 +- .../migrations/0063_port_custom_profil.py | 21 +++++++ topologie/migrations/0064_createprofil.py | 57 +++++++++++++++++++ .../migrations/0065_auto_20180630_1703.py | 23 ++++++++ topologie/models.py | 32 +++++++---- topologie/templates/topologie/aff_port.html | 6 +- 7 files changed, 129 insertions(+), 19 deletions(-) create mode 100644 topologie/migrations/0063_port_custom_profil.py create mode 100644 topologie/migrations/0064_createprofil.py create mode 100644 topologie/migrations/0065_auto_20180630_1703.py diff --git a/topologie/forms.py b/topologie/forms.py index 3ed9e8b3..6e4d5a0d 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -80,8 +80,8 @@ class EditPortForm(FormRevMixin, ModelForm): optimiser le temps de chargement avec select_related (vraiment lent sans)""" class Meta(PortForm.Meta): - fields = ['room', 'related', 'machine_interface', 'radius', - 'vlan_force', 'details'] + fields = ['room', 'related', 'machine_interface', 'custom_profil', + 'details'] def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) @@ -109,8 +109,7 @@ class AddPortForm(FormRevMixin, ModelForm): 'room', 'machine_interface', 'related', - 'radius', - 'vlan_force', + 'custom_profil', 'details' ] diff --git a/topologie/migrations/0061_portprofile.py b/topologie/migrations/0061_portprofile.py index d0e84021..7e130163 100644 --- a/topologie/migrations/0061_portprofile.py +++ b/topologie/migrations/0061_portprofile.py @@ -20,7 +20,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=255, verbose_name='Name')), - ('profil_default', models.CharField(blank=True, choices=[('room', 'room'), ('accespoint', 'accesspoint'), ('uplink', 'uplink'), ('asso_machine', 'asso_machine')], max_length=32, null=True, unique=True, verbose_name='profil default')), + ('profil_default', models.CharField(blank=True, choices=[('room', 'room'), ('nothing', 'nothing'), ('accespoint', 'accesspoint'), ('uplink', 'uplink'), ('asso_machine', 'asso_machine')], max_length=32, null=True, unique=True, verbose_name='profil default')), ('radius_type', models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], max_length=32, verbose_name='RADIUS type')), ('radius_mode', models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], default='COMMON', max_length=32, verbose_name='RADIUS mode')), ('speed', models.CharField(choices=[('10-half', '10-half'), ('100-half', '100-half'), ('10-full', '10-full'), ('100-full', '100-full'), ('1000-full', '1000-full'), ('auto', 'auto'), ('auto-10', 'auto-10'), ('auto-100', 'auto-100')], default='auto', help_text='Mode de transmission et vitesse du port', max_length=32, verbose_name='Speed')), diff --git a/topologie/migrations/0063_port_custom_profil.py b/topologie/migrations/0063_port_custom_profil.py new file mode 100644 index 00000000..15feebce --- /dev/null +++ b/topologie/migrations/0063_port_custom_profil.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-28 07:49 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0062_auto_20180627_0123'), + ] + + operations = [ + migrations.AddField( + model_name='port', + name='custom_profil', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.PortProfile'), + ), + ] diff --git a/topologie/migrations/0064_createprofil.py b/topologie/migrations/0064_createprofil.py new file mode 100644 index 00000000..189d1812 --- /dev/null +++ b/topologie/migrations/0064_createprofil.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-12-31 19:53 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0063_port_custom_profil'), + ] + + def transfer_profil(apps, schema_editor): + db_alias = schema_editor.connection.alias + port = apps.get_model("topologie", "Port") + profil = apps.get_model("topologie", "PortProfile") + vlan = apps.get_model("machines", "Vlan") + port_list = port.objects.using(db_alias).all() + profil_nothing = profil.objects.using(db_alias).create(name='nothing', profil_default='nothing', radius_type='NO') + profil_uplink = profil.objects.using(db_alias).create(name='uplink', profil_default='uplink', radius_type='NO') + profil_machine = profil.objects.using(db_alias).create(name='asso_machine', profil_default='asso_machine', radius_type='NO') + profil_room = profil.objects.using(db_alias).create(name='room', profil_default='room', radius_type='NO') + profil_borne = profil.objects.using(db_alias).create(name='accesspoint', profil_default='accesspoint', radius_type='NO') + for vlan_instance in vlan.objects.using(db_alias).all(): + if port.objects.using(db_alias).filter(vlan_force=vlan_instance): + custom_profil = profil.objects.using(db_alias).create(name='vlan-force-' + str(vlan_instance.vlan_id), radius_type='NO', vlan_untagged=vlan_instance) + port.objects.using(db_alias).filter(vlan_force=vlan_instance).update(custom_profil=custom_profil) + if port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').count() and port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count(): + profil_room.radius_type = 'MAC-radius' + profil_room.radius_mode = 'STRICT' + common_profil = profil.objects.using(db_alias).create(name='mac-radius-common', radius_type='MAC-radius', radius_mode='COMMON') + no_rad_profil = profil.objects.using(db_alias).create(name='no-radius', radius_type='NO') + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').update(custom_profil=common_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=no_rad_profil) + elif port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').count() and port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count(): + profil_room.radius_type = 'MAC-radius' + profil_room.radius_mode = 'COMMON' + strict_profil = profil.objects.using(db_alias).create(name='mac-radius-strict', radius_type='MAC-radius', radius_mode='STRICT') + no_rad_profil = profil.objects.using(db_alias).create(name='no-radius', radius_type='NO') + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').update(custom_profil=strict_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=no_rad_profil) + else: + strict_profil = profil.objects.using(db_alias).create(name='mac-radius-strict', radius_type='MAC-radius', radius_mode='STRICT') + common_profil = profil.objects.using(db_alias).create(name='mac-radius-common', radius_type='MAC-radius', radius_mode='COMMON') + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').update(custom_profil=strict_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=common_profil) + profil_room.save() + + + + def untransfer_profil(apps, schema_editor): + return + + operations = [ + migrations.RunPython(transfer_profil, untransfer_profil), + ] diff --git a/topologie/migrations/0065_auto_20180630_1703.py b/topologie/migrations/0065_auto_20180630_1703.py new file mode 100644 index 00000000..9fed2d83 --- /dev/null +++ b/topologie/migrations/0065_auto_20180630_1703.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-30 15:03 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0064_createprofil'), + ] + + operations = [ + migrations.RemoveField( + model_name='port', + name='radius', + ), + migrations.RemoveField( + model_name='port', + name='vlan_force', + ), + ] diff --git a/topologie/models.py b/topologie/models.py index d3400e41..82c6833f 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -366,12 +366,6 @@ class Port(AclMixin, RevMixin, models.Model): de forcer un port sur un vlan particulier. S'additionne à la politique RADIUS""" PRETTY_NAME = "Port de switch" - STATES = ( - ('NO', 'NO'), - ('STRICT', 'STRICT'), - ('BLOQ', 'BLOQ'), - ('COMMON', 'COMMON'), - ) switch = models.ForeignKey( 'Switch', @@ -397,13 +391,13 @@ class Port(AclMixin, RevMixin, models.Model): blank=True, related_name='related_port' ) - radius = models.CharField(max_length=32, choices=STATES, default='NO') - vlan_force = models.ForeignKey( - 'machines.Vlan', - on_delete=models.SET_NULL, + custom_profil = models.ForeignKey( + 'PortProfile', + on_delete=models.PROTECT, blank=True, null=True ) + details = models.CharField(max_length=255, blank=True) class Meta: @@ -412,6 +406,23 @@ class Port(AclMixin, RevMixin, models.Model): ("view_port", "Peut voir un objet port"), ) + @cached_property + def get_port_profil(self): + """Return the config profil for this port""" + if self.custom_profil: + return custom_profil + elif self.related: + return PortProfil.objects.get(profil_default='uplink') + elif self.machine_interface: + if isinstance(self.machine_interface.machine, AccessPoint): + return PortProfil.objects.get(profil_default='access_point') + else: + return PortProfil.objects.get(profil_default='asso_machine') + elif self.room: + return PortProfil.objects.get(profil_default='room') + else: + return PortProfil.objects.get(profil_default='nothing') + @classmethod def get_instance(cls, portid, *_args, **kwargs): return (cls.objects @@ -515,6 +526,7 @@ class PortProfile(AclMixin, RevMixin, models.Model): ('accespoint', 'accesspoint'), ('uplink', 'uplink'), ('asso_machine', 'asso_machine'), + ('nothing', 'nothing'), ) name = models.CharField(max_length=255, verbose_name=_("Name")) profil_default = models.CharField( diff --git a/topologie/templates/topologie/aff_port.html b/topologie/templates/topologie/aff_port.html index deeb0655..f22ce4c9 100644 --- a/topologie/templates/topologie/aff_port.html +++ b/topologie/templates/topologie/aff_port.html @@ -32,8 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% include "buttons/sort.html" with prefix='port' col='room' text='Room' %} {% include "buttons/sort.html" with prefix='port' col='interface' text='Interface machine' %} {% include "buttons/sort.html" with prefix='port' col='related' text='Related' %}{% include "buttons/sort.html" with prefix='port' col='radius' text='Radius' %}{% include "buttons/sort.html" with prefix='port' col='vlan' text='Vlan forcé' %}Profil du port Détails
{{ port.radius }}{% if not port.vlan_force %}Aucun{% else %}{{ port.vlan_force }}{% endif %}{% if not port.custom_profil %}Par défaut{% else %}{{port.custom_profil}}{% endif %} {{ port.details }} From e6b8c5c899277d80972452e9c48d457a5729d716 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 30 Jun 2018 16:19:02 +0000 Subject: [PATCH 07/12] =?UTF-8?q?Finition,=20gestion=20du=20renvoie=20du?= =?UTF-8?q?=20profil=20par=20d=C3=A9faut=20du=20port?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- search/views.py | 4 +-- topologie/models.py | 30 ++++++++++++++------- topologie/templates/topologie/aff_port.html | 2 +- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/search/views.py b/search/views.py index 871515fa..0ae8470b 100644 --- a/search/views.py +++ b/search/views.py @@ -262,9 +262,9 @@ def search_single_word(word, filters, user, ) | Q( related__switch__interface__domain__name__icontains=word ) | Q( - radius__icontains=word + custom_profil__name__icontains=word ) | Q( - vlan_force__name__icontains=word + custom_profil__profil_default__icontains=word ) | Q( details__icontains=word ) diff --git a/topologie/models.py b/topologie/models.py index 82c6833f..3d30af76 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -409,19 +409,29 @@ class Port(AclMixin, RevMixin, models.Model): @cached_property def get_port_profil(self): """Return the config profil for this port""" - if self.custom_profil: - return custom_profil - elif self.related: - return PortProfil.objects.get(profil_default='uplink') - elif self.machine_interface: - if isinstance(self.machine_interface.machine, AccessPoint): - return PortProfil.objects.get(profil_default='access_point') + def profil_or_nothing(profil): + port_profil = PortProfile.objects.filter(profil_default=profil).first() + if port_profil: + return port_profil else: - return PortProfil.objects.get(profil_default='asso_machine') + nothing = PortProfile.objects.filter(profil_default='nothing').first() + if not nothing: + nothing = PortProfile.objects.create(profil_default='nothing', name='nothing', radius_type='NO') + return nothing + + if self.custom_profil: + return self.custom_profil + elif self.related: + return profil_or_nothing('uplink') + elif self.machine_interface: + if hasattr(self.machine_interface.machine, 'accesspoint'): + return profil_or_nothing('access_point') + else: + return profil_or_nothing('asso_machine') elif self.room: - return PortProfil.objects.get(profil_default='room') + return profil_or_nothing('room') else: - return PortProfil.objects.get(profil_default='nothing') + return profil_or_nothing('nothing') @classmethod def get_instance(cls, portid, *_args, **kwargs): diff --git a/topologie/templates/topologie/aff_port.html b/topologie/templates/topologie/aff_port.html index f22ce4c9..f878365a 100644 --- a/topologie/templates/topologie/aff_port.html +++ b/topologie/templates/topologie/aff_port.html @@ -65,7 +65,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} {% endif %} {% if not port.custom_profil %}Par défaut{% else %}{{port.custom_profil}}{% endif %}{% if not port.custom_profil %}Par défaut : {% endif %}{{port.get_port_profil}} {{ port.details }} From b2d45d002118458bc60fc42f16072d735ffa13f3 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 30 Jun 2018 16:36:14 +0000 Subject: [PATCH 08/12] =?UTF-8?q?Adapte=20freeradius=20pour=20le=20nouveau?= =?UTF-8?q?=20syst=C3=A8me=20de=20profil=20de=20ports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- freeradius_utils/auth.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index fcf55cfa..4e9ad24e 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -359,23 +359,26 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, if not port: return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK) + # On récupère le profil du port + port_profil = port.get_port_profil + # Si un vlan a été précisé, on l'utilise pour VLAN_OK - if port.vlan_force: - DECISION_VLAN = int(port.vlan_force.vlan_id) + if port_profil.vlan_untagged: + DECISION_VLAN = int(port_profil.vlan_untagged.vlan_id) extra_log = u"Force sur vlan " + str(DECISION_VLAN) else: DECISION_VLAN = VLAN_OK - if port.radius == 'NO': + if port_profil.radius_type == 'NO': return (sw_name, "", u"Pas d'authentification sur ce port" + extra_log, DECISION_VLAN) - if port.radius == 'BLOQ': + if port_profil.radius_type == 'BLOQ': return (sw_name, port.room, u'Port desactive', VLAN_NOK) - if port.radius == 'STRICT': + if port_profil.radius_type == 'STRICT': room = port.room if not room: return (sw_name, "Inconnue", u'Chambre inconnue', VLAN_NOK) @@ -390,7 +393,7 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, return (sw_name, room, u'Chambre resident desactive', VLAN_NOK) # else: user OK, on passe à la verif MAC - if port.radius == 'COMMON' or port.radius == 'STRICT': + if port_profil.radius_type == 'COMMON' or port_profil.radius_type == 'STRICT': # Authentification par mac interface = (Interface.objects .filter(mac_address=mac_address) From 51793bdee62ba6fbbf7f5feab6d73386886e5110 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 30 Jun 2018 17:04:15 +0000 Subject: [PATCH 09/12] =?UTF-8?q?Boolean=20direct=20pour=20d=C3=A9sactiver?= =?UTF-8?q?=20un=20port=20+=20logo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- freeradius_utils/auth.py | 6 ++--- topologie/forms.py | 3 ++- .../migrations/0066_auto_20180630_1855.py | 25 +++++++++++++++++++ topologie/models.py | 6 ++++- topologie/templates/topologie/aff_port.html | 2 ++ topologie/templates/topologie/sidebar.html | 4 +-- 6 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 topologie/migrations/0066_auto_20180630_1855.py diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index 4e9ad24e..b318ab63 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -369,15 +369,15 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, else: DECISION_VLAN = VLAN_OK + if not port.state: + return (sw_name, port.room, u'Port desactive', VLAN_NOK) + if port_profil.radius_type == 'NO': return (sw_name, "", u"Pas d'authentification sur ce port" + extra_log, DECISION_VLAN) - if port_profil.radius_type == 'BLOQ': - return (sw_name, port.room, u'Port desactive', VLAN_NOK) - if port_profil.radius_type == 'STRICT': room = port.room if not room: diff --git a/topologie/forms.py b/topologie/forms.py index 6e4d5a0d..63ffc652 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -81,7 +81,7 @@ class EditPortForm(FormRevMixin, ModelForm): lent sans)""" class Meta(PortForm.Meta): fields = ['room', 'related', 'machine_interface', 'custom_profil', - 'details'] + 'state', 'details'] def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) @@ -110,6 +110,7 @@ class AddPortForm(FormRevMixin, ModelForm): 'machine_interface', 'related', 'custom_profil', + 'state', 'details' ] diff --git a/topologie/migrations/0066_auto_20180630_1855.py b/topologie/migrations/0066_auto_20180630_1855.py new file mode 100644 index 00000000..b197f568 --- /dev/null +++ b/topologie/migrations/0066_auto_20180630_1855.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-30 16:55 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0065_auto_20180630_1703'), + ] + + operations = [ + migrations.AddField( + model_name='port', + name='state', + field=models.BooleanField(default=True, help_text='Etat du port Actif', verbose_name='Etat du port Actif'), + ), + migrations.AlterField( + model_name='portprofile', + name='profil_default', + field=models.CharField(blank=True, choices=[('room', 'room'), ('accespoint', 'accesspoint'), ('uplink', 'uplink'), ('asso_machine', 'asso_machine'), ('nothing', 'nothing')], max_length=32, null=True, unique=True, verbose_name='profil default'), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index 3d30af76..d0534dda 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -397,7 +397,11 @@ class Port(AclMixin, RevMixin, models.Model): blank=True, null=True ) - + state = models.BooleanField( + default=True, + help_text='Etat du port Actif', + verbose_name=_("Etat du port Actif") + ) details = models.CharField(max_length=255, blank=True) class Meta: diff --git a/topologie/templates/topologie/aff_port.html b/topologie/templates/topologie/aff_port.html index f878365a..45e81e06 100644 --- a/topologie/templates/topologie/aff_port.html +++ b/topologie/templates/topologie/aff_port.html @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% include "buttons/sort.html" with prefix='port' col='room' text='Room' %} {% include "buttons/sort.html" with prefix='port' col='interface' text='Interface machine' %} {% include "buttons/sort.html" with prefix='port' col='related' text='Related' %}Etat du port Profil du port Détails {% if port.state %} Actif{% else %}Désactivé{% endif %} {% if not port.custom_profil %}Par défaut : {% endif %}{{port.get_port_profil}} {{ port.details }} diff --git a/topologie/templates/topologie/sidebar.html b/topologie/templates/topologie/sidebar.html index c7ea6337..8652690e 100644 --- a/topologie/templates/topologie/sidebar.html +++ b/topologie/templates/topologie/sidebar.html @@ -34,8 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc., Switchs - - Profil des ports switchs + + Config des ports switchs From 2e092b3fde5ce23488f442abf2d84f9bceca1376 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 30 Jun 2018 22:17:24 +0000 Subject: [PATCH 10/12] Fix langue et 802.X radius + divers --- freeradius_utils/auth.py | 39 +++++++- search/views.py | 4 +- topologie/forms.py | 6 +- topologie/migrations/0064_createprofil.py | 80 ++++++++-------- .../migrations/0067_auto_20180701_0016.py | 75 +++++++++++++++ topologie/models.py | 37 ++++---- topologie/templates/topologie/aff_port.html | 10 +- .../templates/topologie/aff_port_profile.html | 94 ++++++++++++------- topologie/templates/topologie/index.html | 1 - .../topologie/index_portprofile.html | 13 ++- topologie/templates/topologie/sidebar.html | 2 +- topologie/views.py | 8 +- 12 files changed, 250 insertions(+), 119 deletions(-) create mode 100644 topologie/migrations/0067_auto_20180701_0016.py diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index b318ab63..8450777b 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -355,30 +355,47 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, port=port_number ) .first()) + # Si le port est inconnu, on place sur le vlan defaut + # Aucune information particulière ne permet de déterminer quelle + # politique à appliquer sur ce port if not port: return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK) # On récupère le profil du port port_profil = port.get_port_profil - # Si un vlan a été précisé, on l'utilise pour VLAN_OK + # Si un vlan a été précisé dans la config du port, + # on l'utilise pour VLAN_OK if port_profil.vlan_untagged: DECISION_VLAN = int(port_profil.vlan_untagged.vlan_id) extra_log = u"Force sur vlan " + str(DECISION_VLAN) else: DECISION_VLAN = VLAN_OK + # Si le port est désactivé, on rejette sur le vlan de déconnexion if not port.state: - return (sw_name, port.room, u'Port desactive', VLAN_NOK) + return (sw_name, port.room, u'Port desactivé', VLAN_NOK) + # Si radius est désactivé, on laisse passer if port_profil.radius_type == 'NO': return (sw_name, "", u"Pas d'authentification sur ce port" + extra_log, DECISION_VLAN) - if port_profil.radius_type == 'STRICT': + # Si le 802.1X est activé sur ce port, cela veut dire que la personne a été accept précédemment + # Par conséquent, on laisse passer sur le bon vlan + if nas_type.port_access_mode == '802.1X' and port_profil.radius_type == '802.1X': + room = port.room or "Chambre/local inconnu" + return (sw_name, room, u'Acceptation authentification 802.1X', DECISION_VLAN) + + # Sinon, cela veut dire qu'on fait de l'auth radius par mac + # Si le port est en mode strict, on vérifie que tous les users + # rattachés à ce port sont bien à jour de cotisation. Sinon on rejette (anti squattage) + # Il n'est pas possible de se connecter sur une prise strict sans adhérent à jour de cotis + # dedans + if port_profil.radius_mode == 'STRICT': room = port.room if not room: return (sw_name, "Inconnue", u'Chambre inconnue', VLAN_NOK) @@ -393,7 +410,8 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, return (sw_name, room, u'Chambre resident desactive', VLAN_NOK) # else: user OK, on passe à la verif MAC - if port_profil.radius_type == 'COMMON' or port_profil.radius_type == 'STRICT': + # Si on fait de l'auth par mac, on cherche l'interface via sa mac dans la bdd + if port_profil.radius_mode == 'COMMON' or port_profil.radius_mode == 'STRICT': # Authentification par mac interface = (Interface.objects .filter(mac_address=mac_address) @@ -402,15 +420,19 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, .first()) if not interface: room = port.room - # On essaye de register la mac + # On essaye de register la mac, si l'autocapture a été activée + # Sinon on rejette sur vlan_nok if not nas_type.autocapture_mac: return (sw_name, "", u'Machine inconnue', VLAN_NOK) + # On ne peut autocapturer que si on connait la chambre et donc l'user correspondant elif not room: return (sw_name, "Inconnue", u'Chambre et machine inconnues', VLAN_NOK) else: + # Si la chambre est vide (local club, prises en libre services) + # Impossible d'autocapturer if not room_user: room_user = User.objects.filter( Q(club__room=port.room) | Q(adherent__room=port.room) @@ -421,6 +443,8 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, u'Machine et propriétaire de la chambre ' 'inconnus', VLAN_NOK) + # Si il y a plus d'un user dans la chambre, impossible de savoir à qui + # Ajouter la machine elif room_user.count() > 1: return (sw_name, room, @@ -428,11 +452,13 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, 'dans la chambre/local -> ajout de mac ' 'automatique impossible', VLAN_NOK) + # Si l'adhérent de la chambre n'est pas à jour de cotis, pas d'autocapture elif not room_user.first().has_access(): return (sw_name, room, u'Machine inconnue et adhérent non cotisant', VLAN_NOK) + # Sinon on capture et on laisse passer sur le bon vlan else: result, reason = (room_user .first() @@ -452,6 +478,9 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, reason + str(mac_address) ), VLAN_NOK) + # L'interface a été trouvée, on vérifie qu'elle est active, sinon on reject + # Si elle n'a pas d'ipv4, on lui en met une + # Enfin on laisse passer sur le vlan pertinent else: room = port.room if not interface.is_active: diff --git a/search/views.py b/search/views.py index 0ae8470b..73333834 100644 --- a/search/views.py +++ b/search/views.py @@ -262,9 +262,9 @@ def search_single_word(word, filters, user, ) | Q( related__switch__interface__domain__name__icontains=word ) | Q( - custom_profil__name__icontains=word + custom_profile__name__icontains=word ) | Q( - custom_profil__profil_default__icontains=word + custom_profile__profil_default__icontains=word ) | Q( details__icontains=word ) diff --git a/topologie/forms.py b/topologie/forms.py index 63ffc652..ba37d395 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -80,8 +80,8 @@ class EditPortForm(FormRevMixin, ModelForm): optimiser le temps de chargement avec select_related (vraiment lent sans)""" class Meta(PortForm.Meta): - fields = ['room', 'related', 'machine_interface', 'custom_profil', - 'state', 'details'] + fields = ['room', 'related', 'machine_interface', 'custom_profile', + 'state', 'details'] def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) @@ -109,7 +109,7 @@ class AddPortForm(FormRevMixin, ModelForm): 'room', 'machine_interface', 'related', - 'custom_profil', + 'custom_profile', 'state', 'details' ] diff --git a/topologie/migrations/0064_createprofil.py b/topologie/migrations/0064_createprofil.py index 189d1812..2f165386 100644 --- a/topologie/migrations/0064_createprofil.py +++ b/topologie/migrations/0064_createprofil.py @@ -5,53 +5,49 @@ from __future__ import unicode_literals from django.db import migrations +def transfer_profil(apps, schema_editor): + db_alias = schema_editor.connection.alias + port = apps.get_model("topologie", "Port") + profil = apps.get_model("topologie", "PortProfile") + vlan = apps.get_model("machines", "Vlan") + port_list = port.objects.using(db_alias).all() + profil_nothing = profil.objects.using(db_alias).create(name='nothing', profil_default='nothing', radius_type='NO') + profil_uplink = profil.objects.using(db_alias).create(name='uplink', profil_default='uplink', radius_type='NO') + profil_machine = profil.objects.using(db_alias).create(name='asso_machine', profil_default='asso_machine', radius_type='NO') + profil_room = profil.objects.using(db_alias).create(name='room', profil_default='room', radius_type='NO') + profil_borne = profil.objects.using(db_alias).create(name='accesspoint', profil_default='accesspoint', radius_type='NO') + for vlan_instance in vlan.objects.using(db_alias).all(): + if port.objects.using(db_alias).filter(vlan_force=vlan_instance): + custom_profil = profil.objects.using(db_alias).create(name='vlan-force-' + str(vlan_instance.vlan_id), radius_type='NO', vlan_untagged=vlan_instance) + port.objects.using(db_alias).filter(vlan_force=vlan_instance).update(custom_profil=custom_profil) + if port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').count() and port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count(): + profil_room.radius_type = 'MAC-radius' + profil_room.radius_mode = 'STRICT' + common_profil = profil.objects.using(db_alias).create(name='mac-radius-common', radius_type='MAC-radius', radius_mode='COMMON') + no_rad_profil = profil.objects.using(db_alias).create(name='no-radius', radius_type='NO') + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').update(custom_profil=common_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=no_rad_profil) + elif port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').count() and port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count(): + profil_room.radius_type = 'MAC-radius' + profil_room.radius_mode = 'COMMON' + strict_profil = profil.objects.using(db_alias).create(name='mac-radius-strict', radius_type='MAC-radius', radius_mode='STRICT') + no_rad_profil = profil.objects.using(db_alias).create(name='no-radius', radius_type='NO') + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').update(custom_profil=strict_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=no_rad_profil) + else: + strict_profil = profil.objects.using(db_alias).create(name='mac-radius-strict', radius_type='MAC-radius', radius_mode='STRICT') + common_profil = profil.objects.using(db_alias).create(name='mac-radius-common', radius_type='MAC-radius', radius_mode='COMMON') + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').update(custom_profil=strict_profil) + port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=common_profil) + profil_room.save() + + class Migration(migrations.Migration): dependencies = [ ('topologie', '0063_port_custom_profil'), ] - def transfer_profil(apps, schema_editor): - db_alias = schema_editor.connection.alias - port = apps.get_model("topologie", "Port") - profil = apps.get_model("topologie", "PortProfile") - vlan = apps.get_model("machines", "Vlan") - port_list = port.objects.using(db_alias).all() - profil_nothing = profil.objects.using(db_alias).create(name='nothing', profil_default='nothing', radius_type='NO') - profil_uplink = profil.objects.using(db_alias).create(name='uplink', profil_default='uplink', radius_type='NO') - profil_machine = profil.objects.using(db_alias).create(name='asso_machine', profil_default='asso_machine', radius_type='NO') - profil_room = profil.objects.using(db_alias).create(name='room', profil_default='room', radius_type='NO') - profil_borne = profil.objects.using(db_alias).create(name='accesspoint', profil_default='accesspoint', radius_type='NO') - for vlan_instance in vlan.objects.using(db_alias).all(): - if port.objects.using(db_alias).filter(vlan_force=vlan_instance): - custom_profil = profil.objects.using(db_alias).create(name='vlan-force-' + str(vlan_instance.vlan_id), radius_type='NO', vlan_untagged=vlan_instance) - port.objects.using(db_alias).filter(vlan_force=vlan_instance).update(custom_profil=custom_profil) - if port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').count() and port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count(): - profil_room.radius_type = 'MAC-radius' - profil_room.radius_mode = 'STRICT' - common_profil = profil.objects.using(db_alias).create(name='mac-radius-common', radius_type='MAC-radius', radius_mode='COMMON') - no_rad_profil = profil.objects.using(db_alias).create(name='no-radius', radius_type='NO') - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').update(custom_profil=common_profil) - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=no_rad_profil) - elif port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').count() and port.objects.using(db_alias).filter(room__isnull=False).filter(radius='COMMON').count() > port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').count(): - profil_room.radius_type = 'MAC-radius' - profil_room.radius_mode = 'COMMON' - strict_profil = profil.objects.using(db_alias).create(name='mac-radius-strict', radius_type='MAC-radius', radius_mode='STRICT') - no_rad_profil = profil.objects.using(db_alias).create(name='no-radius', radius_type='NO') - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').update(custom_profil=strict_profil) - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=no_rad_profil) - else: - strict_profil = profil.objects.using(db_alias).create(name='mac-radius-strict', radius_type='MAC-radius', radius_mode='STRICT') - common_profil = profil.objects.using(db_alias).create(name='mac-radius-common', radius_type='MAC-radius', radius_mode='COMMON') - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='STRICT').update(custom_profil=strict_profil) - port.objects.using(db_alias).filter(room__isnull=False).filter(radius='NO').update(custom_profil=common_profil) - profil_room.save() - - - - def untransfer_profil(apps, schema_editor): - return - operations = [ - migrations.RunPython(transfer_profil, untransfer_profil), + migrations.RunPython(transfer_profil), ] diff --git a/topologie/migrations/0067_auto_20180701_0016.py b/topologie/migrations/0067_auto_20180701_0016.py new file mode 100644 index 00000000..578ee7d6 --- /dev/null +++ b/topologie/migrations/0067_auto_20180701_0016.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-06-30 22:16 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0066_auto_20180630_1855'), + ] + + operations = [ + migrations.RenameField( + model_name='port', + old_name='custom_profil', + new_name='custom_profile', + ), + migrations.AlterField( + model_name='port', + name='state', + field=models.BooleanField(default=True, help_text='Port state Active', verbose_name='Port State Active'), + ), + migrations.AlterField( + model_name='portprofile', + name='arp_protect', + field=models.BooleanField(default=False, help_text='Check if ip is dhcp assigned', verbose_name='Arp protect'), + ), + migrations.AlterField( + model_name='portprofile', + name='dhcp_snooping', + field=models.BooleanField(default=False, help_text='Protect against rogue dhcp', verbose_name='Dhcp snooping'), + ), + migrations.AlterField( + model_name='portprofile', + name='dhcpv6_snooping', + field=models.BooleanField(default=False, help_text='Protect against rogue dhcpv6', verbose_name='Dhcpv6 snooping'), + ), + migrations.AlterField( + model_name='portprofile', + name='flow_control', + field=models.BooleanField(default=False, help_text='Flow control', verbose_name='Flow control'), + ), + migrations.AlterField( + model_name='portprofile', + name='loop_protect', + field=models.BooleanField(default=False, help_text='Protect again loop', verbose_name='Loop Protect'), + ), + migrations.AlterField( + model_name='portprofile', + name='mac_limit', + field=models.IntegerField(blank=True, help_text='Limit of mac-address on this port', null=True, verbose_name='Mac limit'), + ), + migrations.AlterField( + model_name='portprofile', + name='ra_guard', + field=models.BooleanField(default=False, help_text='Protect against rogue ra', verbose_name='Ra guard'), + ), + migrations.AlterField( + model_name='portprofile', + name='radius_mode', + field=models.CharField(choices=[('STRICT', 'STRICT'), ('COMMON', 'COMMON')], default='COMMON', help_text='In case of mac-auth : mode common or strict on this port', max_length=32, verbose_name='RADIUS mode'), + ), + migrations.AlterField( + model_name='portprofile', + name='radius_type', + field=models.CharField(choices=[('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius')], help_text='Type of radius auth : inactive, mac-address or 802.1X', max_length=32, verbose_name='RADIUS type'), + ), + migrations.AlterField( + model_name='portprofile', + name='speed', + field=models.CharField(choices=[('10-half', '10-half'), ('100-half', '100-half'), ('10-full', '10-full'), ('100-full', '100-full'), ('1000-full', '1000-full'), ('auto', 'auto'), ('auto-10', 'auto-10'), ('auto-100', 'auto-100')], default='auto', help_text='Port speed limit', max_length=32, verbose_name='Speed'), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index d0534dda..715ccf52 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -391,7 +391,7 @@ class Port(AclMixin, RevMixin, models.Model): blank=True, related_name='related_port' ) - custom_profil = models.ForeignKey( + custom_profile = models.ForeignKey( 'PortProfile', on_delete=models.PROTECT, blank=True, @@ -399,8 +399,8 @@ class Port(AclMixin, RevMixin, models.Model): ) state = models.BooleanField( default=True, - help_text='Etat du port Actif', - verbose_name=_("Etat du port Actif") + help_text='Port state Active', + verbose_name=_("Port State Active") ) details = models.CharField(max_length=255, blank=True) @@ -412,7 +412,8 @@ class Port(AclMixin, RevMixin, models.Model): @cached_property def get_port_profil(self): - """Return the config profil for this port""" + """Return the config profil for this port + :returns: the profile of self (port)""" def profil_or_nothing(profil): port_profil = PortProfile.objects.filter(profil_default=profil).first() if port_profil: @@ -423,8 +424,8 @@ class Port(AclMixin, RevMixin, models.Model): nothing = PortProfile.objects.create(profil_default='nothing', name='nothing', radius_type='NO') return nothing - if self.custom_profil: - return self.custom_profil + if self.custom_profile: + return self.custom_profile elif self.related: return profil_or_nothing('uplink') elif self.machine_interface: @@ -568,57 +569,57 @@ class PortProfile(AclMixin, RevMixin, models.Model): radius_type = models.CharField( max_length=32, choices=TYPES, - help_text="Choix du type d'authentification radius : non actif, mac ou 802.1X", + help_text="Type of radius auth : inactive, mac-address or 802.1X", verbose_name=_("RADIUS type") ) radius_mode = models.CharField( max_length=32, choices=MODES, default='COMMON', - help_text="En cas d'auth par mac, auth common ou strcit sur le port", + help_text="In case of mac-auth : mode common or strict on this port", verbose_name=_("RADIUS mode") ) speed = models.CharField( max_length=32, choices=SPEED, default='auto', - help_text='Mode de transmission et vitesse du port', + help_text='Port speed limit', verbose_name=_("Speed") ) mac_limit = models.IntegerField( null=True, blank=True, - help_text='Limit du nombre de mac sur le port', + help_text='Limit of mac-address on this port', verbose_name=_("Mac limit") ) flow_control = models.BooleanField( default=False, - help_text='Gestion des débits', + help_text='Flow control', verbose_name=_("Flow control") ) dhcp_snooping = models.BooleanField( default=False, - help_text='Protection dhcp pirate', + help_text='Protect against rogue dhcp', verbose_name=_("Dhcp snooping") ) dhcpv6_snooping = models.BooleanField( default=False, - help_text='Protection dhcpv6 pirate', + help_text='Protect against rogue dhcpv6', verbose_name=_("Dhcpv6 snooping") ) arp_protect = models.BooleanField( default=False, - help_text='Verification assignation de l\'IP par dhcp', + help_text='Check if ip is dhcp assigned', verbose_name=_("Arp protect") ) ra_guard = models.BooleanField( default=False, - help_text='Protection contre ra pirate', + help_text='Protect against rogue ra', verbose_name=_("Ra guard") ) loop_protect = models.BooleanField( default=False, - help_text='Protection contre les boucles', + help_text='Protect again loop', verbose_name=_("Loop Protect") ) @@ -635,6 +636,10 @@ class PortProfile(AclMixin, RevMixin, models.Model): def security_parameters_enabled(self): return [parameter for parameter in self.security_parameters_fields if getattr(self, parameter)] + @cached_property + def security_parameters_as_str(self): + return ','.join(self.security_parameters_enabled) + def __str__(self): return self.name diff --git a/topologie/templates/topologie/aff_port.html b/topologie/templates/topologie/aff_port.html index 45e81e06..86216f15 100644 --- a/topologie/templates/topologie/aff_port.html +++ b/topologie/templates/topologie/aff_port.html @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% include "buttons/sort.html" with prefix='port' col='room' text='Room' %} {% include "buttons/sort.html" with prefix='port' col='interface' text='Interface machine' %} {% include "buttons/sort.html" with prefix='port' col='related' text='Related' %}Etat du portEtat du port Profil du port Détails {% if port.state %} Actif{% else %}Désactivé{% endif %}{% if not port.custom_profil %}Par défaut : {% endif %}{{port.get_port_profil}}{% if not port.custom_profile %}Par défaut : {% endif %}{{port.get_port_profil}} {{ port.details }} @@ -77,10 +77,10 @@ with this program; if not, write to the Free Software Foundation, Inc., - {% acl_end %} + {% acl_end %} {% can_delete port %} - - + + {% acl_end %}
- - - - - - - - - - + + + + + + + + + + {% for port_profile in port_profile_list %} - - - {% endif %} - - - - + + + {% endif %} + + + + {% endfor %}
{% trans "Nom" %}{% trans "Default pour" %}{% trans "VLANs" %}{% trans "Réglages RADIUS" %}{% trans "Vitesse" %}{% trans "Mac address limit" %}{% trans "Sécurité" %}
{% trans "Name" %}{% trans "Default for" %}{% trans "VLANs" %}{% trans "RADIUS settings" %}{% trans "Speed" %}{% trans "Mac address limit" %}{% trans "Security" %}
{{port_profile.name}} {{port_profile.profil_default}} - Untagged : {{port_profile.vlan_untagged}} -
- Tagged : {{port_profile.vlan_tagged.all|join:", "}} -
- Type : {{port_profile.radius_type}} - {% if port_profile.radius_type == "MAC-radius" %} -
- Mode : {{port_profile.radius_mode}}
{{port_profile.speed}}{{port_profile.mac_limit}}{{port_profile.security_parameters_enabled|join:"
"}}
- {% include 'buttons/history.html' with href='topologie:history' name='portprofile' id=port_profile.pk %} - {% can_edit port_profile %} - {% include 'buttons/edit.html' with href='topologie:edit-port-profile' id=port_profile.pk %} - {% acl_end %} - {% can_delete port_profile %} - {% include 'buttons/suppr.html' with href='topologie:del-port-profile' id=port_profile.pk %} - {% acl_end %} - + {% if port_profile.vlan_untagged %} + Untagged : {{port_profile.vlan_untagged}} +
+ {% endif %} + {% if port_profile.vlan_untagged %} + Tagged : {{port_profile.vlan_tagged.all|join:", "}} + {% endif %} +
+ Type : {{port_profile.radius_type}} + {% if port_profile.radius_type == "MAC-radius" %} +
+ Mode : {{port_profile.radius_mode}}
{{port_profile.speed}}{{port_profile.mac_limit}}{{port_profile.security_parameters_enabled|join:"
"}}
+ {% include 'buttons/history.html' with href='topologie:history' name='portprofile' id=port_profile.pk %} + {% can_edit port_profile %} + {% include 'buttons/edit.html' with href='topologie:edit-port-profile' id=port_profile.pk %} + {% acl_end %} + {% can_delete port_profile %} + {% include 'buttons/suppr.html' with href='topologie:del-port-profile' id=port_profile.pk %} + {% acl_end %} +
diff --git a/topologie/templates/topologie/index.html b/topologie/templates/topologie/index.html index 7949f412..f596e6a5 100644 --- a/topologie/templates/topologie/index.html +++ b/topologie/templates/topologie/index.html @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load acl %} -{% load i18n %} {% block title %}Switchs{% endblock %} diff --git a/topologie/templates/topologie/index_portprofile.html b/topologie/templates/topologie/index_portprofile.html index a4287da6..f95415c8 100644 --- a/topologie/templates/topologie/index_portprofile.html +++ b/topologie/templates/topologie/index_portprofile.html @@ -4,9 +4,8 @@ Re2o est un logiciel d'administration développé initiallement au rezometz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. -Copyright © 2017 Gabriel Détraz -Copyright © 2017 Goulven Kermarec -Copyright © 2017 Augustin Lemesle +Copyright © 2018 Gabriel Détraz + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,9 +35,9 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans " Add a port profile" %}
{% acl_end %} - {% include "topologie/aff_port_profile.html" with port_profile_list=port_profile_list %} -
-
-
+{% include "topologie/aff_port_profile.html" with port_profile_list=port_profile_list %} +
+
+
{% endblock %} diff --git a/topologie/templates/topologie/sidebar.html b/topologie/templates/topologie/sidebar.html index 8652690e..04ee5202 100644 --- a/topologie/templates/topologie/sidebar.html +++ b/topologie/templates/topologie/sidebar.html @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Switchs - + Config des ports switchs diff --git a/topologie/views.py b/topologie/views.py index b7761e0c..e5453982 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -1014,11 +1014,11 @@ def del_port_profile(request, port_profile, **_kwargs): if request.method == 'POST': try: port_profile.delete() - messages.success(request, _("The port profile was successfully" - " deleted")) + messages.success(request, + _("The port profile was successfully deleted")) except ProtectedError: - messages.success(request, _("Impossible to delete the port" - " profile")) + messages.success(request, + _("Impossible to delete the port profile")) return redirect(reverse('topologie:index')) return form( {'objet': port_profile, 'objet_name': _("Port profile")}, From a07e0d922acd2a0f4cd511b1dfd708844ceb6772 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 30 Jun 2018 22:23:00 +0000 Subject: [PATCH 11/12] Petit bug affichage vlans --- topologie/templates/topologie/aff_port_profile.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html index 577ac2a5..12c8b365 100644 --- a/topologie/templates/topologie/aff_port_profile.html +++ b/topologie/templates/topologie/aff_port_profile.html @@ -52,7 +52,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Untagged : {{port_profile.vlan_untagged}}
{% endif %} - {% if port_profile.vlan_untagged %} + {% if port_profile.vlan_tagged.all %} Tagged : {{port_profile.vlan_tagged.all|join:", "}} {% endif %} From eefa0b4add2f4ecc1fa8ef48cb3a28ef6d7ac183 Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Wed, 11 Jul 2018 19:37:22 +0200 Subject: [PATCH 12/12] PEP8 mon amour + typos --- freeradius_utils/auth.py | 18 +-- topologie/models.py | 115 ++++++++----- .../templates/topologie/aff_port_profile.html | 4 +- topologie/views.py | 153 ++++++++++-------- 4 files changed, 172 insertions(+), 118 deletions(-) diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index 8450777b..d743495f 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -355,7 +355,7 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, port=port_number ) .first()) - + # Si le port est inconnu, on place sur le vlan defaut # Aucune information particulière ne permet de déterminer quelle # politique à appliquer sur ce port @@ -363,12 +363,12 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK) # On récupère le profil du port - port_profil = port.get_port_profil + port_profile = port.get_port_profile - # Si un vlan a été précisé dans la config du port, + # Si un vlan a été précisé dans la config du port, # on l'utilise pour VLAN_OK - if port_profil.vlan_untagged: - DECISION_VLAN = int(port_profil.vlan_untagged.vlan_id) + if port_profile.vlan_untagged: + DECISION_VLAN = int(port_profile.vlan_untagged.vlan_id) extra_log = u"Force sur vlan " + str(DECISION_VLAN) else: DECISION_VLAN = VLAN_OK @@ -378,7 +378,7 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, return (sw_name, port.room, u'Port desactivé', VLAN_NOK) # Si radius est désactivé, on laisse passer - if port_profil.radius_type == 'NO': + if port_profile.radius_type == 'NO': return (sw_name, "", u"Pas d'authentification sur ce port" + extra_log, @@ -386,7 +386,7 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, # Si le 802.1X est activé sur ce port, cela veut dire que la personne a été accept précédemment # Par conséquent, on laisse passer sur le bon vlan - if nas_type.port_access_mode == '802.1X' and port_profil.radius_type == '802.1X': + if nas_type.port_access_mode == '802.1X' and port_profile.radius_type == '802.1X': room = port.room or "Chambre/local inconnu" return (sw_name, room, u'Acceptation authentification 802.1X', DECISION_VLAN) @@ -395,7 +395,7 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, # rattachés à ce port sont bien à jour de cotisation. Sinon on rejette (anti squattage) # Il n'est pas possible de se connecter sur une prise strict sans adhérent à jour de cotis # dedans - if port_profil.radius_mode == 'STRICT': + if port_profile.radius_mode == 'STRICT': room = port.room if not room: return (sw_name, "Inconnue", u'Chambre inconnue', VLAN_NOK) @@ -411,7 +411,7 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, # else: user OK, on passe à la verif MAC # Si on fait de l'auth par mac, on cherche l'interface via sa mac dans la bdd - if port_profil.radius_mode == 'COMMON' or port_profil.radius_mode == 'STRICT': + if port_profile.radius_mode == 'COMMON' or port_profile.radius_mode == 'STRICT': # Authentification par mac interface = (Interface.objects .filter(mac_address=mac_address) diff --git a/topologie/models.py b/topologie/models.py index 715ccf52..bbcab749 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -40,7 +40,7 @@ from __future__ import unicode_literals import itertools from django.db import models -from django.db.models.signals import pre_save, post_save, post_delete +from django.db.models.signals import post_save, post_delete from django.utils.functional import cached_property from django.dispatch import receiver from django.core.exceptions import ValidationError @@ -52,11 +52,6 @@ from reversion import revisions as reversion from machines.models import Machine, regen from re2o.mixins import AclMixin, RevMixin -from os.path import isfile -from os import remove - - - class Stack(AclMixin, RevMixin, models.Model): """Un objet stack. Regrouppe des switchs en foreign key @@ -123,7 +118,10 @@ class AccessPoint(AclMixin, Machine): ) def building(self): - """Return the building of the AP/Server (building of the switchs connected to...)""" + """ + Return the building of the AP/Server (building of the switchs + connected to...) + """ return Building.objects.filter( switchbay__switch=self.switch() ) @@ -135,14 +133,18 @@ class AccessPoint(AclMixin, Machine): @classmethod def all_ap_in(cls, building_instance): """Get a building as argument, returns all ap of a building""" - return cls.objects.filter(interface__port__switch__switchbay__building=building_instance) + return cls.objects.filter( + interface__port__switch__switchbay__building=building_instance + ) def __str__(self): return str(self.interface_set.first()) class Server(Machine): - """Dummy class, to retrieve servers of a building, or get switch of a server""" + """ + Dummy class, to retrieve servers of a building, or get switch of a server + """ class Meta: proxy = True @@ -160,7 +162,10 @@ class Server(Machine): ) def building(self): - """Return the building of the AP/Server (building of the switchs connected to...)""" + """ + Return the building of the AP/Server + (building of the switchs connected to...) + """ return Building.objects.filter( switchbay__switch=self.switch() ) @@ -172,7 +177,9 @@ class Server(Machine): @classmethod def all_server_in(cls, building_instance): """Get a building as argument, returns all server of a building""" - return cls.objects.filter(interface__port__switch__switchbay__building=building_instance).exclude(accesspoint__isnull=False) + return cls.objects.filter( + interface__port__switch__switchbay__building=building_instance + ).exclude(accesspoint__isnull=False) def __str__(self): return str(self.interface_set.first()) @@ -200,7 +207,7 @@ class Switch(AclMixin, Machine): blank=True, null=True, on_delete=models.SET_NULL - ) + ) stack_member_id = models.PositiveIntegerField( blank=True, null=True, @@ -238,7 +245,7 @@ class Switch(AclMixin, Machine): raise ValidationError( {'stack_member_id': "L'id de ce switch est en\ dehors des bornes permises pas la stack"} - ) + ) else: raise ValidationError({'stack_member_id': "L'id dans la stack\ ne peut être nul"}) @@ -378,25 +385,25 @@ class Port(AclMixin, RevMixin, models.Model): on_delete=models.PROTECT, blank=True, null=True - ) + ) machine_interface = models.ForeignKey( 'machines.Interface', on_delete=models.SET_NULL, blank=True, null=True - ) + ) related = models.OneToOneField( 'self', null=True, blank=True, related_name='related_port' - ) + ) custom_profile = models.ForeignKey( 'PortProfile', on_delete=models.PROTECT, blank=True, null=True - ) + ) state = models.BooleanField( default=True, help_text='Port state Active', @@ -411,32 +418,35 @@ class Port(AclMixin, RevMixin, models.Model): ) @cached_property - def get_port_profil(self): - """Return the config profil for this port + def get_port_profile(self): + """Return the config profile for this port :returns: the profile of self (port)""" - def profil_or_nothing(profil): - port_profil = PortProfile.objects.filter(profil_default=profil).first() - if port_profil: - return port_profil + def profile_or_nothing(profile): + port_profile = PortProfile.objects.filter( + profile_default=profile).first() + if port_profile: + return port_profile else: - nothing = PortProfile.objects.filter(profil_default='nothing').first() - if not nothing: - nothing = PortProfile.objects.create(profil_default='nothing', name='nothing', radius_type='NO') - return nothing + nothing_profile, _created = PortProfile.objects.get_or_create( + profile_default='nothing', + name='nothing', + radius_type='NO' + ) + return nothing_profile if self.custom_profile: return self.custom_profile elif self.related: - return profil_or_nothing('uplink') + return profile_or_nothing('uplink') elif self.machine_interface: if hasattr(self.machine_interface.machine, 'accesspoint'): - return profil_or_nothing('access_point') + return profile_or_nothing('access_point') else: - return profil_or_nothing('asso_machine') + return profile_or_nothing('asso_machine') elif self.room: - return profil_or_nothing('room') + return profile_or_nothing('room') else: - return profil_or_nothing('nothing') + return profile_or_nothing('nothing') @classmethod def get_instance(cls, portid, *_args, **kwargs): @@ -521,11 +531,11 @@ class PortProfile(AclMixin, RevMixin, models.Model): ('NO', 'NO'), ('802.1X', '802.1X'), ('MAC-radius', 'MAC-radius'), - ) + ) MODES = ( ('STRICT', 'STRICT'), ('COMMON', 'COMMON'), - ) + ) SPEED = ( ('10-half', '10-half'), ('100-half', '100-half'), @@ -535,14 +545,14 @@ class PortProfile(AclMixin, RevMixin, models.Model): ('auto', 'auto'), ('auto-10', 'auto-10'), ('auto-100', 'auto-100'), - ) - PROFIL_DEFAULT= ( + ) + PROFIL_DEFAULT = ( ('room', 'room'), ('accespoint', 'accesspoint'), ('uplink', 'uplink'), ('asso_machine', 'asso_machine'), ('nothing', 'nothing'), - ) + ) name = models.CharField(max_length=255, verbose_name=_("Name")) profil_default = models.CharField( max_length=32, @@ -616,25 +626,36 @@ class PortProfile(AclMixin, RevMixin, models.Model): default=False, help_text='Protect against rogue ra', verbose_name=_("Ra guard") - ) + ) loop_protect = models.BooleanField( default=False, help_text='Protect again loop', verbose_name=_("Loop Protect") - ) + ) class Meta: permissions = ( - ("view_port_profile", _("Can view a port profile object")), + ("view_port_profile", _("Can view a port profile object")), ) verbose_name = _("Port profile") verbose_name_plural = _("Port profiles") - security_parameters_fields = ['loop_protect', 'ra_guard', 'arp_protect', 'dhcpv6_snooping', 'dhcp_snooping', 'flow_control'] + security_parameters_fields = [ + 'loop_protect', + 'ra_guard', + 'arp_protect', + 'dhcpv6_snooping', + 'dhcp_snooping', + 'flow_control' + ] @cached_property def security_parameters_enabled(self): - return [parameter for parameter in self.security_parameters_fields if getattr(self, parameter)] + return [ + parameter + for parameter in self.security_parameters_fields + if getattr(self, parameter) + ] @cached_property def security_parameters_as_str(self): @@ -650,45 +671,55 @@ def ap_post_save(**_kwargs): regen('unifi-ap-names') regen("graph_topo") + @receiver(post_delete, sender=AccessPoint) def ap_post_delete(**_kwargs): """Regeneration des noms des bornes vers le controleur""" regen('unifi-ap-names') regen("graph_topo") + @receiver(post_delete, sender=Stack) def stack_post_delete(**_kwargs): """Vide les id des switches membres d'une stack supprimée""" Switch.objects.filter(stack=None).update(stack_member_id=None) + @receiver(post_save, sender=Port) def port_post_save(**_kwargs): regen("graph_topo") + @receiver(post_delete, sender=Port) def port_post_delete(**_kwargs): regen("graph_topo") + @receiver(post_save, sender=ModelSwitch) def modelswitch_post_save(**_kwargs): regen("graph_topo") + @receiver(post_delete, sender=ModelSwitch) def modelswitch_post_delete(**_kwargs): regen("graph_topo") + @receiver(post_save, sender=Building) def building_post_save(**_kwargs): regen("graph_topo") + @receiver(post_delete, sender=Building) def building_post_delete(**_kwargs): regen("graph_topo") + @receiver(post_save, sender=Switch) def switch_post_save(**_kwargs): regen("graph_topo") + @receiver(post_delete, sender=Switch) def switch_post_delete(**_kwargs): regen("graph_topo") diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html index 12c8b365..65446236 100644 --- a/topologie/templates/topologie/aff_port_profile.html +++ b/topologie/templates/topologie/aff_port_profile.html @@ -29,9 +29,9 @@ with this program; if not, write to the Free Software Foundation, Inc., {% include "pagination.html" with list=port_profile_list %} {% endif %} - + @@ -49,7 +49,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% trans "Name" %} {% trans "Default for" %}{{port_profile.profil_default}} {% if port_profile.vlan_untagged %} - Untagged : {{port_profile.vlan_untagged}} + Untagged : {{port_profile.vlan_untagged}}
{% endif %} {% if port_profile.vlan_tagged.all %} diff --git a/topologie/views.py b/topologie/views.py index e5453982..a6c6ab69 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -42,11 +42,7 @@ from django.contrib.auth.decorators import login_required from django.db import IntegrityError from django.db.models import ProtectedError, Prefetch from django.core.exceptions import ValidationError -from django.contrib.staticfiles.storage import staticfiles_storage -from django.template.loader import get_template from django.template import Context, Template, loader -from django.db.models.signals import post_save -from django.dispatch import receiver from django.utils.translation import ugettext as _ import tempfile @@ -105,8 +101,7 @@ from subprocess import ( PIPE ) -from os.path import isfile -from os import remove +from os.path import isfile @login_required @@ -128,13 +123,17 @@ def index(request): SortTable.TOPOLOGIE_INDEX ) - pagination_number = GeneralOption.get_cached_value('pagination_number') switch_list = re2o_paginator(request, switch_list, pagination_number) - if any(service_link.need_regen for service_link in Service_link.objects.filter(service__service_type='graph_topo')): + if any( + service_link.need_regen + for service_link in Service_link.objects.filter( + service__service_type='graph_topo') + ): make_machine_graph() - for service_link in Service_link.objects.filter(service__service_type='graph_topo'): + for service_link in Service_link.objects.filter( + service__service_type='graph_topo'): service_link.done_regen() if not isfile("/var/www/re2o/media/images/switchs.png"): @@ -150,8 +149,10 @@ def index(request): @can_view_all(PortProfile) def index_port_profile(request): pagination_number = GeneralOption.get_cached_value('pagination_number') - port_profile_list = PortProfile.objects.all().select_related('vlan_untagged') - port_profile_list = re2o_paginator(request, port_profile_list, pagination_number) + port_profile_list = PortProfile.objects.all().select_related( + 'vlan_untagged') + port_profile_list = re2o_paginator( + request, port_profile_list, pagination_number) return render( request, 'topologie/index_portprofile.html', @@ -460,7 +461,7 @@ def new_switch(request): ) domain = DomainForm( request.POST or None, - ) + ) if switch.is_valid() and interface.is_valid(): user = AssoOption.get_cached_value('utilisateur_asso') if not user: @@ -530,7 +531,7 @@ def create_ports(request, switchid): return redirect(reverse( 'topologie:index-port', kwargs={'switchid': switchid} - )) + )) return form( {'id_switch': switchid, 'topoform': port_form}, 'topologie/switch.html', @@ -548,16 +549,16 @@ def edit_switch(request, switch, switchid): request.POST or None, instance=switch, user=request.user - ) + ) interface_form = EditInterfaceForm( request.POST or None, instance=switch.interface_set.first(), user=request.user - ) + ) domain_form = DomainForm( request.POST or None, instance=switch.interface_set.first().domain - ) + ) if switch_form.is_valid() and interface_form.is_valid(): new_switch_obj = switch_form.save(commit=False) new_interface_obj = interface_form.save(commit=False) @@ -601,7 +602,7 @@ def new_ap(request): ) domain = DomainForm( request.POST or None, - ) + ) if ap.is_valid() and interface.is_valid(): user = AssoOption.get_cached_value('utilisateur_asso') if not user: @@ -656,7 +657,7 @@ def edit_ap(request, ap, **_kwargs): domain_form = DomainForm( request.POST or None, instance=ap.interface_set.first().domain - ) + ) if ap_form.is_valid() and interface_form.is_valid(): user = AssoOption.get_cached_value('utilisateur_asso') if not user: @@ -970,7 +971,7 @@ def del_constructor_switch(request, constructor_switch, **_kwargs): return form({ 'objet': constructor_switch, 'objet_name': 'Constructeur de switch' - }, 'topologie/delete.html', request) + }, 'topologie/delete.html', request) @login_required @@ -993,7 +994,8 @@ def new_port_profile(request): @can_edit(PortProfile) def edit_port_profile(request, port_profile, **_kwargs): """Edit a port profile""" - port_profile = EditPortProfileForm(request.POST or None, instance=port_profile) + port_profile = EditPortProfileForm( + request.POST or None, instance=port_profile) if port_profile.is_valid(): if port_profile.changed_data: port_profile.save() @@ -1006,7 +1008,6 @@ def edit_port_profile(request, port_profile, **_kwargs): ) - @login_required @can_delete(PortProfile) def del_port_profile(request, port_profile, **_kwargs): @@ -1014,25 +1015,26 @@ def del_port_profile(request, port_profile, **_kwargs): if request.method == 'POST': try: port_profile.delete() - messages.success(request, - _("The port profile was successfully deleted")) + messages.success(request, + _("The port profile was successfully deleted")) except ProtectedError: - messages.success(request, - _("Impossible to delete the port profile")) + messages.success(request, + _("Impossible to delete the port profile")) return redirect(reverse('topologie:index')) return form( - {'objet': port_profile, 'objet_name': _("Port profile")}, - 'topologie/delete.html', - request + {'objet': port_profile, 'objet_name': _("Port profile")}, + 'topologie/delete.html', + request ) + def make_machine_graph(): """ Create the graph of switchs, machines and access points. """ dico = { 'subs': [], - 'links' : [], + 'links': [], 'alone': [], 'colors': { 'head': "#7f0505", # Color parameters for the graph @@ -1041,23 +1043,23 @@ def make_machine_graph(): 'border_bornes': "#02078e", 'head_bornes': "#25771c", 'head_server': "#1c3777" - } } + } missing = list(Switch.objects.all()) detected = [] for building in Building.objects.all(): # Visit all buildings dico['subs'].append( { - 'bat_id': building.id, - 'bat_name': building, - 'switchs': [], - 'bornes': [], - 'machines': [] + 'bat_id': building.id, + 'bat_name': building, + 'switchs': [], + 'bornes': [], + 'machines': [] } ) # Visit all switchs in this building - for switch in Switch.objects.filter(switchbay__building=building): + for switch in Switch.objects.filter(switchbay__building=building): dico['subs'][-1]['switchs'].append({ 'name': switch.main_interface().domain.name, 'nombre': switch.number, @@ -1067,7 +1069,7 @@ def make_machine_graph(): 'ports': [] }) # visit all ports of this switch and add the switchs linked to it - for port in switch.ports.filter(related__isnull=False): + for port in switch.ports.filter(related__isnull=False): dico['subs'][-1]['switchs'][-1]['ports'].append({ 'numero': port.port, 'related': port.related.switch.main_interface().domain.name @@ -1085,50 +1087,58 @@ def make_machine_graph(): dico['subs'][-1]['machines'].append({ 'name': server.short_name, 'switch': server.switch()[0].main_interface().domain.name, - 'port': Port.objects.filter(machine_interface__machine=server)[0].port + 'port': Port.objects.filter( + machine_interface__machine=server + )[0].port }) # While the list of forgotten ones is not empty while missing: if missing[0].ports.count(): # The switch is not empty - links, new_detected = recursive_switchs(missing[0], None, [missing[0]]) + links, new_detected = recursive_switchs( + missing[0], None, [missing[0]]) for link in links: dico['links'].append(link) # Update the lists of missings and already detected switchs - missing=[i for i in missing if i not in new_detected] + missing = [i for i in missing if i not in new_detected] detected += new_detected - else: # If the switch have no ports, don't explore it and hop to the next one + # If the switch have no ports, don't explore it and hop to the next one + else: del missing[0] # Switchs that are not connected or not in a building - for switch in Switch.objects.filter(switchbay__isnull=True).exclude(ports__related__isnull=False): + for switch in Switch.objects.filter( + switchbay__isnull=True).exclude(ports__related__isnull=False): dico['alone'].append({ 'id': switch.id, 'name': switch.main_interface().domain.name - }) + }) + # generate the dot file + dot_data = generate_dot(dico, 'topologie/graph_switch.dot') - dot_data=generate_dot(dico,'topologie/graph_switch.dot') # generate the dot file - - f = tempfile.NamedTemporaryFile(mode='w+', encoding='utf-8', delete=False) # Create a temporary file to store the dot data + # Create a temporary file to store the dot data + f = tempfile.NamedTemporaryFile(mode='w+', encoding='utf-8', delete=False) with f: - f.write(dot_data) + f.write(dot_data) unflatten = Popen( # unflatten the graph to make it look better - ["unflatten","-l", "3", f.name], + ["unflatten", "-l", "3", f.name], stdout=PIPE ) - image = Popen( # pipe the result of the first command into the second + Popen( # pipe the result of the first command into the second ["dot", "-Tpng", "-o", MEDIA_ROOT + "/images/switchs.png"], stdin=unflatten.stdout, stdout=PIPE ) -def generate_dot(data,template): + +def generate_dot(data, template): """create the dot file :param data: dictionary passed to the template :param template: path to the dot template :return: all the lines of the dot file""" t = loader.get_template(template) - if not isinstance(t, Template) and not (hasattr(t, 'template') and isinstance(t.template, Template)): + if not isinstance(t, Template) and \ + not (hasattr(t, 'template') and isinstance(t.template, Template)): raise Exception("Le template par défaut de Django n'est pas utilisé." "Cela peut mener à des erreurs de rendu." "Vérifiez les paramètres") @@ -1136,27 +1146,40 @@ def generate_dot(data,template): dot = t.render(c) return(dot) + def recursive_switchs(switch_start, switch_before, detected): """Visit the switch and travel to the switchs linked to it. :param switch_start: the switch to begin the visit on - :param switch_before: the switch that you come from. None if switch_start is the first one - :param detected: list of all switchs already visited. None if switch_start is the first one - :return: A list of all the links found and a list of all the switchs visited""" + :param switch_before: the switch that you come from. + None if switch_start is the first one + :param detected: list of all switchs already visited. + None if switch_start is the first one + :return: A list of all the links found and a list of + all the switchs visited + """ detected.append(switch_start) - links_return=[] # list of dictionaries of the links to be detected - for port in switch_start.ports.filter(related__isnull=False): # create links to every switchs below - if port.related.switch != switch_before and port.related.switch != port.switch and port.related.switch not in detected: # Not the switch that we come from, not the current switch + links_return = [] # list of dictionaries of the links to be detected + # create links to every switchs below + for port in switch_start.ports.filter(related__isnull=False): + # Not the switch that we come from, not the current switch + if port.related.switch != switch_before \ + and port.related.switch != port.switch \ + and port.related.switch not in detected: links = { # Dictionary of a link - 'depart':switch_start.id, - 'arrive':port.related.switch.id + 'depart': switch_start.id, + 'arrive': port.related.switch.id } links_return.append(links) # Add current and below levels links - for port in switch_start.ports.filter(related__isnull=False): # go down on every related switchs - if port.related.switch not in detected: # The switch at the end of this link has not been visited - links_down, detected = recursive_switchs(port.related.switch, switch_start, detected) # explore it and get the results - for link in links_down: # Add the non empty links to the current list + # go down on every related switchs + for port in switch_start.ports.filter(related__isnull=False): + # The switch at the end of this link has not been visited + if port.related.switch not in detected: + # explore it and get the results + links_down, detected = recursive_switchs( + port.related.switch, switch_start, detected) + # Add the non empty links to the current list + for link in links_down: if link: - links_return.append(link) + links_return.append(link) return (links_return, detected) -