8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-26 06:32:26 +00:00

Merge branch 'fix_room_force_move_in' into 'master'

Fix room force move in

See merge request federez/re2o!161
This commit is contained in:
chirac 2018-05-14 23:27:25 +02:00
commit 9d6562d154
10 changed files with 451 additions and 92 deletions

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2018-05-11 17:54
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('machines', '0080_auto_20180502_2334'),
]
operations = [
migrations.AlterField(
model_name='service_link',
name='server',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='machines.Interface'),
),
]

View file

@ -1373,9 +1373,8 @@ class Service(RevMixin, AclMixin, models.Model):
def regen(service): def regen(service):
""" Fonction externe pour régérération d'un service, prend un objet service """ Fonction externe pour régérération d'un service, prend un objet service
en arg""" en arg"""
obj = Service.objects.filter(service_type=service) obj, created = Service.objects.get_or_create(service_type=service)
if obj: obj.ask_regen()
obj[0].ask_regen()
return return
@ -1384,7 +1383,12 @@ class Service_link(RevMixin, AclMixin, models.Model):
PRETTY_NAME = "Relation entre service et serveur" PRETTY_NAME = "Relation entre service et serveur"
service = models.ForeignKey('Service', on_delete=models.CASCADE) service = models.ForeignKey('Service', on_delete=models.CASCADE)
server = models.ForeignKey('Interface', on_delete=models.CASCADE) server = models.ForeignKey(
'Interface',
on_delete=models.CASCADE,
null=True,
blank=True
)
last_regen = models.DateTimeField(auto_now_add=True) last_regen = models.DateTimeField(auto_now_add=True)
asked_regen = models.BooleanField(default=False) asked_regen = models.BooleanField(default=False)
@ -1525,6 +1529,8 @@ def machine_post_save(**kwargs):
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
regen('dhcp') regen('dhcp')
regen('mac_ip_list') regen('mac_ip_list')
if user == preferences.models.OptionalMachine.get_cached_value('utilisateur_asso'):
regen('graph_topo')
@receiver(post_delete, sender=Machine) @receiver(post_delete, sender=Machine)
@ -1549,6 +1555,8 @@ def interface_post_save(**kwargs):
# Regen services # Regen services
regen('dhcp') regen('dhcp')
regen('mac_ip_list') regen('mac_ip_list')
if interface.machine.user == preferences.models.OptionalMachine.get_cached_value('utilisateur_asso'):
regen('graph_topo')
@receiver(post_delete, sender=Interface) @receiver(post_delete, sender=Interface)
@ -1659,3 +1667,11 @@ def srv_post_save(**_kwargs):
def srv_post_delete(**_kwargs): def srv_post_delete(**_kwargs):
"""Regeneration dns après modification d'un SRV""" """Regeneration dns après modification d'un SRV"""
regen('dns') regen('dns')
@receiver(post_save, sender=Service)
def service_post_save(**kwargs):
"""Création d'un service_link si non existant"""
service = kwargs['instance']
service_link, created = Service_link.objects.get_or_create(service=service)

0
media/images/__init__.py Normal file
View file

View file

@ -24,6 +24,7 @@ A set of mixins used all over the project to avoid duplicating code
""" """
from reversion import revisions as reversion from reversion import revisions as reversion
from django.utils.functional import cached_property
from django.db import transaction from django.db import transaction
@ -161,3 +162,5 @@ class AclMixin(object):
), ),
u"Vous n'avez pas le droit de voir des " + self.get_classname() u"Vous n'avez pas le droit de voir des " + self.get_classname()
) )

View file

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2018-05-11 17:54
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('machines', '0081_auto_20180511_1254'),
('topologie', '0059_auto_20180415_2249'),
]
operations = [
migrations.CreateModel(
name='Server',
fields=[
],
options={
'proxy': True,
},
bases=('machines.machine',),
),
]

View file

@ -40,7 +40,8 @@ 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 post_save, post_delete from django.db.models.signals import pre_save, post_save, post_delete
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
@ -50,6 +51,11 @@ 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
@ -103,6 +109,70 @@ class AccessPoint(AclMixin, Machine):
("view_accesspoint", "Peut voir une borne"), ("view_accesspoint", "Peut voir une borne"),
) )
def port(self):
"""Return the queryset of ports for this device"""
return Port.objects.filter(
machine_interface__machine=self
)
def switch(self):
"""Return the switch where this is plugged"""
return Switch.objects.filter(
ports__machine_interface__machine=self
)
def building(self):
"""Return the building of the AP/Server (building of the switchs connected to...)"""
return Building.objects.filter(
switchbay__switch=self.switch()
)
@cached_property
def short_name(self):
return str(self.interface_set.first().domain.name)
@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)
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"""
class Meta:
proxy = True
def port(self):
"""Return the queryset of ports for this device"""
return Port.objects.filter(
machine_interface__machine=self
)
def switch(self):
"""Return the switch where this is plugged"""
return Switch.objects.filter(
ports__machine_interface__machine=self
)
def building(self):
"""Return the building of the AP/Server (building of the switchs connected to...)"""
return Building.objects.filter(
switchbay__switch=self.switch()
)
@cached_property
def short_name(self):
return str(self.interface_set.first().domain.name)
@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)
def __str__(self): def __str__(self):
return str(self.interface_set.first()) return str(self.interface_set.first())
@ -422,15 +492,47 @@ class Room(AclMixin, RevMixin, models.Model):
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")
@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")
@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)
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")

View file

@ -0,0 +1,135 @@
{% block graph_dot %}
strict digraph {
graph [label="TOPOLOGIE DU RÉSEAU", labelloc=t, fontsize=40];
node [fontname=Helvetica fontsize=8 shape=plaintext];
edge[arrowhead=none];
{% block subgraphs %}
{% for sub in subs %}
subgraph cluster_{{ sub.bat_id }} {
fontsize=15;
label="Batiment {{ sub.bat_name }}";
{% if sub.bornes %}
{% block bornes %}
node [label=<
<TABLE BGCOLOR="{{ colors.back}}" BORDER="0" CELLBORDER="0" CELLSPACING="0">
<TR>
<TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="{{ colors.head_bornes }}">
<FONT FACE="Helvetica Bold" COLOR="white">Borne</FONT></TD>
<TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="{{ colors.head_bornes }}">
<FONT FACE="Helvetica Bold" COLOR="white">Switch</FONT></TD>
<TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="{{ colors.head_bornes }}">
<FONT FACE="Helvetica Bold" COLOR="white">Port</FONT></TD>
</TR>
{% for borne in sub.bornes %}
<TR>
<TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BORDER="0">
<FONT COLOR="{{ colors.texte }}" >{{ borne.name }}</FONT>
</TD>
<TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BORDER="0">
<FONT COLOR="{{ colors.texte }}" >{{ borne.switch }}</FONT>
</TD>
<TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BORDER="0">
<FONT COLOR="{{ colors.texte }}" >{{ borne.port }}</FONT>
</TD>
</TR>
{% endfor %}
</TABLE>
>] {{sub.bat_name}}bornes;
{% endblock %}
{% endif %}
{% if sub.machines %}
{% block machines %}
node [label=<
<TABLE BGCOLOR="{{ colors.back}}" BORDER="0" CELLBORDER="0" CELLSPACING="0">
<TR>
<TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="{{ colors.head_server }}">
<FONT FACE="Helvetica Bold" COLOR="white">Machine</FONT></TD>
<TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="{{ colors.head_server }}">
<FONT FACE="Helvetica Bold" COLOR="white">Switch</FONT></TD>
<TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="{{ colors.head_server }}">
<FONT FACE="Helvetica Bold" COLOR="white">Port</FONT></TD>
</TR>
{% for machine in sub.machines %}
<TR>
<TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BORDER="0">
<FONT COLOR="{{ colors.texte }}" >{{ machine.name }}</FONT>
</TD>
<TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BORDER="0">
<FONT COLOR="{{ colors.texte }}" >{{ machine.switch }}</FONT>
</TD>
<TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BORDER="0">
<FONT COLOR="{{ colors.texte }}" >{{ machine.port }}</FONT>
</TD>
</TR>
{% endfor %}
</TABLE>
>] {{sub.bat_name}}machines;
{% endblock %}
{% endif %}
{% block switchs %}
{% for switch in sub.switchs %}
node [label=<
<TABLE BGCOLOR="{{ colors.back }}" BORDER="0" CELLBORDER="0" CELLSPACING="0">
<TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="{{ colors.head }}">
<FONT FACE="Helvetica Bold" COLOR="white">
{{ switch.name }}
</FONT></TD></TR>
<TR><TD ALIGN="LEFT" BORDER="0">
<FONT COLOR="{{ colors.texte }}" >Modèle</FONT>
</TD>
<TD ALIGN="LEFT">
<FONT COLOR="{{ colors.texte }}" >{{ switch.model }}</FONT>
</TD></TR>
<TR><TD ALIGN="LEFT" BORDER="0">
<FONT COLOR="{{ colors.texte }}" >Taille</FONT>
</TD>
<TD ALIGN="LEFT">
<FONT COLOR="{{ colors.texte }}" >{{ switch.nombre }}</FONT>
</TD></TR>
{% block liens %}
{% for port in switch.ports %}
<TR><TD ALIGN="LEFT" BORDER="0">
<FONT COLOR="{{ colors.texte }}" >{{ port.numero }}</FONT>
</TD>
<TD ALIGN="LEFT">
<FONT COLOR="{{ colors.texte }}" >{{ port.related }}</FONT>
</TD></TR>
{% endfor %}
{% endblock %}
</TABLE>
>] "{{ switch.id }}" ;
{% endfor %}
{% endblock %}
}
{% endfor %}
{% endblock %}
{% block isoles %}
{% for switchs in alone %}
"{{switchs.id}}" [label=<
<TABLE BGCOLOR="{{ colors.back }}" BORDER="0" CELLBORDER="0" CELLSPACING="0">
<TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="{{ colors.head }}">
<FONT FACE="Helvetica Bold" COLOR="white">
{{switchs.name}}
</FONT></TD></TR>
</TABLE>
>]
{% endfor %}
{% endblock %}
{% block links %}
{% for link in links %}
"{{ link.depart }}" -> "{{ link.arrive }}";
{% endfor %}
{% endblock %}
}
{% endblock %}

View file

@ -29,10 +29,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% block title %}Switchs{% endblock %} {% block title %}Switchs{% endblock %}
{% block content %} {% block content %}
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#collImg" aria-expanded="false" aria-controls="collapse">
Topologie des Switchs
</button>
<div id="collImg">
<img id="zoom_01" src="/media/images/switchs.png" data-zoom-image="/media/images/switchs.png" width=100% /> <img id="zoom_01" src="/media/images/switchs.png" data-zoom-image="/media/images/switchs.png" width=100% />
</div>
<script type="text/javascript" src="/static/js/jquery.ez-plus.js"></script> <script type="text/javascript" src="/static/js/jquery.ez-plus.js"></script>
<script> <script>
$("#zoom_01").ezPlus({ $("#zoom_01").ezPlus({
scrollZoom: true, scrollZoom: true,

View file

@ -43,6 +43,10 @@ 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.contrib.staticfiles.storage import staticfiles_storage
from django.template.loader import get_template
from django.template import Context, Template, loader
import pprint
from users.views import form from users.views import form
from re2o.utils import re2o_paginator, SortTable from re2o.utils import re2o_paginator, SortTable
@ -53,13 +57,14 @@ from re2o.acl import (
can_view, can_view,
can_view_all, can_view_all,
) )
from re2o.settings import MEDIA_ROOT
from machines.forms import ( from machines.forms import (
DomainForm, DomainForm,
EditInterfaceForm, EditInterfaceForm,
AddInterfaceForm AddInterfaceForm
) )
from machines.views import generate_ipv4_mbf_param from machines.views import generate_ipv4_mbf_param
from machines.models import Interface from machines.models import Interface, Service_link
from preferences.models import AssoOption, GeneralOption from preferences.models import AssoOption, GeneralOption
from .models import ( from .models import (
@ -71,7 +76,8 @@ from .models import (
ConstructorSwitch, ConstructorSwitch,
AccessPoint, AccessPoint,
SwitchBay, SwitchBay,
Building Building,
Server
) )
from .forms import ( from .forms import (
EditPortForm, EditPortForm,
@ -112,6 +118,12 @@ def index(request):
) )
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')):
make_machine_graph()
for service_link in Service_link.objects.filter(service__service_type='graph_topo'):
service_link.done_regen()
return render( return render(
request, request,
'topologie/index.html', 'topologie/index.html',
@ -937,93 +949,128 @@ def make_machine_graph():
""" """
Crée le fichier dot et l'image du graph des Switchs Crée le fichier dot et l'image du graph des Switchs
""" """
#Syntaxe DOT temporaire, A mettre dans un template: dico = {
lignes=['''digraph Switchs { 'subs': [],
node [ 'links' : [],
fontname=Helvetica 'alone': [],
fontsize=8 'colors': {
shape=plaintext] 'head': "#7f0505",
edge[arrowhead=odot,arrowtail=dot]'''] 'back': "#b5adad",
node_fixe='''node [label=< 'texte': "#563d01",
<TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0"> 'border_bornes': "#02078e",
<TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4"> 'head_bornes': "#25771c",
<FONT FACE="Helvetica Bold" COLOR="white"> 'head_server': "#1c3777"
{} }
</FONT></TD></TR> }
<TR><TD ALIGN="LEFT" BORDER="0"> missing = list(Switch.objects.all())
<FONT COLOR="#7B7B7B" >{}</FONT> detected = []
</TD>
<TD ALIGN="LEFT"> #Parcours tous les batiments
<FONT COLOR="#7B7B7B" >{}</FONT>
</TD></TR>
<TR><TD ALIGN="LEFT" BORDER="0">
<FONT COLOR="#7B7B7B" >{}</FONT>
</TD>
<TD ALIGN="LEFT">
<FONT>{}</FONT>
</TD></TR>'''
node_ports='''<TR><TD ALIGN="LEFT" BORDER="0">
<FONT COLOR="#7B7B7B" >{}</FONT>
</TD>
<TD ALIGN="LEFT">
<FONT>{}</FONT>
</TD></TR>'''
cluster='''subgraph cluster_{} {{
color=blue;
label="Batiment {}";'''
end_table='''</TABLE>
>] \"{}_{}\" ;'''
switch_alone='''{} [label=<
<TABLE BGCOLOR="palegoldenrod" BORDER="0" CELLBORDER="0" CELLSPACING="0">
<TR><TD COLSPAN="2" CELLPADDING="4" ALIGN="CENTER" BGCOLOR="olivedrab4">
<FONT FACE="Helvetica Bold" COLOR="white">
{}
</FONT></TD></TR>
</TABLE>
>]'''
missing=[]
detected=[]
for sw in Switch.objects.all():
if(sw not in detected):
missing.append(sw)
for building in Building.objects.all(): for building in Building.objects.all():
lignes.append(cluster.format(len(lignes),building)) dico['subs'].append(
{
'bat_id': building.id,
'bat_name': building,
'switchs': [],
'bornes': [],
'machines': []
}
)
#Parcours tous les switchs de ce batiment
for switch in Switch.objects.filter(switchbay__building=building): for switch in Switch.objects.filter(switchbay__building=building):
lignes.append(node_fixe.format(switch.main_interface().domain.name,"Modèle",switch.model,"Nombre de ports",switch.number)) dico['subs'][-1]['switchs'].append({
for p in switch.ports.all().filter(related__isnull=False): 'name': switch.main_interface().domain.name,
lignes.append(node_ports.format(p.port,p.related.switch.main_interface().domain.name)) 'nombre': switch.number,
lignes.append(end_table.format(building.id,switch.id)) 'model': switch.model,
lignes.append("}") 'id': switch.id,
while(missing!=[]): 'batiment': building,
lignes,new_detected=recursive_switchs(missing[0].ports.all().filter(related=None).first(),None,lignes,[missing[0]]) 'ports': []
})
#Parcours tous les ports liés de ce switch, on ajoute les switchs relié à celui-ci
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
})
for ap in AccessPoint.all_ap_in(building):
dico['subs'][-1]['bornes'].append({
'name': ap.short_name,
'switch': ap.switch()[0].main_interface().domain.name,
'port': ap.switch()[0].ports.filter(
machine_interface__machine=ap
)[0].port
})
for server in Server.all_server_in(building):
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
})
#Tant que la liste des oubliés n'est pas vide i.e on les a pas tous passer
while missing:
links, new_detected = recursive_switchs(missing[0].ports.filter(related=None).first(), None, [missing[0]])
for link in links:
dico['links'].append(link)
#On recrée la liste des oubliés et des detectés
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
for switch in Switch.objects.all().filter(switchbay__isnull=True).exclude(ports__related__isnull=False):
lignes.append(switch_alone.format(switch.id,switch.main_interface().domain.name)) #Tous ceux qui ne sont ni connectés ni dans un batiment
lignes.append("}") for switch in Switch.objects.filter(switchbay__isnull=True).exclude(ports__related__isnull=False):
fichier = open("media/images/switchs.dot","w") dico['alone'].append({
for ligne in lignes: 'id': switch.id,
fichier.write(ligne+"\n") 'name': switch.main_interface().domain.name
})
#Exportation du dot et génération de l'image
dot_data=generate_image(dico)
fichier = open(MEDIA_ROOT + "/images/switchs.dot","w", encoding='utf-8')
fichier.write(dot_data)
fichier.close() fichier.close()
unflatten = Popen(["unflatten","-l", "3", "media/images/switchs.dot"], stdout=PIPE) unflatten = Popen(
image = Popen(["dot", "-Tpng", "-o", "media/images/switchs.png"], stdin=unflatten.stdout, stdout=PIPE) ["unflatten","-l", "3", MEDIA_ROOT + "/images/switchs.dot"],
stdout=PIPE
)
image = Popen(
["dot", "-Tpng", "-o", MEDIA_ROOT + "/images/switchs.png"],
stdin=unflatten.stdout,
stdout=PIPE
)
def generate_image(data,template='topologie/graph_switch.dot'):
t = loader.get_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")
c = Context(data).flatten()
dot = t.render(c)
return(dot)
def recursive_switchs(port_start, switch_before, lignes,detected): def recursive_switchs(port_start, switch_before, detected):
""" """
Parcour récursivement le switchs auquel appartient port_start pour trouver les ports suivants liés Parcour récursivement le switchs auquel appartient port_start pour trouver les ports suivants liés
""" """
l_ports=port_start.switch.ports.filter(related__isnull=False) detected.append(port_start.switch)
for port in l_ports: links_return=[]#Liste de dictionaires qui stockes les nouveaux liens trouvés
if port.related.switch!=switch_before and port.related.switch!=port.switch: for port in port_start.switch.ports.filter(related__isnull=False):#Liste des ports dont le related est non null
links=[]
for sw in [switch for switch in [port_start.switch,port.related.switch]]: if port.related.switch!=switch_before and port.related.switch != port.switch:#Pas le switch dont on descend, pas le switch actuel
if(sw not in detected):
detected.append(sw) links = {
if(sw.switchbay.building): 'depart':port_start.switch.id,
links.append("\"{}_{}\"".format(sw.switchbay.building.id,sw.id)) 'arrive':port.related.switch.id
else: }
links.append("\"{}\"".format(sw.id)) if(port.related.switch not in detected):
lignes.append(links[0]+" -> "+links[1]) links_down, detected = recursive_switchs(port.related, port_start.switch, detected)
lignes, detected = recursive_switchs(port.related, port_start.switch, lignes, detected) for link in links_down:
return (lignes, detected) if link:
links_return.append(link)
detected.append(port.related.switch)
links_return.append(links)
return (links_return, detected)

View file

@ -340,7 +340,10 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
"""On supprime l'ancien user de la chambre si et seulement si la """On supprime l'ancien user de la chambre si et seulement si la
case est cochée""" case est cochée"""
if self.cleaned_data.get('force', False): if self.cleaned_data.get('force', False):
remove_user_room(self.cleaned_data.get('room')) room = self.cleaned_data.get('room')
if room is None:
raise forms.ValidationError("Invalid Room")
remove_user_room(room)
return return