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 38.7 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
grisel-davy's avatar
grisel-davy committed
45
from django.template import Context, Template, loader
Fernet Laouen's avatar
Fernet Laouen committed
46
from django.utils.translation import ugettext as _
grisel-davy's avatar
grisel-davy committed
47

grisel-davy's avatar
grisel-davy committed
48
import tempfile
Dalahro's avatar
Dalahro committed
49

50 51 52 53 54 55 56 57 58
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,
)
grisel-davy's avatar
grisel-davy committed
59
from re2o.settings import MEDIA_ROOT
60 61 62
from machines.forms import (
    DomainForm,
    EditInterfaceForm,
63 64
    AddInterfaceForm,
    EditOptionVlanForm
65 66
)
from machines.views import generate_ipv4_mbf_param
67 68 69 70 71
from machines.models import (
    Interface,
    Service_link, 
    Vlan
)
72 73 74
from preferences.models import AssoOption, GeneralOption

from .models import (
75 76 77 78 79
    Switch,
    Port,
    Room,
    Stack,
    ModelSwitch,
80
    ConstructorSwitch,
81 82
    AccessPoint,
    SwitchBay,
grisel-davy's avatar
grisel-davy committed
83
    Building,
grisel-davy's avatar
grisel-davy committed
84
    Server,
Fernet Laouen's avatar
Fernet Laouen committed
85
    PortProfile,
86
)
87 88 89 90
from .forms import (
    EditPortForm,
    NewSwitchForm,
    EditSwitchForm,
91 92 93
    AddPortForm,
    EditRoomForm,
    StackForm,
94
    EditModelSwitchForm,
95
    EditConstructorSwitchForm,
96
    CreatePortsForm,
97
    AddAccessPointForm,
98 99
    EditAccessPointForm,
    EditSwitchBayForm,
Fernet Laouen's avatar
Fernet Laouen committed
100 101
    EditBuildingForm,
    EditPortProfileForm,
102
)
103

grisel-davy's avatar
grisel-davy committed
104 105 106 107 108
from subprocess import (
    Popen,
    PIPE
)

Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
109
from os.path import isfile
110

chirac's avatar
chirac committed
111

chirac's avatar
chirac committed
112
@login_required
113
@can_view_all(Switch)
Dalahro's avatar
Dalahro committed
114
def index(request):
115
    """ Vue d'affichage de tous les swicthes"""
116 117 118 119 120 121 122 123
    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'))
124 125 126 127 128 129
    switch_list = SortTable.sort(
        switch_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX
    )
Fernet Laouen's avatar
Fernet Laouen committed
130

131
    pagination_number = GeneralOption.get_cached_value('pagination_number')
132
    switch_list = re2o_paginator(request, switch_list, pagination_number)
grisel-davy's avatar
grisel-davy committed
133

Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
134 135 136 137 138
    if any(
        service_link.need_regen
        for service_link in Service_link.objects.filter(
            service__service_type='graph_topo')
    ):
grisel-davy's avatar
grisel-davy committed
139
        make_machine_graph()
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
140 141
        for service_link in Service_link.objects.filter(
                service__service_type='graph_topo'):
grisel-davy's avatar
grisel-davy committed
142 143
            service_link.done_regen()

grisel-davy's avatar
grisel-davy committed
144 145
    if not isfile("/var/www/re2o/media/images/switchs.png"):
        make_machine_graph()
146 147 148
    return render(
        request,
        'topologie/index.html',
149 150 151 152 153 154 155 156
        {'switch_list': switch_list}
    )


@login_required
@can_view_all(PortProfile)
def index_port_profile(request):
    pagination_number = GeneralOption.get_cached_value('pagination_number')
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
157 158 159 160
    port_profile_list = PortProfile.objects.all().select_related(
        'vlan_untagged')
    port_profile_list = re2o_paginator(
        request, port_profile_list, pagination_number)
161
    vlan_list = Vlan.objects.all().order_by('vlan_id')
162 163 164
    return render(
        request,
        'topologie/index_portprofile.html',
165
        {'port_profile_list': port_profile_list, 'vlan_list': vlan_list}
166
    )
chirac's avatar
chirac committed
167

Dalahro's avatar
Dalahro committed
168

chirac's avatar
chirac committed
169
@login_required
170 171
@can_view_all(Port)
@can_view(Switch)
172
def index_port(request, switch, switchid):
173
    """ Affichage de l'ensemble des ports reliés à un switch particulier"""
174 175 176 177 178 179 180 181 182 183 184 185
    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'))
186 187 188 189 190 191
    port_list = SortTable.sort(
        port_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_PORT
    )
192 193 194 195 196 197
    return render(
        request,
        'topologie/index_p.html',
        {
            'port_list': port_list,
            'id_switch': switchid,
198
            'switch': switch
199 200
        }
    )
chirac's avatar
chirac committed
201

Dalahro's avatar
Dalahro committed
202

chirac's avatar
chirac committed
203
@login_required
204
@can_view_all(Room)
chirac's avatar
chirac committed
205
def index_room(request):
206
    """ Affichage de l'ensemble des chambres"""
207
    room_list = Room.objects
208 209 210 211 212 213
    room_list = SortTable.sort(
        room_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_ROOM
    )
214
    pagination_number = GeneralOption.get_cached_value('pagination_number')
215
    room_list = re2o_paginator(request, room_list, pagination_number)
216 217 218 219 220
    return render(
        request,
        'topologie/index_room.html',
        {'room_list': room_list}
    )
chirac's avatar
chirac committed
221

chirac's avatar
chirac committed
222

223
@login_required
224 225
@can_view_all(AccessPoint)
def index_ap(request):
226
    """ Affichage de l'ensemble des bornes"""
227 228 229 230 231 232 233
    ap_list = (AccessPoint.objects
               .prefetch_related(Prefetch(
                   'interface_set',
                   queryset=(Interface.objects
                             .select_related('ipv4__ip_type__extension')
                             .select_related('domain__extension'))
               )))
234 235
    ap_list = SortTable.sort(
        ap_list,
236 237 238 239 240
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_BORNE
    )
    pagination_number = GeneralOption.get_cached_value('pagination_number')
241
    ap_list = re2o_paginator(request, ap_list, pagination_number)
242 243 244 245 246
    return render(
        request,
        'topologie/index_ap.html',
        {'ap_list': ap_list}
    )
247 248


249
@login_required
250
@can_view_all(Stack, Building, SwitchBay)
251
def index_physical_grouping(request):
chirac's avatar
chirac committed
252
    """Affichage de la liste des stacks (affiche l'ensemble des switches)"""
253 254 255 256
    stack_list = (Stack.objects
                  .prefetch_related(
                      'switch_set__interface_set__domain__extension'
                  ))
257 258
    building_list = Building.objects.all()
    switch_bay_list = SwitchBay.objects.select_related('building')
259 260 261 262 263 264
    stack_list = SortTable.sort(
        stack_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_STACK
    )
265 266 267 268 269 270 271 272 273 274 275 276
    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
    )
277 278 279 280 281 282 283 284 285
    return render(
        request,
        'topologie/index_physical_grouping.html',
        {
            'stack_list': stack_list,
            'switch_bay_list': switch_bay_list,
            'building_list': building_list,
        }
    )
286 287


288
@login_required
289
@can_view_all(ModelSwitch, ConstructorSwitch)
290 291
def index_model_switch(request):
    """ Affichage de l'ensemble des modèles de switches"""
292
    model_switch_list = ModelSwitch.objects.select_related('constructor')
293 294 295 296 297 298 299 300 301 302 303 304 305
    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
    )
306 307 308 309 310 311 312 313
    return render(
        request,
        'topologie/index_model_switch.html',
        {
            'model_switch_list': model_switch_list,
            'constructor_switch_list': constructor_switch_list,
        }
    )
314 315


316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
@login_required
@can_edit(Vlan)
def edit_vlanoptions(request, vlan_instance, **_kwargs):
    """ View used to edit options for switch of VLAN object """
    vlan = EditOptionVlanForm(request.POST or None, instance=vlan_instance)
    if vlan.is_valid():
        if vlan.changed_data:
            vlan.save()
            messages.success(request, "Vlan modifié")
        return redirect(reverse('topologie:index-port-profile'))
    return form(
        {'vlanform': vlan, 'action_name': 'Editer'},
        'machines/machine.html',
        request
    )


chirac's avatar
chirac committed
333
@login_required
334
@can_create(Port)
335
def new_port(request, switchid):
336
    """ Nouveau port"""
Dalahro's avatar
Dalahro committed
337
    try:
338
        switch = Switch.objects.get(pk=switchid)
Dalahro's avatar
Dalahro committed
339
    except Switch.DoesNotExist:
340
        messages.error(request, _("Nonexistent switch."))
341
        return redirect(reverse('topologie:index'))
Dalahro's avatar
Dalahro committed
342 343 344 345 346
    port = AddPortForm(request.POST or None)
    if port.is_valid():
        port = port.save(commit=False)
        port.switch = switch
        try:
347
            port.save()
348
            messages.success(request, _("The port was added."))
Dalahro's avatar
Dalahro committed
349
        except IntegrityError:
350
            messages.error(request, _("The port already exists."))
351
        return redirect(reverse(
352
            'topologie:index-port',
353 354 355
            kwargs={'switchid': switchid}
        ))
    return form(
356
        {'id_switch': switchid, 'topoform': port, 'action_name': _("Add")},
357 358
        'topologie/topo.html',
        request)
chirac's avatar
chirac committed
359

Dalahro's avatar
Dalahro committed
360

chirac's avatar
chirac committed
361
@login_required
362
@can_edit(Port)
363
def edit_port(request, port_object, **_kwargs):
chirac's avatar
chirac committed
364 365
    """ Edition d'un port. Permet de changer le switch parent et
    l'affectation du port"""
366

367
    port = EditPortForm(request.POST or None, instance=port_object)
Dalahro's avatar
Dalahro committed
368
    if port.is_valid():
369 370
        if port.changed_data:
            port.save()
371
            messages.success(request, _("The port was edited."))
372 373
        return redirect(reverse(
            'topologie:index-port',
374
            kwargs={'switchid': str(port_object.switch.id)}
375 376 377 378 379
        ))
    return form(
        {
            'id_switch': str(port_object.switch.id),
            'topoform': port,
380
            'action_name': _("Edit")
381 382 383 384
        },
        'topologie/topo.html',
        request
    )
chirac's avatar
chirac committed
385

chirac's avatar
chirac committed
386

387
@login_required
388
@can_delete(Port)
389
def del_port(request, port, **_kwargs):
390
    """ Supprime le port"""
Gabriel Detraz's avatar
Gabriel Detraz committed
391 392
    if request.method == "POST":
        try:
393
            port.delete()
394
            messages.success(request, _("The port was deleted."))
Gabriel Detraz's avatar
Gabriel Detraz committed
395
        except ProtectedError:
396 397
            messages.error(
                request,
398 399
                (_("The port %s is used by another object, impossible to"
                   " delete it.") % port)
400
            )
401 402
        return redirect(reverse(
            'topologie:index-port',
403 404
            kwargs={'switchid': str(port.switch.id)}
        ))
chirac's avatar
chirac committed
405 406
    return form({'objet': port}, 'topologie/delete.html', request)

Gabriel Detraz's avatar
Gabriel Detraz committed
407 408

@login_required
409
@can_create(Stack)
410
def new_stack(request):
411
    """Ajoute un nouveau stack : stackid_min, max, et nombre de switches"""
412
    stack = StackForm(request.POST or None)
chirac's avatar
chirac committed
413
    if stack.is_valid():
414
        stack.save()
415
        messages.success(request, _("The stack was created."))
Gabriel Detraz's avatar
Gabriel Detraz committed
416
        return redirect(reverse('topologie:index-physical-grouping'))
417
    return form(
418
        {'topoform': stack, 'action_name': _("Create")},
419 420 421
        'topologie/topo.html',
        request
    )
422 423 424


@login_required
425
@can_edit(Stack)
426
def edit_stack(request, stack, **_kwargs):
chirac's avatar
chirac committed
427
    """Edition d'un stack (nombre de switches, nom...)"""
428 429
    stack = StackForm(request.POST or None, instance=stack)
    if stack.is_valid():
430 431
        if stack.changed_data:
            stack.save()
Gabriel Detraz's avatar
Gabriel Detraz committed
432
        return redirect(reverse('topologie:index-physical-grouping'))
433
    return form(
434
        {'topoform': stack, 'action_name': _("Edit")},
435 436 437
        'topologie/topo.html',
        request
    )
chirac's avatar
chirac committed
438

439

440
@login_required
441
@can_delete(Stack)
442
def del_stack(request, stack, **_kwargs):
chirac's avatar
chirac committed
443
    """Supprime un stack"""
444 445
    if request.method == "POST":
        try:
446
            stack.delete()
447
            messages.success(request, _("The stack was deleted."))
448
        except ProtectedError:
449 450
            messages.error(
                request,
451 452
                (_("The stack %s is used by another object, impossible to"
                   " deleted it.") % stack)
453
            )
454
        return redirect(reverse('topologie:index-physical-grouping'))
chirac's avatar
chirac committed
455 456
    return form({'objet': stack}, 'topologie/delete.html', request)

457

458
@login_required
459
@can_edit(Stack)
460
def edit_switchs_stack(request, stack, **_kwargs):
chirac's avatar
chirac committed
461
    """Permet d'éditer la liste des switches dans une stack et l'ajouter"""
462

463 464 465 466 467 468 469 470
    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
471
@login_required
472
@can_create(Switch)
Dalahro's avatar
Dalahro committed
473
def new_switch(request):
chirac's avatar
chirac committed
474 475 476
    """ 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"""
477
    switch = NewSwitchForm(
478 479 480
        request.POST or None,
        user=request.user
    )
481
    interface = AddInterfaceForm(
chirac's avatar
chirac committed
482
        request.POST or None,
483
        user=request.user
484
    )
485
    domain = DomainForm(
chirac's avatar
chirac committed
486
        request.POST or None,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
487
    )
488
    if switch.is_valid() and interface.is_valid():
489
        user = AssoOption.get_cached_value('utilisateur_asso')
490
        if not user:
491 492
            messages.error(
                request,
493 494
                (_("The organisation's user doesn't exist yet, please create"
                   " or link it in the preferences."))
495
            )
496
            return redirect(reverse('topologie:index'))
497 498 499 500
        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
501
        if domain.is_valid():
502 503 504 505 506 507
            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()
508
            messages.success(request, _("The switch was created."))
509
            return redirect(reverse('topologie:index'))
510
    i_mbf_param = generate_ipv4_mbf_param(interface, False)
511 512 513 514 515 516 517 518 519 520 521
    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
522

Dalahro's avatar
Dalahro committed
523

524
@login_required
525
@can_create(Port)
526
def create_ports(request, switchid):
527 528
    """ Création d'une liste de ports pour un switch."""
    try:
529
        switch = Switch.objects.get(pk=switchid)
530
    except Switch.DoesNotExist:
531
        messages.error(request, _("Nonexistent switch"))
532
        return redirect(reverse('topologie:index'))
533

534
    s_begin = s_end = 0
535 536
    nb_ports = switch.ports.count()
    if nb_ports > 0:
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
537 538 539
        ports = switch.ports.order_by('port').values('port')
        s_begin = ports.first().get('port')
        s_end = ports.last().get('port')
540

541 542
    port_form = CreatePortsForm(
        request.POST or None,
543
        initial={'begin': s_begin, 'end': s_end}
544
    )
545

546 547 548
    if port_form.is_valid():
        begin = port_form.cleaned_data['begin']
        end = port_form.cleaned_data['end']
549 550
        try:
            switch.create_ports(begin, end)
551
            messages.success(request, _("The ports were created."))
552 553
        except ValidationError as e:
            messages.error(request, ''.join(e))
554 555
        return redirect(reverse(
            'topologie:index-port',
556
            kwargs={'switchid': switchid}
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
557
        ))
558 559 560 561 562
    return form(
        {'id_switch': switchid, 'topoform': port_form},
        'topologie/switch.html',
        request
    )
563

Dalahro's avatar
Dalahro committed
564

chirac's avatar
chirac committed
565
@login_required
566
@can_edit(Switch)
567
def edit_switch(request, switch, switchid):
chirac's avatar
chirac committed
568 569
    """ Edition d'un switch. Permet de chambre nombre de ports,
    place dans le stack, interface et machine associée"""
570

571
    switch_form = EditSwitchForm(
chirac's avatar
chirac committed
572
        request.POST or None,
573
        instance=switch,
574
        user=request.user
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
575
    )
576
    interface_form = EditInterfaceForm(
chirac's avatar
chirac committed
577
        request.POST or None,
578
        instance=switch.interface_set.first(),
579
        user=request.user
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
580
    )
581
    domain_form = DomainForm(
chirac's avatar
chirac committed
582
        request.POST or None,
583
        instance=switch.interface_set.first().domain
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
584
    )
585
    if switch_form.is_valid() and interface_form.is_valid():
586 587 588
        new_switch_obj = switch_form.save(commit=False)
        new_interface_obj = interface_form.save(commit=False)
        new_domain_obj = domain_form.save(commit=False)
589
        if switch_form.changed_data:
590
            new_switch_obj.save()
591
        if interface_form.changed_data:
592
            new_interface_obj.save()
593
        if domain_form.changed_data:
594
            new_domain_obj.save()
595
        messages.success(request, _("The switch was edited."))
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
596
        return redirect(reverse('topologie:index'))
597 598 599 600 601 602 603 604 605 606 607 608 609
    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
610

chirac's avatar
chirac committed
611

612
@login_required
613 614 615
@can_create(AccessPoint)
def new_ap(request):
    """ Creation d'une ap. Cree en meme temps l'interface et la machine
616 617
    associée. Vue complexe. Appelle successivement les 3 models forms
    adaptés : machine, interface, domain et switch"""
618
    ap = AddAccessPointForm(
619 620 621
        request.POST or None,
        user=request.user
    )
622
    interface = AddInterfaceForm(
623 624 625 626 627
        request.POST or None,
        user=request.user
    )
    domain = DomainForm(
        request.POST or None,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
628
    )
629
    if ap.is_valid() and interface.is_valid():
630 631
        user = AssoOption.get_cached_value('utilisateur_asso')
        if not user:
632 633
            messages.error(
                request,
634 635
                (_("The organisation's user doesn't exist yet, please create"
                   " or link it in the preferences."))
636
            )
637
            return redirect(reverse('topologie:index'))
638 639 640 641
        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
642
        if domain.is_valid():
643 644 645 646 647 648
            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()
649
            messages.success(request, _("The access point was created."))
650
            return redirect(reverse('topologie:index-ap'))
651
    i_mbf_param = generate_ipv4_mbf_param(interface, False)
652 653 654 655 656 657 658 659 660 661 662
    return form(
        {
            'topoform': interface,
            'machineform': ap,
            'domainform': domain,
            'i_mbf_param': i_mbf_param,
            'device': 'wifi ap',
        },
        'topologie/topo_more.html',
        request
    )
663 664 665


@login_required
666
@can_edit(AccessPoint)
667
def edit_ap(request, ap, **_kwargs):
668 669
    """ Edition d'un switch. Permet de chambre nombre de ports,
    place dans le stack, interface et machine associée"""
670
    interface_form = EditInterfaceForm(
671 672
        request.POST or None,
        user=request.user,
673
        instance=ap.interface_set.first()
674
    )
675
    ap_form = EditAccessPointForm(
676 677
        request.POST or None,
        user=request.user,
678
        instance=ap
679 680 681
    )
    domain_form = DomainForm(
        request.POST or None,
682
        instance=ap.interface_set.first().domain
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
683
    )
684
    if ap_form.is_valid() and interface_form.is_valid():
685 686
        user = AssoOption.get_cached_value('utilisateur_asso')
        if not user:
687 688
            messages.error(
                request,
689 690
                (_("The organisation's user doesn't exist yet, please create"
                   " or link it in the preferences."))
691
            )
692
            return redirect(reverse('topologie:index-ap'))
693 694 695
        new_ap_obj = ap_form.save(commit=False)
        new_interface_obj = interface_form.save(commit=False)
        new_domain_obj = domain_form.save(commit=False)
696
        if ap_form.changed_data:
697
            new_ap_obj.save()
698
        if interface_form.changed_data:
699
            new_interface_obj.save()
700
        if domain_form.changed_data:
701
            new_domain_obj.save()
702
        messages.success(request, _("The access point was edited."))
703
        return redirect(reverse('topologie:index-ap'))
704 705 706 707 708 709 710 711 712 713 714 715 716
    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
    )

717

chirac's avatar
chirac committed
718
@login_required
719
@can_create(Room)
chirac's avatar
chirac committed
720
def new_room(request):
721
    """Nouvelle chambre """
chirac's avatar
chirac committed
722 723
    room = EditRoomForm(request.POST or None)
    if room.is_valid():
724
        room.save()
725
        messages.success(request, _("The room was created."))
726
        return redirect(reverse('topologie:index-room'))
727
    return form(
728
        {'topoform': room, 'action_name': _("Add")},
729 730 731
        'topologie/topo.html',
        request
    )
chirac's avatar
chirac committed
732

chirac's avatar
chirac committed
733 734

@login_required
735
@can_edit(Room)
736
def edit_room(request, room, **_kwargs):
737
    """ Edition numero et details de la chambre"""
chirac's avatar
chirac committed
738 739
    room = EditRoomForm(request.POST or None, instance=room)
    if room.is_valid():
740 741
        if room.changed_data:
            room.save()
742
            messages.success(request, _("The room was edited."))
743
        return redirect(reverse('topologie:index-room'))
744
    return form(
745
        {'topoform': room, 'action_name': _("Edit")},
746 747 748
        'topologie/topo.html',
        request
    )
chirac's avatar
chirac committed
749

chirac's avatar
chirac committed
750 751

@login_required
752
@can_delete(Room)
753
def del_room(request, room, **_kwargs):
754
    """ Suppression d'un chambre"""
chirac's avatar
chirac committed
755
    if request.method == "POST":
756
        try:
757
            room.delete()
758
            messages.success(request, _("The room was deleted."))
759
        except ProtectedError:
760 761
            messages.error(
                request,
762 763
                (_("The room %s is used by another object, impossible to"
                   " deleted it.") % room)
764
            )
765
        return redirect(reverse('topologie:index-room'))
766
    return form(
767
        {'objet': room, 'objet_name': _("Room")},
768 769 770
        'topologie/delete.html',
        request
    )
771 772 773


@login_required
774
@can_create(ModelSwitch)
775 776 777 778
def new_model_switch(request):
    """Nouveau modèle de switch"""
    model_switch = EditModelSwitchForm(request.POST or None)
    if model_switch.is_valid():
779
        model_switch.save()
780
        messages.success(request, _("The swich model was created."))
781
        return redirect(reverse('topologie:index-model-switch'))
782
    return form(
783
        {'topoform': model_switch, 'action_name': _("Add")},
784 785 786
        'topologie/topo.html',
        request
    )
787 788 789


@login_required
790
@can_edit(ModelSwitch)
791
def edit_model_switch(request, model_switch, **_kwargs):
792
    """ Edition d'un modèle de switch"""
793

794 795 796 797
    model_switch = EditModelSwitchForm(
        request.POST or None,
        instance=model_switch
    )
798
    if model_switch.is_valid():
799 800
        if model_switch.changed_data:
            model_switch.save()
801
            messages.success(request, _("The switch model was edited."))
802
        return redirect(reverse('topologie:index-model-switch'))
803
    return form(
804
        {'topoform': model_switch, 'action_name': _("Edit")},
805 806 807
        'topologie/topo.html',
        request
    )
808 809 810


@login_required
811
@can_delete(ModelSwitch)
812
def del_model_switch(request, model_switch, **_kwargs):
813 814 815
    """ Suppression d'un modèle de switch"""
    if request.method == "POST":
        try:
816
            model_switch.delete()
817
            messages.success(request, _("The switch model was deleted."))
818
        except ProtectedError:
819 820
            messages.error(
                request,
821 822
                (_("The switch model %s is used by another object,"
                   " impossible to delete it.") % model_switch)
823
            )
824
        return redirect(reverse('topologie:index-model-switch'))
825
    return form(
826
        {'objet': model_switch, 'objet_name': _("Switch model")},
827 828 829
        'topologie/delete.html',
        request
    )
830 831


832 833 834 835 836 837 838
@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()
839
        messages.success(request, _("The switch bay was created."))
840
        return redirect(reverse('topologie:index-physical-grouping'))
841
    return form(
842
        {'topoform': switch_bay, 'action_name': _("Add")},
843 844 845
        'topologie/topo.html',
        request
    )
846 847 848 849


@login_required
@can_edit(SwitchBay)
850
def edit_switch_bay(request, switch_bay, **_kwargs):
851 852 853 854 855
    """ 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()
856
            messages.success(request, _("The switch bay was edited."))
857
        return redirect(reverse('topologie:index-physical-grouping'))
858
    return form(
859
        {'topoform': switch_bay, 'action_name': _("Edit")},
860 861 862
        'topologie/topo.html',
        request
    )
863 864 865 866


@login_required
@can_delete(SwitchBay)
867
def del_switch_bay(request, switch_bay, **_kwargs):
868 869 870 871
    """ Suppression d'une baie de switch"""
    if request.method == "POST":
        try:
            switch_bay.delete()
872
            messages.success(request, _("The switch bay was deleted."))
873
        except ProtectedError:
874 875
            messages.error(
                request,
876 877
                (_("The switch bay %s is used by another object,"
                   " impossible to delete it.") % switch_bay)
878
            )
879
        return redirect(reverse('topologie:index-physical-grouping'))
880
    return form(
881
        {'objet': switch_bay, 'objet_name': _("Switch bay")},
882 883 884
        'topologie/delete.html',
        request
    )
885 886


887 888 889 890 891 892 893
@login_required
@can_create(Building)
def new_building(request):
    """Nouveau batiment"""
    building = EditBuildingForm(request.POST or None)
    if building.is_valid():
        building.save()
894
        messages.success(request, _("The building was created."))
895
        return redirect(reverse('topologie:index-physical-grouping'))
896
    return form(
897
        {'topoform': building, 'action_name': _("Add")},
898 899 900
        'topologie/topo.html',
        request
    )
901 902 903 904


@login_required
@can_edit(Building)
905
def edit_building(request, building, **_kwargs):
906 907 908 909 910
    """ Edition d'un batiment"""
    building = EditBuildingForm(request.POST or None, instance=building)
    if building.is_valid():
        if building.changed_data:
            building.save()
911
            messages.success(request, _("The building was edited."))
912
        return redirect(reverse('topologie:index-physical-grouping'))
913
    return form(
914
        {'topoform': building, 'action_name': _("Edit")},
915 916 917
        'topologie/topo.html',
        request
    )
918 919 920 921


@login_required
@can_delete(Building)
922
def del_building(request, building, **_kwargs):
923 924 925 926
    """ Suppression d'un batiment"""
    if request.method == "POST":
        try:
            building.delete()
927
            messages.success(request, _("The building was deleted."))
928
        except ProtectedError:
929 930
            messages.error(
                request,
931 932
                (_("The building %s is used by another object, impossible"
                   " to delete it.") % building)
933
            )
934
        return redirect(reverse('topologie:index-physical-grouping'))
935
    return form(
936
        {'objet': building, 'objet_name': _("Building")},
937 938 939
        'topologie/delete.html',
        request
    )
940 941


942
@login_required
943
@can_create(ConstructorSwitch)
944 945 946 947
def new_constructor_switch(request):
    """Nouveau constructeur de switch"""
    constructor_switch = EditConstructorSwitchForm(request.POST or None)
    if constructor_switch.is_valid():
948
        constructor_switch.save()
949
        messages.success(request, _("The switch constructor was created."))
950
        return redirect(reverse('topologie:index-model-switch'))
951
    return form(
952
        {'topoform': constructor_switch, 'action_name': _("Add")},
953 954 955
        'topologie/topo.html',
        request
    )
956 957 958


@login_required
959
@can_edit(ConstructorSwitch)
960
def edit_constructor_switch(request, constructor_switch, **_kwargs):
961
    """ Edition d'un constructeur de switch"""
962

963 964 965 966
    constructor_switch = EditConstructorSwitchForm(
        request.POST or None,
        instance=constructor_switch
    )
967
    if constructor_switch.is_valid():
968 969
        if constructor_switch.changed_data:
            constructor_switch.save()
970
            messages.success(request, _("The switch constructor was edited."))
971
        return redirect(reverse('topologie:index-model-switch'))
972
    return form(
973
        {'topoform': constructor_switch, 'action_name': _("Edit")},
974 975 976
        'topologie/topo.html',
        request
    )
977 978 979


@login_required
980
@can_delete(ConstructorSwitch)
981
def del_constructor_switch(request, constructor_switch, **_kwargs):
982 983 984
    """ Suppression d'un constructeur de switch"""
    if request.method == "POST":
        try:
985
            constructor_switch.delete()
986
            messages.success(request, _("The switch constructor was deleted."))
987
        except ProtectedError:
988 989
            messages.error(
                request,
990 991
                (_("The switch constructor %s is used by another object,"
                   " impossible to delete it.") % constructor_switch)
992
            )
993
        return redirect(reverse('topologie:index-model-switch'))
994 995
    return form({
        'objet': constructor_switch,
996
        'objet_name': _("Switch constructor")
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
997
    }, 'topologie/delete.html', request)
998 999


Fernet Laouen's avatar
Fernet Laouen committed
1000 1001 1002 1003
@login_required
@can_create(PortProfile)
def new_port_profile(request):
    """Create a new port profile"""
1004
    port_profile = EditPortProfileForm(request.POST or None)
Fernet Laouen's avatar
Fernet Laouen committed
1005 1006
    if port_profile.is_valid():
        port_profile.save()
1007
        messages.success(request, _("The port profile was created."))
1008
        return redirect(reverse('topologie:index-port-profile'))
Fernet Laouen's avatar
Fernet Laouen committed
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
    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"""
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1020 1021
    port_profile = EditPortProfileForm(
        request.POST or None, instance=port_profile)
Fernet Laouen's avatar
Fernet Laouen committed
1022 1023 1024
    if port_profile.is_valid():
        if port_profile.changed_data:
            port_profile.save()
1025
            messages.success(request, _("The port profile was edited."))
1026
        return redirect(reverse('topologie:index-port-profile'))
Fernet Laouen's avatar
Fernet Laouen committed
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
    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()
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1041
            messages.success(request,
1042
                             _("The port profile was deleted."))
Fernet Laouen's avatar
Fernet Laouen committed
1043
        except ProtectedError:
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1044
            messages.success(request,
1045
                             _("Impossible to delete the port profile."))
1046
        return redirect(reverse('topologie:index-port-profile'))
Fernet Laouen's avatar
Fernet Laouen committed
1047
    return form(
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1048 1049 1050
        {'objet': port_profile, 'objet_name': _("Port profile")},
        'topologie/delete.html',
        request
Fernet Laouen's avatar
Fernet Laouen committed
1051 1052
    )

Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1053

1054 1055
def make_machine_graph():
    """
grisel-davy's avatar
grisel-davy committed
1056
    Create the graph of switchs, machines and access points.
1057
    """
grisel-davy's avatar
grisel-davy committed
1058 1059
    dico = {
        'subs': [],
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1060
        'links': [],
grisel-davy's avatar
grisel-davy committed
1061 1062
        'alone': [],
        'colors': {
grisel-davy's avatar
grisel-davy committed
1063
            'head': "#7f0505",  # Color parameters for the graph
grisel-davy's avatar
grisel-davy committed
1064 1065 1066 1067 1068 1069
            'back': "#b5adad",
            'texte': "#563d01",
            'border_bornes': "#02078e",
            'head_bornes': "#25771c",
            'head_server': "#1c3777"
        }
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1070
    }
grisel-davy's avatar
grisel-davy committed
1071 1072
    missing = list(Switch.objects.all())
    detected = []
grisel-davy's avatar
grisel-davy committed
1073 1074
    for building in Building.objects.all():  # Visit all buildings

grisel-davy's avatar
grisel-davy committed
1075 1076
        dico['subs'].append(
            {
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1077 1078 1079 1080 1081
                'bat_id': building.id,
                'bat_name': building,
                'switchs': [],
                'bornes': [],
                'machines': []
grisel-davy's avatar
grisel-davy committed
1082 1083
            }
        )
grisel-davy's avatar
grisel-davy committed
1084
        # Visit all switchs in this building
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1085
        for switch in Switch.objects.filter(switchbay__building=building):
grisel-davy's avatar
grisel-davy committed
1086 1087 1088 1089 1090 1091 1092 1093
            dico['subs'][-1]['switchs'].append({
                'name': switch.main_interface().domain.name,
                'nombre': switch.number,
                'model': switch.model,
                'id': switch.id,
                'batiment': building,
                'ports': []
            })
grisel-davy's avatar
grisel-davy committed
1094
            # visit all ports of this switch and add the switchs linked to it
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1095
            for port in switch.ports.filter(related__isnull=False):
grisel-davy's avatar
grisel-davy committed
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
                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,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1113 1114 1115
                'port': Port.objects.filter(
                    machine_interface__machine=server
                )[0].port
grisel-davy's avatar
grisel-davy committed
1116 1117
            })

grisel-davy's avatar
grisel-davy committed
1118
    # While the list of forgotten ones is not empty
grisel-davy's avatar
grisel-davy committed
1119
    while missing:
grisel-davy's avatar
grisel-davy committed
1120
        if missing[0].ports.count():  # The switch is not empty
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1121 1122
            links, new_detected = recursive_switchs(
                missing[0], None, [missing[0]])
grisel-davy's avatar
grisel-davy committed
1123 1124
            for link in links:
                dico['links'].append(link)
grisel-davy's avatar
grisel-davy committed
1125
            # Update the lists of missings and already detected switchs
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1126
            missing = [i for i in missing if i not in new_detected]
grisel-davy's avatar
grisel-davy committed
1127
            detected += new_detected
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
1128 1129
        # If the switch have no ports, don't explore it and hop to the next one
        else: