diff --git a/machines/admin.py b/machines/admin.py index 26d7a6a3..0c83e9ab 100644 --- a/machines/admin.py +++ b/machines/admin.py @@ -42,6 +42,7 @@ from .models import ( SshFp, Nas, Service, + Role, OuverturePort, Ipv6List, OuverturePortList, @@ -146,6 +147,11 @@ class ServiceAdmin(VersionAdmin): """ Admin view of a ServiceAdmin object """ list_display = ('service_type', 'min_time_regen', 'regular_time_regen') +class RoleAdmin(VersionAdmin): + """ Admin view of a RoleAdmin object """ + list_display = ('role_type') + + admin.site.register(Machine, MachineAdmin) admin.site.register(MachineType, MachineTypeAdmin) @@ -162,6 +168,7 @@ admin.site.register(IpList, IpListAdmin) admin.site.register(Interface, InterfaceAdmin) admin.site.register(Domain, DomainAdmin) admin.site.register(Service, ServiceAdmin) +admin.site.register(Role, RoleAdmin) admin.site.register(Vlan, VlanAdmin) admin.site.register(Ipv6List, Ipv6ListAdmin) admin.site.register(Nas, NasAdmin) diff --git a/machines/forms.py b/machines/forms.py index 23c2aa39..c8584d30 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -53,6 +53,7 @@ from .models import ( Txt, DName, Ns, + Role, Service, Vlan, Srv, @@ -497,6 +498,40 @@ class DelNasForm(FormRevMixin, Form): self.fields['nas'].queryset = Nas.objects.all() +class RoleForm(FormRevMixin, ModelForm): + """Ajout et edition d'un role""" + class Meta: + model = Role + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(RoleForm, self).__init__(*args, prefix=prefix, **kwargs) + self.fields['servers'].queryset = (Interface.objects.all() + .select_related( + 'domain__extension' + )) + + +class DelRoleForm(FormRevMixin, Form): + """Suppression d'un ou plusieurs service""" + role = forms.ModelMultipleChoiceField( + queryset=Role.objects.none(), + label="Roles actuels", + widget=forms.CheckboxSelectMultiple + ) + + def __init__(self, *args, **kwargs): + instances = kwargs.pop('instances', None) + super(DelRoleForm, self).__init__(*args, **kwargs) + if instances: + self.fields['role'].queryset = instances + else: + self.fields['role'].queryset = role.objects.all() + + + + class ServiceForm(FormRevMixin, ModelForm): """Ajout et edition d'une classe de service : dns, dhcp, etc""" class Meta: diff --git a/machines/models.py b/machines/models.py index 7be76e74..4b3ee891 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1441,6 +1441,26 @@ class IpList(RevMixin, AclMixin, models.Model): return self.ipv4 + +class Role(RevMixin, AclMixin, models.Model): + """ Definition d'un role (routeur principal, routeur de backkup)""" + """ Sert à la génération automatique de la conf des serveurs""" + PRETTY_NAME = "Roles des serveurs" + + role_type = models.CharField(max_length=255, unique=True) + servers = models.ManyToManyField('Interface') + + class Meta: + permissions = ( + ("view_role", "Peut voir un objet service"), + ) + + def save(self, *args, **kwargs): + super(Role, self).save(*args, **kwargs) + + def __str__(self): + return str(self.role_type) + class Service(RevMixin, AclMixin, models.Model): """ Definition d'un service (dhcp, dns, etc)""" PRETTY_NAME = "Services à générer (dhcp, dns, etc)" diff --git a/machines/templates/machines/aff_role.html b/machines/templates/machines/aff_role.html new file mode 100644 index 00000000..f914cd24 --- /dev/null +++ b/machines/templates/machines/aff_role.html @@ -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 %} + +{% load acl %} + + + + + + + + + + + {% for role in role_list %} + + + + + + {% endfor %} +
Nom du roleServeurs inclus
{{ role.role_type }}{% for serv in role.servers.all %}{{ serv }}, {% endfor %} + {% can_edit role %} + {% include 'buttons/edit.html' with href='machines:edit-role' id=role.id %} + {% acl_end %} + {% include 'buttons/history.html' with href='machines:history' name='role' id=role.id %} +
+ diff --git a/machines/templates/machines/index_role.html b/machines/templates/machines/index_role.html new file mode 100644 index 00000000..93a99577 --- /dev/null +++ b/machines/templates/machines/index_role.html @@ -0,0 +1,41 @@ +{% 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 %} +{% load acl %} + +{% block title %}Machines{% endblock %} + +{% block content %} +

Liste des roles

+ {% can_create Role %} + Ajouter un role + {% acl_end %} + Supprimer un ou plusieurs role + {% include "machines/aff_role.html" with role_list=role_list %} +
+
+{% endblock %} + diff --git a/machines/templates/machines/machine.html b/machines/templates/machines/machine.html index 7ec4212a..cd00ba0c 100644 --- a/machines/templates/machines/machine.html +++ b/machines/templates/machines/machine.html @@ -71,6 +71,8 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} {% if sshfpform %} {% bootstrap_form_errors sshfpform %} +{% if roleform %} + {% bootstrap_form_errors roleform %} {% endif %} {% if vlanform %} {% bootstrap_form_errors vlanform %} @@ -148,6 +150,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,

Service

{% massive_bootstrap_form serviceform 'servers' %} {% endif %} + {% if roleform %} +

Role

+ {% massive_bootstrap_form roleform 'servers' %} + {% endif %} {% if vlanform %}

Vlan

{% bootstrap_form vlanform %} diff --git a/machines/templates/machines/sidebar.html b/machines/templates/machines/sidebar.html index 5a0f975d..68031f29 100644 --- a/machines/templates/machines/sidebar.html +++ b/machines/templates/machines/sidebar.html @@ -68,6 +68,12 @@ with this program; if not, write to the Free Software Foundation, Inc., Services (dhcp, dns...) {% acl_end %} + {% can_view_all Role %} + + + Roles des serveurs + + {% acl_end %} {% can_view_all OuverturePortList %} diff --git a/machines/urls.py b/machines/urls.py index ce0a7a78..8c670308 100644 --- a/machines/urls.py +++ b/machines/urls.py @@ -21,7 +21,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """machines.urls -The defined URLs for the Cotisations app +The defined URLs for the Machines app """ from __future__ import unicode_literals @@ -125,6 +125,12 @@ urlpatterns = [ 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'^add_role/$', views.add_role, name='add-role'), + url(r'^edit_role/(?P[0-9]+)$', + views.edit_role, + name='edit-role'), + url(r'^del_role/$', views.del_role, name='del-role'), + url(r'^index_role/$', views.index_role, name='index-role'), url(r'^add_vlan/$', views.add_vlan, name='add-vlan'), url(r'^edit_vlan/(?P[0-9]+)$', views.edit_vlan, name='edit-vlan'), url(r'^del_vlan/$', views.del_vlan, name='del-vlan'), diff --git a/machines/views.py b/machines/views.py index 398b9250..134560ba 100644 --- a/machines/views.py +++ b/machines/views.py @@ -101,6 +101,8 @@ from .forms import ( DelMxForm, VlanForm, DelVlanForm, + RoleForm, + DelRoleForm, ServiceForm, DelServiceForm, SshFpForm, @@ -122,6 +124,7 @@ from .models import ( Mx, Ns, Domain, + Role, Service, Service_link, Vlan, @@ -1141,6 +1144,65 @@ def del_alias(request, interface, interfaceid): ) +@login_required +@can_create(Role) +def add_role(request): + """ View used to add a Role object """ + role = RoleForm(request.POST or None) + if role.is_valid(): + role.save() + messages.success(request, "Cet enregistrement role a été ajouté") + return redirect(reverse('machines:index-role')) + return form( + {'roleform': role, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + + +@login_required +@can_edit(Role) +def edit_role(request, role_instance, **_kwargs): + """ View used to edit a Role object """ + role = RoleForm(request.POST or None, instance=role_instance) + if role.is_valid(): + if role.changed_data: + role.save() + messages.success(request, "Role modifié") + return redirect(reverse('machines:index-role')) + return form( + {'roleform': role, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + + +@login_required +@can_delete_set(Role) +def del_role(request, instances): + """ View used to delete a Service object """ + role = DelRoleForm(request.POST or None, instances=instances) + if role.is_valid(): + role_dels = role.cleaned_data['role'] + for role_del in role_dels: + try: + role_del.delete() + messages.success(request, "Le role a été supprimée") + except ProtectedError: + messages.error( + request, + ("Erreur le role suivant %s ne peut être supprimé" + % role_del) + ) + return redirect(reverse('machines:index-role')) + return form( + {'roleform': role, 'action_name': 'Supprimer'}, + 'machines/machine.html', + request + ) + + + @login_required @can_create(Service) def add_service(request): @@ -1481,6 +1543,21 @@ def index_ipv6(request, interface, interfaceid): ) +@login_required +@can_view_all(Role) +def index_role(request): + """ View used to display the list of existing roles """ + role_list = (Role.objects + .prefetch_related( + 'servers__domain__extension' + ).all()) + return render( + request, + 'machines/index_role.html', + {'role_list': role_list} + ) + + @login_required @can_view_all(Service) def index_service(request): diff --git a/re2o/templatetags/acl.py b/re2o/templatetags/acl.py index 9a439f88..fe13c5ac 100644 --- a/re2o/templatetags/acl.py +++ b/re2o/templatetags/acl.py @@ -79,6 +79,7 @@ from django.contrib.contenttypes.models import ContentType register = template.Library() + def get_model(model_name): """Retrieve the model object from its name""" splitted = model_name.split('.')