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 59.1 KB
Newer Older
1
# -*- mode: python; coding: utf-8 -*-
2 3 4 5
# 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.
#
6
# Copyright © 2016-2018  Gabriel Détraz
7 8
# Copyright © 2017  Goulven Kermarec
# Copyright © 2017  Augustin Lemesle
9 10
# Copyright © 2017-2018  Maël Kervella
# Copyright © 2018  Charlie Jacomme
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
#
# 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.

26
# App de gestion des machines pour re2o
Dalahro's avatar
Dalahro committed
27
# Gabriel Détraz, Augustin Lemesle
28
# Gplv2
29 30 31
"""machines.views
The views for the Machines app
"""
32 33 34

from __future__ import unicode_literals

35
from django.urls import reverse
36
from django.http import HttpResponse, HttpResponseRedirect
37 38
from django.shortcuts import render, redirect
from django.contrib import messages
39
from django.contrib.auth.decorators import login_required, permission_required
40
from django.db.models import ProtectedError, F
41
from django.forms import modelformset_factory
Dalahro's avatar
Dalahro committed
42
from django.views.decorators.csrf import csrf_exempt
43
from django.utils.translation import ugettext as _
44

Dalahro's avatar
Dalahro committed
45
from rest_framework.renderers import JSONRenderer
46 47 48 49 50 51 52 53 54 55 56 57

from users.models import User
from preferences.models import GeneralOption
from re2o.utils import (
    all_active_assigned_interfaces,
    filter_active_interfaces,
    SortTable,
    re2o_paginator,
)
from re2o.acl import (
    can_create,
    can_edit,
58
    can_view,
59 60 61 62 63 64 65
    can_delete,
    can_view_all,
    can_delete_set,
)
from re2o.views import form

from .serializers import (
66
    FullInterfaceSerializer,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
67 68 69
    InterfaceSerializer,
    TypeSerializer,
    DomainSerializer,
Gabriel Detraz's avatar
Gabriel Detraz committed
70
    TxtSerializer,
Gabriel Detraz's avatar
Gabriel Detraz committed
71
    SrvSerializer,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
72 73 74 75 76
    MxSerializer,
    ExtensionSerializer,
    ServiceServersSerializer,
    NsSerializer,
)
Dalahro's avatar
Dalahro committed
77

Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
from .forms import (
    NewMachineForm,
    EditMachineForm,
    EditInterfaceForm,
    AddInterfaceForm,
    MachineTypeForm,
    DelMachineTypeForm,
    ExtensionForm,
    DelExtensionForm,
    EditIpTypeForm,
    IpTypeForm,
    DelIpTypeForm,
    DomainForm,
    AliasForm,
    DelAliasForm,
93 94
    SOAForm,
    DelSOAForm,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
95 96
    NsForm,
    DelNsForm,
97 98
    TxtForm,
    DelTxtForm,
Charlie Jacomme's avatar
Charlie Jacomme committed
99 100
    DNameForm,
    DelDNameForm,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
101 102 103 104
    MxForm,
    DelMxForm,
    VlanForm,
    DelVlanForm,
105 106
    RoleForm,
    DelRoleForm,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
107 108
    ServiceForm,
    DelServiceForm,
109
    SshFpForm,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
110
    NasForm,
Gabriel Detraz's avatar
Gabriel Detraz committed
111 112 113
    DelNasForm,
    SrvForm,
    DelSrvForm,
114
    Ipv6ListForm,
115
    EditOuverturePortListForm,
116
    EditOuverturePortConfigForm,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
117 118 119 120 121 122 123
)
from .models import (
    IpType,
    Machine,
    Interface,
    MachineType,
    Extension,
124
    SOA,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
125 126 127
    Mx,
    Ns,
    Domain,
128
    Role,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
129 130
    Service,
    Service_link,
131
    regen,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
132 133
    Vlan,
    Nas,
Gabriel Detraz's avatar
Gabriel Detraz committed
134
    Txt,
Charlie Jacomme's avatar
Charlie Jacomme committed
135
    DName,
Gabriel Detraz's avatar
Gabriel Detraz committed
136
    Srv,
137
    SshFp,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
138
    OuverturePortList,
Gabriel Detraz's avatar
Gabriel Detraz committed
139
    OuverturePort,
140
    Ipv6List,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
141
)
142

143 144

def f_type_id(is_type_tt):
145 146 147
    """ The id that will be used in HTML to store the value of the field
    type. Depends on the fact that type is generate using typeahead or not
    """
148
    return 'id_Interface-type_hidden' if is_type_tt else 'id_Interface-type'
149

150

151
def generate_ipv4_choices(form_obj):
152
    """ Generate the parameter choices for the massive_bootstrap_form tag
153
    """
154
    f_ipv4 = form_obj.fields['ipv4']
155
    used_mtype_id = []
156
    choices = '{"":[{key:"",value:"'+_("Select a machine type first.") + '"}'
157 158
    mtype_id = -1

159 160 161 162
    for ip in (f_ipv4.queryset
               .annotate(mtype_id=F('ip_type__machinetype__id'))
               .order_by('mtype_id', 'id')):
        if mtype_id != ip.mtype_id:
163 164
            mtype_id = ip.mtype_id
            used_mtype_id.append(mtype_id)
165
            choices += '],"{t}":[{{key:"",value:"{v}"}},'.format(
166 167
                t=mtype_id,
                v=f_ipv4.empty_label or '""'
168 169
            )
        choices += '{{key:{k},value:"{v}"}},'.format(
170 171
            k=ip.id,
            v=ip.ipv4
172
        )
173

174
    for t in form_obj.fields['type'].queryset.exclude(id__in=used_mtype_id):
175 176 177 178 179
        choices += '], "'+str(t.id)+'": ['
        choices += '{key: "", value: "' + str(f_ipv4.empty_label) + '"},'
    choices += ']}'
    return choices

180 181

def generate_ipv4_engine(is_type_tt):
182
    """ Generate the parameter engine for the massive_bootstrap_form tag
183
    """
184 185
    return (
        'new Bloodhound( {{'
186 187 188 189
        'datumTokenizer: Bloodhound.tokenizers.obj.whitespace( "value" ),'
        'queryTokenizer: Bloodhound.tokenizers.whitespace,'
        'local: choices_ipv4[ $( "#{type_id}" ).val() ],'
        'identify: function( obj ) {{ return obj.key; }}'
190
        '}} )'
191 192 193
    ).format(
        type_id=f_type_id(is_type_tt)
    )
194

195 196

def generate_ipv4_match_func(is_type_tt):
197
    """ Generate the parameter match_func for the massive_bootstrap_form tag
198
    """
199 200
    return (
        'function(q, sync) {{'
201 202 203 204 205 206
        'if (q === "") {{'
        'var first = choices_ipv4[$("#{type_id}").val()].slice(0, 5);'
        'first = first.map( function (obj) {{ return obj.key; }} );'
        'sync(engine_ipv4.get(first));'
        '}} else {{'
        'engine_ipv4.search(q, sync);'
207
        '}}'
208 209 210 211
        '}}'
    ).format(
        type_id=f_type_id(is_type_tt)
    )
212

213

214
def generate_ipv4_mbf_param(form_obj, is_type_tt):
215
    """ Generate all the parameters to use with the massive_bootstrap_form
216
    tag """
217
    i_choices = {'ipv4': generate_ipv4_choices(form_obj)}
218 219 220 221
    i_engine = {'ipv4': generate_ipv4_engine(is_type_tt)}
    i_match_func = {'ipv4': generate_ipv4_match_func(is_type_tt)}
    i_update_on = {'ipv4': [f_type_id(is_type_tt)]}
    i_gen_select = {'ipv4': False}
222
    i_mbf_param = {
223 224 225
        'choices': i_choices,
        'engine': i_engine,
        'match_func': i_match_func,
Maël Kervella's avatar
Maël Kervella committed
226 227
        'update_on': i_update_on,
        'gen_select': i_gen_select
228
    }
229
    return i_mbf_param
230

231

chirac's avatar
chirac committed
232
@login_required
233
@can_create(Machine)
234
@can_edit(User)
235
def new_machine(request, user, **_kwargs):
236
    """ Fonction de creation d'une machine. Cree l'objet machine,
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
237
    le sous objet interface et l'objet domain à partir de model forms.
Gabriel Detraz's avatar
Gabriel Detraz committed
238
    Trop complexe, devrait être simplifié"""
Maël Kervella's avatar
Maël Kervella committed
239

240
    machine = NewMachineForm(request.POST or None, user=request.user)
Maël Kervella's avatar
Maël Kervella committed
241 242
    interface = AddInterfaceForm(
        request.POST or None,
243
        user=request.user
Maël Kervella's avatar
Maël Kervella committed
244
    )
245
    domain = DomainForm(request.POST or None, user=user)
246
    if machine.is_valid() and interface.is_valid():
247 248 249 250
        new_machine_obj = machine.save(commit=False)
        new_machine_obj.user = user
        new_interface_obj = interface.save(commit=False)
        domain.instance.interface_parent = new_interface_obj
251
        if domain.is_valid():
252
            new_domain = domain.save(commit=False)
253 254 255 256
            new_machine_obj.save()
            new_interface_obj.machine = new_machine_obj
            new_interface_obj.save()
            new_domain.interface_parent = new_interface_obj
257
            new_domain.save()
258
            messages.success(request, _("The machine was created."))
259 260
            return redirect(reverse(
                'users:profil',
261
                kwargs={'userid': str(user.id)}
Maël Kervella's avatar
Maël Kervella committed
262 263 264 265 266 267 268
            ))
    i_mbf_param = generate_ipv4_mbf_param(interface, False)
    return form(
        {
            'machineform': machine,
            'interfaceform': interface,
            'domainform': domain,
269
            'i_mbf_param': i_mbf_param,
270
            'action_name': _("Create a machine")
Maël Kervella's avatar
Maël Kervella committed
271 272 273 274
        },
        'machines/machine.html',
        request
    )
275

276

chirac's avatar
chirac committed
277
@login_required
278
@can_edit(Interface)
279
def edit_interface(request, interface_instance, **_kwargs):
280 281 282
    """ Edition d'une interface. Distingue suivant les droits les valeurs
    de interfaces et machines que l'user peut modifier infra permet de
    modifier le propriétaire"""
283

284 285 286 287 288
    machine_form = EditMachineForm(
        request.POST or None,
        instance=interface_instance.machine,
        user=request.user
    )
289 290 291 292 293 294 295 296 297 298 299 300
    interface_form = EditInterfaceForm(
        request.POST or None,
        instance=interface_instance,
        user=request.user
    )
    domain_form = DomainForm(
        request.POST or None,
        instance=interface_instance.domain
    )
    if (machine_form.is_valid() and
            interface_form.is_valid() and
            domain_form.is_valid()):
301 302 303
        new_machine_obj = machine_form.save(commit=False)
        new_interface_obj = interface_form.save(commit=False)
        new_domain_obj = domain_form.save(commit=False)
304
        if machine_form.changed_data:
305
            new_machine_obj.save()
306
        if interface_form.changed_data:
307
            new_interface_obj.save()
308
        if domain_form.changed_data:
309
            new_domain_obj.save()
310
        messages.success(request, _("The machine was edited."))
311 312
        return redirect(reverse(
            'users:profil',
313 314 315 316 317 318 319 320 321
            kwargs={'userid': str(interface_instance.machine.user.id)}
        ))
    i_mbf_param = generate_ipv4_mbf_param(interface_form, False)
    return form(
        {
            'machineform': machine_form,
            'interfaceform': interface_form,
            'domainform': domain_form,
            'i_mbf_param': i_mbf_param,
322
            'action_name': _("Edit")
323 324 325 326 327
        },
        'machines/machine.html',
        request
    )

328

329
@login_required
330
@can_delete(Machine)
331
def del_machine(request, machine, **_kwargs):
Gabriel Detraz's avatar
Gabriel Detraz committed
332
    """ Supprime une machine, interfaces en mode cascade"""
333
    if request.method == "POST":
334
        machine.delete()
335
        messages.success(request, _("The machine was deleted."))
336 337
        return redirect(reverse(
            'users:profil',
338 339 340 341 342 343 344 345
            kwargs={'userid': str(machine.user.id)}
        ))
    return form(
        {'objet': machine, 'objet_name': 'machine'},
        'machines/delete.html',
        request
    )

346

chirac's avatar
chirac committed
347
@login_required
348
@can_create(Interface)
349
@can_edit(Machine)
350
def new_interface(request, machine, **_kwargs):
Gabriel Detraz's avatar
Gabriel Detraz committed
351
    """ Ajoute une interface et son domain associé à une machine existante"""
352

353
    interface_form = AddInterfaceForm(request.POST or None, user=request.user)
354
    domain_form = DomainForm(request.POST or None)
355
    if interface_form.is_valid():
356 357 358
        new_interface_obj = interface_form.save(commit=False)
        domain_form.instance.interface_parent = new_interface_obj
        new_interface_obj.machine = machine
359
        if domain_form.is_valid():
360 361 362 363
            new_domain_obj = domain_form.save(commit=False)
            new_interface_obj.save()
            new_domain_obj.interface_parent = new_interface_obj
            new_domain_obj.save()
364
            messages.success(request, _("The interface was created."))
365 366
            return redirect(reverse(
                'users:profil',
367 368 369 370 371 372 373 374
                kwargs={'userid': str(machine.user.id)}
            ))
    i_mbf_param = generate_ipv4_mbf_param(interface_form, False)
    return form(
        {
            'interfaceform': interface_form,
            'domainform': domain_form,
            'i_mbf_param': i_mbf_param,
375
            'action_name': _("Create an interface")
376 377 378 379 380
        },
        'machines/machine.html',
        request
    )

381

382
@login_required
383
@can_delete(Interface)
384
def del_interface(request, interface, **_kwargs):
Gabriel Detraz's avatar
Gabriel Detraz committed
385
    """ Supprime une interface. Domain objet en mode cascade"""
386
    if request.method == "POST":
387
        machine = interface.machine
388 389 390
        interface.delete()
        if not machine.interface_set.all():
            machine.delete()
391
        messages.success(request, _("The interface was deleted."))
392 393
        return redirect(reverse(
            'users:profil',
394 395 396 397 398 399 400 401
            kwargs={'userid': str(request.user.id)}
        ))
    return form(
        {'objet': interface, 'objet_name': 'interface'},
        'machines/delete.html',
        request
    )

402

403 404 405
@login_required
@can_create(Ipv6List)
@can_edit(Interface)
406
def new_ipv6list(request, interface, **_kwargs):
407
    """Nouvelle ipv6"""
408
    ipv6list_instance = Ipv6List(interface=interface)
409 410 411 412 413
    ipv6 = Ipv6ListForm(
        request.POST or None,
        instance=ipv6list_instance,
        user=request.user
    )
414
    if ipv6.is_valid():
415
        ipv6.save()
416
        messages.success(request, _("The IPv6 addresses list was created."))
417 418
        return redirect(reverse(
            'machines:index-ipv6',
419 420 421
            kwargs={'interfaceid': str(interface.id)}
        ))
    return form(
422
        {'ipv6form': ipv6, 'action_name': _("Create an IPv6 addresses list")},
423 424 425 426
        'machines/machine.html',
        request
    )

427 428 429

@login_required
@can_edit(Ipv6List)
430
def edit_ipv6list(request, ipv6list_instance, **_kwargs):
431
    """Edition d'une ipv6"""
432 433 434 435 436
    ipv6 = Ipv6ListForm(
        request.POST or None,
        instance=ipv6list_instance,
        user=request.user
    )
437
    if ipv6.is_valid():
438 439
        if ipv6.changed_data:
            ipv6.save()
440
            messages.success(request, _("The IPv6 addresses list was edited."))
441 442
        return redirect(reverse(
            'machines:index-ipv6',
443 444 445
            kwargs={'interfaceid': str(ipv6list_instance.interface.id)}
        ))
    return form(
446
        {'ipv6form': ipv6, 'action_name': _("Edit")},
447 448 449 450
        'machines/machine.html',
        request
    )

451 452 453

@login_required
@can_delete(Ipv6List)
454
def del_ipv6list(request, ipv6list, **_kwargs):
455 456 457
    """ Supprime une ipv6"""
    if request.method == "POST":
        interfaceid = ipv6list.interface.id
458
        ipv6list.delete()
459
        messages.success(request, _("The IPv6 addresses list was deleted."))
460 461
        return redirect(reverse(
            'machines:index-ipv6',
462 463 464 465 466 467 468 469
            kwargs={'interfaceid': str(interfaceid)}
        ))
    return form(
        {'objet': ipv6list, 'objet_name': 'ipv6'},
        'machines/delete.html',
        request
    )

470

471
@login_required
472
@can_create(SshFp)
473
@can_edit(Machine)
474 475 476 477
def new_sshfp(request, machine, **_kwargs):
    """Creates an SSHFP record associated with a machine"""
    sshfp_instance = SshFp(machine=machine)
    sshfp = SshFpForm(
478
        request.POST or None,
479
        instance=sshfp_instance
480
    )
481 482
    if sshfp.is_valid():
        sshfp.save()
483
        messages.success(request, _("The SSHFP record was created."))
484
        return redirect(reverse(
485
            'machines:index-sshfp',
486
            kwargs={'machineid': str(machine.id)}
487 488
        ))
    return form(
489
        {'sshfpform': sshfp, 'action_name': _("Create a SSHFP record")},
490 491 492 493 494 495
        'machines/machine.html',
        request
    )


@login_required
496 497 498 499
@can_edit(SshFp)
def edit_sshfp(request, sshfp_instance, **_kwargs):
    """Edits an SSHFP record"""
    sshfp = SshFpForm(
500
        request.POST or None,
501
        instance=sshfp_instance
502
    )
503 504 505
    if sshfp.is_valid():
        if sshfp.changed_data:
            sshfp.save()
506
            messages.success(request, _("The SSHFP record was edited."))
507
        return redirect(reverse(
508 509
            'machines:index-sshfp',
            kwargs={'machineid': str(sshfp_instance.machine.id)}
510 511
        ))
    return form(
512
        {'sshfpform': sshfp, 'action_name': _("Edit")},
513 514 515 516 517 518
        'machines/machine.html',
        request
    )


@login_required
519 520 521
@can_delete(SshFp)
def del_sshfp(request, sshfp, **_kwargs):
    """Deletes an SSHFP record"""
522
    if request.method == "POST":
523 524
        machineid = sshfp.machine.id
        sshfp.delete()
525
        messages.success(request, _("The SSHFP record was deleted."))
526
        return redirect(reverse(
527
            'machines:index-sshfp',
528 529 530
            kwargs={'machineid': str(machineid)}
        ))
    return form(
531
        {'objet': sshfp, 'objet_name': 'sshfp'},
532 533 534 535 536
        'machines/delete.html',
        request
    )


537
@login_required
538
@can_create(IpType)
539
def add_iptype(request):
540 541
    """ Ajoute un range d'ip. Intelligence dans le models, fonction views
    minimaliste"""
542

543 544
    iptype = IpTypeForm(request.POST or None)
    if iptype.is_valid():
545
        iptype.save()
546
        messages.success(request, _("The IP type was created."))
547
        return redirect(reverse('machines:index-iptype'))
548
    return form(
549
        {'iptypeform': iptype, 'action_name': _("Create an IP type")},
550 551 552 553
        'machines/machine.html',
        request
    )

554 555

@login_required
556
@can_edit(IpType)
557
def edit_iptype(request, iptype_instance, **_kwargs):
558 559
    """ Edition d'un range. Ne permet pas de le redimensionner pour éviter
    l'incohérence"""
560

561
    iptype = EditIpTypeForm(request.POST or None, instance=iptype_instance)
562
    if iptype.is_valid():
563 564
        if iptype.changed_data:
            iptype.save()
565
            messages.success(request, _("The IP type was edited."))
566
        return redirect(reverse('machines:index-iptype'))
567
    return form(
568
        {'iptypeform': iptype, 'action_name': _("Edit")},
569 570 571 572
        'machines/machine.html',
        request
    )

573 574

@login_required
575 576
@can_delete_set(IpType)
def del_iptype(request, instances):
Gabriel Detraz's avatar
Gabriel Detraz committed
577
    """ Suppression d'un range ip. Supprime les objets ip associés"""
578
    iptype = DelIpTypeForm(request.POST or None, instances=instances)
579 580 581 582
    if iptype.is_valid():
        iptype_dels = iptype.cleaned_data['iptypes']
        for iptype_del in iptype_dels:
            try:
583
                iptype_del.delete()
584
                messages.success(request, _("The IP type was deleted."))
585
            except ProtectedError:
586 587
                messages.error(
                    request,
588 589
                    (_("The IP type %s is assigned to at least one machine,"
                       " you can't delete it.") % iptype_del)
590
                )
591
        return redirect(reverse('machines:index-iptype'))
592
    return form(
593
        {'iptypeform': iptype, 'action_name': _("Delete")},
594 595 596 597
        'machines/machine.html',
        request
    )

598

chirac's avatar
chirac committed
599
@login_required
600
@can_create(MachineType)
601
def add_machinetype(request):
602
    """ View used to add a Machinetype object """
603 604
    machinetype = MachineTypeForm(request.POST or None)
    if machinetype.is_valid():
605
        machinetype.save()
606
        messages.success(request, _("The machine type was created."))
607
        return redirect(reverse('machines:index-machinetype'))
608
    return form(
609 610
        {'machinetypeform': machinetype, 'action_name': _("Create a machine"
                                                          " type")},
611 612 613 614
        'machines/machine.html',
        request
    )

615

chirac's avatar
chirac committed
616
@login_required
617
@can_edit(MachineType)
618
def edit_machinetype(request, machinetype_instance, **_kwargs):
619
    """ View used to edit a MachineType object """
620 621 622 623
    machinetype = MachineTypeForm(
        request.POST or None,
        instance=machinetype_instance
    )
624
    if machinetype.is_valid():
625 626
        if machinetype.changed_data:
            machinetype.save()
627
            messages.success(request, _("The machine type was edited."))
628
        return redirect(reverse('machines:index-machinetype'))
629
    return form(
630
        {'machinetypeform': machinetype, 'action_name': _("Edit")},
631 632 633 634
        'machines/machine.html',
        request
    )

635

chirac's avatar
chirac committed
636
@login_required
637 638
@can_delete_set(MachineType)
def del_machinetype(request, instances):
639
    """ View used to delete a MachineType object """
640
    machinetype = DelMachineTypeForm(request.POST or None, instances=instances)
641 642 643 644
    if machinetype.is_valid():
        machinetype_dels = machinetype.cleaned_data['machinetypes']
        for machinetype_del in machinetype_dels:
            try:
645
                machinetype_del.delete()
646
                messages.success(request, _("The machine type was deleted."))
647
            except ProtectedError:
648 649
                messages.error(
                    request,
650 651
                    (_("The machine type %s is assigned to at least one"
                       " machine, you can't delete it.") % machinetype_del)
652
                )
653
        return redirect(reverse('machines:index-machinetype'))
654
    return form(
655
        {'machinetypeform': machinetype, 'action_name': _("Delete")},
656 657 658 659
        'machines/machine.html',
        request
    )

660

661
@login_required
662
@can_create(Extension)
663
def add_extension(request):
664
    """ View used to add an Extension object """
665 666
    extension = ExtensionForm(request.POST or None)
    if extension.is_valid():
667
        extension.save()
668
        messages.success(request, _("The extension was created."))
669
        return redirect(reverse('machines:index-extension'))
670
    return form(
671
        {'extensionform': extension, 'action_name': _("Create an extension")},
672 673 674 675
        'machines/machine.html',
        request
    )

676 677

@login_required
678
@can_edit(Extension)
679
def edit_extension(request, extension_instance, **_kwargs):
680
    """ View used to edit an Extension object """
681 682 683 684
    extension = ExtensionForm(
        request.POST or None,
        instance=extension_instance
    )
685
    if extension.is_valid():
686 687
        if extension.changed_data:
            extension.save()
688
            messages.success(request, _("The extension was edited."))
689
        return redirect(reverse('machines:index-extension'))
690
    return form(
691
        {'extensionform': extension, 'action_name': _("Edit")},
692 693 694 695
        'machines/machine.html',
        request
    )

696 697

@login_required
698 699
@can_delete_set(Extension)
def del_extension(request, instances):
700
    """ View used to delete an Extension object """
701
    extension = DelExtensionForm(request.POST or None, instances=instances)
702 703 704 705
    if extension.is_valid():
        extension_dels = extension.cleaned_data['extensions']
        for extension_del in extension_dels:
            try:
706
                extension_del.delete()
707
                messages.success(request, _("The extension was deleted."))
708
            except ProtectedError:
709 710
                messages.error(
                    request,
711 712
                    (_("The extension %s is assigned to at least one machine"
                       " type, you can't delete it." % extension_del))
713
                )
714
        return redirect(reverse('machines:index-extension'))
715
    return form(
716
        {'extensionform': extension, 'action_name': _("Delete")},
717 718 719 720
        'machines/machine.html',
        request
    )

721

722
@login_required
723
@can_create(SOA)
724
def add_soa(request):
725
    """ View used to add a SOA object """
726 727
    soa = SOAForm(request.POST or None)
    if soa.is_valid():
728
        soa.save()
729
        messages.success(request, _("The SOA record was created."))
730
        return redirect(reverse('machines:index-extension'))
731
    return form(
732
        {'soaform': soa, 'action_name': _("Create an SOA record")},
733 734 735 736
        'machines/machine.html',
        request
    )

737 738

@login_required
739
@can_edit(SOA)
740
def edit_soa(request, soa_instance, **_kwargs):
741
    """ View used to edit a SOA object """
742 743
    soa = SOAForm(request.POST or None, instance=soa_instance)
    if soa.is_valid():
744 745
        if soa.changed_data:
            soa.save()
746
            messages.success(request, _("The SOA record was edited."))
747
        return redirect(reverse('machines:index-extension'))
748
    return form(
749
        {'soaform': soa, 'action_name': _("Edit")},
750 751 752 753
        'machines/machine.html',
        request
    )

754 755

@login_required
756 757
@can_delete_set(SOA)
def del_soa(request, instances):
758
    """ View used to delete a SOA object """
759
    soa = DelSOAForm(request.POST or None, instances=instances)
760 761 762 763
    if soa.is_valid():
        soa_dels = soa.cleaned_data['soa']
        for soa_del in soa_dels:
            try:
764
                soa_del.delete()
765
                messages.success(request, _("The SOA record was deleted."))
766
            except ProtectedError:
767 768
                messages.error(
                    request,
769
                    (_("Error: the SOA record %s can't be deleted.") % soa_del)
770
                )
771
        return redirect(reverse('machines:index-extension'))
772
    return form(
773
        {'soaform': soa, 'action_name': _("Delete")},
774 775 776 777
        'machines/machine.html',
        request
    )

778

779
@login_required
780
@can_create(Mx)
781
def add_mx(request):
782
    """ View used to add a MX object """
783 784
    mx = MxForm(request.POST or None)
    if mx.is_valid():
785
        mx.save()
786
        messages.success(request, _("The MX record was created."))
787
        return redirect(reverse('machines:index-extension'))
788
    return form(
789
        {'mxform': mx, 'action_name': _("Create an MX record")},
790 791 792 793
        'machines/machine.html',
        request
    )

794 795

@login_required
796
@can_edit(Mx)
797
def edit_mx(request, mx_instance, **_kwargs):
798
    """ View used to edit a MX object """
799 800
    mx = MxForm(request.POST or None, instance=mx_instance)
    if mx.is_valid():
801 802
        if mx.changed_data:
            mx.save()
803
            messages.success(request, _("The MX record was edited."))
804
        return redirect(reverse('machines:index-extension'))
805
    return form(
806
        {'mxform': mx, 'action_name': _("Edit")},
807 808 809 810
        'machines/machine.html',
        request
    )

811 812

@login_required
813 814
@can_delete_set(Mx)
def del_mx(request, instances):
815
    """ View used to delete a MX object """
816
    mx = DelMxForm(request.POST or None, instances=instances)
817 818 819 820
    if mx.is_valid():
        mx_dels = mx.cleaned_data['mx']
        for mx_del in mx_dels:
            try:
821
                mx_del.delete()
822
                messages.success(request, _("The MX record was deleted."))
823
            except ProtectedError:
824 825
                messages.error(
                    request,
826
                    (_("Error: the MX record %s can't be deleted.") % mx_del)
827
                )
828
        return redirect(reverse('machines:index-extension'))
829
    return form(
830
        {'mxform': mx, 'action_name': _("Delete")},
831 832 833 834
        'machines/machine.html',
        request
    )

835

836
@login_required
837
@can_create(Ns)
838
def add_ns(request):
839
    """ View used to add a NS object """
840 841
    ns = NsForm(request.POST or None)
    if ns.is_valid():
842
        ns.save()
843
        messages.success(request, _("The NS record was created."))
844
        return redirect(reverse('machines:index-extension'))
845
    return form(
846
        {'nsform': ns, 'action_name': _("Create an NS record")},
847 848 849 850
        'machines/machine.html',
        request
    )

851 852

@login_required
853
@can_edit(Ns)
854
def edit_ns(request, ns_instance, **_kwargs):
855
    """ View used to edit a NS object """
856 857
    ns = NsForm(request.POST or None, instance=ns_instance)
    if ns.is_valid():
858 859
        if ns.changed_data:
            ns.save()
860
            messages.success(request, _("The NS record was edited."))
861
        return redirect(reverse('machines:index-extension'))
862
    return form(
863
        {'nsform': ns, 'action_name': _("Edit")},
864 865 866 867
        'machines/machine.html',
        request
    )

868 869

@login_required
870 871
@can_delete_set(Ns)
def del_ns(request, instances):
872
    """ View used to delete a NS object """
873
    ns = DelNsForm(request.POST or None, instances=instances)
874 875 876 877
    if ns.is_valid():
        ns_dels = ns.cleaned_data['ns']
        for ns_del in ns_dels:
            try:
878
                ns_del.delete()
879
                messages.success(request, _("The NS record was deleted."))
880
            except ProtectedError:
881 882
                messages.error(
                    request,
883
                    (_("Error: the NS record %s can't be deleted.") % ns_del)
884
                )
885
        return redirect(reverse('machines:index-extension'))
886
    return form(
887
        {'nsform': ns, 'action_name': _("Delete")},
888 889 890 891
        'machines/machine.html',
        request
    )

Charlie Jacomme's avatar
Charlie Jacomme committed
892 893 894 895 896 897 898
@login_required
@can_create(DName)
def add_dname(request):
    """ View used to add a DName object """
    dname = DNameForm(request.POST or None)
    if dname.is_valid():
        dname.save()
899
        messages.success(request, _("The DNAME record was created."))
Charlie Jacomme's avatar
Charlie Jacomme committed
900 901
        return redirect(reverse('machines:index-extension'))
    return form(
902
        {'dnameform': dname, 'action_name': _("Create a DNAME record")},
Charlie Jacomme's avatar
Charlie Jacomme committed
903 904 905 906 907 908 909 910 911 912 913 914 915
        'machines/machine.html',
        request
    )


@login_required
@can_edit(DName)
def edit_dname(request, dname_instance, **_kwargs):
    """ View used to edit a DName object """
    dname = DNameForm(request.POST or None, instance=dname_instance)
    if dname.is_valid():
        if dname.changed_data:
            dname.save()
916
            messages.success(request, _("The DNAME record was edited."))
Charlie Jacomme's avatar
Charlie Jacomme committed
917 918
        return redirect(reverse('machines:index-extension'))
    return form(
919
        {'dnameform': dname, 'action_name': _("Edit")},
Charlie Jacomme's avatar
Charlie Jacomme committed
920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
        'machines/machine.html',
        request
    )


@login_required
@can_delete_set(DName)
def del_dname(request, instances):
    """ View used to delete a DName object """
    dname = DelDNameForm(request.POST or None, instances=instances)
    if dname.is_valid():
        dname_dels = dname.cleaned_data['dname']
        for dname_del in dname_dels:
            try:
                dname_del.delete()
935
                messages.success(request, _("The DNAME record was deleted."))
Charlie Jacomme's avatar
Charlie Jacomme committed
936 937
            except ProtectedError:
                messages.error(
938 939 940
                        request,
                        _("Error: the DNAME record %s can't be deleted.")
                        % dname_del
Charlie Jacomme's avatar
Charlie Jacomme committed
941 942 943
                )
        return redirect(reverse('machines:index-extension'))
    return form(
944
        {'dnameform': dname, 'action_name': _("Delete")},
Charlie Jacomme's avatar
Charlie Jacomme committed
945 946 947 948
        'machines/machine.html',
        request
    )

949

Gabriel Detraz's avatar
Gabriel Detraz committed
950
@login_required
951
@can_create(Txt)
952
def add_txt(request):
953
    """ View used to add a TXT object """
954 955
    txt = TxtForm(request.POST or None)
    if txt.is_valid():
956
        txt.save()
957
        messages.success(request, _("The TXT record was created."))
958
        return redirect(reverse('machines:index-extension'))
959
    return form(
960
        {'txtform': txt, 'action_name': _("Create a TXT record")},
961 962 963 964
        'machines/machine.html',
        request
    )

Gabriel Detraz's avatar
Gabriel Detraz committed
965 966

@login_required
967
@can_edit(Txt)
968
def edit_txt(request, txt_instance, **_kwargs):
969
    """ View used to edit a TXT object """
970 971
    txt = TxtForm(request.POST or None, instance=txt_instance)
    if txt.is_valid():
972 973
        if txt.changed_data:
            txt.save()
974
            messages.success(request, _("The TXT record was edited."))
975
        return redirect(reverse('machines:index-extension'))
976
    return form(
977
        {'txtform': txt, 'action_name': _("Edit")},
978 979 980 981
        'machines/machine.html',
        request
    )

Gabriel Detraz's avatar
Gabriel Detraz committed
982 983

@login_required
984 985
@can_delete_set(Txt)
def del_txt(request, instances):
986
    """ View used to delete a TXT object """
987
    txt = DelTxtForm(request.POST or None, instances=instances)
988 989 990
    if txt.is_valid():
        txt_dels = txt.cleaned_data['txt']
        for txt_del in txt_dels:
Gabriel Detraz's avatar
Gabriel Detraz committed
991
            try:
992
                txt_del.delete()
993
                messages.success(request, _("The TXT record was deleted."))
Gabriel Detraz's avatar
Gabriel Detraz committed
994
            except ProtectedError:
995 996
                messages.error(
                    request,
997
                    (_("Error: the TXT record %s can't be deleted.") % txt_del)
998
                )
999
        return redirect(reverse('machines:index-extension'))
1000
    return form(
1001
        {'txtform': txt, 'action_name': _("Delete")},
1002 1003 1004 1005
        'machines/machine.html',
        request
    )

Gabriel Detraz's avatar
Gabriel Detraz committed
1006

Gabriel Detraz's avatar
Gabriel Detraz committed
1007
@login_required
1008
@can_create(Srv)
Gabriel Detraz's avatar
Gabriel Detraz committed
1009
def add_srv(request):
1010
    """ View used to add a SRV object """
Gabriel Detraz's avatar
Gabriel Detraz committed
1011 1012
    srv = SrvForm(request.POST or None)
    if srv.is_valid():
1013
        srv.save()
1014
        messages.success(request, _("The SRV record was created."))
Gabriel Detraz's avatar
Gabriel Detraz committed
1015
        return redirect(reverse('machines:index-extension'))
1016
    return form(
1017
        {'srvform': srv, 'action_name': _("Create an SRV record")},
1018 1019 1020 1021
        'machines/machine.html',
        request
    )

Gabriel Detraz's avatar
Gabriel Detraz committed
1022 1023

@login_required
1024
@can_edit(Srv)
1025
def edit_srv(request, srv_instance, **_kwargs):
1026
    """ View used to edit a SRV object """
Gabriel Detraz's avatar
Gabriel Detraz committed
1027 1028
    srv = SrvForm(request.POST or None, instance=srv_instance)
    if srv.is_valid():
1029 1030
        if srv.changed_data:
            srv.save()
1031 1032
            messages.success(request, _("The SRV record was edited."))
        return redirect(reverse('machines:1index-extension'))
1033
    return form(
1034
        {'srvform': srv, 'action_name': _("Edit")},
1035 1036 1037 1038
        'machines/machine.html',
        request
    )

Gabriel Detraz's avatar
Gabriel Detraz committed
1039 1040

@login_required
1041 1042
@can_delete_set(Srv)
def del_srv(request, instances):
1043
    """ View used to delete a SRV object """
1044
    srv = DelSrvForm(request.POST or None, instances=instances)
Gabriel Detraz's avatar
Gabriel Detraz committed
1045 1046 1047 1048
    if srv.is_valid():
        srv_dels = srv.cleaned_data['srv']
        for srv_del in srv_dels:
            try:
1049
                srv_del.delete()
1050
                messages.success(request, _("The SRV record was deleted."))
Gabriel Detraz's avatar
Gabriel Detraz committed
1051
            except ProtectedError:
1052 1053
                messages.error(
                    request,
1054
                    (_("Error: the SRV record %s can't be deleted.") % srv_del)
1055
                )
Gabriel Detraz's avatar
Gabriel Detraz committed
1056
        return redirect(reverse('machines:index-extension'))
1057
    return form(
1058
        {'srvform': srv, 'action_name': _("Delete")},
1059 1060 1061 1062
        'machines/machine.html',
        request
    )

Gabriel Detraz's avatar
Gabriel Detraz committed
1063

1064
@login_required
1065