diff --git a/re2o/utils.py b/re2o/utils.py index 739dd200..2560e6c2 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -216,6 +216,15 @@ class SortTable: 'stack_id': ['stack_id'], 'default': ['stack_id'], } + TOPOLOGIE_INDEX_MODEL_SWITCH = { + 'model_switch_name': ['reference'], + 'model_switch__contructor' : ['constructor__name'], + 'default': ['reference'], + } + TOPOLOGIE_INDEX_CONSTRUCTOR_SWITCH = { + 'room_name': ['name'], + 'default': ['name'], + } LOGS_INDEX = { 'sum_date': ['revision__date_created'], 'default': ['-revision__date_created'], diff --git a/topologie/admin.py b/topologie/admin.py index bfc2a393..a4591222 100644 --- a/topologie/admin.py +++ b/topologie/admin.py @@ -29,7 +29,7 @@ from __future__ import unicode_literals from django.contrib import admin from reversion.admin import VersionAdmin -from .models import Port, Room, Switch, Stack +from .models import Port, Room, Switch, Stack, ModelSwitch, ConstructorSwitch class StackAdmin(VersionAdmin): @@ -52,7 +52,19 @@ class RoomAdmin(VersionAdmin): pass +class ModelSwitchAdmin(VersionAdmin): + """Administration d'un modèle de switch""" + pass + + +class ConstructorSwitchAdmin(VersionAdmin): + """Administration d'un constructeur d'un switch""" + pass + + admin.site.register(Port, PortAdmin) admin.site.register(Room, RoomAdmin) admin.site.register(Switch, SwitchAdmin) admin.site.register(Stack, StackAdmin) +admin.site.register(ModelSwitch, ModelSwitchAdmin) +admin.site.register(ConstructorSwitch, ConstructorSwitchAdmin) diff --git a/topologie/forms.py b/topologie/forms.py index 958c6008..0119435d 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -34,8 +34,8 @@ from __future__ import unicode_literals from machines.models import Interface from django import forms -from django.forms import ModelForm, Form -from .models import Port, Switch, Room, Stack +from django.forms import ModelForm +from .models import Port, Switch, Room, Stack, ModelSwitch, ConstructorSwitch class PortForm(ModelForm): @@ -126,7 +126,8 @@ class NewSwitchForm(ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(NewSwitchForm, self).__init__(*args, prefix=prefix, **kwargs) - + self.fields['location'].label = 'Localisation' + self.fields['number'].label = 'Nombre de ports' class EditRoomForm(ModelForm): """Permet d'éediter le nom et commentaire d'une prise murale""" @@ -145,4 +146,23 @@ class CreatePortsForm(Form): end = forms.IntegerField(label="Fin :", min_value=0) - +class EditModelSwitchForm(ModelForm): + """Permet d'éediter un modèle de switch : nom et constructeur""" + class Meta: + model = ModelSwitch + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(EditModelSwitchForm, self).__init__(*args, prefix=prefix, **kwargs) + + +class EditConstructorSwitchForm(ModelForm): + """Permet d'éediter le nom d'un constructeur""" + class Meta: + model = ConstructorSwitch + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(EditConstructorSwitchForm, self).__init__(*args, prefix=prefix, **kwargs) diff --git a/topologie/migrations/0032_auto_20171026_0338.py b/topologie/migrations/0032_auto_20171026_0338.py new file mode 100644 index 00000000..37548306 --- /dev/null +++ b/topologie/migrations/0032_auto_20171026_0338.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-10-26 01:38 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0031_auto_20171015_2033'), + ] + + operations = [ + migrations.CreateModel( + name='ConstructorSwitch', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name='ModelSwitch', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('reference', models.CharField(max_length=255)), + ('constructor', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='topologie.ConstructorSwitch')), + ], + ), + migrations.AddField( + model_name='switch', + name='model', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.ModelSwitch'), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index 924002b3..0f277434 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -93,12 +93,18 @@ class Switch(models.Model): number = models.PositiveIntegerField() details = models.CharField(max_length=255, blank=True) stack = models.ForeignKey( - Stack, + 'topologie.Stack', blank=True, null=True, on_delete=models.SET_NULL ) stack_member_id = models.PositiveIntegerField(blank=True, null=True) + model = models.ForeignKey( + 'topologie.ModelSwitch', + blank=True, + null=True, + on_delete=models.SET_NULL + ) class Meta: unique_together = ('stack', 'stack_member_id') @@ -120,6 +126,27 @@ class Switch(models.Model): raise ValidationError({'stack_member_id': "L'id dans la stack\ ne peut être nul"}) + +class ModelSwitch(models.Model): + """Un modèle (au sens constructeur) de switch""" + reference = models.CharField(max_length=255) + constructor = models.ForeignKey( + 'topologie.ConstructorSwitch', + on_delete=models.PROTECT + ) + + def __str__(self): + return str(self.constructor) + ' ' + str(self.reference) + + +class ConstructorSwitch(models.Model): + """Un constructeur de switch""" + name = models.CharField(max_length=255) + + def __str__(self): + return str(self.name) + + class Port(models.Model): """ Definition d'un port. Relié à un switch(foreign_key), un port peut etre relié de manière exclusive à : diff --git a/topologie/templates/topologie/aff_constructor_switch.html b/topologie/templates/topologie/aff_constructor_switch.html new file mode 100644 index 00000000..02002f6c --- /dev/null +++ b/topologie/templates/topologie/aff_constructor_switch.html @@ -0,0 +1,54 @@ +{% 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 %} + +{% if constructor_switch_list.paginator %} +{% include "pagination.html" with list=constructor_switch_list %} +{% endif %} + + + + + + + + + {% for constructor_switch in constructor_switch_list %} + + + + + {% endfor %} +
{% include "buttons/sort.html" with prefix='constructor-switch' col='name' text='Constructeur' %}
{{constructor_switch}} + + + + {% if is_infra %} + + + + + + + {% endif %} +
diff --git a/topologie/templates/topologie/aff_model_switch.html b/topologie/templates/topologie/aff_model_switch.html new file mode 100644 index 00000000..2e84fb69 --- /dev/null +++ b/topologie/templates/topologie/aff_model_switch.html @@ -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 %} + +{% if model_switch_list.paginator %} +{% include "pagination.html" with list=model_switch_list %} +{% endif %} + + + + + + + + + + {% for model_switch in model_switch_list %} + + + + + + {% endfor %} +
{% include "buttons/sort.html" with prefix='model-switch' col='reference' text='Référence' %}{% include "buttons/sort.html" with prefix='model-switch' col='constructor' text='Constructeur' %}
{{model_switch.reference}}{{model_switch.constructor}} + + + + {% if is_infra %} + + + + + + + {% endif %} +
diff --git a/topologie/templates/topologie/aff_switch.html b/topologie/templates/topologie/aff_switch.html index b533edec..25f466e8 100644 --- a/topologie/templates/topologie/aff_switch.html +++ b/topologie/templates/topologie/aff_switch.html @@ -22,6 +22,10 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. {% endcomment %} +{% if switch_list.paginator %} +{% include "pagination.html" with list=switch_list %} +{% endif %} + @@ -30,7 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc., - + + @@ -47,6 +52,7 @@ with this program; if not, write to the Free Software Foundation, Inc., +
{% include "buttons/sort.html" with prefix='switch' col='loc' text='Localisation' %} {% include "buttons/sort.html" with prefix='switch' col='ports' text='Ports' %} {% include "buttons/sort.html" with prefix='switch' col='stack' text='Stack' %}Id interne stackId stackModèle Détails
{{switch.number}} {{switch.stack.name}} {{switch.stack_member_id}}{{switch.model}} {{switch.details}} {% include 'buttons/history.html' with href='topologie:history' name='switch' id=switch.pk%} diff --git a/topologie/templates/topologie/index.html b/topologie/templates/topologie/index.html index 72b522d0..6b17b6de 100644 --- a/topologie/templates/topologie/index.html +++ b/topologie/templates/topologie/index.html @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

Switchs

{% if is_infra %} Ajouter un switch +
{% endif %} {% include "topologie/aff_switch.html" with switch_list=switch_list %}
diff --git a/topologie/templates/topologie/index_model_switch.html b/topologie/templates/topologie/index_model_switch.html new file mode 100644 index 00000000..784b5ea6 --- /dev/null +++ b/topologie/templates/topologie/index_model_switch.html @@ -0,0 +1,46 @@ +{% 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 %}Modèles de switches{% endblock %} + +{% block content %} +

Modèles de switches

+{% if is_infra %} + Ajouter un modèle +
+{% endif %} +{% include "topologie/aff_model_switch.html" with model_switch_list=model_switch_list %} +

Constructeurs de switches

+{% if is_infra %} + Ajouter un constructeur +
+{% endif %} +{% include "topologie/aff_constructor_switch.html" with constructor_switch_list=constructor_switch_list %} +
+
+
+{% endblock %} diff --git a/topologie/templates/topologie/sidebar.html b/topologie/templates/topologie/sidebar.html index 833af2e3..a2d42896 100644 --- a/topologie/templates/topologie/sidebar.html +++ b/topologie/templates/topologie/sidebar.html @@ -37,4 +37,8 @@ with this program; if not, write to the Free Software Foundation, Inc., Stacks + + + Modèles switches et constructeurs + {% endblock %} diff --git a/topologie/templates/topologie/switch.html b/topologie/templates/topologie/switch.html index e87570c4..1753161e 100644 --- a/topologie/templates/topologie/switch.html +++ b/topologie/templates/topologie/switch.html @@ -47,12 +47,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% csrf_token %} {% if topoform %} +

Réglage spécifiques du switch

{% massive_bootstrap_form topoform 'switch_interface' %} {% endif %} {% if machineform %} +

Réglages généraux de la machine associée au switch

{% massive_bootstrap_form machineform 'user' %} {% endif %} {% if interfaceform %} +

Réglages généraux de l'interface associée au switch

{% if i_mbf_param %} {% massive_bootstrap_form interfaceform 'ipv4,machine' mbf_param=i_mbf_param %} {% else %} @@ -60,6 +63,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} {% endif %} {% if domainform %} +

Nom de la machine

{% bootstrap_form domainform %} {% endif %} {% bootstrap_button "Créer ou modifier" button_type="submit" icon="ok" %} diff --git a/topologie/urls.py b/topologie/urls.py index 4b39d5c5..8f52758f 100644 --- a/topologie/urls.py +++ b/topologie/urls.py @@ -55,6 +55,12 @@ urlpatterns = [ url(r'^history/(?Pstack)/(?P[0-9]+)$', views.history, name='history'), + url(r'^history/(?Pmodel_switch)/(?P[0-9]+)$', + views.history, + name='history'), + url(r'^history/(?Pconstructor_switch)/(?P[0-9]+)$', + views.history, + name='history'), url(r'^edit_port/(?P[0-9]+)$', views.edit_port, name='edit-port'), url(r'^new_port/(?P[0-9]+)$', views.new_port, name='new-port'), url(r'^del_port/(?P[0-9]+)$', views.del_port, name='del-port'), @@ -69,4 +75,32 @@ urlpatterns = [ url(r'^del_stack/(?P[0-9]+)$', views.del_stack, name='del-stack'), + url(r'^index_model_switch/$', + views.index_model_switch, + name='index-model-switch' + ), + url(r'^index_model_switch/$', + views.index_model_switch, + name='index-model-switch' + ), + url(r'^new_model_switch/$', + views.new_model_switch, + name='new-model-switch' + ), + url(r'^edit_model_switch/(?P[0-9]+)$', + views.edit_model_switch, + name='edit-model-switch'), + url(r'^del_model_switch/(?P[0-9]+)$', + views.del_model_switch, + name='del-model-switch'), + url(r'^new_constructor_switch/$', + views.new_constructor_switch, + name='new-constructor-switch' + ), + url(r'^edit_constructor_switch/(?P[0-9]+)$', + views.edit_constructor_switch, + name='edit-constructor-switch'), + url(r'^del_constructor_switch/(?P[0-9]+)$', + views.del_constructor_switch, + name='del-constructor-switch'), ] diff --git a/topologie/views.py b/topologie/views.py index bad4d246..58a45866 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -47,13 +47,32 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from reversion import revisions as reversion from reversion.models import Version -from topologie.models import Switch, Port, Room, Stack +from topologie.models import ( + Switch, + Port, + Room, + Stack, + ModelSwitch, + ConstructorSwitch +) from topologie.forms import EditPortForm, NewSwitchForm, EditSwitchForm -from topologie.forms import AddPortForm, EditRoomForm, StackForm -from topologie.forms import CreatePortsForm +from topologie.forms import ( + AddPortForm, + EditRoomForm, + StackForm, + EditModelSwitchForm, + EditConstructorSwitchForm, + CreatePortsForm +) from users.views import form from re2o.utils import SortTable -from machines.forms import DomainForm, NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm +from machines.forms import ( + DomainForm, + NewMachineForm, + EditMachineForm, + EditInterfaceForm, + AddInterfaceForm +) from machines.views import generate_ipv4_mbf_param from preferences.models import AssoOption, GeneralOption @@ -73,6 +92,18 @@ def index(request): request.GET.get('order'), SortTable.TOPOLOGIE_INDEX ) + options, _created = GeneralOption.objects.get_or_create() + pagination_number = options.pagination_number + paginator = Paginator(switch_list, pagination_number) + page = request.GET.get('page') + try: + switch_list = paginator.page(page) + except PageNotAnInteger: + # If page is not an integer, deliver first page. + switch_list = paginator.page(1) + except EmptyPage: + # If page is out of range (e.g. 9999), deliver last page of results. + switch_list = paginator.page(paginator.num_pages) return render(request, 'topologie/index.html', { 'switch_list': switch_list }) @@ -106,6 +137,18 @@ def history(request, object_name, object_id): except Room.DoesNotExist: messages.error(request, "Stack inexistante") return redirect("/topologie/") + elif object_name == 'model_switch': + try: + object_instance = ModelSwitch.objects.get(pk=object_id) + except ModelSwitch.DoesNotExist: + messages.error(request, "SwitchModel inexistant") + return redirect("/topologie/") + elif object_name == 'constructor_switch': + try: + object_instance = ConstructorSwitch.objects.get(pk=object_id) + except ConstructorSwitch.DoesNotExist: + messages.error(request, "SwitchConstructor inexistant") + return redirect("/topologie/") else: messages.error(request, "Objet inconnu") return redirect("/topologie/") @@ -201,6 +244,30 @@ def index_stack(request): }) +@login_required +@permission_required('cableur') +def index_model_switch(request): + """ Affichage de l'ensemble des modèles de switches""" + model_switch_list = ModelSwitch.objects + constructor_switch_list = ConstructorSwitch.objects + model_switch_list = SortTable.sort( + model_switch_list, + request.GET.get('col'), + request.GET.get('order'), + SortTable.TOPOLOGIE_INDEX_MODEL_SWITCH + ) + constructor_switch_list = SortTable.sort( + constructor_switch_list, + request.GET.get('col'), + request.GET.get('order'), + SortTable.TOPOLOGIE_INDEX_CONSTRUCTOR_SWITCH + ) + return render(request, 'topologie/index_model_switch.html', { + 'model_switch_list': model_switch_list, + 'constructor_switch_list': constructor_switch_list, + }) + + @login_required @permission_required('infra') def new_port(request, switch_id): @@ -592,3 +659,129 @@ def del_room(request, room_id): 'objet': room, 'objet_name': 'Chambre' }, 'topologie/delete.html', request) + + +@login_required +@permission_required('infra') +def new_model_switch(request): + """Nouveau modèle de switch""" + model_switch = EditModelSwitchForm(request.POST or None) + if model_switch.is_valid(): + with transaction.atomic(), reversion.create_revision(): + model_switch.save() + reversion.set_user(request.user) + reversion.set_comment("Création") + messages.success(request, "Le modèle a été créé") + return redirect("/topologie/index_model_switch/") + return form({'topoform': model_switch}, 'topologie/topo.html', request) + + +@login_required +@permission_required('infra') +def edit_model_switch(request, model_switch_id): + """ Edition d'un modèle de switch""" + try: + model_switch = ModelSwitch.objects.get(pk=model_switch_id) + except ModelSwitch.DoesNotExist: + messages.error(request, u"Modèle inconnu") + return redirect("/topologie/index_model_switch/") + model_switch = EditModelSwitchForm(request.POST or None, instance=model_switch) + if model_switch.is_valid(): + with transaction.atomic(), reversion.create_revision(): + model_switch.save() + reversion.set_user(request.user) + reversion.set_comment("Champs modifié(s) : %s" % ', '.join( + field for field in model_switch.changed_data) + ) + messages.success(request, "Le modèle a bien été modifié") + return redirect("/topologie/index_model_switch/") + return form({'topoform': model_switch}, 'topologie/topo.html', request) + + +@login_required +@permission_required('infra') +def del_model_switch(request, model_switch_id): + """ Suppression d'un modèle de switch""" + try: + model_switch = ModelSwitch.objects.get(pk=model_switch_id) + except ModelSwitch.DoesNotExist: + messages.error(request, u"Modèle inexistant") + return redirect("/topologie/index_model_switch/") + if request.method == "POST": + try: + with transaction.atomic(), reversion.create_revision(): + model_switch.delete() + reversion.set_user(request.user) + reversion.set_comment("Destruction") + messages.success(request, "Le modèle a été détruit") + except ProtectedError: + messages.error(request, "Le modèle %s est affectée à un autre objet,\ + impossible de la supprimer (switch ou user)" % model_switch) + return redirect("/topologie/index_model_switch/") + return form({ + 'objet': model_switch, + 'objet_name': 'Modèle de switch' + }, 'topologie/delete.html', request) + + +@login_required +@permission_required('infra') +def new_constructor_switch(request): + """Nouveau constructeur de switch""" + constructor_switch = EditConstructorSwitchForm(request.POST or None) + if constructor_switch.is_valid(): + with transaction.atomic(), reversion.create_revision(): + constructor_switch.save() + reversion.set_user(request.user) + reversion.set_comment("Création") + messages.success(request, "Le constructeur a été créé") + return redirect("/topologie/index_model_switch/") + return form({'topoform': constructor_switch}, 'topologie/topo.html', request) + + +@login_required +@permission_required('infra') +def edit_constructor_switch(request, constructor_switch_id): + """ Edition d'un constructeur de switch""" + try: + constructor_switch = ConstructorSwitch.objects.get(pk=constructor_switch_id) + except ConstructorSwitch.DoesNotExist: + messages.error(request, u"Constructeur inconnu") + return redirect("/topologie/index_model_switch/") + constructor_switch = EditConstructorSwitchForm(request.POST or None, instance=constructor_switch) + if constructor_switch.is_valid(): + with transaction.atomic(), reversion.create_revision(): + constructor_switch.save() + reversion.set_user(request.user) + reversion.set_comment("Champs modifié(s) : %s" % ', '.join( + field for field in constructor_switch.changed_data) + ) + messages.success(request, "Le modèle a bien été modifié") + return redirect("/topologie/index_model_switch/") + return form({'topoform': constructor_switch}, 'topologie/topo.html', request) + + +@login_required +@permission_required('infra') +def del_constructor_switch(request, constructor_switch_id): + """ Suppression d'un constructeur de switch""" + try: + constructor_switch = ConstructorSwitch.objects.get(pk=constructor_switch_id) + except ConstructorSwitch.DoesNotExist: + messages.error(request, u"Constructeur inexistant") + return redirect("/topologie/index_model_switch/") + if request.method == "POST": + try: + with transaction.atomic(), reversion.create_revision(): + constructor_switch.delete() + reversion.set_user(request.user) + reversion.set_comment("Destruction") + messages.success(request, "Le constructeur a été détruit") + except ProtectedError: + messages.error(request, "Le constructeur %s est affecté à un autre objet,\ + impossible de la supprimer (switch ou user)" % constructor_switch) + return redirect("/topologie/index_model_switch/") + return form({ + 'objet': constructor_switch, + 'objet_name': 'Constructeur de switch' + }, 'topologie/delete.html', request)