Ce serveur Gitlab sera éteint le 30 juin 2020, pensez à migrer vos projets vers les serveurs gitlab-research.centralesupelec.fr et gitlab-student.centralesupelec.fr !

views.py 32.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
# 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.
chirac's avatar
chirac committed
22 23
"""
Page des vues de l'application topologie
24

chirac's avatar
chirac committed
25 26 27 28 29 30 31 32 33 34 35
Permet de créer, modifier et supprimer :
- un port (add_port, edit_port, del_port)
- un switch : les vues d'ajout et d'édition font appel aux forms de creation
de switch, mais aussi aux forms de machines.forms (domain, interface et
machine). Le views les envoie et les save en même temps. TODO : rationaliser
et faire que la creation de machines (interfaces, domain etc) soit gérée
coté models et forms de topologie
- une chambre (new_room, edit_room, del_room)
- une stack
- l'historique de tous les objets cités
"""
36 37
from __future__ import unicode_literals

38
from django.urls import reverse
Dalahro's avatar
Dalahro committed
39 40
from django.shortcuts import render, redirect
from django.contrib import messages
41
from django.contrib.auth.decorators import login_required
Dalahro's avatar
Dalahro committed
42
from django.db import IntegrityError
43
from django.db.models import ProtectedError, Prefetch
44
from django.core.exceptions import ValidationError
45
from django.contrib.staticfiles.storage import staticfiles_storage
grisel-davy's avatar
grisel-davy committed
46 47 48 49
from django.template.loader import get_template
from django.template import Context, Template, loader

import pprint
Dalahro's avatar
Dalahro committed
50

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
from users.views import form
from re2o.utils import re2o_paginator, SortTable
from re2o.acl import (
    can_create,
    can_edit,
    can_delete,
    can_view,
    can_view_all,
)
from machines.forms import (
    DomainForm,
    EditInterfaceForm,
    AddInterfaceForm
)
from machines.views import generate_ipv4_mbf_param
from machines.models import Interface
from preferences.models import AssoOption, GeneralOption

from .models import (
70 71 72 73 74
    Switch,
    Port,
    Room,
    Stack,
    ModelSwitch,
75
    ConstructorSwitch,
76 77 78
    AccessPoint,
    SwitchBay,
    Building
79
)
80 81 82 83
from .forms import (
    EditPortForm,
    NewSwitchForm,
    EditSwitchForm,
84 85 86
    AddPortForm,
    EditRoomForm,
    StackForm,
87
    EditModelSwitchForm,
88
    EditConstructorSwitchForm,
89
    CreatePortsForm,
90
    AddAccessPointForm,
91 92 93
    EditAccessPointForm,
    EditSwitchBayForm,
    EditBuildingForm
94
)
95

96 97
from subprocess import Popen,PIPE

chirac's avatar
chirac committed
98

chirac's avatar
chirac committed
99
@login_required
100
@can_view_all(Switch)
Dalahro's avatar
Dalahro committed
101
def index(request):
102
    """ Vue d'affichage de tous les swicthes"""
103 104 105 106 107 108 109 110
    switch_list = (Switch.objects
                   .prefetch_related(Prefetch(
                       'interface_set',
                       queryset=(Interface.objects
                                 .select_related('ipv4__ip_type__extension')
                                 .select_related('domain__extension'))
                   ))
                   .select_related('stack'))
111 112 113 114 115 116
    switch_list = SortTable.sort(
        switch_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX
    )
117
    pagination_number = GeneralOption.get_cached_value('pagination_number')
118
    switch_list = re2o_paginator(request, switch_list, pagination_number)
119 120 121 122 123
    return render(
        request,
        'topologie/index.html',
        {'switch_list': switch_list}
    )
chirac's avatar
chirac committed
124

Dalahro's avatar
Dalahro committed
125

chirac's avatar
chirac committed
126
@login_required
127 128
@can_view_all(Port)
@can_view(Switch)
129
def index_port(request, switch, switchid):
130
    """ Affichage de l'ensemble des ports reliés à un switch particulier"""
131 132 133 134 135 136 137 138 139 140 141 142
    port_list = (Port.objects
                 .filter(switch=switch)
                 .select_related('room')
                 .select_related('machine_interface__domain__extension')
                 .select_related('machine_interface__machine__user')
                 .select_related('related__switch')
                 .prefetch_related(Prefetch(
                     'related__switch__interface_set',
                     queryset=(Interface.objects
                               .select_related('domain__extension'))
                 ))
                 .select_related('switch'))
143 144 145 146 147 148
    port_list = SortTable.sort(
        port_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_PORT
    )
149 150 151 152 153 154 155 156 157
    return render(
        request,
        'topologie/index_p.html',
        {
            'port_list': port_list,
            'id_switch': switchid,
            'nom_switch': switch
        }
    )
chirac's avatar
chirac committed
158

Dalahro's avatar
Dalahro committed
159

chirac's avatar
chirac committed
160
@login_required
161
@can_view_all(Room)
chirac's avatar
chirac committed
162
def index_room(request):
163
    """ Affichage de l'ensemble des chambres"""
164
    room_list = Room.objects
165 166 167 168 169 170
    room_list = SortTable.sort(
        room_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_ROOM
    )
171
    pagination_number = GeneralOption.get_cached_value('pagination_number')
172
    room_list = re2o_paginator(request, room_list, pagination_number)
173 174 175 176 177
    return render(
        request,
        'topologie/index_room.html',
        {'room_list': room_list}
    )
chirac's avatar
chirac committed
178

chirac's avatar
chirac committed
179

180
@login_required
181 182
@can_view_all(AccessPoint)
def index_ap(request):
183
    """ Affichage de l'ensemble des bornes"""
184 185 186 187 188 189 190
    ap_list = (AccessPoint.objects
               .prefetch_related(Prefetch(
                   'interface_set',
                   queryset=(Interface.objects
                             .select_related('ipv4__ip_type__extension')
                             .select_related('domain__extension'))
               )))
191 192
    ap_list = SortTable.sort(
        ap_list,
193 194 195 196 197
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_BORNE
    )
    pagination_number = GeneralOption.get_cached_value('pagination_number')
198
    ap_list = re2o_paginator(request, ap_list, pagination_number)
199 200 201 202 203
    return render(
        request,
        'topologie/index_ap.html',
        {'ap_list': ap_list}
    )
204 205


206
@login_required
207
@can_view_all(Stack)
208 209
@can_view_all(Building)
@can_view_all(SwitchBay)
210
def index_physical_grouping(request):
chirac's avatar
chirac committed
211
    """Affichage de la liste des stacks (affiche l'ensemble des switches)"""
212 213 214 215
    stack_list = (Stack.objects
                  .prefetch_related(
                      'switch_set__interface_set__domain__extension'
                  ))
216 217
    building_list = Building.objects.all()
    switch_bay_list = SwitchBay.objects.select_related('building')
218 219 220 221 222 223
    stack_list = SortTable.sort(
        stack_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_STACK
    )
224 225 226 227 228 229 230 231 232 233 234 235
    building_list = SortTable.sort(
        building_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_BUILDING
    )
    switch_bay_list = SortTable.sort(
        switch_bay_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_SWITCH_BAY
    )
236 237 238 239 240 241 242 243 244
    return render(
        request,
        'topologie/index_physical_grouping.html',
        {
            'stack_list': stack_list,
            'switch_bay_list': switch_bay_list,
            'building_list': building_list,
        }
    )
245 246


247
@login_required
248 249
@can_view_all(ModelSwitch)
@can_view_all(ConstructorSwitch)
250 251
def index_model_switch(request):
    """ Affichage de l'ensemble des modèles de switches"""
252
    model_switch_list = ModelSwitch.objects.select_related('constructor')
253 254 255 256 257 258 259 260 261 262 263 264 265
    constructor_switch_list = ConstructorSwitch.objects
    model_switch_list = SortTable.sort(
        model_switch_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_MODEL_SWITCH
    )
    constructor_switch_list = SortTable.sort(
        constructor_switch_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_CONSTRUCTOR_SWITCH
    )
266 267 268 269 270 271 272 273
    return render(
        request,
        'topologie/index_model_switch.html',
        {
            'model_switch_list': model_switch_list,
            'constructor_switch_list': constructor_switch_list,
        }
    )
274 275


chirac's avatar
chirac committed
276
@login_required
277
@can_create(Port)
278
def new_port(request, switchid):
279
    """ Nouveau port"""
Dalahro's avatar
Dalahro committed
280
    try:
281
        switch = Switch.objects.get(pk=switchid)
Dalahro's avatar
Dalahro committed
282 283
    except Switch.DoesNotExist:
        messages.error(request, u"Switch inexistant")
284
        return redirect(reverse('topologie:index'))
Dalahro's avatar
Dalahro committed
285 286 287 288 289
    port = AddPortForm(request.POST or None)
    if port.is_valid():
        port = port.save(commit=False)
        port.switch = switch
        try:
290
            port.save()
Dalahro's avatar
Dalahro committed
291 292
            messages.success(request, "Port ajouté")
        except IntegrityError:
chirac's avatar
chirac committed
293
            messages.error(request, "Ce port existe déjà")
294
        return redirect(reverse(
295
            'topologie:index-port',
296 297 298 299 300 301
            kwargs={'switchid': switchid}
        ))
    return form(
        {'id_switch': switchid, 'topoform': port, 'action_name': 'Ajouter'},
        'topologie/topo.html',
        request)
chirac's avatar
chirac committed
302

Dalahro's avatar
Dalahro committed
303

chirac's avatar
chirac committed
304
@login_required
305
@can_edit(Port)
306
def edit_port(request, port_object, **_kwargs):
chirac's avatar
chirac committed
307 308
    """ Edition d'un port. Permet de changer le switch parent et
    l'affectation du port"""
309

310
    port = EditPortForm(request.POST or None, instance=port_object)
Dalahro's avatar
Dalahro committed
311
    if port.is_valid():
312 313 314
        if port.changed_data:
            port.save()
            messages.success(request, "Le port a bien été modifié")
315 316
        return redirect(reverse(
            'topologie:index-port',
317
            kwargs={'switchid': str(port_object.switch.id)}
318 319 320 321 322 323 324 325 326 327
        ))
    return form(
        {
            'id_switch': str(port_object.switch.id),
            'topoform': port,
            'action_name': 'Editer'
        },
        'topologie/topo.html',
        request
    )
chirac's avatar
chirac committed
328

chirac's avatar
chirac committed
329

330
@login_required
331
@can_delete(Port)
332
def del_port(request, port, **_kwargs):
333
    """ Supprime le port"""
Gabriel Detraz's avatar
Gabriel Detraz committed
334 335
    if request.method == "POST":
        try:
336 337
            port.delete()
            messages.success(request, "Le port a été détruit")
Gabriel Detraz's avatar
Gabriel Detraz committed
338
        except ProtectedError:
339 340 341 342 343
            messages.error(
                request,
                ("Le port %s est affecté à un autre objet, impossible "
                 "de le supprimer" % port)
            )
344 345
        return redirect(reverse(
            'topologie:index-port',
346 347
            kwargs={'switchid': str(port.switch.id)}
        ))
chirac's avatar
chirac committed
348 349
    return form({'objet': port}, 'topologie/delete.html', request)

Gabriel Detraz's avatar
Gabriel Detraz committed
350 351

@login_required
352
@can_create(Stack)
353
def new_stack(request):
354
    """Ajoute un nouveau stack : stackid_min, max, et nombre de switches"""
355
    stack = StackForm(request.POST or None)
chirac's avatar
chirac committed
356
    if stack.is_valid():
357
        stack.save()
chirac's avatar
chirac committed
358
        messages.success(request, "Stack crée")
Gabriel Detraz's avatar
Gabriel Detraz committed
359
        return redirect(reverse('topologie:index-physical-grouping'))
360 361 362 363 364
    return form(
        {'topoform': stack, 'action_name': 'Créer'},
        'topologie/topo.html',
        request
    )
365 366 367


@login_required
368
@can_edit(Stack)
369
def edit_stack(request, stack, **_kwargs):
chirac's avatar
chirac committed
370
    """Edition d'un stack (nombre de switches, nom...)"""
371 372
    stack = StackForm(request.POST or None, instance=stack)
    if stack.is_valid():
373 374
        if stack.changed_data:
            stack.save()
Gabriel Detraz's avatar
Gabriel Detraz committed
375
        return redirect(reverse('topologie:index-physical-grouping'))
376 377 378 379 380
    return form(
        {'topoform': stack, 'action_name': 'Editer'},
        'topologie/topo.html',
        request
    )
chirac's avatar
chirac committed
381

382

383
@login_required
384
@can_delete(Stack)
385
def del_stack(request, stack, **_kwargs):
chirac's avatar
chirac committed
386
    """Supprime un stack"""
387 388
    if request.method == "POST":
        try:
389 390
            stack.delete()
            messages.success(request, "La stack a eté détruite")
391
        except ProtectedError:
392 393 394 395 396
            messages.error(
                request,
                ("La stack %s est affectée à un autre objet, impossible "
                 "de la supprimer" % stack)
            )
397
        return redirect(reverse('topologie:index-physical-grouping'))
chirac's avatar
chirac committed
398 399
    return form({'objet': stack}, 'topologie/delete.html', request)

400

401
@login_required
402
@can_edit(Stack)
403
def edit_switchs_stack(request, stack, **_kwargs):
chirac's avatar
chirac committed
404
    """Permet d'éditer la liste des switches dans une stack et l'ajouter"""
405

406 407 408 409 410 411 412 413
    if request.method == "POST":
        pass
    else:
        context = {'stack': stack}
        context['switchs_stack'] = stack.switchs_set.all()
        context['switchs_autres'] = Switch.object.filter(stack=None)


chirac's avatar
chirac committed
414
@login_required
415
@can_create(Switch)
Dalahro's avatar
Dalahro committed
416
def new_switch(request):
chirac's avatar
chirac committed
417 418 419
    """ Creation d'un switch. Cree en meme temps l'interface et la machine
    associée. Vue complexe. Appelle successivement les 4 models forms
    adaptés : machine, interface, domain et switch"""
420
    switch = NewSwitchForm(
421 422 423
        request.POST or None,
        user=request.user
    )
424
    interface = AddInterfaceForm(
chirac's avatar
chirac committed
425
        request.POST or None,
426
        user=request.user
427
    )
428
    domain = DomainForm(
chirac's avatar
chirac committed
429 430
        request.POST or None,
        )
431
    if switch.is_valid() and interface.is_valid():
432
        user = AssoOption.get_cached_value('utilisateur_asso')
433
        if not user:
434 435 436 437 438
            messages.error(
                request,
                ("L'user association n'existe pas encore, veuillez le "
                 "créer ou le linker dans preferences")
            )
439
            return redirect(reverse('topologie:index'))
440 441 442 443
        new_switch_obj = switch.save(commit=False)
        new_switch_obj.user = user
        new_interface_obj = interface.save(commit=False)
        domain.instance.interface_parent = new_interface_obj
444
        if domain.is_valid():
445 446 447 448 449 450
            new_domain_obj = domain.save(commit=False)
            new_switch_obj.save()
            new_interface_obj.machine = new_switch_obj
            new_interface_obj.save()
            new_domain_obj.interface_parent = new_interface_obj
            new_domain_obj.save()
451 452
            messages.success(request, "Le switch a été créé")
            return redirect(reverse('topologie:index'))
453
    i_mbf_param = generate_ipv4_mbf_param(interface, False)
454 455 456 457 458 459 460 461 462 463 464
    return form(
        {
            'topoform': interface,
            'machineform': switch,
            'domainform': domain,
            'i_mbf_param': i_mbf_param,
            'device': 'switch',
        },
        'topologie/topo_more.html',
        request
    )
chirac's avatar
chirac committed
465

Dalahro's avatar
Dalahro committed
466

467
@login_required
468
@can_create(Port)
469
def create_ports(request, switchid):
470 471
    """ Création d'une liste de ports pour un switch."""
    try:
472
        switch = Switch.objects.get(pk=switchid)
473 474
    except Switch.DoesNotExist:
        messages.error(request, u"Switch inexistant")
475
        return redirect(reverse('topologie:index'))
476

477
    s_begin = s_end = 0
478 479
    nb_ports = switch.ports.count()
    if nb_ports > 0:
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
480 481 482
        ports = switch.ports.order_by('port').values('port')
        s_begin = ports.first().get('port')
        s_end = ports.last().get('port')
483

484 485
    port_form = CreatePortsForm(
        request.POST or None,
486
        initial={'begin': s_begin, 'end': s_end}
487
    )
488

489 490 491
    if port_form.is_valid():
        begin = port_form.cleaned_data['begin']
        end = port_form.cleaned_data['end']
492 493 494 495 496
        try:
            switch.create_ports(begin, end)
            messages.success(request, "Ports créés.")
        except ValidationError as e:
            messages.error(request, ''.join(e))
497 498
        return redirect(reverse(
            'topologie:index-port',
499
            kwargs={'switchid': switchid}
500
            ))
501 502 503 504 505
    return form(
        {'id_switch': switchid, 'topoform': port_form},
        'topologie/switch.html',
        request
    )
506

Dalahro's avatar
Dalahro committed
507

chirac's avatar
chirac committed
508
@login_required
509
@can_edit(Switch)
510
def edit_switch(request, switch, switchid):
chirac's avatar
chirac committed
511 512
    """ Edition d'un switch. Permet de chambre nombre de ports,
    place dans le stack, interface et machine associée"""
513

514
    switch_form = EditSwitchForm(
chirac's avatar
chirac committed
515
        request.POST or None,
516
        instance=switch,
517
        user=request.user
chirac's avatar
chirac committed
518
        )
519
    interface_form = EditInterfaceForm(
chirac's avatar
chirac committed
520
        request.POST or None,
521
        instance=switch.interface_set.first(),
522
        user=request.user
chirac's avatar
chirac committed
523
        )
524
    domain_form = DomainForm(
chirac's avatar
chirac committed
525
        request.POST or None,
526
        instance=switch.interface_set.first().domain
chirac's avatar
chirac committed
527
        )
528
    if switch_form.is_valid() and interface_form.is_valid():
529 530 531
        new_switch_obj = switch_form.save(commit=False)
        new_interface_obj = interface_form.save(commit=False)
        new_domain_obj = domain_form.save(commit=False)
532
        if switch_form.changed_data:
533
            new_switch_obj.save()
534
        if interface_form.changed_data:
535
            new_interface_obj.save()
536
        if domain_form.changed_data:
537
            new_domain_obj.save()
538
        messages.success(request, "Le switch a bien été modifié")
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
539
        return redirect(reverse('topologie:index'))
540 541 542 543 544 545 546 547 548 549 550 551 552
    i_mbf_param = generate_ipv4_mbf_param(interface_form, False)
    return form(
        {
            'id_switch': switchid,
            'topoform': interface_form,
            'machineform': switch_form,
            'domainform': domain_form,
            'i_mbf_param': i_mbf_param,
            'device': 'switch',
        },
        'topologie/topo_more.html',
        request
    )
chirac's avatar
chirac committed
553

chirac's avatar
chirac committed
554

555
@login_required
556 557 558
@can_create(AccessPoint)
def new_ap(request):
    """ Creation d'une ap. Cree en meme temps l'interface et la machine
559 560
    associée. Vue complexe. Appelle successivement les 3 models forms
    adaptés : machine, interface, domain et switch"""
561
    ap = AddAccessPointForm(
562 563 564
        request.POST or None,
        user=request.user
    )
565
    interface = AddInterfaceForm(
566 567 568 569 570 571
        request.POST or None,
        user=request.user
    )
    domain = DomainForm(
        request.POST or None,
        )
572
    if ap.is_valid() and interface.is_valid():
573 574
        user = AssoOption.get_cached_value('utilisateur_asso')
        if not user:
575 576 577 578 579
            messages.error(
                request,
                ("L'user association n'existe pas encore, veuillez le "
                 "créer ou le linker dans preferences")
            )
580
            return redirect(reverse('topologie:index'))
581 582 583 584
        new_ap_obj = ap.save(commit=False)
        new_ap_obj.user = user
        new_interface_obj = interface.save(commit=False)
        domain.instance.interface_parent = new_interface_obj
585
        if domain.is_valid():
586 587 588 589 590 591
            new_domain_obj = domain.save(commit=False)
            new_ap_obj.save()
            new_interface_obj.machine = new_ap_obj
            new_interface_obj.save()
            new_domain_obj.interface_parent = new_interface_obj
            new_domain_obj.save()
592
            messages.success(request, "La borne a été créé")
593
            return redirect(reverse('topologie:index-ap'))
594
    i_mbf_param = generate_ipv4_mbf_param(interface, False)
595 596 597 598 599 600 601 602 603 604 605
    return form(
        {
            'topoform': interface,
            'machineform': ap,
            'domainform': domain,
            'i_mbf_param': i_mbf_param,
            'device': 'wifi ap',
        },
        'topologie/topo_more.html',
        request
    )
606 607 608


@login_required
609
@can_edit(AccessPoint)
610
def edit_ap(request, ap, **_kwargs):
611 612
    """ Edition d'un switch. Permet de chambre nombre de ports,
    place dans le stack, interface et machine associée"""
613
    interface_form = EditInterfaceForm(
614 615
        request.POST or None,
        user=request.user,
616
        instance=ap.interface_set.first()
617
    )
618
    ap_form = EditAccessPointForm(
619 620
        request.POST or None,
        user=request.user,
621
        instance=ap
622 623 624
    )
    domain_form = DomainForm(
        request.POST or None,
625
        instance=ap.interface_set.first().domain
626
        )
627
    if ap_form.is_valid() and interface_form.is_valid():
628 629
        user = AssoOption.get_cached_value('utilisateur_asso')
        if not user:
630 631 632 633 634
            messages.error(
                request,
                ("L'user association n'existe pas encore, veuillez le "
                 "créer ou le linker dans preferences")
            )
635
            return redirect(reverse('topologie:index-ap'))
636 637 638
        new_ap_obj = ap_form.save(commit=False)
        new_interface_obj = interface_form.save(commit=False)
        new_domain_obj = domain_form.save(commit=False)
639
        if ap_form.changed_data:
640
            new_ap_obj.save()
641
        if interface_form.changed_data:
642
            new_interface_obj.save()
643
        if domain_form.changed_data:
644
            new_domain_obj.save()
645
        messages.success(request, "La borne a été modifiée")
646
        return redirect(reverse('topologie:index-ap'))
647 648 649 650 651 652 653 654 655 656 657 658 659
    i_mbf_param = generate_ipv4_mbf_param(interface_form, False)
    return form(
        {
            'topoform': interface_form,
            'machineform': ap_form,
            'domainform': domain_form,
            'i_mbf_param': i_mbf_param,
            'device': 'wifi ap',
        },
        'topologie/topo_more.html',
        request
    )

660

chirac's avatar
chirac committed
661
@login_required
662
@can_create(Room)
chirac's avatar
chirac committed
663
def new_room(request):
664
    """Nouvelle chambre """
chirac's avatar
chirac committed
665 666
    room = EditRoomForm(request.POST or None)
    if room.is_valid():
667
        room.save()
chirac's avatar
chirac committed
668
        messages.success(request, "La chambre a été créé")
669
        return redirect(reverse('topologie:index-room'))
670 671 672 673 674
    return form(
        {'topoform': room, 'action_name': 'Ajouter'},
        'topologie/topo.html',
        request
    )
chirac's avatar
chirac committed
675

chirac's avatar
chirac committed
676 677

@login_required
678
@can_edit(Room)
679
def edit_room(request, room, **_kwargs):
680
    """ Edition numero et details de la chambre"""
chirac's avatar
chirac committed
681 682
    room = EditRoomForm(request.POST or None, instance=room)
    if room.is_valid():
683 684 685
        if room.changed_data:
            room.save()
            messages.success(request, "La chambre a bien été modifiée")
686
        return redirect(reverse('topologie:index-room'))
687 688 689 690 691
    return form(
        {'topoform': room, 'action_name': 'Editer'},
        'topologie/topo.html',
        request
    )
chirac's avatar
chirac committed
692

chirac's avatar
chirac committed
693 694

@login_required
695
@can_delete(Room)
696
def del_room(request, room, **_kwargs):
697
    """ Suppression d'un chambre"""
chirac's avatar
chirac committed
698
    if request.method == "POST":
699
        try:
700 701
            room.delete()
            messages.success(request, "La chambre/prise a été détruite")
702
        except ProtectedError:
703 704 705 706 707
            messages.error(
                request,
                ("La chambre %s est affectée à un autre objet, impossible "
                 "de la supprimer (switch ou user)" % room)
            )
708
        return redirect(reverse('topologie:index-room'))
709 710 711 712 713
    return form(
        {'objet': room, 'objet_name': 'Chambre'},
        'topologie/delete.html',
        request
    )
714 715 716


@login_required
717
@can_create(ModelSwitch)
718 719 720 721
def new_model_switch(request):
    """Nouveau modèle de switch"""
    model_switch = EditModelSwitchForm(request.POST or None)
    if model_switch.is_valid():
722
        model_switch.save()
723
        messages.success(request, "Le modèle a été créé")
724
        return redirect(reverse('topologie:index-model-switch'))
725 726 727 728 729
    return form(
        {'topoform': model_switch, 'action_name': 'Ajouter'},
        'topologie/topo.html',
        request
    )
730 731 732


@login_required
733
@can_edit(ModelSwitch)
734
def edit_model_switch(request, model_switch, **_kwargs):
735
    """ Edition d'un modèle de switch"""
736

737 738 739 740
    model_switch = EditModelSwitchForm(
        request.POST or None,
        instance=model_switch
    )
741
    if model_switch.is_valid():
742 743 744
        if model_switch.changed_data:
            model_switch.save()
            messages.success(request, "Le modèle a bien été modifié")
745
        return redirect(reverse('topologie:index-model-switch'))
746 747 748 749 750
    return form(
        {'topoform': model_switch, 'action_name': 'Editer'},
        'topologie/topo.html',
        request
    )
751 752 753


@login_required
754
@can_delete(ModelSwitch)
755
def del_model_switch(request, model_switch, **_kwargs):
756 757 758
    """ Suppression d'un modèle de switch"""
    if request.method == "POST":
        try:
759 760
            model_switch.delete()
            messages.success(request, "Le modèle a été détruit")
761
        except ProtectedError:
762 763 764 765 766
            messages.error(
                request,
                ("Le modèle %s est affectée à un autre objet, impossible "
                 "de la supprimer (switch ou user)" % model_switch)
            )
767
        return redirect(reverse('topologie:index-model-switch'))
768 769 770 771 772
    return form(
        {'objet': model_switch, 'objet_name': 'Modèle de switch'},
        'topologie/delete.html',
        request
    )
773 774


775 776 777 778 779 780 781 782
@login_required
@can_create(SwitchBay)
def new_switch_bay(request):
    """Nouvelle baie de switch"""
    switch_bay = EditSwitchBayForm(request.POST or None)
    if switch_bay.is_valid():
        switch_bay.save()
        messages.success(request, "La baie a été créé")
783
        return redirect(reverse('topologie:index-physical-grouping'))
784 785 786 787 788
    return form(
        {'topoform': switch_bay, 'action_name': 'Ajouter'},
        'topologie/topo.html',
        request
    )
789 790 791 792


@login_required
@can_edit(SwitchBay)
793
def edit_switch_bay(request, switch_bay, **_kwargs):
794 795 796 797 798 799
    """ Edition d'une baie de switch"""
    switch_bay = EditSwitchBayForm(request.POST or None, instance=switch_bay)
    if switch_bay.is_valid():
        if switch_bay.changed_data:
            switch_bay.save()
            messages.success(request, "Le switch a bien été modifié")
800
        return redirect(reverse('topologie:index-physical-grouping'))
801 802 803 804 805
    return form(
        {'topoform': switch_bay, 'action_name': 'Editer'},
        'topologie/topo.html',
        request
    )
806 807 808 809


@login_required
@can_delete(SwitchBay)
810
def del_switch_bay(request, switch_bay, **_kwargs):
811 812 813 814 815 816
    """ Suppression d'une baie de switch"""
    if request.method == "POST":
        try:
            switch_bay.delete()
            messages.success(request, "La baie a été détruite")
        except ProtectedError:
817 818 819 820 821
            messages.error(
                request,
                ("La baie %s est affecté à un autre objet, impossible "
                 "de la supprimer (switch ou user)" % switch_bay)
            )
822
        return redirect(reverse('topologie:index-physical-grouping'))
823 824 825 826 827
    return form(
        {'objet': switch_bay, 'objet_name': 'Baie de switch'},
        'topologie/delete.html',
        request
    )
828 829


830 831 832 833 834 835 836 837
@login_required
@can_create(Building)
def new_building(request):
    """Nouveau batiment"""
    building = EditBuildingForm(request.POST or None)
    if building.is_valid():
        building.save()
        messages.success(request, "Le batiment a été créé")
838
        return redirect(reverse('topologie:index-physical-grouping'))
839 840 841 842 843
    return form(
        {'topoform': building, 'action_name': 'Ajouter'},
        'topologie/topo.html',
        request
    )
844 845 846 847


@login_required
@can_edit(Building)
848
def edit_building(request, building, **_kwargs):
849 850 851 852 853 854
    """ Edition d'un batiment"""
    building = EditBuildingForm(request.POST or None, instance=building)
    if building.is_valid():
        if building.changed_data:
            building.save()
            messages.success(request, "Le batiment a bien été modifié")
855
        return redirect(reverse('topologie:index-physical-grouping'))
856 857 858 859 860
    return form(
        {'topoform': building, 'action_name': 'Editer'},
        'topologie/topo.html',
        request
    )
861 862 863 864


@login_required
@can_delete(Building)
865
def del_building(request, building, **_kwargs):
866 867 868 869 870 871
    """ Suppression d'un batiment"""
    if request.method == "POST":
        try:
            building.delete()
            messages.success(request, "La batiment a été détruit")
        except ProtectedError:
872 873 874 875 876
            messages.error(
                request,
                ("Le batiment %s est affecté à un autre objet, impossible "
                 "de la supprimer (switch ou user)" % building)
            )
877
        return redirect(reverse('topologie:index-physical-grouping'))
878 879 880 881 882
    return form(
        {'objet': building, 'objet_name': 'Bâtiment'},
        'topologie/delete.html',
        request
    )
883 884


885
@login_required
886
@can_create(ConstructorSwitch)
887 888 889 890
def new_constructor_switch(request):
    """Nouveau constructeur de switch"""
    constructor_switch = EditConstructorSwitchForm(request.POST or None)
    if constructor_switch.is_valid():
891
        constructor_switch.save()
892
        messages.success(request, "Le constructeur a été créé")
893
        return redirect(reverse('topologie:index-model-switch'))
894 895 896 897 898
    return form(
        {'topoform': constructor_switch, 'action_name': 'Ajouter'},
        'topologie/topo.html',
        request
    )
899 900 901


@login_required
902
@can_edit(ConstructorSwitch)
903
def edit_constructor_switch(request, constructor_switch, **_kwargs):
904
    """ Edition d'un constructeur de switch"""
905

906 907 908 909
    constructor_switch = EditConstructorSwitchForm(
        request.POST or None,
        instance=constructor_switch
    )
910
    if constructor_switch.is_valid():
911 912 913
        if constructor_switch.changed_data:
            constructor_switch.save()
            messages.success(request, "Le modèle a bien été modifié")
914
        return redirect(reverse('topologie:index-model-switch'))
915 916 917 918 919
    return form(
        {'topoform': constructor_switch, 'action_name': 'Editer'},
        'topologie/topo.html',
        request
    )
920 921 922


@login_required
923
@can_delete(ConstructorSwitch)
924
def del_constructor_switch(request, constructor_switch, **_kwargs):
925 926 927
    """ Suppression d'un constructeur de switch"""
    if request.method == "POST":
        try:
928 929
            constructor_switch.delete()
            messages.success(request, "Le constructeur a été détruit")
930
        except ProtectedError:
931 932 933 934 935
            messages.error(
                request,
                ("Le constructeur %s est affecté à un autre objet, impossible "
                 "de la supprimer (switch ou user)" % constructor_switch)
            )
936
        return redirect(reverse('topologie:index-model-switch'))
937 938 939 940
    return form({
        'objet': constructor_switch,
        'objet_name': 'Constructeur de switch'
        }, 'topologie/delete.html', request)
941 942 943 944 945 946


def make_machine_graph():
    """
    Crée le fichier dot et l'image du graph des Switchs
    """
grisel-davy's avatar
grisel-davy committed
947
    dico={'subs':[],'links':[],'alone':[]}
948 949 950 951 952
    missing=[]
    detected=[]
    for sw in Switch.objects.all():
        if(sw not in detected):
            missing.append(sw)
grisel-davy's avatar
grisel-davy committed
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
    for building in Building.objects.all():#Parcour tous les batiments
        dico['subs'].append({'bat_id':building.id,'bat_name':building,'switchs':[]})
        for switch in Switch.objects.filter(switchbay__building=building):#Parcour tous les switchs de ce batiment
            dico['subs'][-1]['switchs'].append({'name':switch.main_interface().domain.name,'nombre':switch.number,'model':switch.model,'id':switch.id,'batiment':building,'ports':[]})


            for p in switch.ports.all().filter(related__isnull=False):#Parcour tout les ports liés de ce switch 
                dico['subs'][-1]['switchs'][-1]['ports'].append({'numero':p.port,'related':p.related.switch.main_interface().domain.name})

    while(missing!=[]):#Tant que la liste des oubliés n'est pas vide i.e on les a pas tous passer
        links,new_detected=recursive_switchs(missing[0].ports.all().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
969 970
        missing=[i for i in missing if i not in new_detected]
        detected+=new_detected
grisel-davy's avatar
grisel-davy committed
971 972 973 974 975 976


    for switch in Switch.objects.all().filter(switchbay__isnull=True).exclude(ports__related__isnull=False):#Tous ceux qui ne sont ni connectés ni dans un batiment
        dico['alone'].append({'id':switch.id,'name':switch.main_interface().domain.name})

    dot_data=generate_image(dico)
977
    fichier = open("media/images/switchs.dot","w")
grisel-davy's avatar
grisel-davy committed
978
    fichier.write(dot_data)
979 980 981 982
    fichier.close()
    unflatten = Popen(["unflatten","-l", "3", "media/images/switchs.dot"], stdout=PIPE)
    image = Popen(["dot", "-Tpng", "-o", "media/images/switchs.png"], stdin=unflatten.stdout, stdout=PIPE)

grisel-davy's avatar
grisel-davy committed
983 984 985 986
def generate_image(data,template='topologie/graph_switch.dot'):


    t = loader.get_template(template)
987

grisel-davy's avatar
grisel-davy committed
988 989 990 991 992 993 994 995 996 997 998
    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, detected):
999 1000 1001
    """
    Parcour récursivement le switchs auquel appartient port_start pour trouver les ports suivants liés
    """
grisel-davy's avatar
grisel-davy committed
1002 1003 1004
    links_return=[]#Liste de dictionaires qui stockes les nouveaux liens détéctés

    l_ports=port_start.switch.ports.filter(related__isnull=False)#Liste des ports dont le related est non null
1005
    for port in l_ports:
grisel-davy's avatar
grisel-davy committed
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021

        if port.related.switch!=switch_before and port.related.switch!=port.switch:#Pas le switch dont on vient, pas le switch actuel

            detected.append(port_start.switch)
            detected.append(port.related.switch)

            links={'depart':port_start.switch.id,'arrive':port.related.switch.id}
            links_down, detected = recursive_switchs(port.related, port_start.switch, detected)

            links_return.append(links)
            for link in links_down:
                if(link!=[]):
                    links_return.append(link)

    return (links_return, detected)