mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-26 14:42:25 +00:00
Merge branch 'switch_conf_json' into 'dev'
Switch conf json See merge request federez/re2o!180
This commit is contained in:
commit
a02e03ac71
19 changed files with 888 additions and 132 deletions
|
@ -355,27 +355,47 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
port=port_number
|
port=port_number
|
||||||
)
|
)
|
||||||
.first())
|
.first())
|
||||||
|
|
||||||
# Si le port est inconnu, on place sur le vlan defaut
|
# 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:
|
if not port:
|
||||||
return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK)
|
return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK)
|
||||||
|
|
||||||
# Si un vlan a été précisé, on l'utilise pour VLAN_OK
|
# On récupère le profil du port
|
||||||
if port.vlan_force:
|
port_profile = port.get_port_profile
|
||||||
DECISION_VLAN = int(port.vlan_force.vlan_id)
|
|
||||||
|
# Si un vlan a été précisé dans la config du port,
|
||||||
|
# on l'utilise pour VLAN_OK
|
||||||
|
if port_profile.vlan_untagged:
|
||||||
|
DECISION_VLAN = int(port_profile.vlan_untagged.vlan_id)
|
||||||
extra_log = u"Force sur vlan " + str(DECISION_VLAN)
|
extra_log = u"Force sur vlan " + str(DECISION_VLAN)
|
||||||
else:
|
else:
|
||||||
DECISION_VLAN = VLAN_OK
|
DECISION_VLAN = VLAN_OK
|
||||||
|
|
||||||
if port.radius == 'NO':
|
# 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 desactivé', VLAN_NOK)
|
||||||
|
|
||||||
|
# Si radius est désactivé, on laisse passer
|
||||||
|
if port_profile.radius_type == 'NO':
|
||||||
return (sw_name,
|
return (sw_name,
|
||||||
"",
|
"",
|
||||||
u"Pas d'authentification sur ce port" + extra_log,
|
u"Pas d'authentification sur ce port" + extra_log,
|
||||||
DECISION_VLAN)
|
DECISION_VLAN)
|
||||||
|
|
||||||
if port.radius == 'BLOQ':
|
# Si le 802.1X est activé sur ce port, cela veut dire que la personne a été accept précédemment
|
||||||
return (sw_name, port.room, u'Port desactive', VLAN_NOK)
|
# Par conséquent, on laisse passer sur le bon vlan
|
||||||
|
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)
|
||||||
|
|
||||||
if port.radius == 'STRICT':
|
# 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_profile.radius_mode == 'STRICT':
|
||||||
room = port.room
|
room = port.room
|
||||||
if not room:
|
if not room:
|
||||||
return (sw_name, "Inconnue", u'Chambre inconnue', VLAN_NOK)
|
return (sw_name, "Inconnue", u'Chambre inconnue', VLAN_NOK)
|
||||||
|
@ -390,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)
|
return (sw_name, room, u'Chambre resident desactive', VLAN_NOK)
|
||||||
# else: user OK, on passe à la verif MAC
|
# else: user OK, on passe à la verif MAC
|
||||||
|
|
||||||
if port.radius == 'COMMON' or port.radius == 'STRICT':
|
# Si on fait de l'auth par mac, on cherche l'interface via sa mac dans la bdd
|
||||||
|
if port_profile.radius_mode == 'COMMON' or port_profile.radius_mode == 'STRICT':
|
||||||
# Authentification par mac
|
# Authentification par mac
|
||||||
interface = (Interface.objects
|
interface = (Interface.objects
|
||||||
.filter(mac_address=mac_address)
|
.filter(mac_address=mac_address)
|
||||||
|
@ -399,15 +420,19 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
.first())
|
.first())
|
||||||
if not interface:
|
if not interface:
|
||||||
room = port.room
|
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:
|
if not nas_type.autocapture_mac:
|
||||||
return (sw_name, "", u'Machine inconnue', VLAN_NOK)
|
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:
|
elif not room:
|
||||||
return (sw_name,
|
return (sw_name,
|
||||||
"Inconnue",
|
"Inconnue",
|
||||||
u'Chambre et machine inconnues',
|
u'Chambre et machine inconnues',
|
||||||
VLAN_NOK)
|
VLAN_NOK)
|
||||||
else:
|
else:
|
||||||
|
# Si la chambre est vide (local club, prises en libre services)
|
||||||
|
# Impossible d'autocapturer
|
||||||
if not room_user:
|
if not room_user:
|
||||||
room_user = User.objects.filter(
|
room_user = User.objects.filter(
|
||||||
Q(club__room=port.room) | Q(adherent__room=port.room)
|
Q(club__room=port.room) | Q(adherent__room=port.room)
|
||||||
|
@ -418,6 +443,8 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
u'Machine et propriétaire de la chambre '
|
u'Machine et propriétaire de la chambre '
|
||||||
'inconnus',
|
'inconnus',
|
||||||
VLAN_NOK)
|
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:
|
elif room_user.count() > 1:
|
||||||
return (sw_name,
|
return (sw_name,
|
||||||
room,
|
room,
|
||||||
|
@ -425,11 +452,13 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
'dans la chambre/local -> ajout de mac '
|
'dans la chambre/local -> ajout de mac '
|
||||||
'automatique impossible',
|
'automatique impossible',
|
||||||
VLAN_NOK)
|
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():
|
elif not room_user.first().has_access():
|
||||||
return (sw_name,
|
return (sw_name,
|
||||||
room,
|
room,
|
||||||
u'Machine inconnue et adhérent non cotisant',
|
u'Machine inconnue et adhérent non cotisant',
|
||||||
VLAN_NOK)
|
VLAN_NOK)
|
||||||
|
# Sinon on capture et on laisse passer sur le bon vlan
|
||||||
else:
|
else:
|
||||||
result, reason = (room_user
|
result, reason = (room_user
|
||||||
.first()
|
.first()
|
||||||
|
@ -449,6 +478,9 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
reason + str(mac_address)
|
reason + str(mac_address)
|
||||||
),
|
),
|
||||||
VLAN_NOK)
|
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:
|
else:
|
||||||
room = port.room
|
room = port.room
|
||||||
if not interface.is_active:
|
if not interface.is_active:
|
||||||
|
|
|
@ -262,9 +262,9 @@ def search_single_word(word, filters, user,
|
||||||
) | Q(
|
) | Q(
|
||||||
related__switch__interface__domain__name__icontains=word
|
related__switch__interface__domain__name__icontains=word
|
||||||
) | Q(
|
) | Q(
|
||||||
radius__icontains=word
|
custom_profile__name__icontains=word
|
||||||
) | Q(
|
) | Q(
|
||||||
vlan_force__name__icontains=word
|
custom_profile__profil_default__icontains=word
|
||||||
) | Q(
|
) | Q(
|
||||||
details__icontains=word
|
details__icontains=word
|
||||||
)
|
)
|
||||||
|
|
|
@ -38,7 +38,8 @@ from .models import (
|
||||||
ConstructorSwitch,
|
ConstructorSwitch,
|
||||||
AccessPoint,
|
AccessPoint,
|
||||||
SwitchBay,
|
SwitchBay,
|
||||||
Building
|
Building,
|
||||||
|
PortProfile,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,6 +87,9 @@ class BuildingAdmin(VersionAdmin):
|
||||||
"""Administration d'un batiment"""
|
"""Administration d'un batiment"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class PortProfileAdmin(VersionAdmin):
|
||||||
|
"""Administration of a port profile"""
|
||||||
|
pass
|
||||||
|
|
||||||
admin.site.register(Port, PortAdmin)
|
admin.site.register(Port, PortAdmin)
|
||||||
admin.site.register(AccessPoint, AccessPointAdmin)
|
admin.site.register(AccessPoint, AccessPointAdmin)
|
||||||
|
@ -96,3 +100,4 @@ admin.site.register(ModelSwitch, ModelSwitchAdmin)
|
||||||
admin.site.register(ConstructorSwitch, ConstructorSwitchAdmin)
|
admin.site.register(ConstructorSwitch, ConstructorSwitchAdmin)
|
||||||
admin.site.register(Building, BuildingAdmin)
|
admin.site.register(Building, BuildingAdmin)
|
||||||
admin.site.register(SwitchBay, SwitchBayAdmin)
|
admin.site.register(SwitchBay, SwitchBayAdmin)
|
||||||
|
admin.site.register(PortProfile, PortProfileAdmin)
|
||||||
|
|
|
@ -35,6 +35,7 @@ from __future__ import unicode_literals
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms import ModelForm
|
from django.forms import ModelForm
|
||||||
from django.db.models import Prefetch
|
from django.db.models import Prefetch
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from machines.models import Interface
|
from machines.models import Interface
|
||||||
from machines.forms import (
|
from machines.forms import (
|
||||||
|
@ -53,6 +54,7 @@ from .models import (
|
||||||
AccessPoint,
|
AccessPoint,
|
||||||
SwitchBay,
|
SwitchBay,
|
||||||
Building,
|
Building,
|
||||||
|
PortProfile,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,8 +80,8 @@ class EditPortForm(FormRevMixin, ModelForm):
|
||||||
optimiser le temps de chargement avec select_related (vraiment
|
optimiser le temps de chargement avec select_related (vraiment
|
||||||
lent sans)"""
|
lent sans)"""
|
||||||
class Meta(PortForm.Meta):
|
class Meta(PortForm.Meta):
|
||||||
fields = ['room', 'related', 'machine_interface', 'radius',
|
fields = ['room', 'related', 'machine_interface', 'custom_profile',
|
||||||
'vlan_force', 'details']
|
'state', 'details']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
|
@ -99,8 +101,8 @@ class AddPortForm(FormRevMixin, ModelForm):
|
||||||
'room',
|
'room',
|
||||||
'machine_interface',
|
'machine_interface',
|
||||||
'related',
|
'related',
|
||||||
'radius',
|
'custom_profile',
|
||||||
'vlan_force',
|
'state',
|
||||||
'details'
|
'details'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -254,3 +256,16 @@ class EditBuildingForm(FormRevMixin, ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
super(EditBuildingForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(EditBuildingForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
44
topologie/migrations/0061_portprofile.py
Normal file
44
topologie/migrations/0061_portprofile.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-06-26 16:37
|
||||||
|
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', '0082_auto_20180525_2209'),
|
||||||
|
('topologie', '0060_server'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='PortProfile',
|
||||||
|
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'), ('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')),
|
||||||
|
('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, 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, re2o.mixins.RevMixin, models.Model),
|
||||||
|
),
|
||||||
|
]
|
25
topologie/migrations/0062_auto_20180627_0123.py
Normal file
25
topologie/migrations/0062_auto_20180627_0123.py
Normal file
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
21
topologie/migrations/0063_port_custom_profil.py
Normal file
21
topologie/migrations/0063_port_custom_profil.py
Normal file
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
53
topologie/migrations/0064_createprofil.py
Normal file
53
topologie/migrations/0064_createprofil.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
# -*- 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
|
||||||
|
|
||||||
|
|
||||||
|
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'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(transfer_profil),
|
||||||
|
]
|
23
topologie/migrations/0065_auto_20180630_1703.py
Normal file
23
topologie/migrations/0065_auto_20180630_1703.py
Normal file
|
@ -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',
|
||||||
|
),
|
||||||
|
]
|
25
topologie/migrations/0066_auto_20180630_1855.py
Normal file
25
topologie/migrations/0066_auto_20180630_1855.py
Normal file
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
75
topologie/migrations/0067_auto_20180701_0016.py
Normal file
75
topologie/migrations/0067_auto_20180701_0016.py
Normal file
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -40,22 +40,18 @@ from __future__ import unicode_literals
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from django.db import models
|
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.utils.functional import cached_property
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
|
|
||||||
from machines.models import Machine, regen
|
from machines.models import Machine, regen
|
||||||
from re2o.mixins import AclMixin, RevMixin
|
from re2o.mixins import AclMixin, RevMixin
|
||||||
|
|
||||||
from os.path import isfile
|
|
||||||
from os import remove
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Stack(AclMixin, RevMixin, models.Model):
|
class Stack(AclMixin, RevMixin, models.Model):
|
||||||
"""Un objet stack. Regrouppe des switchs en foreign key
|
"""Un objet stack. Regrouppe des switchs en foreign key
|
||||||
|
@ -122,7 +118,10 @@ class AccessPoint(AclMixin, Machine):
|
||||||
)
|
)
|
||||||
|
|
||||||
def building(self):
|
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(
|
return Building.objects.filter(
|
||||||
switchbay__switch=self.switch()
|
switchbay__switch=self.switch()
|
||||||
)
|
)
|
||||||
|
@ -134,14 +133,18 @@ class AccessPoint(AclMixin, Machine):
|
||||||
@classmethod
|
@classmethod
|
||||||
def all_ap_in(cls, building_instance):
|
def all_ap_in(cls, building_instance):
|
||||||
"""Get a building as argument, returns all ap of a building"""
|
"""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):
|
def __str__(self):
|
||||||
return str(self.interface_set.first())
|
return str(self.interface_set.first())
|
||||||
|
|
||||||
|
|
||||||
class Server(Machine):
|
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:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
|
@ -159,7 +162,10 @@ class Server(Machine):
|
||||||
)
|
)
|
||||||
|
|
||||||
def building(self):
|
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(
|
return Building.objects.filter(
|
||||||
switchbay__switch=self.switch()
|
switchbay__switch=self.switch()
|
||||||
)
|
)
|
||||||
|
@ -171,7 +177,9 @@ class Server(Machine):
|
||||||
@classmethod
|
@classmethod
|
||||||
def all_server_in(cls, building_instance):
|
def all_server_in(cls, building_instance):
|
||||||
"""Get a building as argument, returns all server of a building"""
|
"""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):
|
def __str__(self):
|
||||||
return str(self.interface_set.first())
|
return str(self.interface_set.first())
|
||||||
|
@ -369,12 +377,6 @@ class Port(AclMixin, RevMixin, models.Model):
|
||||||
de forcer un port sur un vlan particulier. S'additionne à la politique
|
de forcer un port sur un vlan particulier. S'additionne à la politique
|
||||||
RADIUS"""
|
RADIUS"""
|
||||||
PRETTY_NAME = "Port de switch"
|
PRETTY_NAME = "Port de switch"
|
||||||
STATES = (
|
|
||||||
('NO', 'NO'),
|
|
||||||
('STRICT', 'STRICT'),
|
|
||||||
('BLOQ', 'BLOQ'),
|
|
||||||
('COMMON', 'COMMON'),
|
|
||||||
)
|
|
||||||
|
|
||||||
switch = models.ForeignKey(
|
switch = models.ForeignKey(
|
||||||
'Switch',
|
'Switch',
|
||||||
|
@ -400,13 +402,17 @@ class Port(AclMixin, RevMixin, models.Model):
|
||||||
blank=True,
|
blank=True,
|
||||||
related_name='related_port'
|
related_name='related_port'
|
||||||
)
|
)
|
||||||
radius = models.CharField(max_length=32, choices=STATES, default='NO')
|
custom_profile = models.ForeignKey(
|
||||||
vlan_force = models.ForeignKey(
|
'PortProfile',
|
||||||
'machines.Vlan',
|
on_delete=models.PROTECT,
|
||||||
on_delete=models.SET_NULL,
|
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
state = models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
help_text='Port state Active',
|
||||||
|
verbose_name=_("Port State Active")
|
||||||
|
)
|
||||||
details = models.CharField(max_length=255, blank=True)
|
details = models.CharField(max_length=255, blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -415,6 +421,37 @@ class Port(AclMixin, RevMixin, models.Model):
|
||||||
("view_port", "Peut voir un objet port"),
|
("view_port", "Peut voir un objet port"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def get_port_profile(self):
|
||||||
|
"""Return the config profile for this port
|
||||||
|
:returns: the profile of self (port)"""
|
||||||
|
def profile_or_nothing(profile):
|
||||||
|
port_profile = PortProfile.objects.filter(
|
||||||
|
profile_default=profile).first()
|
||||||
|
if port_profile:
|
||||||
|
return port_profile
|
||||||
|
else:
|
||||||
|
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 profile_or_nothing('uplink')
|
||||||
|
elif self.machine_interface:
|
||||||
|
if hasattr(self.machine_interface.machine, 'accesspoint'):
|
||||||
|
return profile_or_nothing('access_point')
|
||||||
|
else:
|
||||||
|
return profile_or_nothing('asso_machine')
|
||||||
|
elif self.room:
|
||||||
|
return profile_or_nothing('room')
|
||||||
|
else:
|
||||||
|
return profile_or_nothing('nothing')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_instance(cls, portid, *_args, **kwargs):
|
def get_instance(cls, portid, *_args, **kwargs):
|
||||||
return (cls.objects
|
return (cls.objects
|
||||||
|
@ -492,51 +529,201 @@ class Room(AclMixin, RevMixin, models.Model):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class PortProfile(AclMixin, RevMixin, 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'),
|
||||||
|
)
|
||||||
|
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'),
|
||||||
|
('nothing', 'nothing'),
|
||||||
|
)
|
||||||
|
name = models.CharField(max_length=255, verbose_name=_("Name"))
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
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,
|
||||||
|
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="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='Port speed limit',
|
||||||
|
verbose_name=_("Speed")
|
||||||
|
)
|
||||||
|
mac_limit = models.IntegerField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text='Limit of mac-address on this port',
|
||||||
|
verbose_name=_("Mac limit")
|
||||||
|
)
|
||||||
|
flow_control = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text='Flow control',
|
||||||
|
verbose_name=_("Flow control")
|
||||||
|
)
|
||||||
|
dhcp_snooping = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text='Protect against rogue dhcp',
|
||||||
|
verbose_name=_("Dhcp snooping")
|
||||||
|
)
|
||||||
|
dhcpv6_snooping = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text='Protect against rogue dhcpv6',
|
||||||
|
verbose_name=_("Dhcpv6 snooping")
|
||||||
|
)
|
||||||
|
arp_protect = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text='Check if ip is dhcp assigned',
|
||||||
|
verbose_name=_("Arp protect")
|
||||||
|
)
|
||||||
|
ra_guard = models.BooleanField(
|
||||||
|
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")),
|
||||||
|
)
|
||||||
|
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)
|
||||||
|
]
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def security_parameters_as_str(self):
|
||||||
|
return ','.join(self.security_parameters_enabled)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=AccessPoint)
|
@receiver(post_save, sender=AccessPoint)
|
||||||
def ap_post_save(**_kwargs):
|
def ap_post_save(**_kwargs):
|
||||||
"""Regeneration des noms des bornes vers le controleur"""
|
"""Regeneration des noms des bornes vers le controleur"""
|
||||||
regen('unifi-ap-names')
|
regen('unifi-ap-names')
|
||||||
regen("graph_topo")
|
regen("graph_topo")
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=AccessPoint)
|
@receiver(post_delete, sender=AccessPoint)
|
||||||
def ap_post_delete(**_kwargs):
|
def ap_post_delete(**_kwargs):
|
||||||
"""Regeneration des noms des bornes vers le controleur"""
|
"""Regeneration des noms des bornes vers le controleur"""
|
||||||
regen('unifi-ap-names')
|
regen('unifi-ap-names')
|
||||||
regen("graph_topo")
|
regen("graph_topo")
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Stack)
|
@receiver(post_delete, sender=Stack)
|
||||||
def stack_post_delete(**_kwargs):
|
def stack_post_delete(**_kwargs):
|
||||||
"""Vide les id des switches membres d'une stack supprimée"""
|
"""Vide les id des switches membres d'une stack supprimée"""
|
||||||
Switch.objects.filter(stack=None).update(stack_member_id=None)
|
Switch.objects.filter(stack=None).update(stack_member_id=None)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Port)
|
@receiver(post_save, sender=Port)
|
||||||
def port_post_save(**_kwargs):
|
def port_post_save(**_kwargs):
|
||||||
regen("graph_topo")
|
regen("graph_topo")
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Port)
|
@receiver(post_delete, sender=Port)
|
||||||
def port_post_delete(**_kwargs):
|
def port_post_delete(**_kwargs):
|
||||||
regen("graph_topo")
|
regen("graph_topo")
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=ModelSwitch)
|
@receiver(post_save, sender=ModelSwitch)
|
||||||
def modelswitch_post_save(**_kwargs):
|
def modelswitch_post_save(**_kwargs):
|
||||||
regen("graph_topo")
|
regen("graph_topo")
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=ModelSwitch)
|
@receiver(post_delete, sender=ModelSwitch)
|
||||||
def modelswitch_post_delete(**_kwargs):
|
def modelswitch_post_delete(**_kwargs):
|
||||||
regen("graph_topo")
|
regen("graph_topo")
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Building)
|
@receiver(post_save, sender=Building)
|
||||||
def building_post_save(**_kwargs):
|
def building_post_save(**_kwargs):
|
||||||
regen("graph_topo")
|
regen("graph_topo")
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Building)
|
@receiver(post_delete, sender=Building)
|
||||||
def building_post_delete(**_kwargs):
|
def building_post_delete(**_kwargs):
|
||||||
regen("graph_topo")
|
regen("graph_topo")
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Switch)
|
@receiver(post_save, sender=Switch)
|
||||||
def switch_post_save(**_kwargs):
|
def switch_post_save(**_kwargs):
|
||||||
regen("graph_topo")
|
regen("graph_topo")
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Switch)
|
@receiver(post_delete, sender=Switch)
|
||||||
def switch_post_delete(**_kwargs):
|
def switch_post_delete(**_kwargs):
|
||||||
regen("graph_topo")
|
regen("graph_topo")
|
||||||
|
|
|
@ -40,6 +40,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
{% for port in port_list %}
|
{% for port in port_list %}
|
||||||
|
<tr>
|
||||||
|
<th>{% include "buttons/sort.html" with prefix='port' col='port' text='Port' %}</th>
|
||||||
|
<th>{% include "buttons/sort.html" with prefix='port' col='room' text='Room' %}</th>
|
||||||
|
<th>{% include "buttons/sort.html" with prefix='port' col='interface' text='Interface machine' %}</th>
|
||||||
|
<th>{% include "buttons/sort.html" with prefix='port' col='related' text='Related' %}</th>
|
||||||
|
<th>Etat du port</th>
|
||||||
|
<th>Profil du port</th>
|
||||||
|
<th>Détails</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for port in port_list %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ port.port }}</td>
|
<td>{{ port.port }}</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -50,9 +62,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% can_view port.machine_interface.machine.user %}
|
{% can_view port.machine_interface.machine.user %}
|
||||||
<a href="{% url 'users:profil' userid=port.machine_interface.machine.user.id %}">
|
<a href="{% url 'users:profil' userid=port.machine_interface.machine.user.id %}">
|
||||||
{{ port.machine_interface }}
|
{{ port.machine_interface }}
|
||||||
</a>
|
|
||||||
{% acl_else %}
|
|
||||||
{{ port.machine_interface }}
|
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
@ -64,14 +73,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</a>
|
</a>
|
||||||
{% acl_else %}
|
{% acl_else %}
|
||||||
{{ port.related }}
|
{{ port.related }}
|
||||||
|
</a>
|
||||||
|
{% acl_else %}
|
||||||
|
{{ port.related }}
|
||||||
{% acl_end %}
|
{% acl_end %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ port.radius }}</td>
|
<td>{% if port.state %} <i class="text-success">Actif</i>{% else %}<i class="text-danger">Désactivé</i>{% endif %}</td>
|
||||||
<td>{% if not port.vlan_force %}Aucun{% else %}{{ port.vlan_force }}{% endif %}</td>
|
<td>{% if not port.custom_profile %}<u>Par défaut</u> : {% endif %}{{port.get_port_profil}}</td>
|
||||||
<td>{{ port.details }}</td>
|
<td>{{ port.details }}</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{% history_button port %}
|
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'port' port.pk %}">
|
||||||
|
<i class="fa fa-history"></i>
|
||||||
|
</a>
|
||||||
{% can_edit port %}
|
{% can_edit port %}
|
||||||
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-port' port.id %}">
|
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-port' port.id %}">
|
||||||
<i class="fa fa-edit"></i>
|
<i class="fa fa-edit"></i>
|
||||||
|
|
85
topologie/templates/topologie/aff_port_profile.html
Normal file
85
topologie/templates/topologie/aff_port_profile.html
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
{% 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 %}
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
|
||||||
|
{% if port_profile_list.paginator %}
|
||||||
|
{% include "pagination.html" with list=port_profile_list %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Name" %}</th>
|
||||||
|
<th>{% trans "Default for" %}</th>
|
||||||
|
<th>{% trans "VLANs" %}</th>
|
||||||
|
<th>{% trans "RADIUS settings" %}</th>
|
||||||
|
<th>{% trans "Speed" %}</th>
|
||||||
|
<th>{% trans "Mac address limit" %}</th>
|
||||||
|
<th>{% trans "Security" %}</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for port_profile in port_profile_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{port_profile.name}}</td>
|
||||||
|
<td>{{port_profile.profil_default}}</td>
|
||||||
|
<td>
|
||||||
|
{% if port_profile.vlan_untagged %}
|
||||||
|
<b>Untagged : </b>{{port_profile.vlan_untagged}}
|
||||||
|
<br>
|
||||||
|
{% endif %}
|
||||||
|
{% if port_profile.vlan_tagged.all %}
|
||||||
|
<b>Tagged : </b>{{port_profile.vlan_tagged.all|join:", "}}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<b>Type : </b>{{port_profile.radius_type}}
|
||||||
|
{% if port_profile.radius_type == "MAC-radius" %}
|
||||||
|
<br>
|
||||||
|
<b>Mode : </b>{{port_profile.radius_mode}}</td>
|
||||||
|
{% endif %}
|
||||||
|
<td>{{port_profile.speed}}</td>
|
||||||
|
<td>{{port_profile.mac_limit}}</td>
|
||||||
|
<td>{{port_profile.security_parameters_enabled|join:"<br>"}}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{% 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 %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% if port_profile_list.paginator %}
|
||||||
|
{% include "pagination.html" with list=port_profile_list %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
|
@ -72,5 +72,4 @@ Topologie des Switchs
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
43
topologie/templates/topologie/index_portprofile.html
Normal file
43
topologie/templates/topologie/index_portprofile.html
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
{% 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 © 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 bootstrap3 %}
|
||||||
|
{% load acl %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}Switchs{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h2>{% trans "Port profiles" %}</h2>
|
||||||
|
{% can_create PortProfile %}
|
||||||
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-port-profile' %}"><i class="fa fa-plus"></i>{% trans " Add a port profile" %}</a>
|
||||||
|
<hr>
|
||||||
|
{% acl_end %}
|
||||||
|
{% include "topologie/aff_port_profile.html" with port_profile_list=port_profile_list %}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -33,6 +33,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<i class="fa fa-microchip"></i>
|
<i class="fa fa-microchip"></i>
|
||||||
Switchs
|
Switchs
|
||||||
</a>
|
</a>
|
||||||
|
<a class="list-group-item list-group-item-info" href="{% url "topologie:index-port-profile" %}">
|
||||||
|
<i class="fa fa-cogs"></i>
|
||||||
|
Config des ports switchs
|
||||||
|
</a>
|
||||||
<a class="list-group-item list-group-item-info" href="{% url "topologie:index-ap" %}">
|
<a class="list-group-item list-group-item-info" href="{% url "topologie:index-ap" %}">
|
||||||
<i class="fa fa-wifi"></i>
|
<i class="fa fa-wifi"></i>
|
||||||
Bornes WiFi
|
Bornes WiFi
|
||||||
|
|
|
@ -108,4 +108,16 @@ urlpatterns = [
|
||||||
url(r'^del_building/(?P<buildingid>[0-9]+)$',
|
url(r'^del_building/(?P<buildingid>[0-9]+)$',
|
||||||
views.del_building,
|
views.del_building,
|
||||||
name='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'),
|
||||||
|
url(r'^edit_port_profile/(?P<portprofileid>[0-9]+)$',
|
||||||
|
views.edit_port_profile,
|
||||||
|
name='edit-port-profile'),
|
||||||
|
url(r'^del_port_profile/(?P<portprofileid>[0-9]+)$',
|
||||||
|
views.del_port_profile,
|
||||||
|
name='del-port-profile'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -42,11 +42,8 @@ from django.contrib.auth.decorators import login_required
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
from django.db.models import ProtectedError, Prefetch
|
from django.db.models import ProtectedError, Prefetch
|
||||||
from django.core.exceptions import ValidationError
|
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.template import Context, Template, loader
|
||||||
from django.db.models.signals import post_save
|
from django.utils.translation import ugettext as _
|
||||||
from django.dispatch import receiver
|
|
||||||
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
@ -80,6 +77,7 @@ from .models import (
|
||||||
SwitchBay,
|
SwitchBay,
|
||||||
Building,
|
Building,
|
||||||
Server,
|
Server,
|
||||||
|
PortProfile,
|
||||||
)
|
)
|
||||||
from .forms import (
|
from .forms import (
|
||||||
EditPortForm,
|
EditPortForm,
|
||||||
|
@ -94,7 +92,8 @@ from .forms import (
|
||||||
AddAccessPointForm,
|
AddAccessPointForm,
|
||||||
EditAccessPointForm,
|
EditAccessPointForm,
|
||||||
EditSwitchBayForm,
|
EditSwitchBayForm,
|
||||||
EditBuildingForm
|
EditBuildingForm,
|
||||||
|
EditPortProfileForm,
|
||||||
)
|
)
|
||||||
|
|
||||||
from subprocess import (
|
from subprocess import (
|
||||||
|
@ -103,7 +102,6 @@ from subprocess import (
|
||||||
)
|
)
|
||||||
|
|
||||||
from os.path import isfile
|
from os.path import isfile
|
||||||
from os import remove
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -124,12 +122,18 @@ def index(request):
|
||||||
request.GET.get('order'),
|
request.GET.get('order'),
|
||||||
SortTable.TOPOLOGIE_INDEX
|
SortTable.TOPOLOGIE_INDEX
|
||||||
)
|
)
|
||||||
|
|
||||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||||
switch_list = re2o_paginator(request, switch_list, 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()
|
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()
|
service_link.done_regen()
|
||||||
|
|
||||||
if not isfile("/var/www/re2o/media/images/switchs.png"):
|
if not isfile("/var/www/re2o/media/images/switchs.png"):
|
||||||
|
@ -141,6 +145,21 @@ def index(request):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@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}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_view_all(Port)
|
@can_view_all(Port)
|
||||||
@can_view(Switch)
|
@can_view(Switch)
|
||||||
|
@ -955,6 +974,60 @@ def del_constructor_switch(request, constructor_switch, **_kwargs):
|
||||||
}, 'topologie/delete.html', request)
|
}, 'topologie/delete.html', request)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@can_create(PortProfile)
|
||||||
|
def new_port_profile(request):
|
||||||
|
"""Create a new port profile"""
|
||||||
|
port_profile = EditPortProfileForm(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():
|
def make_machine_graph():
|
||||||
"""
|
"""
|
||||||
Create the graph of switchs, machines and access points.
|
Create the graph of switchs, machines and access points.
|
||||||
|
@ -1014,50 +1087,58 @@ def make_machine_graph():
|
||||||
dico['subs'][-1]['machines'].append({
|
dico['subs'][-1]['machines'].append({
|
||||||
'name': server.short_name,
|
'name': server.short_name,
|
||||||
'switch': server.switch()[0].main_interface().domain.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 the list of forgotten ones is not empty
|
||||||
while missing:
|
while missing:
|
||||||
if missing[0].ports.count(): # The switch is not empty
|
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:
|
for link in links:
|
||||||
dico['links'].append(link)
|
dico['links'].append(link)
|
||||||
# Update the lists of missings and already detected switchs
|
# 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
|
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]
|
del missing[0]
|
||||||
# Switchs that are not connected or not in a building
|
# 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({
|
dico['alone'].append({
|
||||||
'id': switch.id,
|
'id': switch.id,
|
||||||
'name': switch.main_interface().domain.name
|
'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
|
# Create a temporary file to store the dot data
|
||||||
|
f = tempfile.NamedTemporaryFile(mode='w+', encoding='utf-8', delete=False)
|
||||||
f = tempfile.NamedTemporaryFile(mode='w+', encoding='utf-8', delete=False) # Create a temporary file to store the dot data
|
|
||||||
with f:
|
with f:
|
||||||
f.write(dot_data)
|
f.write(dot_data)
|
||||||
unflatten = Popen( # unflatten the graph to make it look better
|
unflatten = Popen( # unflatten the graph to make it look better
|
||||||
["unflatten", "-l", "3", f.name],
|
["unflatten", "-l", "3", f.name],
|
||||||
stdout=PIPE
|
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"],
|
["dot", "-Tpng", "-o", MEDIA_ROOT + "/images/switchs.png"],
|
||||||
stdin=unflatten.stdout,
|
stdin=unflatten.stdout,
|
||||||
stdout=PIPE
|
stdout=PIPE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def generate_dot(data, template):
|
def generate_dot(data, template):
|
||||||
"""create the dot file
|
"""create the dot file
|
||||||
:param data: dictionary passed to the template
|
:param data: dictionary passed to the template
|
||||||
:param template: path to the dot template
|
:param template: path to the dot template
|
||||||
:return: all the lines of the dot file"""
|
:return: all the lines of the dot file"""
|
||||||
t = loader.get_template(template)
|
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é."
|
raise Exception("Le template par défaut de Django n'est pas utilisé."
|
||||||
"Cela peut mener à des erreurs de rendu."
|
"Cela peut mener à des erreurs de rendu."
|
||||||
"Vérifiez les paramètres")
|
"Vérifiez les paramètres")
|
||||||
|
@ -1065,27 +1146,40 @@ def generate_dot(data,template):
|
||||||
dot = t.render(c)
|
dot = t.render(c)
|
||||||
return(dot)
|
return(dot)
|
||||||
|
|
||||||
|
|
||||||
def recursive_switchs(switch_start, switch_before, detected):
|
def recursive_switchs(switch_start, switch_before, detected):
|
||||||
"""Visit the switch and travel to the switchs linked to it.
|
"""Visit the switch and travel to the switchs linked to it.
|
||||||
:param switch_start: the switch to begin the visit on
|
: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 switch_before: the switch that you come from.
|
||||||
:param detected: list of all switchs already visited. None if switch_start is the first one
|
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 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)
|
detected.append(switch_start)
|
||||||
links_return = [] # list of dictionaries of the links to be detected
|
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
|
# 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
|
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
|
links = { # Dictionary of a link
|
||||||
'depart': switch_start.id,
|
'depart': switch_start.id,
|
||||||
'arrive': port.related.switch.id
|
'arrive': port.related.switch.id
|
||||||
}
|
}
|
||||||
links_return.append(links) # Add current and below levels links
|
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
|
# 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
|
for port in switch_start.ports.filter(related__isnull=False):
|
||||||
links_down, detected = recursive_switchs(port.related.switch, switch_start, detected) # explore it and get the results
|
# The switch at the end of this link has not been visited
|
||||||
for link in links_down: # Add the non empty links to the current list
|
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:
|
if link:
|
||||||
links_return.append(link)
|
links_return.append(link)
|
||||||
return (links_return, detected)
|
return (links_return, detected)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue