8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-22 19:33:11 +00:00

Nouvelle app pour gestion de la regen des services

This commit is contained in:
Gabriel Detraz 2017-08-08 06:56:19 +02:00 committed by root
parent 9da1844943
commit 01b13d1ac4
11 changed files with 343 additions and 18 deletions

View file

@ -23,7 +23,7 @@
from django.contrib import admin from django.contrib import admin
from reversion.admin import VersionAdmin from reversion.admin import VersionAdmin
from .models import IpType, Machine, MachineType, Domain, IpList, Interface, Extension, Mx, Ns from .models import IpType, Machine, MachineType, Domain, IpList, Interface, Extension, Mx, Ns, Service
class MachineAdmin(VersionAdmin): class MachineAdmin(VersionAdmin):
list_display = ('user','name','active') list_display = ('user','name','active')
@ -53,6 +53,9 @@ class InterfaceAdmin(VersionAdmin):
class DomainAdmin(VersionAdmin): class DomainAdmin(VersionAdmin):
list_display = ('interface_parent', 'name', 'extension', 'cname') list_display = ('interface_parent', 'name', 'extension', 'cname')
class ServiceAdmin(VersionAdmin):
list_display = ('service_type', 'time_regen')
admin.site.register(Machine, MachineAdmin) admin.site.register(Machine, MachineAdmin)
admin.site.register(MachineType, MachineTypeAdmin) admin.site.register(MachineType, MachineTypeAdmin)
admin.site.register(IpType, IpTypeAdmin) admin.site.register(IpType, IpTypeAdmin)
@ -62,3 +65,4 @@ admin.site.register(Ns, NsAdmin)
admin.site.register(IpList, IpListAdmin) admin.site.register(IpList, IpListAdmin)
admin.site.register(Interface, InterfaceAdmin) admin.site.register(Interface, InterfaceAdmin)
admin.site.register(Domain, DomainAdmin) admin.site.register(Domain, DomainAdmin)
admin.site.register(Service, ServiceAdmin)

View file

@ -22,7 +22,7 @@
from django.forms import ModelForm, Form, ValidationError from django.forms import ModelForm, Form, ValidationError
from django import forms from django import forms
from .models import Domain, Machine, Interface, IpList, MachineType, Extension, Mx, Ns, IpType from .models import Domain, Machine, Interface, IpList, MachineType, Extension, Mx, Ns, Service, IpType
from django.db.models import Q from django.db.models import Q
from django.core.validators import validate_email from django.core.validators import validate_email
@ -141,13 +141,9 @@ class MachineTypeForm(ModelForm):
self.fields['type'].label = 'Type de machine à ajouter' self.fields['type'].label = 'Type de machine à ajouter'
self.fields['ip_type'].label = "Type d'ip relié" self.fields['ip_type'].label = "Type d'ip relié"
class DelMachineTypeForm(ModelForm): class DelMachineTypeForm(Form):
machinetypes = forms.ModelMultipleChoiceField(queryset=MachineType.objects.all(), label="Types de machines actuelles", widget=forms.CheckboxSelectMultiple) machinetypes = forms.ModelMultipleChoiceField(queryset=MachineType.objects.all(), label="Types de machines actuelles", widget=forms.CheckboxSelectMultiple)
class Meta:
exclude = ['type','ip_type']
model = MachineType
class IpTypeForm(ModelForm): class IpTypeForm(ModelForm):
class Meta: class Meta:
model = IpType model = IpType
@ -161,7 +157,7 @@ class EditIpTypeForm(IpTypeForm):
class Meta(IpTypeForm.Meta): class Meta(IpTypeForm.Meta):
fields = ['extension','type','need_infra'] fields = ['extension','type','need_infra']
class DelIpTypeForm(forms.Form): class DelIpTypeForm(Form):
iptypes = forms.ModelMultipleChoiceField(queryset=IpType.objects.all(), label="Types d'ip actuelles", widget=forms.CheckboxSelectMultiple) iptypes = forms.ModelMultipleChoiceField(queryset=IpType.objects.all(), label="Types d'ip actuelles", widget=forms.CheckboxSelectMultiple)
class ExtensionForm(ModelForm): class ExtensionForm(ModelForm):
@ -190,13 +186,9 @@ class MxForm(ModelForm):
super(MxForm, self).__init__(*args, **kwargs) super(MxForm, self).__init__(*args, **kwargs)
self.fields['name'].queryset = Domain.objects.exclude(interface_parent=None) self.fields['name'].queryset = Domain.objects.exclude(interface_parent=None)
class DelMxForm(ModelForm): class DelMxForm(Form):
mx = forms.ModelMultipleChoiceField(queryset=Mx.objects.all(), label="MX actuels", widget=forms.CheckboxSelectMultiple) mx = forms.ModelMultipleChoiceField(queryset=Mx.objects.all(), label="MX actuels", widget=forms.CheckboxSelectMultiple)
class Meta:
exclude = ['zone', 'priority', 'name']
model = Mx
class NsForm(ModelForm): class NsForm(ModelForm):
class Meta: class Meta:
model = Ns model = Ns
@ -206,9 +198,21 @@ class NsForm(ModelForm):
super(NsForm, self).__init__(*args, **kwargs) super(NsForm, self).__init__(*args, **kwargs)
self.fields['ns'].queryset = Domain.objects.exclude(interface_parent=None) self.fields['ns'].queryset = Domain.objects.exclude(interface_parent=None)
class DelNsForm(ModelForm): class DelNsForm(Form):
ns = forms.ModelMultipleChoiceField(queryset=Ns.objects.all(), label="Enregistrements NS actuels", widget=forms.CheckboxSelectMultiple) ns = forms.ModelMultipleChoiceField(queryset=Ns.objects.all(), label="Enregistrements NS actuels", widget=forms.CheckboxSelectMultiple)
class ServiceForm(ModelForm):
class Meta: class Meta:
exclude = ['zone', 'ns'] model = Service
model = Ns fields = '__all__'
def save(self, commit=True):
instance = super(ServiceForm, self).save(commit=False)
if commit:
instance.save()
instance.process_link(self.cleaned_data.get('servers'))
return instance
class DelServiceForm(Form):
service = forms.ModelMultipleChoiceField(queryset=Service.objects.all(), label="Services actuels", widget=forms.CheckboxSelectMultiple)

View file

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-08-08 00:33
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('machines', '0043_auto_20170721_0350'),
]
operations = [
migrations.CreateModel(
name='Service',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('service_type', models.CharField(blank=True, max_length=255, unique=True)),
('time_regen', models.DurationField()),
],
),
migrations.CreateModel(
name='Service_link',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('last_regen', models.DateTimeField()),
('asked_regen', models.BooleanField()),
('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='machines.Interface')),
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='machines.Service')),
],
),
migrations.AddField(
model_name='service',
name='servers',
field=models.ManyToManyField(through='machines.Service_link', to='machines.Interface'),
),
]

View file

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-08-08 01:48
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('machines', '0044_auto_20170808_0233'),
]
operations = [
migrations.AlterField(
model_name='service_link',
name='asked_regen',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='service_link',
name='last_regen',
field=models.DateTimeField(auto_now_add=True),
),
]

View file

@ -25,6 +25,7 @@ from django.db.models.signals import post_save, pre_delete, post_delete
from django.dispatch import receiver from django.dispatch import receiver
from django.forms import ValidationError from django.forms import ValidationError
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils import timezone
from macaddress.fields import MACAddressField from macaddress.fields import MACAddressField
from netaddr import mac_bare, EUI, IPSet, IPNetwork from netaddr import mac_bare, EUI, IPSet, IPNetwork
from django.core.validators import MinValueValidator,MaxValueValidator from django.core.validators import MinValueValidator,MaxValueValidator
@ -269,6 +270,48 @@ class IpList(models.Model):
def __str__(self): def __str__(self):
return self.ipv4 return self.ipv4
class Service(models.Model):
""" Definition d'un service (dhcp, dns, etc)"""
service_type = models.CharField(max_length=255, blank=True, unique=True)
time_regen = models.DurationField()
servers = models.ManyToManyField('Interface', through='Service_link')
def ask_regen(self):
for serv in Service_link.objects.filter(service=self):
serv.asked_regen = True
serv.save()
def process_link(self, servers):
for serv in servers.exclude(pk__in=Interface.objects.filter(service=self)):
link = Service_link(service=self, server=serv)
link.save()
for serv in Service_link.objects.filter(service=self).exclude(server__in=servers):
serv.delete()
return
def save(self, *args, **kwargs):
super(Service, self).save(*args, **kwargs)
def __str__(self):
return str(self.service_type)
class Service_link(models.Model):
""" Definition du lien entre serveurs et services"""
service = models.ForeignKey('Service', on_delete=models.CASCADE)
server = models.ForeignKey('Interface', on_delete=models.CASCADE)
last_regen = models.DateTimeField(auto_now_add=True)
asked_regen = models.BooleanField(default=False)
def need_regen(self):
if self.asked_regen and (self.last_regen + self.service.time_regen) > timezone.now():
return True
else:
return False
def __str__(self):
return str(self.server) + " " + str(self.service)
@receiver(post_save, sender=Machine) @receiver(post_save, sender=Machine)
def machine_post_save(sender, **kwargs): def machine_post_save(sender, **kwargs):
user = kwargs['instance'].user user = kwargs['instance'].user

View file

@ -0,0 +1,47 @@
{% 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 du service</th>
<th>Serveur</th>
<th>Dernière régénération</th>
<th>Régénération nécessaire</th>
<th>Régénération activée</th>
</tr>
</thead>
{% for server in servers_list %}
<tr>
<td>{{ server.service }}</td>
<td>{{ server.server }}</td>
<td>{{ server.last_regen }}</td>
<td>{{ server.asked_regen }}</td>
<td>{{ server.need_regen }}</td>
<td class="text-right">
</td>
</tr>
{% endfor %}
</table>

View file

@ -0,0 +1,49 @@
{% 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 du service</th>
<th>Temps minimum de régénération</th>
<th>Serveurs inclus</th>
<th></th>
<th></th>
</tr>
</thead>
{% for service in service_list %}
<tr>
<td>{{ service.service_type }}</td>
<td>{{ service.time_regen }}</td>
<td>{% for serv in service.servers.all %}{{ serv }}, {% endfor %}</td>
<td class="text-right">
{% if is_infra %}
{% include 'buttons/edit.html' with href='machines:edit-service' id=service.id %}
{% endif %}
{% include 'buttons/history.html' with href='machines:history' name='service' id=service.id %}
</td>
</tr>
{% endfor %}
</table>

View file

@ -0,0 +1,43 @@
{% extends "machines/sidebar.html" %}
{% 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 %}
{% load bootstrap3 %}
{% block title %}Machines{% endblock %}
{% block content %}
<h2>Liste des services</h2>
{% if is_infra %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-service' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un service</a>
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-service' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un ou plusieurs service</a>
{% endif %}
{% include "machines/aff_service.html" with service_list=service_list %}
<h2>Etat des serveurs</h2>
{% include "machines/aff_servers.html" with servers_list=servers_list %}
<br />
<br />
<br />
{% endblock %}

View file

@ -42,5 +42,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Plages d'IP Plages d'IP
</a> </a>
<a class="list-group-item list-group-item-info" href="{% url "machines:index-service" %}">
<i class="glyphicon glyphicon-list"></i>
Services (dhcp, dns...)
</a>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View file

@ -52,6 +52,10 @@ urlpatterns = [
url(r'^edit_alias/(?P<aliasid>[0-9]+)$', views.edit_alias, name='edit-alias'), url(r'^edit_alias/(?P<aliasid>[0-9]+)$', views.edit_alias, name='edit-alias'),
url(r'^del_alias/(?P<interfaceid>[0-9]+)$', views.del_alias, name='del-alias'), url(r'^del_alias/(?P<interfaceid>[0-9]+)$', views.del_alias, name='del-alias'),
url(r'^index_alias/(?P<interfaceid>[0-9]+)$', views.index_alias, name='index-alias'), url(r'^index_alias/(?P<interfaceid>[0-9]+)$', views.index_alias, name='index-alias'),
url(r'^add_service/$', views.add_service, name='add-service'),
url(r'^edit_service/(?P<serviceid>[0-9]+)$', views.edit_service, name='edit-service'),
url(r'^del_service/$', views.del_service, name='del-service'),
url(r'^index_service/$', views.index_service, name='index-service'),
url(r'^history/(?P<object>machine)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>machine)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>interface)/(?P<id>[0-9]+)$', views.history, name='history'), 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>machinetype)/(?P<id>[0-9]+)$', views.history, name='history'),
@ -60,6 +64,7 @@ urlpatterns = [
url(r'^history/(?P<object>ns)/(?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>iptype)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>iptype)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>alias)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>alias)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>service)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^$', views.index, name='index'), url(r'^$', views.index, name='index'),
url(r'^rest/mac-ip/$', views.mac_ip, name='mac-ip'), url(r'^rest/mac-ip/$', views.mac_ip, name='mac-ip'),
url(r'^rest/login/$', views.login_user, name='login'), url(r'^rest/login/$', views.login_user, name='login'),

View file

@ -44,8 +44,8 @@ from reversion.models import Version
import re import re
from .forms import NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm, MachineTypeForm, DelMachineTypeForm, ExtensionForm, DelExtensionForm, BaseEditInterfaceForm, BaseEditMachineForm from .forms import NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm, MachineTypeForm, DelMachineTypeForm, ExtensionForm, DelExtensionForm, BaseEditInterfaceForm, BaseEditMachineForm
from .forms import EditIpTypeForm, IpTypeForm, DelIpTypeForm, DomainForm, AliasForm, DelAliasForm, NsForm, DelNsForm, MxForm, DelMxForm from .forms import EditIpTypeForm, IpTypeForm, DelIpTypeForm, DomainForm, AliasForm, DelAliasForm, NsForm, DelNsForm, MxForm, DelMxForm, ServiceForm, DelServiceForm
from .models import IpType, Machine, Interface, IpList, MachineType, Extension, Mx, Ns, Domain from .models import IpType, Machine, Interface, IpList, MachineType, Extension, Mx, Ns, Domain, Service, Service_link
from users.models import User from users.models import User
from users.models import all_has_access from users.models import all_has_access
from preferences.models import GeneralOption, OptionalMachine from preferences.models import GeneralOption, OptionalMachine
@ -543,6 +543,55 @@ def del_alias(request, interfaceid):
return redirect("/machines/index_alias/" + str(interfaceid)) return redirect("/machines/index_alias/" + str(interfaceid))
return form({'machineform': alias, 'interfaceform': None}, 'machines/machine.html', request) return form({'machineform': alias, 'interfaceform': None}, 'machines/machine.html', request)
@login_required
@permission_required('infra')
def add_service(request):
service = ServiceForm(request.POST or None)
if service.is_valid():
with transaction.atomic(), reversion.create_revision():
service.save()
reversion.set_user(request.user)
reversion.set_comment("Création")
messages.success(request, "Cet enregistrement service a été ajouté")
return redirect("/machines/index_service")
return form({'machineform': service}, 'machines/machine.html', request)
@login_required
@permission_required('infra')
def edit_service(request, serviceid):
try:
service_instance = Service.objects.get(pk=serviceid)
except Ns.DoesNotExist:
messages.error(request, u"Entrée inexistante" )
return redirect("/machines/index_extension/")
service = ServiceForm(request.POST or None, instance=service_instance)
if service.is_valid():
with transaction.atomic(), reversion.create_revision():
service.save()
reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in service.changed_data))
messages.success(request, "Service modifié")
return redirect("/machines/index_service/")
return form({'machineform': service}, 'machines/machine.html', request)
@login_required
@permission_required('infra')
def del_service(request):
service = DelServiceForm(request.POST or None)
if service.is_valid():
service_dels = service.cleaned_data['service']
for service_del in service_dels:
try:
with transaction.atomic(), reversion.create_revision():
service_del.delete()
reversion.set_user(request.user)
messages.success(request, "Le service a été supprimée")
except ProtectedError:
messages.error(request, "Erreur le service suivant %s ne peut être supprimé" % service_del)
return redirect("/machines/index_service")
return form({'machineform': service}, 'machines/machine.html', request)
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def index(request): def index(request):
@ -594,6 +643,13 @@ def index_alias(request, interfaceid):
alias_list = Domain.objects.filter(cname=Domain.objects.filter(interface_parent=interface)).order_by('name') alias_list = Domain.objects.filter(cname=Domain.objects.filter(interface_parent=interface)).order_by('name')
return render(request, 'machines/index_alias.html', {'alias_list':alias_list, 'interface_id': interfaceid}) return render(request, 'machines/index_alias.html', {'alias_list':alias_list, 'interface_id': interfaceid})
@login_required
@permission_required('cableur')
def index_service(request):
service_list = Service.objects.all()
servers_list = Service_link.objects.all()
return render(request, 'machines/index_service.html', {'service_list':service_list, 'servers_list':servers_list})
@login_required @login_required
def history(request, object, id): def history(request, object, id):
if object == 'machine': if object == 'machine':
@ -653,6 +709,12 @@ def history(request, object, id):
except Ns.DoesNotExist: except Ns.DoesNotExist:
messages.error(request, "Ns inexistant") messages.error(request, "Ns inexistant")
return redirect("/machines/") return redirect("/machines/")
elif object == 'service' and request.user.has_perms(('cableur',)):
try:
object_instance = Service.objects.get(pk=id)
except Service.DoesNotExist:
messages.error(request, "Service inexistant")
return redirect("/machines/")
else: else:
messages.error(request, "Objet inconnu") messages.error(request, "Objet inconnu")
return redirect("/machines/") return redirect("/machines/")