views.py 35.7 KB
Newer Older
lhark's avatar
lhark committed
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
lhark's avatar
lhark committed
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
from django.template.loader import get_template
from django.template import Context, Template, loader
grisel-davy's avatar
grisel-davy committed
48
49
from django.db.models.signals import post_save
from django.dispatch import receiver
grisel-davy's avatar
grisel-davy committed
50

grisel-davy's avatar
grisel-davy committed
51
import tempfile
Dalahro's avatar
Dalahro committed
52

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

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

grisel-davy's avatar
grisel-davy committed
100
101
102
103
104
105
106
from subprocess import (
    Popen,
    PIPE
)

from os.path import isfile 
from os import remove
107

chirac's avatar
chirac committed
108

chirac's avatar
chirac committed
109
@login_required
110
@can_view_all(Switch)
Dalahro's avatar
Dalahro committed
111
def index(request):
Gabriel Detraz's avatar
Gabriel Detraz committed
112
    """ Vue d'affichage de tous les swicthes"""
Maël Kervella's avatar
Maël Kervella committed
113
114
115
116
117
118
119
120
    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'))
121
122
123
124
125
126
    switch_list = SortTable.sort(
        switch_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX
    )
127
    pagination_number = GeneralOption.get_cached_value('pagination_number')
128
    switch_list = re2o_paginator(request, switch_list, pagination_number)
grisel-davy's avatar
grisel-davy committed
129
130
131
132
133
134

    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()

grisel-davy's avatar
grisel-davy committed
135
136
    if not isfile("/var/www/re2o/media/images/switchs.png"):
        make_machine_graph()
Maël Kervella's avatar
Maël Kervella committed
137
138
139
140
141
    return render(
        request,
        'topologie/index.html',
        {'switch_list': switch_list}
    )
chirac's avatar
chirac committed
142

Dalahro's avatar
Dalahro committed
143

chirac's avatar
chirac committed
144
@login_required
145
146
@can_view_all(Port)
@can_view(Switch)
147
def index_port(request, switch, switchid):
Gabriel Detraz's avatar
Gabriel Detraz committed
148
    """ Affichage de l'ensemble des ports reliés à un switch particulier"""
Maël Kervella's avatar
Maël Kervella committed
149
150
151
152
153
154
155
156
157
158
159
160
    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'))
161
162
163
164
165
166
    port_list = SortTable.sort(
        port_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_PORT
    )
Maël Kervella's avatar
Maël Kervella committed
167
168
169
170
171
172
173
174
175
    return render(
        request,
        'topologie/index_p.html',
        {
            'port_list': port_list,
            'id_switch': switchid,
            'nom_switch': switch
        }
    )
chirac's avatar
chirac committed
176

Dalahro's avatar
Dalahro committed
177

chirac's avatar
chirac committed
178
@login_required
179
@can_view_all(Room)
chirac's avatar
chirac committed
180
def index_room(request):
Gabriel Detraz's avatar
Gabriel Detraz committed
181
    """ Affichage de l'ensemble des chambres"""
182
    room_list = Room.objects
183
184
185
186
187
188
    room_list = SortTable.sort(
        room_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_ROOM
    )
189
    pagination_number = GeneralOption.get_cached_value('pagination_number')
190
    room_list = re2o_paginator(request, room_list, pagination_number)
Maël Kervella's avatar
Maël Kervella committed
191
192
193
194
195
    return render(
        request,
        'topologie/index_room.html',
        {'room_list': room_list}
    )
chirac's avatar
chirac committed
196

chirac's avatar
chirac committed
197

Gabriel Detraz's avatar
Gabriel Detraz committed
198
@login_required
Gabriel Detraz's avatar
Gabriel Detraz committed
199
200
@can_view_all(AccessPoint)
def index_ap(request):
Gabriel Detraz's avatar
Gabriel Detraz committed
201
    """ Affichage de l'ensemble des bornes"""
Maël Kervella's avatar
Maël Kervella committed
202
203
204
205
206
207
208
    ap_list = (AccessPoint.objects
               .prefetch_related(Prefetch(
                   'interface_set',
                   queryset=(Interface.objects
                             .select_related('ipv4__ip_type__extension')
                             .select_related('domain__extension'))
               )))
Gabriel Detraz's avatar
Gabriel Detraz committed
209
210
    ap_list = SortTable.sort(
        ap_list,
Gabriel Detraz's avatar
Gabriel Detraz committed
211
212
213
214
215
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_BORNE
    )
    pagination_number = GeneralOption.get_cached_value('pagination_number')
216
    ap_list = re2o_paginator(request, ap_list, pagination_number)
Maël Kervella's avatar
Maël Kervella committed
217
218
219
220
221
    return render(
        request,
        'topologie/index_ap.html',
        {'ap_list': ap_list}
    )
Gabriel Detraz's avatar
Gabriel Detraz committed
222
223


224
@login_required
225
@can_view_all(Stack, Building, SwitchBay)
226
def index_physical_grouping(request):
chirac's avatar
chirac committed
227
    """Affichage de la liste des stacks (affiche l'ensemble des switches)"""
Maël Kervella's avatar
Maël Kervella committed
228
229
230
231
    stack_list = (Stack.objects
                  .prefetch_related(
                      'switch_set__interface_set__domain__extension'
                  ))
232
233
    building_list = Building.objects.all()
    switch_bay_list = SwitchBay.objects.select_related('building')
234
235
236
237
238
239
    stack_list = SortTable.sort(
        stack_list,
        request.GET.get('col'),
        request.GET.get('order'),
        SortTable.TOPOLOGIE_INDEX_STACK
    )
240
241
242
243
244
245
246
247
248
249
250
251
    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
    )
Maël Kervella's avatar
Maël Kervella committed
252
253
254
255
256
257
258
259
260
    return render(
        request,
        'topologie/index_physical_grouping.html',
        {
            'stack_list': stack_list,
            'switch_bay_list': switch_bay_list,
            'building_list': building_list,
        }
    )
261
262


263
@login_required
264
@can_view_all(ModelSwitch, ConstructorSwitch)
265
266
def index_model_switch(request):
    """ Affichage de l'ensemble des modèles de switches"""
267
    model_switch_list = ModelSwitch.objects.select_related('constructor')
268
269
270
271
272
273
274
275
276
277
278
279
280
    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
    )
Maël Kervella's avatar
Maël Kervella committed
281
282
283
284
285
286
287
288
    return render(
        request,
        'topologie/index_model_switch.html',
        {
            'model_switch_list': model_switch_list,
            'constructor_switch_list': constructor_switch_list,
        }
    )
289
290


chirac's avatar
chirac committed
291
@login_required
292
@can_create(Port)
293
def new_port(request, switchid):
Gabriel Detraz's avatar
Gabriel Detraz committed
294
    """ Nouveau port"""
Dalahro's avatar
Dalahro committed
295
    try:
296
        switch = Switch.objects.get(pk=switchid)
Dalahro's avatar
Dalahro committed
297
298
    except Switch.DoesNotExist:
        messages.error(request, u"Switch inexistant")
299
        return redirect(reverse('topologie:index'))
Dalahro's avatar
Dalahro committed
300
301
302
303
304
    port = AddPortForm(request.POST or None)
    if port.is_valid():
        port = port.save(commit=False)
        port.switch = switch
        try:
305
            port.save()
Dalahro's avatar
Dalahro committed
306
307
            messages.success(request, "Port ajouté")
        except IntegrityError:
chirac's avatar
chirac committed
308
            messages.error(request, "Ce port existe déjà")
309
        return redirect(reverse(
310
            'topologie:index-port',
Maël Kervella's avatar
Maël Kervella committed
311
312
313
314
315
316
            kwargs={'switchid': switchid}
        ))
    return form(
        {'id_switch': switchid, 'topoform': port, 'action_name': 'Ajouter'},
        'topologie/topo.html',
        request)
chirac's avatar
chirac committed
317

Dalahro's avatar
Dalahro committed
318

chirac's avatar
chirac committed
319
@login_required
320
@can_edit(Port)
321
def edit_port(request, port_object, **_kwargs):
chirac's avatar
chirac committed
322
323
    """ Edition d'un port. Permet de changer le switch parent et
    l'affectation du port"""
324

325
    port = EditPortForm(request.POST or None, instance=port_object)
Dalahro's avatar
Dalahro committed
326
    if port.is_valid():
327
328
329
        if port.changed_data:
            port.save()
            messages.success(request, "Le port a bien été modifié")
330
331
        return redirect(reverse(
            'topologie:index-port',
332
            kwargs={'switchid': str(port_object.switch.id)}
Maël Kervella's avatar
Maël Kervella committed
333
334
335
336
337
338
339
340
341
342
        ))
    return form(
        {
            'id_switch': str(port_object.switch.id),
            'topoform': port,
            'action_name': 'Editer'
        },
        'topologie/topo.html',
        request
    )
chirac's avatar
chirac committed
343

chirac's avatar
chirac committed
344

345
@login_required
346
@can_delete(Port)
347
def del_port(request, port, **_kwargs):
Gabriel Detraz's avatar
Gabriel Detraz committed
348
    """ Supprime le port"""
Gabriel Detraz's avatar
Gabriel Detraz committed
349
350
    if request.method == "POST":
        try:
351
352
            port.delete()
            messages.success(request, "Le port a été détruit")
Gabriel Detraz's avatar
Gabriel Detraz committed
353
        except ProtectedError:
Maël Kervella's avatar
Maël Kervella committed
354
355
356
357
358
            messages.error(
                request,
                ("Le port %s est affecté à un autre objet, impossible "
                 "de le supprimer" % port)
            )
359
360
        return redirect(reverse(
            'topologie:index-port',
Maël Kervella's avatar
Maël Kervella committed
361
362
            kwargs={'switchid': str(port.switch.id)}
        ))
chirac's avatar
chirac committed
363
364
    return form({'objet': port}, 'topologie/delete.html', request)

Gabriel Detraz's avatar
Gabriel Detraz committed
365
366

@login_required
367
@can_create(Stack)
368
def new_stack(request):
369
    """Ajoute un nouveau stack : stackid_min, max, et nombre de switches"""
370
    stack = StackForm(request.POST or None)
chirac's avatar
chirac committed
371
    if stack.is_valid():
372
        stack.save()
chirac's avatar
chirac committed
373
        messages.success(request, "Stack crée")
Gabriel Detraz's avatar
Gabriel Detraz committed
374
        return redirect(reverse('topologie:index-physical-grouping'))
Maël Kervella's avatar
Maël Kervella committed
375
376
377
378
379
    return form(
        {'topoform': stack, 'action_name': 'Créer'},
        'topologie/topo.html',
        request
    )
380
381
382


@login_required
383
@can_edit(Stack)
384
def edit_stack(request, stack, **_kwargs):
chirac's avatar
chirac committed
385
    """Edition d'un stack (nombre de switches, nom...)"""
386
387
    stack = StackForm(request.POST or None, instance=stack)
    if stack.is_valid():
388
389
        if stack.changed_data:
            stack.save()
Gabriel Detraz's avatar
Gabriel Detraz committed
390
        return redirect(reverse('topologie:index-physical-grouping'))
Maël Kervella's avatar
Maël Kervella committed
391
392
393
394
395
    return form(
        {'topoform': stack, 'action_name': 'Editer'},
        'topologie/topo.html',
        request
    )
chirac's avatar
chirac committed
396

397

guimoz's avatar
guimoz committed
398
@login_required
399
@can_delete(Stack)
400
def del_stack(request, stack, **_kwargs):
chirac's avatar
chirac committed
401
    """Supprime un stack"""
guimoz's avatar
guimoz committed
402
403
    if request.method == "POST":
        try:
404
405
            stack.delete()
            messages.success(request, "La stack a eté détruite")
guimoz's avatar
guimoz committed
406
        except ProtectedError:
Maël Kervella's avatar
Maël Kervella committed
407
408
409
410
411
            messages.error(
                request,
                ("La stack %s est affectée à un autre objet, impossible "
                 "de la supprimer" % stack)
            )
412
        return redirect(reverse('topologie:index-physical-grouping'))
chirac's avatar
chirac committed
413
414
    return form({'objet': stack}, 'topologie/delete.html', request)

guimoz's avatar
guimoz committed
415

416
@login_required
417
@can_edit(Stack)
418
def edit_switchs_stack(request, stack, **_kwargs):
chirac's avatar
chirac committed
419
    """Permet d'éditer la liste des switches dans une stack et l'ajouter"""
420

421
422
423
424
425
426
427
428
    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
429
@login_required
430
@can_create(Switch)
Dalahro's avatar
Dalahro committed
431
def new_switch(request):
chirac's avatar
chirac committed
432
433
434
    """ 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"""
Gabriel Detraz's avatar
Gabriel Detraz committed
435
    switch = NewSwitchForm(
436
437
438
        request.POST or None,
        user=request.user
    )
Gabriel Detraz's avatar
Gabriel Detraz committed
439
    interface = AddInterfaceForm(
chirac's avatar
chirac committed
440
        request.POST or None,
441
        user=request.user
Gabriel Detraz's avatar
Gabriel Detraz committed
442
    )
443
    domain = DomainForm(
chirac's avatar
chirac committed
444
445
        request.POST or None,
        )
Gabriel Detraz's avatar
Gabriel Detraz committed
446
    if switch.is_valid() and interface.is_valid():
447
        user = AssoOption.get_cached_value('utilisateur_asso')
448
        if not user:
Maël Kervella's avatar
Maël Kervella committed
449
450
451
452
453
            messages.error(
                request,
                ("L'user association n'existe pas encore, veuillez le "
                 "créer ou le linker dans preferences")
            )
454
            return redirect(reverse('topologie:index'))
455
456
457
458
        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
Gabriel Detraz's avatar
Gabriel Detraz committed
459
        if domain.is_valid():
460
461
462
463
464
465
            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()
Gabriel Detraz's avatar
Gabriel Detraz committed
466
467
            messages.success(request, "Le switch a été créé")
            return redirect(reverse('topologie:index'))
Gabriel Detraz's avatar
Gabriel Detraz committed
468
    i_mbf_param = generate_ipv4_mbf_param(interface, False)
Maël Kervella's avatar
Maël Kervella committed
469
470
471
472
473
474
475
476
477
478
479
    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
480

Dalahro's avatar
Dalahro committed
481

482
@login_required
483
@can_create(Port)
484
def create_ports(request, switchid):
485
486
    """ Création d'une liste de ports pour un switch."""
    try:
487
        switch = Switch.objects.get(pk=switchid)
488
489
    except Switch.DoesNotExist:
        messages.error(request, u"Switch inexistant")
490
        return redirect(reverse('topologie:index'))
491

492
    s_begin = s_end = 0
LEVY-FALK Hugo's avatar
LEVY-FALK Hugo committed
493
494
    nb_ports = switch.ports.count()
    if nb_ports > 0:
LEVY-FALK Hugo's avatar
LEVY-FALK Hugo committed
495
496
497
        ports = switch.ports.order_by('port').values('port')
        s_begin = ports.first().get('port')
        s_end = ports.last().get('port')
498

499
500
    port_form = CreatePortsForm(
        request.POST or None,
Maël Kervella's avatar
Maël Kervella committed
501
        initial={'begin': s_begin, 'end': s_end}
502
    )
503

504
505
506
    if port_form.is_valid():
        begin = port_form.cleaned_data['begin']
        end = port_form.cleaned_data['end']
507
508
509
510
511
        try:
            switch.create_ports(begin, end)
            messages.success(request, "Ports créés.")
        except ValidationError as e:
            messages.error(request, ''.join(e))
512
513
        return redirect(reverse(
            'topologie:index-port',
Maël Kervella's avatar
Maël Kervella committed
514
            kwargs={'switchid': switchid}
515
            ))
Maël Kervella's avatar
Maël Kervella committed
516
517
518
519
520
    return form(
        {'id_switch': switchid, 'topoform': port_form},
        'topologie/switch.html',
        request
    )
Maël Kervella's avatar
Maël Kervella committed
521

Dalahro's avatar
Dalahro committed
522

chirac's avatar
chirac committed
523
@login_required
524
@can_edit(Switch)
525
def edit_switch(request, switch, switchid):
chirac's avatar
chirac committed
526
527
    """ Edition d'un switch. Permet de chambre nombre de ports,
    place dans le stack, interface et machine associée"""
528

Gabriel Detraz's avatar
Gabriel Detraz committed
529
    switch_form = EditSwitchForm(
chirac's avatar
chirac committed
530
        request.POST or None,
Gabriel Detraz's avatar
Gabriel Detraz committed
531
        instance=switch,
532
        user=request.user
chirac's avatar
chirac committed
533
        )
Gabriel Detraz's avatar
Gabriel Detraz committed
534
    interface_form = EditInterfaceForm(
chirac's avatar
chirac committed
535
        request.POST or None,
Gabriel Detraz's avatar
Gabriel Detraz committed
536
        instance=switch.interface_set.first(),
537
        user=request.user
chirac's avatar
chirac committed
538
        )
539
    domain_form = DomainForm(
chirac's avatar
chirac committed
540
        request.POST or None,
Gabriel Detraz's avatar
Gabriel Detraz committed
541
        instance=switch.interface_set.first().domain
chirac's avatar
chirac committed
542
        )
Gabriel Detraz's avatar
Gabriel Detraz committed
543
    if switch_form.is_valid() and interface_form.is_valid():
544
545
546
        new_switch_obj = switch_form.save(commit=False)
        new_interface_obj = interface_form.save(commit=False)
        new_domain_obj = domain_form.save(commit=False)
547
        if switch_form.changed_data:
548
            new_switch_obj.save()
549
        if interface_form.changed_data:
550
            new_interface_obj.save()
551
        if domain_form.changed_data:
552
            new_domain_obj.save()
553
        messages.success(request, "Le switch a bien été modifié")
LEVY-FALK Hugo's avatar
LEVY-FALK Hugo committed
554
        return redirect(reverse('topologie:index'))
Maël Kervella's avatar
Maël Kervella committed
555
556
557
558
559
560
561
562
563
564
565
566
567
    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
568

chirac's avatar
chirac committed
569

Gabriel Detraz's avatar
Gabriel Detraz committed
570
@login_required
Gabriel Detraz's avatar
Gabriel Detraz committed
571
572
573
@can_create(AccessPoint)
def new_ap(request):
    """ Creation d'une ap. Cree en meme temps l'interface et la machine
Gabriel Detraz's avatar
Gabriel Detraz committed
574
575
    associée. Vue complexe. Appelle successivement les 3 models forms
    adaptés : machine, interface, domain et switch"""
Gabriel Detraz's avatar
Gabriel Detraz committed
576
    ap = AddAccessPointForm(
Gabriel Detraz's avatar
Gabriel Detraz committed
577
578
579
        request.POST or None,
        user=request.user
    )
Gabriel Detraz's avatar
Gabriel Detraz committed
580
    interface = AddInterfaceForm(
Gabriel Detraz's avatar
Gabriel Detraz committed
581
582
583
584
585
586
        request.POST or None,
        user=request.user
    )
    domain = DomainForm(
        request.POST or None,
        )
Gabriel Detraz's avatar
Gabriel Detraz committed
587
    if ap.is_valid() and interface.is_valid():
Gabriel Detraz's avatar
Gabriel Detraz committed
588
589
        user = AssoOption.get_cached_value('utilisateur_asso')
        if not user:
Maël Kervella's avatar
Maël Kervella committed
590
591
592
593
594
            messages.error(
                request,
                ("L'user association n'existe pas encore, veuillez le "
                 "créer ou le linker dans preferences")
            )
Gabriel Detraz's avatar
Gabriel Detraz committed
595
            return redirect(reverse('topologie:index'))
596
597
598
599
        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
Gabriel Detraz's avatar
Gabriel Detraz committed
600
        if domain.is_valid():
601
602
603
604
605
606
            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()
Gabriel Detraz's avatar
Gabriel Detraz committed
607
            messages.success(request, "La borne a été créé")
Gabriel Detraz's avatar
Gabriel Detraz committed
608
            return redirect(reverse('topologie:index-ap'))
Gabriel Detraz's avatar
Gabriel Detraz committed
609
    i_mbf_param = generate_ipv4_mbf_param(interface, False)
Maël Kervella's avatar
Maël Kervella committed
610
611
612
613
614
615
616
617
618
619
620
    return form(
        {
            'topoform': interface,
            'machineform': ap,
            'domainform': domain,
            'i_mbf_param': i_mbf_param,
            'device': 'wifi ap',
        },
        'topologie/topo_more.html',
        request
    )
Gabriel Detraz's avatar
Gabriel Detraz committed
621
622
623


@login_required
Gabriel Detraz's avatar
Gabriel Detraz committed
624
@can_edit(AccessPoint)
625
def edit_ap(request, ap, **_kwargs):
Gabriel Detraz's avatar
Gabriel Detraz committed
626
627
    """ Edition d'un switch. Permet de chambre nombre de ports,
    place dans le stack, interface et machine associée"""
Gabriel Detraz's avatar
Gabriel Detraz committed
628
    interface_form = EditInterfaceForm(
Gabriel Detraz's avatar
Gabriel Detraz committed
629
630
        request.POST or None,
        user=request.user,
Gabriel Detraz's avatar
Gabriel Detraz committed
631
        instance=ap.interface_set.first()
Gabriel Detraz's avatar
Gabriel Detraz committed
632
    )
Gabriel Detraz's avatar
Gabriel Detraz committed
633
    ap_form = EditAccessPointForm(
Gabriel Detraz's avatar
Gabriel Detraz committed
634
635
        request.POST or None,
        user=request.user,
Gabriel Detraz's avatar
Gabriel Detraz committed
636
        instance=ap
Gabriel Detraz's avatar
Gabriel Detraz committed
637
638
639
    )
    domain_form = DomainForm(
        request.POST or None,
Gabriel Detraz's avatar
Gabriel Detraz committed
640
        instance=ap.interface_set.first().domain
Gabriel Detraz's avatar
Gabriel Detraz committed
641
        )
Gabriel Detraz's avatar
Gabriel Detraz committed
642
    if ap_form.is_valid() and interface_form.is_valid():
Gabriel Detraz's avatar
Gabriel Detraz committed
643
644
        user = AssoOption.get_cached_value('utilisateur_asso')
        if not user:
Maël Kervella's avatar
Maël Kervella committed
645
646
647
648
649
            messages.error(
                request,
                ("L'user association n'existe pas encore, veuillez le "
                 "créer ou le linker dans preferences")
            )
Gabriel Detraz's avatar
Gabriel Detraz committed
650
            return redirect(reverse('topologie:index-ap'))
651
652
653
        new_ap_obj = ap_form.save(commit=False)
        new_interface_obj = interface_form.save(commit=False)
        new_domain_obj = domain_form.save(commit=False)
654
        if ap_form.changed_data:
655
            new_ap_obj.save()
656
        if interface_form.changed_data:
657
            new_interface_obj.save()
658
        if domain_form.changed_data:
659
            new_domain_obj.save()
Gabriel Detraz's avatar
Gabriel Detraz committed
660
        messages.success(request, "La borne a été modifiée")
Gabriel Detraz's avatar
Gabriel Detraz committed
661
        return redirect(reverse('topologie:index-ap'))
Maël Kervella's avatar
Maël Kervella committed
662
663
664
665
666
667
668
669
670
671
672
673
674
    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
    )

Gabriel Detraz's avatar
Gabriel Detraz committed
675

chirac's avatar
chirac committed
676
@login_required
677
@can_create(Room)
chirac's avatar
chirac committed
678
def new_room(request):
Gabriel Detraz's avatar
Gabriel Detraz committed
679
    """Nouvelle chambre """
chirac's avatar
chirac committed
680
681
    room = EditRoomForm(request.POST or None)
    if room.is_valid():
682
        room.save()
chirac's avatar
chirac committed
683
        messages.success(request, "La chambre a été créé")
684
        return redirect(reverse('topologie:index-room'))
Maël Kervella's avatar
Maël Kervella committed
685
686
687
688
689
    return form(
        {'topoform': room, 'action_name': 'Ajouter'},
        'topologie/topo.html',
        request
    )
chirac's avatar
chirac committed
690

chirac's avatar
chirac committed
691
692

@login_required
693
@can_edit(Room)
694
def edit_room(request, room, **_kwargs):
Gabriel Detraz's avatar
Gabriel Detraz committed
695
    """ Edition numero et details de la chambre"""
chirac's avatar
chirac committed
696
697
    room = EditRoomForm(request.POST or None, instance=room)
    if room.is_valid():
698
699
700
        if room.changed_data:
            room.save()
            messages.success(request, "La chambre a bien été modifiée")
701
        return redirect(reverse('topologie:index-room'))
Maël Kervella's avatar
Maël Kervella committed
702
703
704
705
706
    return form(
        {'topoform': room, 'action_name': 'Editer'},
        'topologie/topo.html',
        request
    )
chirac's avatar
chirac committed
707

chirac's avatar
chirac committed
708
709

@login_required
710
@can_delete(Room)
711
def del_room(request, room, **_kwargs):
Gabriel Detraz's avatar
Gabriel Detraz committed
712
    """ Suppression d'un chambre"""
chirac's avatar
chirac committed
713
    if request.method == "POST":
714
        try:
715
716
            room.delete()
            messages.success(request, "La chambre/prise a été détruite")
717
        except ProtectedError:
Maël Kervella's avatar
Maël Kervella committed
718
719
720
721
722
            messages.error(
                request,
                ("La chambre %s est affectée à un autre objet, impossible "
                 "de la supprimer (switch ou user)" % room)
            )
723
        return redirect(reverse('topologie:index-room'))
Maël Kervella's avatar
Maël Kervella committed
724
725
726
727
728
    return form(
        {'objet': room, 'objet_name': 'Chambre'},
        'topologie/delete.html',
        request
    )
729
730
731


@login_required
732
@can_create(ModelSwitch)
733
734
735
736
def new_model_switch(request):
    """Nouveau modèle de switch"""
    model_switch = EditModelSwitchForm(request.POST or None)
    if model_switch.is_valid():
737
        model_switch.save()
738
        messages.success(request, "Le modèle a été créé")
739
        return redirect(reverse('topologie:index-model-switch'))
Maël Kervella's avatar
Maël Kervella committed
740
741
742
743
744
    return form(
        {'topoform': model_switch, 'action_name': 'Ajouter'},
        'topologie/topo.html',
        request
    )
745
746
747


@login_required
748
@can_edit(ModelSwitch)
749
def edit_model_switch(request, model_switch, **_kwargs):
750
    """ Edition d'un modèle de switch"""
751

Maël Kervella's avatar
Maël Kervella committed
752
753
754
755
    model_switch = EditModelSwitchForm(
        request.POST or None,
        instance=model_switch
    )
756
    if model_switch.is_valid():
757
758
759
        if model_switch.changed_data:
            model_switch.save()
            messages.success(request, "Le modèle a bien été modifié")
760
        return redirect(reverse('topologie:index-model-switch'))
Maël Kervella's avatar
Maël Kervella committed
761
762
763
764
765
    return form(
        {'topoform': model_switch, 'action_name': 'Editer'},
        'topologie/topo.html',
        request
    )
766
767
768


@login_required
769
@can_delete(ModelSwitch)
770
def del_model_switch(request, model_switch, **_kwargs):
771
772
773
    """ Suppression d'un modèle de switch"""
    if request.method == "POST":
        try:
774
775
            model_switch.delete()
            messages.success(request, "Le modèle a été détruit")
776
        except ProtectedError:
Maël Kervella's avatar
Maël Kervella committed
777
778
779
780
781
            messages.error(
                request,
                ("Le modèle %s est affectée à un autre objet, impossible "
                 "de la supprimer (switch ou user)" % model_switch)
            )
782
        return redirect(reverse('topologie:index-model-switch'))
Maël Kervella's avatar
Maël Kervella committed
783
784
785
786
787
    return form(
        {'objet': model_switch, 'objet_name': 'Modèle de switch'},
        'topologie/delete.html',
        request
    )
788
789


790
791
792
793
794
795
796
797
@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éé")
798
        return redirect(reverse('topologie:index-physical-grouping'))
Maël Kervella's avatar
Maël Kervella committed
799
800
801
802
803
    return form(
        {'topoform': switch_bay, 'action_name': 'Ajouter'},
        'topologie/topo.html',
        request
    )
804
805
806
807


@login_required
@can_edit(SwitchBay)
808
def edit_switch_bay(request, switch_bay, **_kwargs):
809
810
811
812
813
814
    """ 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é")
815
        return redirect(reverse('topologie:index-physical-grouping'))
Maël Kervella's avatar
Maël Kervella committed
816
817
818
819
820
    return form(
        {'topoform': switch_bay, 'action_name': 'Editer'},
        'topologie/topo.html',
        request
    )
821
822
823
824


@login_required
@can_delete(SwitchBay)
825
def del_switch_bay(request, switch_bay, **_kwargs):
826
827
828
829
830
831
    """ 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:
Maël Kervella's avatar
Maël Kervella committed
832
833
834
835
836
            messages.error(
                request,
                ("La baie %s est affecté à un autre objet, impossible "
                 "de la supprimer (switch ou user)" % switch_bay)
            )
837
        return redirect(reverse('topologie:index-physical-grouping'))
Maël Kervella's avatar
Maël Kervella committed
838
839
840
841
842
    return form(
        {'objet': switch_bay, 'objet_name': 'Baie de switch'},
        'topologie/delete.html',
        request
    )
843
844


845
846
847
848
849
850
851
852
@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éé")
853
        return redirect(reverse('topologie:index-physical-grouping'))
Maël Kervella's avatar
Maël Kervella committed
854
855
856
857
858
    return form(
        {'topoform': building, 'action_name': 'Ajouter'},
        'topologie/topo.html',
        request
    )
859
860
861
862


@login_required
@can_edit(Building)
863
def edit_building(request, building, **_kwargs):
864
865
866
867
868
869
    """ 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é")
870
        return redirect(reverse('topologie:index-physical-grouping'))
Maël Kervella's avatar
Maël Kervella committed
871
872
873
874
875
    return form(
        {'topoform': building, 'action_name': 'Editer'},
        'topologie/topo.html',
        request
    )
876
877
878
879


@login_required
@can_delete(Building)
880
def del_building(request, building, **_kwargs):
881
882
883
884
885
886
    """ Suppression d'un batiment"""
    if request.method == "POST":
        try:
            building.delete()
            messages.success(request, "La batiment a été détruit")
        except ProtectedError:
Maël Kervella's avatar
Maël Kervella committed
887
888
889
890
891
            messages.error(
                request,
                ("Le batiment %s est affecté à un autre objet, impossible "
                 "de la supprimer (switch ou user)" % building)
            )
892
        return redirect(reverse('topologie:index-physical-grouping'))
Maël Kervella's avatar
Maël Kervella committed
893
894
895
896
897
    return form(
        {'objet': building, 'objet_name': 'Bâtiment'},
        'topologie/delete.html',
        request
    )
898
899


900
@login_required
901
@can_create(ConstructorSwitch)
902
903
904
905
def new_constructor_switch(request):
    """Nouveau constructeur de switch"""
    constructor_switch = EditConstructorSwitchForm(request.POST or None)
    if constructor_switch.is_valid():
906
        constructor_switch.save()
907
        messages.success(request, "Le constructeur a été créé")
908
        return redirect(reverse('topologie:index-model-switch'))
Maël Kervella's avatar
Maël Kervella committed
909
910
911
912
913
    return form(
        {'topoform': constructor_switch, 'action_name': 'Ajouter'},
        'topologie/topo.html',
        request
    )
914
915
916


@login_required
917
@can_edit(ConstructorSwitch)
918
def edit_constructor_switch(request, constructor_switch, **_kwargs):
919
    """ Edition d'un constructeur de switch"""
920

Maël Kervella's avatar
Maël Kervella committed
921
922
923
924
    constructor_switch = EditConstructorSwitchForm(
        request.POST or None,
        instance=constructor_switch
    )
925
    if constructor_switch.is_valid():
926
927
928
        if constructor_switch.changed_data:
            constructor_switch.save()
            messages.success(request, "Le modèle a bien été modifié")
929
        return redirect(reverse('topologie:index-model-switch'))
Maël Kervella's avatar
Maël Kervella committed
930
931
932
933
934
    return form(
        {'topoform': constructor_switch, 'action_name': 'Editer'},
        'topologie/topo.html',
        request
    )
935
936
937


@login_required
938
@can_delete(ConstructorSwitch)
939
def del_constructor_switch(request, constructor_switch, **_kwargs):
940
941
942
    """ Suppression d'un constructeur de switch"""
    if request.method == "POST":
        try:
943
944
            constructor_switch.delete()
            messages.success(request, "Le constructeur a été détruit")
945
        except ProtectedError:
Maël Kervella's avatar
Maël Kervella committed
946
947
948
949
950
            messages.error(
                request,
                ("Le constructeur %s est affecté à un autre objet, impossible "
                 "de la supprimer (switch ou user)" % constructor_switch)
            )
951
        return redirect(reverse('topologie:index-model-switch'))
952
953
954
955
    return form({
        'objet': constructor_switch,
        'objet_name': 'Constructeur de switch'
        }, 'topologie/delete.html', request)
956
957
958
959


def make_machine_graph():
    """
grisel-davy's avatar
grisel-davy committed
960
    Create the graph of switchs, machines and access points.
961
    """
grisel-davy's avatar
grisel-davy committed
962
963
964
965
966
    dico = {
        'subs': [],
        'links' : [],
        'alone': [],
        'colors': {
grisel-davy's avatar
grisel-davy committed
967
            'head': "#7f0505",  # Color parameters for the graph
grisel-davy's avatar
grisel-davy committed
968
969
970
971
972
973
974
975
976
            'back': "#b5adad",
            'texte': "#563d01",
            'border_bornes': "#02078e",
            'head_bornes': "#25771c",
            'head_server': "#1c3777"
            }
        }
    missing = list(Switch.objects.all())
    detected = []
grisel-davy's avatar
grisel-davy committed
977
978
    for building in Building.objects.all():  # Visit all buildings

grisel-davy's avatar
grisel-davy committed
979
980
981
982
983
984
985
986
987
        dico['subs'].append(
            {
            'bat_id': building.id,
            'bat_name': building,
            'switchs': [],
            'bornes': [],
            'machines': []
            }
        )
grisel-davy's avatar
grisel-davy committed
988
989
        # Visit all switchs in this building
        for switch in Switch.objects.filter(switchbay__building=building):   
grisel-davy's avatar
grisel-davy committed
990
991
992
993
994
995
996
997
            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
998
            # visit all ports of this switch and add the switchs linked to it
grisel-davy's avatar
grisel-davy committed
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
            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
            })

grisel-davy's avatar
grisel-davy committed
1020
    # While the list of forgotten ones is not empty
grisel-davy's avatar
grisel-davy committed
1021
    while missing:
grisel-davy's avatar
grisel-davy committed
1022
        if missing[0].ports.count():  # The switch is not empty
grisel-davy's avatar
grisel-davy committed
1023
            links, new_detected = recursive_switchs(missing[0], None, [missing[0]])
grisel-davy's avatar
grisel-davy committed
1024
1025
            for link in links:
                dico['links'].append(link)
grisel-davy's avatar
grisel-davy committed
1026
            # Update the lists of missings and already detected switchs
grisel-davy's avatar
grisel-davy committed
1027
1028
            missing=[i for i in missing if i not in new_detected]
            detected += new_detected
grisel-davy's avatar
grisel-davy committed
1029
        else:  # If the switch have no ports, don't explore it and hop to the next one
grisel-davy's avatar
grisel-davy committed
1030
            del missing[0]