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

Merge branch 'infra2.0'

This commit is contained in:
guimoz 2017-09-02 12:37:08 +02:00 committed by root
commit a0605088b4
14 changed files with 407 additions and 12 deletions

View file

@ -24,7 +24,10 @@
from django.contrib import admin from django.contrib import admin
from reversion.admin import VersionAdmin from reversion.admin import VersionAdmin
from .models import Port, Room, Switch from .models import Port, Room, Switch, Stack
class StackAdmin(VersionAdmin):
list_display = ('name', 'stack_id', 'details')
class SwitchAdmin(VersionAdmin): class SwitchAdmin(VersionAdmin):
list_display = ('switch_interface','location','number','details') list_display = ('switch_interface','location','number','details')
@ -38,3 +41,4 @@ class RoomAdmin(VersionAdmin):
admin.site.register(Port, PortAdmin) admin.site.register(Port, PortAdmin)
admin.site.register(Room, RoomAdmin) admin.site.register(Room, RoomAdmin)
admin.site.register(Switch, SwitchAdmin) admin.site.register(Switch, SwitchAdmin)
admin.site.register(Stack, StackAdmin)

View file

@ -20,7 +20,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from .models import Port, Switch, Room from .models import Port, Switch, Room, Stack
from django.forms import ModelForm, Form from django.forms import ModelForm, Form
from machines.models import Interface from machines.models import Interface
@ -42,6 +42,11 @@ class AddPortForm(ModelForm):
class Meta(PortForm.Meta): class Meta(PortForm.Meta):
fields = ['port', 'room', 'machine_interface', 'related', 'radius', 'details'] fields = ['port', 'room', 'machine_interface', 'related', 'radius', 'details']
class StackForm(ModelForm):
class Meta:
model = Stack
fields = '__all__'
class EditSwitchForm(ModelForm): class EditSwitchForm(ModelForm):
class Meta: class Meta:
model = Switch model = Switch
@ -54,9 +59,10 @@ class EditSwitchForm(ModelForm):
class NewSwitchForm(ModelForm): class NewSwitchForm(ModelForm):
class Meta(EditSwitchForm.Meta): class Meta(EditSwitchForm.Meta):
fields = ['location', 'number', 'details'] fields = ['location', 'number', 'details', 'stack', 'stack_member_id']
class EditRoomForm(ModelForm): class EditRoomForm(ModelForm):
class Meta: class Meta:
model = Room model = Room
fields = '__all__' fields = '__all__'

View file

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-08-17 14:54
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('topologie', '0022_auto_20161211_1622'),
]
operations = [
migrations.CreateModel(
name='Stack',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, max_length=32, null=True)),
('stack_id', models.CharField(max_length=32, unique=True)),
('details', models.CharField(blank=True, max_length=255, null=True)),
('member_id_min', models.IntegerField()),
('member_id_max', models.IntegerField()),
],
),
migrations.AddField(
model_name='switch',
name='stack_member_id',
field=models.IntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='switch',
name='stack',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='topologie.Stack'),
),
migrations.AlterUniqueTogether(
name='switch',
unique_together=set([('stack', 'stack_member_id')]),
),
]

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-08-18 08:21
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('topologie', '0023_auto_20170817_1654'),
]
operations = [
migrations.AlterField(
model_name='switch',
name='stack',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.Stack'),
),
]

View file

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-09-02 10:42
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('topologie', '0024_auto_20170818_1021'),
('topologie', '0024_auto_20170826_1800'),
]
operations = [
]

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-09-02 10:45
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('topologie', '0025_merge_20170902_1242'),
]
operations = [
migrations.AlterField(
model_name='port',
name='radius',
field=models.CharField(choices=[('NO', 'NO'), ('STRICT', 'STRICT'), ('BLOQ', 'BLOQ'), ('COMMON', 'COMMON'), ('3', '3'), ('7', '7'), ('8', '8'), ('13', '13'), ('20', '20'), ('42', '42'), ('69', '69')], default='NO', max_length=32),
),
]

View file

@ -21,6 +21,8 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.db import models from django.db import models
from django.db.models.signals import post_delete
from django.dispatch import receiver
from django.forms import ModelForm, Form from django.forms import ModelForm, Form
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
@ -39,6 +41,29 @@ def clean_port_related(port):
related_port.related = None related_port.related = None
related_port.save() related_port.save()
class Stack(models.Model):
PRETTY_NAME = "Stack de switchs"
name = models.CharField(max_length=32, blank=True, null=True)
stack_id = models.CharField(max_length=32, unique=True)
details = models.CharField(max_length=255, blank=True, null=True)
member_id_min = models.IntegerField()
member_id_max = models.IntegerField()
def __str__(self):
return " ".join([self.name, self.stack_id])
def save(self, *args, **kwargs):
if not self.name:
self.name = self.stack_id
super(Stack, self).save(*args, **kwargs)
def clean(self):
if self.member_id_max < self.member_id_min:
import traceback
traceback.print_exc()
raise ValidationError({'member_id_max':"L'id maximale est inférieure à l'id minimale"})
class Switch(models.Model): class Switch(models.Model):
PRETTY_NAME = "Switch / Commutateur" PRETTY_NAME = "Switch / Commutateur"
@ -46,10 +71,23 @@ class Switch(models.Model):
location = models.CharField(max_length=255) location = models.CharField(max_length=255)
number = models.IntegerField() number = models.IntegerField()
details = models.CharField(max_length=255, blank=True) details = models.CharField(max_length=255, blank=True)
stack = models.ForeignKey(Stack, blank=True, null=True, on_delete=models.SET_NULL)
stack_member_id = models.IntegerField(blank=True, null=True)
class Meta:
unique_together = ('stack','stack_member_id')
def __str__(self): def __str__(self):
return str(self.location) + ' ' + str(self.switch_interface) return str(self.location) + ' ' + str(self.switch_interface)
def clean(self):
if self.stack is not None:
if self.stack_member_id is not None:
if (self.stack_member_id > self.stack.member_id_max) or (self.stack_member_id < self.stack.member_id_min):
raise ValidationError({'stack_member_id': "L'id de ce switch est en dehors des bornes permises pas la stack"})
else:
raise ValidationError({'stack_member_id': "L'id dans la stack ne peut être nul"})
class Port(models.Model): class Port(models.Model):
PRETTY_NAME = "Port de switch" PRETTY_NAME = "Port de switch"
STATES_BASE = ( STATES_BASE = (
@ -102,3 +140,6 @@ class Room(models.Model):
def __str__(self): def __str__(self):
return str(self.name) return str(self.name)
@receiver(post_delete, sender=Stack)
def stack_post_delete(sender, **kwargs):
Switch.objects.filter(stack=None).update(stack_member_id = None)

View file

@ -0,0 +1,53 @@
{% 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>Stack</th>
<th>ID</th>
<th>Details</th>
</tr>
</thead>
{% for stack in stack_list %}
<tr>
<td>{{stack.name}}</td>
<td>{{stack.stack_id}}</td>
<td>{{stack.details}}</td>
<td class="text-right">
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'stack' stack.pk %}">
<i class="glyphicon glyphicon-time"></i>
</a>
{% if is_infra %}
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-stack' stack.id %}">
<i class="glyphicon glyphicon-edit"></i>
</a>
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}">
<i class="glyphicon glyphicon-trash"></i>
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</table>

View file

@ -29,6 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<th>Ipv4</th> <th>Ipv4</th>
<th>Localisation</th> <th>Localisation</th>
<th>Ports</th> <th>Ports</th>
<th>Stack</th>
<th>id interne Stack</th>
<th>Détails</th> <th>Détails</th>
<th></th> <th></th>
</tr> </tr>
@ -43,6 +45,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{switch.switch_interface.ipv4}}</td> <td>{{switch.switch_interface.ipv4}}</td>
<td>{{switch.location}}</td> <td>{{switch.location}}</td>
<td>{{switch.number}}</td> <td>{{switch.number}}</td>
<td>{{switch.stack.name}}</td>
<td>{{switch.stack_member_id}}</td>
<td>{{switch.details}}</td> <td>{{switch.details}}</td>
<td class="text-right"> <td class="text-right">
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'switch' switch.pk %}"> <a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'switch' switch.pk %}">

View file

@ -0,0 +1,53 @@
{% 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>Stack</th>
<th>ID</th>
<th>Details</th>
</tr>
</thead>
{% for stack in stack_list %}
<tr>
<td>{{stack.name}}</td>
<td>{{stack.stack_id}}</td>
<td>{{stack.details}}</td>
<td class="text-right">
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'stack' stack.pk %}">
<i class="glyphicon glyphicon-time"></i>
</a>
{% if is_infra %}
<a class="btn btn-primary btn-sm" role="button" title="Éditer" href="{% url 'topologie:edit-stack' stack.id %}">
<i class="glyphicon glyphicon-edit"></i>
</a>
<a class="btn btn-danger btn-sm" role="button" title="Supprimer" href="{% url 'topologie:del-stack' stack.pk %}">
<i class="glyphicon glyphicon-trash"></i>
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</table>

View file

@ -0,0 +1,39 @@
{% extends "topologie/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 %}Stacks{% endblock %}
{% block content %}
<h2>Stacks</h2>
{% if is_infra %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-stack' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter une stack</a>
{% endif %}
{% include "topologie/aff_stacks.html" with stack_list=stack_list %}
<br />
<br />
<br />
{% endblock %}

View file

@ -29,8 +29,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Chambres Chambres
</a> </a>
<a class="list-group-item list-group-item-info" href="{% url "topologie:index" %}"> <a class="list-group-item list-group-item-info" href="{% url "topologie:index" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Switchs Switchs
</a> </a>
<a class="list-group-item list-group-item-info" href="{% url "topologie:index-stack" %}">
<i class="glyphicon glyphicon-list"></i>
Stacks
</a>
{% endblock %} {% endblock %}

View file

@ -35,8 +35,13 @@ urlpatterns = [
url(r'^history/(?P<object>switch)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>switch)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>port)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>port)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>room)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>room)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>stack)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^edit_port/(?P<port_id>[0-9]+)$', views.edit_port, name='edit-port'), url(r'^edit_port/(?P<port_id>[0-9]+)$', views.edit_port, name='edit-port'),
url(r'^new_port/(?P<switch_id>[0-9]+)$', views.new_port, name='new-port'), url(r'^new_port/(?P<switch_id>[0-9]+)$', views.new_port, name='new-port'),
url(r'^edit_switch/(?P<switch_id>[0-9]+)$', views.edit_switch, name='edit-switch'), url(r'^edit_switch/(?P<switch_id>[0-9]+)$', views.edit_switch, name='edit-switch'),
url(r'^new_stack/$', views.new_stack, name='new-stack'),
url(r'^index_stack/$', views.index_stack, name='index-stack'),
url(r'^edit_stack/(?P<stack_id>[0-9]+)$', views.edit_stack, name='edit-stack'),
url(r'^del_stack/(?P<stack_id>[0-9]+)$', views.del_stack, name='del-stack'),
] ]

View file

@ -25,12 +25,13 @@ from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required, permission_required
from django.db import IntegrityError from django.db import IntegrityError
from django.db import transaction from django.db import transaction
from django.db.models import ProtectedError
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from reversion import revisions as reversion from reversion import revisions as reversion
from reversion.models import Version from reversion.models import Version
from topologie.models import Switch, Port, Room from topologie.models import Switch, Port, Room, Stack
from topologie.forms import EditPortForm, NewSwitchForm, EditSwitchForm, AddPortForm, EditRoomForm from topologie.forms import EditPortForm, NewSwitchForm, EditSwitchForm, AddPortForm, EditRoomForm, StackForm
from users.views import form from users.views import form
from users.models import User from users.models import User
@ -41,7 +42,7 @@ from preferences.models import AssoOption, GeneralOption
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def index(request): def index(request):
switch_list = Switch.objects.order_by('location').select_related('switch_interface__domain__extension').select_related('switch_interface__ipv4').select_related('switch_interface__domain') switch_list = Switch.objects.order_by('stack','stack_member_id','location').select_related('switch_interface__domain__extension').select_related('switch_interface__ipv4').select_related('switch_interface__domain')
return render(request, 'topologie/index.html', {'switch_list': switch_list}) return render(request, 'topologie/index.html', {'switch_list': switch_list})
@login_required @login_required
@ -65,6 +66,12 @@ def history(request, object, id):
except Room.DoesNotExist: except Room.DoesNotExist:
messages.error(request, "Chambre inexistante") messages.error(request, "Chambre inexistante")
return redirect("/topologie/") return redirect("/topologie/")
elif object == 'stack':
try:
object_instance = Stack.objects.get(pk=id)
except Room.DoesNotExist:
messages.error(request, "Stack inexistante")
return redirect("/topologie/")
else: else:
messages.error(request, "Objet inconnu") messages.error(request, "Objet inconnu")
return redirect("/topologie/") return redirect("/topologie/")
@ -100,6 +107,13 @@ def index_room(request):
room_list = Room.objects.order_by('name') room_list = Room.objects.order_by('name')
return render(request, 'topologie/index_room.html', {'room_list': room_list}) return render(request, 'topologie/index_room.html', {'room_list': room_list})
@login_required
@permission_required('infra')
def index_stack(request):
stack_list = Stack.objects.order_by('name')
return render(request, 'topologie/index_stack.html', {'stack_list': stack_list})
@login_required @login_required
@permission_required('infra') @permission_required('infra')
def new_port(request, switch_id): def new_port(request, switch_id):
@ -141,6 +155,77 @@ def edit_port(request, port_id):
return redirect("/topologie/switch/" + str(port_object.switch.id)) return redirect("/topologie/switch/" + str(port_object.switch.id))
return form({'topoform':port}, 'topologie/topo.html', request) return form({'topoform':port}, 'topologie/topo.html', request)
@login_required
@permission_required('infra')
def new_stack(request):
stack = StackForm(request.POST or None)
#if stack.is_valid():
if request.POST:
try:
with transaction.atomic(), reversion.create_revision():
stack.save()
reversion.set_user(request.user)
reversion.set_comment("Création")
messages.success(request, "Stack crée")
except:
messages.error(request, "Cette stack existe déjà")
return form({'topoform':stack}, 'topologie/topo.html', request)
@login_required
@permission_required('infra')
def edit_stack(request,stack_id):
try:
stack = Stack.objects.get(pk=stack_id)
except Stack.DoesNotExist:
messages.error(request, u"Stack inexistante")
return redirect('/topologie/index_stack/')
stack = StackForm(request.POST or None, instance=stack)
if stack.is_valid():
with transaction.atomic(), reversion.create_revision():
stack.save()
reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in stack.changed_data))
return redirect('/topologie/index_stack')
return form({'topoform':stack}, 'topologie/topo.html', request)
@login_required
@permission_required('infra')
def del_stack(request,stack_id):
try:
stack = Stack.objects.get(pk=stack_id)
except Stack.DoesNotExist:
messages.error(request, u"Stack inexistante")
return redirect('/topologie/index_stack')
if request.method == "POST":
try:
with transaction.atomic(), reversion.create_revision():
stack.delete()
reversion.set_user(request.user)
reversion.set_comment("Destruction")
messages.success(request, "La stack a eté détruite")
except ProtectedError:
messages.error(request, "La stack %s est affectée à un autre objet, impossible de la supprimer" % stack)
return redirect('/topologie/index_stack')
return form({'objet':stack}, 'topologie/delete.html', request)
@login_required
@permission_required('infra')
def edit_switchs_stack(request,stack_id):
try:
stack = Stack.objects.get(pk=stack_id)
except Stack.DoesNotExist:
messages.error(request, u"Stack inexistante")
return redirect('/topologie/index_stack')
if request.method == "POST":
pass
else:
context = {'stack': stack}
context['switchs_stack'] = stack.switchs_set.all()
context['switchs_autres'] = Switch.object.filter(stack=None)
pass
@login_required @login_required
@permission_required('infra') @permission_required('infra')
def new_switch(request): def new_switch(request):
@ -259,10 +344,13 @@ def del_room(request, room_id):
messages.error(request, u"Chambre inexistante" ) messages.error(request, u"Chambre inexistante" )
return redirect("/topologie/index_room/") return redirect("/topologie/index_room/")
if request.method == "POST": if request.method == "POST":
with transaction.atomic(), reversion.create_revision(): try:
room.delete() with transaction.atomic(), reversion.create_revision():
reversion.set_user(request.user) room.delete()
reversion.set_comment("Destruction") reversion.set_user(request.user)
messages.success(request, "La chambre/prise a été détruite") reversion.set_comment("Destruction")
messages.success(request, "La chambre/prise a été détruite")
except ProtectedError:
messages.error(request, "La chambre %s est affectée à un autre objet, impossible de la supprimer (switch ou user)" % room)
return redirect("/topologie/index_room/") return redirect("/topologie/index_room/")
return form({'objet': room, 'objet_name': 'Chambre'}, 'topologie/delete.html', request) return form({'objet': room, 'objet_name': 'Chambre'}, 'topologie/delete.html', request)