mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-08-20 06:03:40 +00:00
Ajoute un model SOA pour les données SOA d'une extension
This commit is contained in:
parent
e404f59b63
commit
aa5cedc37a
10 changed files with 294 additions and 9 deletions
|
@ -27,8 +27,8 @@ from django.contrib import admin
|
|||
from reversion.admin import VersionAdmin
|
||||
|
||||
from .models import IpType, Machine, MachineType, Domain, IpList, Interface
|
||||
from .models import Extension, Mx, Ns, Vlan, Text, Nas, Service, OuverturePort
|
||||
from .models import OuverturePortList
|
||||
from .models import Extension, SOA, Mx, Ns, Vlan, Text, Nas, Service
|
||||
from .models import OuverturePort, OuverturePortList
|
||||
|
||||
|
||||
class MachineAdmin(VersionAdmin):
|
||||
|
@ -51,6 +51,10 @@ class ExtensionAdmin(VersionAdmin):
|
|||
pass
|
||||
|
||||
|
||||
class SOAAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class MxAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
@ -95,6 +99,7 @@ admin.site.register(Machine, MachineAdmin)
|
|||
admin.site.register(MachineType, MachineTypeAdmin)
|
||||
admin.site.register(IpType, IpTypeAdmin)
|
||||
admin.site.register(Extension, ExtensionAdmin)
|
||||
admin.site.register(SOA, SOAAdmin)
|
||||
admin.site.register(Mx, MxAdmin)
|
||||
admin.site.register(Ns, NsAdmin)
|
||||
admin.site.register(Text, TextAdmin)
|
||||
|
|
|
@ -45,6 +45,7 @@ from .models import (
|
|||
IpList,
|
||||
MachineType,
|
||||
Extension,
|
||||
SOA,
|
||||
Mx,
|
||||
Text,
|
||||
Ns,
|
||||
|
@ -274,6 +275,7 @@ class ExtensionForm(ModelForm):
|
|||
self.fields['name'].label = 'Extension à ajouter'
|
||||
self.fields['origin'].label = 'Enregistrement A origin'
|
||||
self.fields['origin_v6'].label = 'Enregistrement AAAA origin'
|
||||
self.fields['soa'].label = 'En-tête SOA à utiliser'
|
||||
|
||||
|
||||
class DelExtensionForm(Form):
|
||||
|
@ -285,6 +287,26 @@ class DelExtensionForm(Form):
|
|||
)
|
||||
|
||||
|
||||
class SOAForm(ModelForm):
|
||||
"""Ajout et edition d'un SOA"""
|
||||
class Meta:
|
||||
model = SOA
|
||||
fields = '__all__'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(SOAForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class DelSOAForm(Form):
|
||||
"""Suppression d'un ou plusieurs SOA"""
|
||||
soa = forms.ModelMultipleChoiceField(
|
||||
queryset=SOA.objects.all(),
|
||||
label="SOA actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
|
||||
class MxForm(ModelForm):
|
||||
"""Ajout et edition d'un MX"""
|
||||
class Meta:
|
||||
|
|
34
machines/migrations/0063_auto_20171020_0040.py
Normal file
34
machines/migrations/0063_auto_20171020_0040.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-10-19 22:40
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import machines.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('machines', '0062_extension_origin_v6'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SOA',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(default='SOA', max_length=255)),
|
||||
('mail', models.EmailField(default='postmaster@example.com', help_text='Email du contact pour la zone', max_length=254)),
|
||||
('refresh', models.PositiveIntegerField(default=86400, help_text='Secondes avant que les DNS secondaires doivent demander le serial du DNS primaire pour détecter une modification')),
|
||||
('retry', models.PositiveIntegerField(default=7200, help_text='Secondes avant que les DNS secondaires fassent une nouvelle demande de serial en cas de timeout du DNS primaire')),
|
||||
('expire', models.PositiveIntegerField(default=3600000, help_text='Secondes après lesquelles les DNS secondaires arrêtent de de répondre aux requêtes en cas de timeout du DNS primaire')),
|
||||
('ttl', models.PositiveIntegerField(default=172800, help_text='Time To Live')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='extension',
|
||||
name='soa',
|
||||
field=models.ForeignKey(default=machines.models.SOA.new_default_soa, on_delete=django.db.models.deletion.CASCADE, to='machines.SOA'),
|
||||
),
|
||||
]
|
|
@ -234,6 +234,79 @@ class Nas(models.Model):
|
|||
return self.name
|
||||
|
||||
|
||||
class SOA(models.Model):
|
||||
"""
|
||||
Un enregistrement SOA associé à une extension
|
||||
Les valeurs par défault viennent des recommandations RIPE :
|
||||
https://www.ripe.net/publications/docs/ripe-203
|
||||
"""
|
||||
PRETTY_NAME = "Enregistrement SOA"
|
||||
|
||||
name = models.CharField(max_length=255, default='SOA')
|
||||
mail = models.EmailField(
|
||||
default='postmaster@example.com',
|
||||
help_text='Email du contact pour la zone'
|
||||
)
|
||||
refresh = models.PositiveIntegerField(
|
||||
default=86400, # 24 hours
|
||||
help_text='Secondes avant que les DNS secondaires doivent demander le\
|
||||
serial du DNS primaire pour détecter une modification'
|
||||
)
|
||||
retry = models.PositiveIntegerField(
|
||||
default=7200, # 2 hours
|
||||
help_text='Secondes avant que les DNS secondaires fassent une nouvelle\
|
||||
demande de serial en cas de timeout du DNS primaire'
|
||||
)
|
||||
expire = models.PositiveIntegerField(
|
||||
default=3600000, # 1000 hours
|
||||
help_text='Secondes après lesquelles les DNS secondaires arrêtent de\
|
||||
de répondre aux requêtes en cas de timeout du DNS primaire'
|
||||
)
|
||||
ttl = models.PositiveIntegerField(
|
||||
default=172800, # 2 days
|
||||
help_text='Time To Live'
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.name)
|
||||
|
||||
@cached_property
|
||||
def dns_soa_param(self):
|
||||
"""
|
||||
Renvoie la partie de l'enregistrement SOA correspondant aux champs :
|
||||
<refresh> ; refresh
|
||||
<retry> ; retry
|
||||
<expire> ; expire
|
||||
<ttl> ; TTL
|
||||
"""
|
||||
return (
|
||||
' {refresh};refresh\n'
|
||||
' {retry};retry\n'
|
||||
' {expire};expire\n'
|
||||
' {ttl};TTL'
|
||||
).format(
|
||||
refresh=self.refresh,
|
||||
retry=self.retry,
|
||||
expire=self.expire,
|
||||
ttl=self.expire
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def dns_soa_mail(self):
|
||||
""" Renvoie le mail dans l'enregistrement SOA """
|
||||
mail_fields = str(self.mail).split('@')
|
||||
return mail_fields[0].replace('.', '\\.') + '.' +mail_fields[1]
|
||||
|
||||
@classmethod
|
||||
def new_default_soa(cls):
|
||||
""" Fonction pour créer un SOA par défaut, utile pour les nouvelles
|
||||
extensions .
|
||||
/!\ Ne jamais supprimer ou renommer cette fonction car elle est
|
||||
utilisée dans les migrations de la BDD. """
|
||||
return cls.objects.get_or_create(name="SOA to edit", mail="postmaser@example.com")[0].pk
|
||||
|
||||
|
||||
|
||||
class Extension(models.Model):
|
||||
""" Extension dns type example.org. Précise si tout le monde peut
|
||||
l'utiliser, associé à un origin (ip d'origine)"""
|
||||
|
@ -252,6 +325,11 @@ class Extension(models.Model):
|
|||
null=True,
|
||||
blank=True
|
||||
)
|
||||
soa = models.ForeignKey(
|
||||
'SOA',
|
||||
on_delete=models.CASCADE,
|
||||
default=SOA.new_default_soa
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def dns_entry(self):
|
||||
|
@ -307,7 +385,7 @@ class Ns(models.Model):
|
|||
|
||||
class Text(models.Model):
|
||||
""" Un enregistrement TXT associé à une extension"""
|
||||
PRETTY_NAME = "Enregistrement text"
|
||||
PRETTY_NAME = "Enregistrement TXT"
|
||||
|
||||
zone = models.ForeignKey('Extension', on_delete=models.PROTECT)
|
||||
field1 = models.CharField(max_length=255)
|
||||
|
@ -790,6 +868,18 @@ def extension_post_selete(sender, **kwargs):
|
|||
regen('dns')
|
||||
|
||||
|
||||
@receiver(post_save, sender=SOA)
|
||||
def soa_post_save(sender, **kwargs):
|
||||
"""Regeneration dns après modification d'un SOA"""
|
||||
regen('dns')
|
||||
|
||||
|
||||
@receiver(post_delete, sender=SOA)
|
||||
def soa_post_delete(sender, **kwargs):
|
||||
"""Regeneration dns après suppresson d'un SOA"""
|
||||
regen('dns')
|
||||
|
||||
|
||||
@receiver(post_save, sender=Mx)
|
||||
def mx_post_save(sender, **kwargs):
|
||||
"""Regeneration dns après modification d'un MX"""
|
||||
|
|
|
@ -26,19 +26,25 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<thead>
|
||||
<tr>
|
||||
<th>Extension</th>
|
||||
<th>Autorisation infra pour utiliser l'extension</th>
|
||||
<th>Droit infra pour utiliser ?</th>
|
||||
<th>Enregistrement SOA</th>
|
||||
<th>Enregistrement A origin</th>
|
||||
{% if ipv6_enabled %}
|
||||
<th>Enregistrement AAAA origin</th>
|
||||
{% endif %}
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for extension in extension_list %}
|
||||
<tr>
|
||||
<td>{{ extension.name }}</td>
|
||||
<td>{{ extension.need_infra }}</td>
|
||||
<td>{{ extension.need_infra }}</td>
|
||||
<td>{{ extension.soa}}</td>
|
||||
<td>{{ extension.origin }}</td>
|
||||
<td>{{ extension.origin_v6 }}</td>
|
||||
<td class="text-right">
|
||||
{% if ipv6_enabled %}
|
||||
<td>{{ extension.origin_v6 }}</td>
|
||||
{% endif %}
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-extension' id=extension.id %}
|
||||
{% endif %}
|
||||
|
|
56
machines/templates/machines/aff_soa.html
Normal file
56
machines/templates/machines/aff_soa.html
Normal file
|
@ -0,0 +1,56 @@
|
|||
{% comment %}
|
||||
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.
|
||||
{% endcomment %}
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nom</th>
|
||||
<th>Mail</th>
|
||||
<th>Refresh</th>
|
||||
<th>Retry</th>
|
||||
<th>Expire</th>
|
||||
<th>TTL</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for soa in soa_list %}
|
||||
<tr>
|
||||
<td>{{ soa.name }}</td>
|
||||
<td>{{ soa.mail }}</td>
|
||||
<td>{{ soa.refresh }}</td>
|
||||
<td>{{ soa.retry }}</td>
|
||||
<td>{{ soa.expire }}</td>
|
||||
<td>{{ soa.ttl }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-soa' id=soa.id %}
|
||||
{% endif %}
|
||||
{% include 'buttons/history.html' with href='machines:history' name='soa' id=soa.id %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
|
|
@ -35,6 +35,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endif %}
|
||||
{% include "machines/aff_extension.html" with extension_list=extension_list %}
|
||||
|
||||
<h2>Liste des enregistrements SOA</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-soa' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement SOA</a>
|
||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-soa' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement SOA</a>
|
||||
{% endif %}
|
||||
{% include "machines/aff_soa.html" with soa_list=soa_list %}
|
||||
<h2>Liste des enregistrements MX</h2>
|
||||
{% if is_infra %}
|
||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-mx' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement MX</a>
|
||||
|
|
|
@ -100,6 +100,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<h3>Extension</h3>
|
||||
{% massive_bootstrap_form extensionform 'origin' %}
|
||||
{% endif %}
|
||||
{% if soaform %}
|
||||
<h3>Enregistrement SOA</h3>
|
||||
{% bootstrap_form soaform %}
|
||||
{% endif %}
|
||||
{% if mxform %}
|
||||
<h3>Enregistrement MX</h3>
|
||||
{% massive_bootstrap_form mxform 'name' %}
|
||||
|
|
|
@ -44,6 +44,9 @@ urlpatterns = [
|
|||
url(r'^add_extension/$', views.add_extension, name='add-extension'),
|
||||
url(r'^edit_extension/(?P<extensionid>[0-9]+)$', views.edit_extension, name='edit-extension'),
|
||||
url(r'^del_extension/$', views.del_extension, name='del-extension'),
|
||||
url(r'^add_soa/$', views.add_soa, name='add-soa'),
|
||||
url(r'^edit_soa/(?P<soaid>[0-9]+)$', views.edit_soa, name='edit-soa'),
|
||||
url(r'^del_soa/$', views.del_soa, name='del-soa'),
|
||||
url(r'^add_mx/$', views.add_mx, name='add-mx'),
|
||||
url(r'^edit_mx/(?P<mxid>[0-9]+)$', views.edit_mx, name='edit-mx'),
|
||||
url(r'^del_mx/$', views.del_mx, name='del-mx'),
|
||||
|
@ -74,6 +77,7 @@ urlpatterns = [
|
|||
url(r'^history/(?P<object>interface)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>machinetype)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>extension)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>soa)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>mx)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>ns)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
url(r'^history/(?P<object>txt)/(?P<id>[0-9]+)$', views.history, name='history'),
|
||||
|
|
|
@ -77,6 +77,8 @@ from .forms import (
|
|||
DomainForm,
|
||||
AliasForm,
|
||||
DelAliasForm,
|
||||
SOAForm,
|
||||
DelSOAForm,
|
||||
NsForm,
|
||||
DelNsForm,
|
||||
TxtForm,
|
||||
|
@ -98,6 +100,7 @@ from .models import (
|
|||
IpList,
|
||||
MachineType,
|
||||
Extension,
|
||||
SOA,
|
||||
Mx,
|
||||
Ns,
|
||||
Domain,
|
||||
|
@ -519,6 +522,54 @@ def del_extension(request):
|
|||
return redirect("/machines/index_extension")
|
||||
return form({'extensionform': extension}, 'machines/machine.html', request)
|
||||
|
||||
@login_required
|
||||
@permission_required('infra')
|
||||
def add_soa(request):
|
||||
soa = SOAForm(request.POST or None)
|
||||
if soa.is_valid():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
soa.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Création")
|
||||
messages.success(request, "Cet enregistrement SOA a été ajouté")
|
||||
return redirect("/machines/index_extension")
|
||||
return form({'soaform': soa}, 'machines/machine.html', request)
|
||||
|
||||
@login_required
|
||||
@permission_required('infra')
|
||||
def edit_soa(request, soaid):
|
||||
try:
|
||||
soa_instance = SOA.objects.get(pk=soaid)
|
||||
except SOA.DoesNotExist:
|
||||
messages.error(request, u"Entrée inexistante" )
|
||||
return redirect("/machines/index_extension/")
|
||||
soa = SOAForm(request.POST or None, instance=soa_instance)
|
||||
if soa.is_valid():
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
soa.save()
|
||||
reversion.set_user(request.user)
|
||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in soa.changed_data))
|
||||
messages.success(request, "SOA modifié")
|
||||
return redirect("/machines/index_extension/")
|
||||
return form({'soaform': soa}, 'machines/machine.html', request)
|
||||
|
||||
@login_required
|
||||
@permission_required('infra')
|
||||
def del_soa(request):
|
||||
soa = DelSOAForm(request.POST or None)
|
||||
if soa.is_valid():
|
||||
soa_dels = soa.cleaned_data['soa']
|
||||
for soa_del in soa_dels:
|
||||
try:
|
||||
with transaction.atomic(), reversion.create_revision():
|
||||
soa_del.delete()
|
||||
reversion.set_user(request.user)
|
||||
messages.success(request, "Le SOA a été supprimée")
|
||||
except ProtectedError:
|
||||
messages.error(request, "Erreur le SOA suivant %s ne peut être supprimé" % soa_del)
|
||||
return redirect("/machines/index_extension")
|
||||
return form({'soaform': soa}, 'machines/machine.html', request)
|
||||
|
||||
@login_required
|
||||
@permission_required('infra')
|
||||
def add_mx(request):
|
||||
|
@ -925,11 +976,12 @@ def index_nas(request):
|
|||
@login_required
|
||||
@permission_required('cableur')
|
||||
def index_extension(request):
|
||||
extension_list = Extension.objects.select_related('origin').order_by('name')
|
||||
extension_list = Extension.objects.select_related('origin').select_related('soa').order_by('name')
|
||||
soa_list = SOA.objects.order_by('name')
|
||||
mx_list = Mx.objects.order_by('zone').select_related('zone').select_related('name__extension')
|
||||
ns_list = Ns.objects.order_by('zone').select_related('zone').select_related('ns__extension')
|
||||
text_list = Text.objects.all().select_related('zone')
|
||||
return render(request, 'machines/index_extension.html', {'extension_list':extension_list, 'mx_list': mx_list, 'ns_list': ns_list, 'text_list' : text_list})
|
||||
return render(request, 'machines/index_extension.html', {'extension_list':extension_list, 'soa_list': soa_list, 'mx_list': mx_list, 'ns_list': ns_list, 'text_list' : text_list})
|
||||
|
||||
@login_required
|
||||
def index_alias(request, interfaceid):
|
||||
|
@ -998,6 +1050,12 @@ def history(request, object, id):
|
|||
except Extension.DoesNotExist:
|
||||
messages.error(request, "Extension inexistante")
|
||||
return redirect("/machines/")
|
||||
elif object == 'soa' and request.user.has_perms(('cableur',)):
|
||||
try:
|
||||
object_instance = SOA.objects.get(pk=id)
|
||||
except SOA.DoesNotExist:
|
||||
messages.error(request, "SOA inexistant")
|
||||
return redirect("/machines/")
|
||||
elif object == 'mx' and request.user.has_perms(('cableur',)):
|
||||
try:
|
||||
object_instance = Mx.objects.get(pk=id)
|
||||
|
|
Loading…
Reference in a new issue