From aec2c645a24c4db42ff40630577b7c6313a69d2c Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Mon, 2 Jul 2018 11:00:32 +0200 Subject: [PATCH 01/29] Serialisation de la conf des swicthes --- api/serializers.py | 55 ++++++++++++++++++++++++++++++++++++++++++++- api/urls.py | 5 ++++- api/views.py | 16 +++++++++++++ topologie/models.py | 8 +++++++ 4 files changed, 82 insertions(+), 2 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index 65a82eb6..9b425c4d 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -467,16 +467,30 @@ class BuildingSerializer(NamespacedHMSerializer): class SwitchPortSerializer(NamespacedHMSerializer): """Serialize `topologie.models.Port` objects """ + + get_port_profil = NamespacedHIField(view_name='portprofile-detail', read_only=True) + class Meta: model = topologie.Port fields = ('switch', 'port', 'room', 'machine_interface', 'related', - 'custom_profile', 'state', 'details', 'api_url') + 'custom_profile', 'state', 'get_port_profil', 'details', 'api_url') extra_kwargs = { 'related': {'view_name': 'switchport-detail'}, 'api_url': {'view_name': 'switchport-detail'}, } +class PortProfileSerializer(NamespacedHMSerializer): + """Serialize `topologie.models.Room` objects + """ + class Meta: + model = topologie.PortProfile + fields = ('name', 'profil_default', 'vlan_untagged', 'vlan_tagged', + 'radius_type', 'radius_mode', 'speed', 'mac_limit', 'flow_control', + 'dhcp_snooping', 'dhcpv6_snooping', 'dhcpv6_snooping', 'arp_protect', + 'ra_guard', 'loop_protect', 'api_url') + + class RoomSerializer(NamespacedHMSerializer): """Serialize `topologie.models.Room` objects """ @@ -644,6 +658,45 @@ class ServiceRegenSerializer(NamespacedHMSerializer): 'api_url': {'view_name': 'serviceregen-detail'} } +# Switches et ports + +class ProfilSerializer(NamespacedHMSerializer): + class Meta: + model = topologie.PortProfile + fields = ('name', 'profil_default', 'vlan_untagged', 'vlan_tagged', 'radius_type', 'radius_mode', 'speed', 'mac_limit', 'flow_control', 'dhcp_snooping', 'dhcpv6_snooping', 'arp_protect', 'ra_guard', 'loop_protect') + + +class ModelSwitchSerializer(NamespacedHMSerializer): + class Meta: + model = topologie.ModelSwitch + fields = ('reference',) + + +class SwitchBaySerializer(NamespacedHMSerializer): + class Meta: + model = topologie.SwitchBay + fields = ('name',) + + +class PortsSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Ipv6List` objects. + """ + get_port_profil = ProfilSerializer(read_only=True) + + class Meta: + model = topologie.Port + fields = ('state', 'port', 'get_port_profil') + + +class SwitchPortSerializer(serializers.ModelSerializer): + """Serialize the data about the switches""" + ports = PortsSerializer(many=True, read_only=True) + model = ModelSwitchSerializer(read_only=True) + switchbay = SwitchBaySerializer(read_only=True) + + class Meta: + model = topologie.Switch + fields = ('short_name', 'model', 'switchbay', 'ports', 'subnet', 'subnet6') # LOCAL EMAILS diff --git a/api/urls.py b/api/urls.py index abc466e1..9de02578 100644 --- a/api/urls.py +++ b/api/urls.py @@ -81,7 +81,8 @@ router.register_viewset(r'topologie/modelswitch', views.ModelSwitchViewSet) router.register_viewset(r'topologie/constructorswitch', views.ConstructorSwitchViewSet) router.register_viewset(r'topologie/switchbay', views.SwitchBayViewSet) router.register_viewset(r'topologie/building', views.BuildingViewSet) -router.register(r'topologie/switchport', views.SwitchPortViewSet, base_name='switchport') +router.register_viewset(r'topologie/switchport', views.SwitchPortViewSet, base_name='switchport') +router.register_viewset(r'topologie/portprofile', views.PortProfileViewSet, base_name='portprofile') router.register_viewset(r'topologie/room', views.RoomViewSet) router.register(r'topologie/portprofile', views.PortProfileViewSet) # USERS @@ -105,6 +106,8 @@ router.register_view(r'localemail/users', views.LocalEmailUsersView), # Firewall router.register_view(r'firewall/subnet-ports', views.SubnetPortsOpenView), router.register_view(r'firewall/interface-ports', views.InterfacePortsOpenView), +# Switches config +router.register_view(r'switchs/ports-config', views.SwitchPortView), # DNS router.register_view(r'dns/zones', views.DNSZonesView), router.register_view(r'dns/reverse-zones', views.DNSReverseZonesView), diff --git a/api/views.py b/api/views.py index 6e5265f6..3c2fe7d3 100644 --- a/api/views.py +++ b/api/views.py @@ -397,6 +397,13 @@ class SwitchPortViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = serializers.SwitchPortSerializer +class PortProfileViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `topologie.models.PortProfile` objects. + """ + queryset = topologie.PortProfile.objects.all() + serializer_class = serializers.PortProfileSerializer + + class RoomViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `topologie.models.Room` objects. """ @@ -515,6 +522,15 @@ class ServiceRegenViewSet(viewsets.ModelViewSet): queryset = queryset.filter(server__domain__name__iexact=hostname) return queryset +# Config des switches + +class SwitchPortView(generics.ListAPIView): + """Exposes the associations between hostname, mac address and IPv4 in + order to build the DHCP lease files. + """ + queryset = topologie.Switch.objects.all().prefetch_related('ports__custom_profile') + serializer_class = serializers.SwitchPortSerializer + # LOCAL EMAILS diff --git a/topologie/models.py b/topologie/models.py index b63bece6..aa1c4ef8 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -292,6 +292,14 @@ class Switch(AclMixin, Machine): def get_name(self): return self.name or self.main_interface().domain.name + @cached_property + def subnet(self): + return self.main_interface().type.ip_type.ip_set_full_info + + @cached_property + def subnet6(self): + return self.main_interface().type.ip_type.ip6_set_full_info + def __str__(self): return str(self.get_name) From 20126a3cd6651a29eac9d41b4de921c057f845ca Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 7 Jul 2018 17:06:08 +0200 Subject: [PATCH 02/29] Serialisation de la liste des vlans --- api/serializers.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/api/serializers.py b/api/serializers.py index 9b425c4d..a9c05978 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -660,10 +660,19 @@ class ServiceRegenSerializer(NamespacedHMSerializer): # Switches et ports +class VlanPortSerializer(NamespacedHMSerializer): + class Meta: + model = machines.Vlan + fields = ('vlan_id', 'name') + + class ProfilSerializer(NamespacedHMSerializer): + vlan_untagged = VlanSerializer(read_only=True) + vlan_tagged = VlanPortSerializer(read_only=True, many=True) + class Meta: model = topologie.PortProfile - fields = ('name', 'profil_default', 'vlan_untagged', 'vlan_tagged', 'radius_type', 'radius_mode', 'speed', 'mac_limit', 'flow_control', 'dhcp_snooping', 'dhcpv6_snooping', 'arp_protect', 'ra_guard', 'loop_protect') + fields = ('name', 'profil_default', 'vlan_untagged', 'vlan_tagged', 'radius_type', 'radius_mode', 'speed', 'mac_limit', 'flow_control', 'dhcp_snooping', 'dhcpv6_snooping', 'arp_protect', 'ra_guard', 'loop_protect', 'vlan_untagged', 'vlan_tagged') class ModelSwitchSerializer(NamespacedHMSerializer): @@ -683,17 +692,20 @@ class PortsSerializer(NamespacedHMSerializer): """ get_port_profil = ProfilSerializer(read_only=True) + class Meta: model = topologie.Port fields = ('state', 'port', 'get_port_profil') + class SwitchPortSerializer(serializers.ModelSerializer): """Serialize the data about the switches""" ports = PortsSerializer(many=True, read_only=True) model = ModelSwitchSerializer(read_only=True) switchbay = SwitchBaySerializer(read_only=True) + class Meta: model = topologie.Switch fields = ('short_name', 'model', 'switchbay', 'ports', 'subnet', 'subnet6') From 30bb38d75f96071fcc2bba3624de8ee1b20dee74 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 8 Jul 2018 04:08:01 +0200 Subject: [PATCH 03/29] =?UTF-8?q?Serialisation=20des=20roles=20et=20r?= =?UTF-8?q?=C3=A9glages=20par=20vlan=20des=20switches?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/serializers.py | 46 ++++++++++++++-- api/urls.py | 2 + api/views.py | 15 +++++ .../migrations/0091_auto_20180707_2040.py | 30 ++++++++++ machines/models.py | 8 ++- topologie/models.py | 24 +++++++- .../templates/topologie/aff_vlanoptions.html | 55 +++++++++++++++++++ .../topologie/index_portprofile.html | 6 ++ topologie/urls.py | 5 +- topologie/views.py | 29 +++++++++- 10 files changed, 208 insertions(+), 12 deletions(-) create mode 100644 machines/migrations/0091_auto_20180707_2040.py create mode 100644 topologie/templates/topologie/aff_vlanoptions.html diff --git a/api/serializers.py b/api/serializers.py index a9c05978..8d556771 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -153,7 +153,7 @@ class VlanSerializer(NamespacedHMSerializer): """ class Meta: model = machines.Vlan - fields = ('vlan_id', 'name', 'comment', 'api_url') + fields = ('vlan_id', 'name', 'comment', 'arp_protect', 'dhcp_snooping', 'dhcpv6_snooping', 'api_url') class NasSerializer(NamespacedHMSerializer): @@ -310,6 +310,16 @@ class OuverturePortSerializer(NamespacedHMSerializer): fields = ('begin', 'end', 'port_list', 'protocole', 'io', 'api_url') +class RoleSerializer(NamespacedHMSerializer): + """Serialize `machines.models.OuverturePort` objects. + """ + servers = InterfaceSerializer(read_only=True, many=True) + + class Meta: + model = machines.Role + fields = ('role_type', 'servers', 'api_url') + + # PREFERENCES @@ -660,10 +670,38 @@ class ServiceRegenSerializer(NamespacedHMSerializer): # Switches et ports +class InterfaceVlanSerializer(NamespacedHMSerializer): + domain = serializers.CharField(read_only=True) + ipv4 = serializers.CharField(read_only=True) + ipv6 = Ipv6ListSerializer(read_only=True, many=True) + vlan_id = serializers.IntegerField(source='type.ip_type.vlan.vlan_id', read_only=True) + + class Meta: + model = machines.Interface + fields = ('ipv4', 'ipv6', 'domain', 'vlan_id') + +class InterfaceRoleSerializer(NamespacedHMSerializer): + interface = InterfaceVlanSerializer(source='machine.interface_set', read_only=True, many=True) + + class Meta: + model = machines.Interface + fields = ('interface',) + + +class RoleSerializer(NamespacedHMSerializer): + """Serialize `machines.models.OuverturePort` objects. + """ + servers = InterfaceRoleSerializer(read_only=True, many=True) + + class Meta: + model = machines.Role + fields = ('role_type', 'servers') + + class VlanPortSerializer(NamespacedHMSerializer): class Meta: model = machines.Vlan - fields = ('vlan_id', 'name') + fields = ('vlan_id', 'name') class ProfilSerializer(NamespacedHMSerializer): @@ -695,7 +733,7 @@ class PortsSerializer(NamespacedHMSerializer): class Meta: model = topologie.Port - fields = ('state', 'port', 'get_port_profil') + fields = ('state', 'port', 'pretty_name', 'get_port_profil') @@ -708,7 +746,7 @@ class SwitchPortSerializer(serializers.ModelSerializer): class Meta: model = topologie.Switch - fields = ('short_name', 'model', 'switchbay', 'ports', 'subnet', 'subnet6') + fields = ('short_name', 'model', 'switchbay', 'ports', 'ipv4', 'ipv6', 'subnet', 'subnet6') # LOCAL EMAILS diff --git a/api/urls.py b/api/urls.py index 9de02578..e8c63657 100644 --- a/api/urls.py +++ b/api/urls.py @@ -63,6 +63,7 @@ router.register_viewset(r'machines/service', views.ServiceViewSet) router.register_viewset(r'machines/servicelink', views.ServiceLinkViewSet, base_name='servicelink') router.register_viewset(r'machines/ouvertureportlist', views.OuverturePortListViewSet) router.register_viewset(r'machines/ouvertureport', views.OuverturePortViewSet) +router.register_viewset(r'machines/role', views.RoleViewSet) # PREFERENCES router.register_view(r'preferences/optionaluser', views.OptionalUserView), router.register_view(r'preferences/optionalmachine', views.OptionalMachineView), @@ -108,6 +109,7 @@ router.register_view(r'firewall/subnet-ports', views.SubnetPortsOpenView), router.register_view(r'firewall/interface-ports', views.InterfacePortsOpenView), # Switches config router.register_view(r'switchs/ports-config', views.SwitchPortView), +router.register_view(r'switchs/role', views.RoleView), # DNS router.register_view(r'dns/zones', views.DNSZonesView), router.register_view(r'dns/reverse-zones', views.DNSReverseZonesView), diff --git a/api/views.py b/api/views.py index 3c2fe7d3..40cf8eae 100644 --- a/api/views.py +++ b/api/views.py @@ -242,6 +242,13 @@ class OuverturePortViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = serializers.OuverturePortSerializer +class RoleViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Machine` objects. + """ + queryset = machines.Role.objects.all() + serializer_class = serializers.RoleSerializer + + # PREFERENCES # Those views differ a bit because there is only one object # to display, so we don't bother with the listing part @@ -532,6 +539,14 @@ class SwitchPortView(generics.ListAPIView): serializer_class = serializers.SwitchPortSerializer +class RoleView(generics.ListAPIView): + """Exposes the associations between hostname, mac address and IPv4 in + order to build the DHCP lease files. + """ + queryset = machines.Role.objects.all().prefetch_related('servers') + serializer_class = serializers.RoleSerializer + + # LOCAL EMAILS diff --git a/machines/migrations/0091_auto_20180707_2040.py b/machines/migrations/0091_auto_20180707_2040.py new file mode 100644 index 00000000..a2aea3a6 --- /dev/null +++ b/machines/migrations/0091_auto_20180707_2040.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-07-07 18:40 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0090_auto_20180625_1706'), + ] + + operations = [ + migrations.AddField( + model_name='vlan', + name='arp_protect', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='vlan', + name='dhcp_snooping', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='vlan', + name='dhcpv6_snooping', + field=models.BooleanField(default=False), + ), + ] diff --git a/machines/models.py b/machines/models.py index 4de0b012..b59d48ae 100644 --- a/machines/models.py +++ b/machines/models.py @@ -371,7 +371,7 @@ class IpType(RevMixin, AclMixin, models.Model): 'netmask' : 'ffff:ffff:ffff:ffff::', 'netmask_cidr' : str(self.prefix_v6_length), 'vlan': str(self.vlan), - 'vlan_id': self.vlan.vlan_id + 'vlan_id': str(self.vlan.vlan_id) } else: return None @@ -515,7 +515,11 @@ class Vlan(RevMixin, AclMixin, models.Model): vlan_id = models.PositiveIntegerField(validators=[MaxValueValidator(4095)]) name = models.CharField(max_length=256) comment = models.CharField(max_length=256, blank=True) - + #Réglages supplémentaires + arp_protect = models.BooleanField(default=False) + dhcp_snooping = models.BooleanField(default=False) + dhcpv6_snooping = models.BooleanField(default=False) + class Meta: permissions = ( ("view_vlan", _("Can view a VLAN object")), diff --git a/topologie/models.py b/topologie/models.py index aa1c4ef8..7ca285be 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -291,6 +291,14 @@ class Switch(AclMixin, Machine): @cached_property def get_name(self): return self.name or self.main_interface().domain.name + + @cached_property + def ipv4(self): + return str(self.main_interface().ipv4) + + @cached_property + def ipv6(self): + return str(self.main_interface().ipv6().first()) @cached_property def subnet(self): @@ -445,8 +453,20 @@ class Port(AclMixin, RevMixin, models.Model): verbose_name_plural = _("ports") @cached_property - def get_port_profile(self): - """Return the config profile for this port + def pretty_name(self): + """More elaborated name for label on switch conf""" + if self.related: + return "Uplink : " + self.related.switch.short_name + elif self.machine_interface: + return "Machine : " + str(self.machine_interface.domain) + elif self.room: + return "Chambre : " + str(self.room) + else: + return "Inconnue" + + @cached_property + def get_port_profil(self): + """Return the config profil for this port :returns: the profile of self (port)""" def profile_or_nothing(profile): port_profile = PortProfile.objects.filter( diff --git a/topologie/templates/topologie/aff_vlanoptions.html b/topologie/templates/topologie/aff_vlanoptions.html new file mode 100644 index 00000000..d9e6f117 --- /dev/null +++ b/topologie/templates/topologie/aff_vlanoptions.html @@ -0,0 +1,55 @@ +{% 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 vlan in vlan_list %} + + + + + + + + + {% endfor %} +
IdNomArp ProtectDhcp SnoopingDhcpv6 Snooping
{{ vlan.vlan_id }}{{ vlan.name }}{{ vlan.arp_protect }}{{ vlan.dhcp_snooping }}{{ vlan.dhcpv6_snooping }} + {% can_edit vlan %} + {% include 'buttons/edit.html' with href='topologie:edit-vlanoptions' id=vlan.id %} + {% acl_end %} + {% include 'buttons/history.html' with href='machines:history' name='vlan' id=vlan.id %} +
+
diff --git a/topologie/templates/topologie/index_portprofile.html b/topologie/templates/topologie/index_portprofile.html index 730823f6..4a603210 100644 --- a/topologie/templates/topologie/index_portprofile.html +++ b/topologie/templates/topologie/index_portprofile.html @@ -30,12 +30,18 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block content %} +

{% trans "Port profiles" %}

{% can_create PortProfile %} {% trans " Add a port profile" %}
{% acl_end %} {% include "topologie/aff_port_profile.html" with port_profile_list=port_profile_list %} + + +

{% trans "Sécurité par vlan" %}

+{% include "topologie/aff_vlanoptions.html" with vlan_list=vlan_list %} +


diff --git a/topologie/urls.py b/topologie/urls.py index c314c800..77d68d50 100644 --- a/topologie/urls.py +++ b/topologie/urls.py @@ -120,4 +120,7 @@ urlpatterns = [ url(r'^del_port_profile/(?P[0-9]+)$', views.del_port_profile, name='del-port-profile'), -] + url(r'^edit_vlanoptions/(?P[0-9]+)$', + views.edit_vlanoptions, + name='edit-vlanoptions'), + ] diff --git a/topologie/views.py b/topologie/views.py index 3d8a3044..74ea1148 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -60,10 +60,15 @@ from re2o.settings import MEDIA_ROOT from machines.forms import ( DomainForm, EditInterfaceForm, - AddInterfaceForm + AddInterfaceForm, + EditOptionVlanForm ) from machines.views import generate_ipv4_mbf_param -from machines.models import Interface, Service_link +from machines.models import ( + Interface, + Service_link, + Vlan +) from preferences.models import AssoOption, GeneralOption from .models import ( @@ -153,10 +158,11 @@ def index_port_profile(request): 'vlan_untagged') port_profile_list = re2o_paginator( request, port_profile_list, pagination_number) + vlan_list = Vlan.objects.all().order_by('vlan_id') return render( request, 'topologie/index_portprofile.html', - {'port_profile_list': port_profile_list} + {'port_profile_list': port_profile_list, 'vlan_list': vlan_list} ) @@ -307,6 +313,23 @@ def index_model_switch(request): ) +@login_required +@can_edit(Vlan) +def edit_vlanoptions(request, vlan_instance, **_kwargs): + """ View used to edit options for switch of VLAN object """ + vlan = EditOptionVlanForm(request.POST or None, instance=vlan_instance) + if vlan.is_valid(): + if vlan.changed_data: + vlan.save() + messages.success(request, "Vlan modifié") + return redirect(reverse('topologie:index-port-profile')) + return form( + {'vlanform': vlan, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + + @login_required @can_create(Port) def new_port(request, switchid): From d2d6f7e5e44b6f524a3c83a73f6f5397b0737134 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 8 Jul 2018 20:32:47 +0200 Subject: [PATCH 04/29] Gestion igmp et mld par vlan --- api/serializers.py | 3 ++- machines/forms.py | 13 +++++++++- .../migrations/0092_auto_20180708_2018.py | 25 +++++++++++++++++++ machines/models.py | 10 +++++++- .../templates/topologie/aff_vlanoptions.html | 4 +++ 5 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 machines/migrations/0092_auto_20180708_2018.py diff --git a/api/serializers.py b/api/serializers.py index 8d556771..52902265 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -153,7 +153,8 @@ class VlanSerializer(NamespacedHMSerializer): """ class Meta: model = machines.Vlan - fields = ('vlan_id', 'name', 'comment', 'arp_protect', 'dhcp_snooping', 'dhcpv6_snooping', 'api_url') + fields = ('vlan_id', 'name', 'comment', 'arp_protect', 'dhcp_snooping', + 'dhcpv6_snooping', 'igmp', 'mld', 'api_url') class NasSerializer(NamespacedHMSerializer): diff --git a/machines/forms.py b/machines/forms.py index 4af060d3..e9c75d59 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -576,13 +576,24 @@ class VlanForm(FormRevMixin, ModelForm): """Ajout d'un vlan : id, nom""" class Meta: model = Vlan - fields = '__all__' + fields = ['vlan_id', 'name', 'comment'] def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(VlanForm, self).__init__(*args, prefix=prefix, **kwargs) +class EditOptionVlanForm(FormRevMixin, ModelForm): + """Ajout d'un vlan : id, nom""" + class Meta: + model = Vlan + fields = ['dhcp_snooping', 'dhcpv6_snooping', 'arp_protect', 'igmp', 'mld'] + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(EditOptionVlanForm, self).__init__(*args, prefix=prefix, **kwargs) + + class DelVlanForm(FormRevMixin, Form): """Suppression d'un ou plusieurs vlans""" vlan = forms.ModelMultipleChoiceField( diff --git a/machines/migrations/0092_auto_20180708_2018.py b/machines/migrations/0092_auto_20180708_2018.py new file mode 100644 index 00000000..b4f41c14 --- /dev/null +++ b/machines/migrations/0092_auto_20180708_2018.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-07-08 18:18 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0091_auto_20180707_2040'), + ] + + operations = [ + migrations.AddField( + model_name='vlan', + name='igmp', + field=models.BooleanField(default=False, help_text='Gestion multicast v4'), + ), + migrations.AddField( + model_name='vlan', + name='mld', + field=models.BooleanField(default=False, help_text='Gestion multicast v6'), + ), + ] diff --git a/machines/models.py b/machines/models.py index b59d48ae..3526108f 100644 --- a/machines/models.py +++ b/machines/models.py @@ -519,7 +519,15 @@ class Vlan(RevMixin, AclMixin, models.Model): arp_protect = models.BooleanField(default=False) dhcp_snooping = models.BooleanField(default=False) dhcpv6_snooping = models.BooleanField(default=False) - + igmp = models.BooleanField( + default=False, + help_text="Gestion multicast v4" + ) + mld = models.BooleanField( + default=False, + help_text="Gestion multicast v6" + ) + class Meta: permissions = ( ("view_vlan", _("Can view a VLAN object")), diff --git a/topologie/templates/topologie/aff_vlanoptions.html b/topologie/templates/topologie/aff_vlanoptions.html index d9e6f117..7684e951 100644 --- a/topologie/templates/topologie/aff_vlanoptions.html +++ b/topologie/templates/topologie/aff_vlanoptions.html @@ -33,6 +33,8 @@ with this program; if not, write to the Free Software Foundation, Inc., Arp Protect Dhcp Snooping Dhcpv6 Snooping + Igmp + Mld @@ -43,6 +45,8 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ vlan.arp_protect }} {{ vlan.dhcp_snooping }} {{ vlan.dhcpv6_snooping }} + {{ vlan.igmp }} + {{ vlan.mld }} {% can_edit vlan %} {% include 'buttons/edit.html' with href='topologie:edit-vlanoptions' id=vlan.id %} From 29f00f21cd0ff9877cc68505295ca5b54baa71be Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 8 Jul 2018 22:28:10 +0200 Subject: [PATCH 05/29] Stockage du firmware dans la bdd --- api/serializers.py | 2 +- .../migrations/0068_modelswitch_firmware.py | 20 +++++++++++++++++++ topologie/models.py | 5 +++++ .../templates/topologie/aff_model_switch.html | 4 ++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 topologie/migrations/0068_modelswitch_firmware.py diff --git a/api/serializers.py b/api/serializers.py index 52902265..8bb0833e 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -717,7 +717,7 @@ class ProfilSerializer(NamespacedHMSerializer): class ModelSwitchSerializer(NamespacedHMSerializer): class Meta: model = topologie.ModelSwitch - fields = ('reference',) + fields = ('reference', 'firmware') class SwitchBaySerializer(NamespacedHMSerializer): diff --git a/topologie/migrations/0068_modelswitch_firmware.py b/topologie/migrations/0068_modelswitch_firmware.py new file mode 100644 index 00000000..8596f58f --- /dev/null +++ b/topologie/migrations/0068_modelswitch_firmware.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-07-08 19:56 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0067_auto_20180701_0016'), + ] + + operations = [ + migrations.AddField( + model_name='modelswitch', + name='firmware', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index 7ca285be..1c275026 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -320,6 +320,11 @@ class ModelSwitch(AclMixin, RevMixin, models.Model): 'topologie.ConstructorSwitch', on_delete=models.PROTECT ) + firmware = models.CharField( + max_length=255, + null=True, + blank=True + ) class Meta: permissions = ( diff --git a/topologie/templates/topologie/aff_model_switch.html b/topologie/templates/topologie/aff_model_switch.html index 6b3e7156..7fd1b37c 100644 --- a/topologie/templates/topologie/aff_model_switch.html +++ b/topologie/templates/topologie/aff_model_switch.html @@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Reference" as tr_ref %} {% include "buttons/sort.html" with prefix='model-switch' col='reference' text=tr_ref %} + Firmware {% trans "Switch constructor" as tr_constructor %} {% include "buttons/sort.html" with prefix='model-switch' col='constructor' text=tr_constructor %} @@ -43,6 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% for model_switch in model_switch_list %} {{ model_switch.reference }} + {{model_switch.firmware}} {{ model_switch.constructor }} {% can_edit model_switch %} @@ -64,4 +66,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% if model_switch_list.paginator %} {% include "pagination.html" with list=model_switch_list %} {% endif %} + + From 07eddfffb78940cbd88d476f1d5957c0d336879e Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 8 Jul 2018 23:39:15 +0200 Subject: [PATCH 06/29] Optimisation chargement des switchs --- api/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/views.py b/api/views.py index 40cf8eae..22901d09 100644 --- a/api/views.py +++ b/api/views.py @@ -535,7 +535,8 @@ class SwitchPortView(generics.ListAPIView): """Exposes the associations between hostname, mac address and IPv4 in order to build the DHCP lease files. """ - queryset = topologie.Switch.objects.all().prefetch_related('ports__custom_profile') + queryset = topologie.Switch.objects.all().select_related("switchbay").select_related("model__constructor").prefetch_related("ports__custom_profile__vlan_tagged").prefetch_related("ports__custom_profile__vlan_untagged").prefetch_related("ports__machine_interface__domain__extension").prefetch_related("ports__room") + serializer_class = serializers.SwitchPortSerializer From cedd49e9092e2fa117d693cd41b422e9a7ec9623 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Mon, 9 Jul 2018 00:42:29 +0200 Subject: [PATCH 07/29] Serialisation du constructeur --- api/serializers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/serializers.py b/api/serializers.py index 8bb0833e..2e675add 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -715,9 +715,11 @@ class ProfilSerializer(NamespacedHMSerializer): class ModelSwitchSerializer(NamespacedHMSerializer): + constructor = serializers.CharField(read_only=True) + class Meta: model = topologie.ModelSwitch - fields = ('reference', 'firmware') + fields = ('reference', 'firmware', 'constructor') class SwitchBaySerializer(NamespacedHMSerializer): From daaeb00e89a86c1adb7965793cc80866a71822d1 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Mon, 9 Jul 2018 21:06:21 +0200 Subject: [PATCH 08/29] Complete_name d'un switch --- api/serializers.py | 2 +- machines/models.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/api/serializers.py b/api/serializers.py index 2e675add..ceb4c3c4 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -749,7 +749,7 @@ class SwitchPortSerializer(serializers.ModelSerializer): class Meta: model = topologie.Switch - fields = ('short_name', 'model', 'switchbay', 'ports', 'ipv4', 'ipv6', 'subnet', 'subnet6') + fields = ('short_name', 'complete_name', 'model', 'switchbay', 'ports', 'ipv4', 'ipv6', 'subnet', 'subnet6') # LOCAL EMAILS diff --git a/machines/models.py b/machines/models.py index 3526108f..212d65e8 100644 --- a/machines/models.py +++ b/machines/models.py @@ -201,6 +201,12 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): de cette machine""" return str(self.interface_set.first().domain.name) + @cached_property + def complete_name(self): + """Par defaut, renvoie le nom de la première interface + de cette machine""" + return str(self.interface_set.first()) + @cached_property def all_short_names(self): """Renvoie de manière unique, le nom des interfaces de cette From 5184fc165f4ea19b06c073749cefb0a83efffd64 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Mon, 9 Jul 2018 22:47:20 +0200 Subject: [PATCH 09/29] =?UTF-8?q?Repare=20mes=20b=C3=A9tises=20=C3=A0=20mo?= =?UTF-8?q?i=20sur=20massive=20bootstrap=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- preferences/templates/preferences/edit_preferences.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preferences/templates/preferences/edit_preferences.html b/preferences/templates/preferences/edit_preferences.html index 30ab2423..a1540f33 100644 --- a/preferences/templates/preferences/edit_preferences.html +++ b/preferences/templates/preferences/edit_preferences.html @@ -36,7 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% csrf_token %} - {% massive_bootstrap_form options 'utilisateur_asso' %} + {% massive_bootstrap_form options 'utilisateur_asso,automatic_provision_switchs' %} {% trans "Edit" as tr_edit %} {% bootstrap_button tr_edit button_type="submit" icon='ok' button_class='btn-success' %}
From 396853db0e58d054135cc262dd3aed9151730277 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Mon, 9 Jul 2018 22:59:47 +0200 Subject: [PATCH 10/29] =?UTF-8?q?Serialisation=20des=20r=C3=A9glages=20pou?= =?UTF-8?q?r=20la=20provision?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/serializers.py | 4 +++- topologie/models.py | 13 ++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index ceb4c3c4..fec0c9c1 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -749,7 +749,9 @@ class SwitchPortSerializer(serializers.ModelSerializer): class Meta: model = topologie.Switch - fields = ('short_name', 'complete_name', 'model', 'switchbay', 'ports', 'ipv4', 'ipv6', 'subnet', 'subnet6') + fields = ('short_name', 'model', 'switchbay', 'ports', 'ipv4', 'ipv6', + 'subnet', 'subnet6', 'automatic_provision', 'rest_enabled', + 'web_management_enabled') # LOCAL EMAILS diff --git a/topologie/models.py b/topologie/models.py index 1c275026..43a9c2f9 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -285,7 +285,11 @@ class Switch(AclMixin, Machine): ValidationError(_("Creation of an existing port.")) def main_interface(self): - """ Returns the 'main' interface of the switch """ + """ Returns the 'main' interface of the switch + It must the the management interface for that device""" + switch_iptype = OptionalTopologie.get_cached_value('switchs_ip_type') + if switch_iptype: + return self.interface_set.filter(type__ip_type=switch_iptype).first() return self.interface_set.first() @cached_property @@ -294,19 +298,22 @@ class Switch(AclMixin, Machine): @cached_property def ipv4(self): + """Return the switch's management ipv4""" return str(self.main_interface().ipv4) @cached_property def ipv6(self): + """Returne the switch's management ipv6""" return str(self.main_interface().ipv6().first()) @cached_property def subnet(self): - return self.main_interface().type.ip_type.ip_set_full_info + """ Return the subnet of the management ip""" + return self.main_interface().type.ip_type.ip_set_full_info @cached_property def subnet6(self): - return self.main_interface().type.ip_type.ip6_set_full_info + return self.main_interface().type.ip_type.ip6_set_full_info def __str__(self): return str(self.get_name) From 54326a35b91c57f705338932aa76d9d81a0cdc11 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 10 Jul 2018 00:44:22 +0200 Subject: [PATCH 11/29] Renvoie le mode plain ou ssl pour webmanagement --- topologie/models.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/topologie/models.py b/topologie/models.py index 43a9c2f9..e1d017be 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -296,6 +296,21 @@ class Switch(AclMixin, Machine): def get_name(self): return self.name or self.main_interface().domain.name + @cached_property + def rest_enabled(self): + return OptionalTopologie.get_cached_value('switchs_rest_management') or self.automatic_provision + + @cached_property + def web_management_enabled(self): + sw_management = OptionalTopologie.get_cached_value('switchs_web_management') + sw_management_ssl = OptionalTopologie.get_cached_value('switchs_web_management_ssl') + if sw_management_ssl: + return "ssl" + elif sw_management: + return "plain" + else: + return self.automatic_provision + @cached_property def ipv4(self): """Return the switch's management ipv4""" From 49411450af32f2ab5b997cd88abce1953af8a447 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 10 Jul 2018 15:34:49 +0200 Subject: [PATCH 12/29] Migration preferences ssl + merge --- .../migrations/0093_merge_20180710_0226.py | 16 +++++++++++++++ ...naltopologie_switchs_web_management_ssl.py | 20 +++++++++++++++++++ .../migrations/0046_merge_20180710_1533.py | 16 +++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 machines/migrations/0093_merge_20180710_0226.py create mode 100644 preferences/migrations/0045_optionaltopologie_switchs_web_management_ssl.py create mode 100644 preferences/migrations/0046_merge_20180710_1533.py diff --git a/machines/migrations/0093_merge_20180710_0226.py b/machines/migrations/0093_merge_20180710_0226.py new file mode 100644 index 00000000..c3347890 --- /dev/null +++ b/machines/migrations/0093_merge_20180710_0226.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-07-10 00:26 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0092_auto_20180708_2018'), + ('machines', '0083_remove_duplicate_rights'), + ] + + operations = [ + ] diff --git a/preferences/migrations/0045_optionaltopologie_switchs_web_management_ssl.py b/preferences/migrations/0045_optionaltopologie_switchs_web_management_ssl.py new file mode 100644 index 00000000..47633c05 --- /dev/null +++ b/preferences/migrations/0045_optionaltopologie_switchs_web_management_ssl.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-07-09 21:50 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0044_auto_20180709_2237'), + ] + + operations = [ + migrations.AddField( + model_name='optionaltopologie', + name='switchs_web_management_ssl', + field=models.BooleanField(default=False, help_text='Web management ssl. Assurez-vous que un certif est installé sur le switch !'), + ), + ] diff --git a/preferences/migrations/0046_merge_20180710_1533.py b/preferences/migrations/0046_merge_20180710_1533.py new file mode 100644 index 00000000..13ffa0d3 --- /dev/null +++ b/preferences/migrations/0046_merge_20180710_1533.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-07-10 13:33 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0045_remove_unused_payment_fields'), + ('preferences', '0045_optionaltopologie_switchs_web_management_ssl'), + ] + + operations = [ + ] From 282ea9b2c6d37cc47a5473bac2c4ec50b7a7c23e Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 11 Jul 2018 00:16:35 +0200 Subject: [PATCH 13/29] Bricoles, web management ssl --- preferences/models.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/preferences/models.py b/preferences/models.py index 3199dd6c..cb89a361 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -200,6 +200,31 @@ class OptionalTopologie(AclMixin, PreferencesModel): blank=True, null=True ) + switchs_web_management = models.BooleanField( + default=False, + help_text="Web management, activé si provision automatique" + ) + switchs_web_management_ssl = models.BooleanField( + default=False, + help_text="Web management ssl. Assurez-vous que un certif est installé sur le switch !" + ) + switchs_rest_management = models.BooleanField( + default=False, + help_text="Rest management, activé si provision auto" + ) + switchs_ip_type = models.OneToOneField( + 'machines.IpType', + on_delete=models.PROTECT, + blank=True, + null=True, + help_text="Plage d'ip de management des switchs" + ) + + + @cached_property + def provisioned_switchs(self): + from topologie.models import Switch + return Switch.objects.filter(automatic_provision=True) class Meta: permissions = ( @@ -383,8 +408,8 @@ def homeoption_post_save(**kwargs): class MailMessageOption(AclMixin, models.Model): """Reglages, mail de bienvenue et autre""" - welcome_mail_fr = models.TextField(default="") - welcome_mail_en = models.TextField(default="") + welcome_mail_fr = models.TextField(default="", help_text="Mail de bienvenue en français") + welcome_mail_en = models.TextField(default="", help_text="Mail de bienvenue en anglais") class Meta: permissions = ( From b9dbce8ccc4bdeb62a2f0a6c23a4ed05c7e69ace Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 11 Jul 2018 01:07:31 +0200 Subject: [PATCH 14/29] Gestion de la clef radius, et serialisation --- api/serializers.py | 2 +- preferences/admin.py | 10 ++- preferences/forms.py | 31 ++++++- .../migrations/0047_auto_20180711_0015.py | 40 +++++++++ preferences/models.py | 62 +++++++++++++ .../templates/preferences/aff_radiuskey.html | 49 +++++++++++ .../preferences/display_preferences.html | 32 +++++++ .../templates/preferences/preferences.html | 3 +- preferences/urls.py | 14 +++ preferences/views.py | 88 +++++++++++++++++++ .../migrations/0070_switch_radius_key.py | 22 +++++ topologie/models.py | 21 ++++- 12 files changed, 368 insertions(+), 6 deletions(-) create mode 100644 preferences/migrations/0047_auto_20180711_0015.py create mode 100644 preferences/templates/preferences/aff_radiuskey.html create mode 100644 topologie/migrations/0070_switch_radius_key.py diff --git a/api/serializers.py b/api/serializers.py index fec0c9c1..454ca954 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -751,7 +751,7 @@ class SwitchPortSerializer(serializers.ModelSerializer): model = topologie.Switch fields = ('short_name', 'model', 'switchbay', 'ports', 'ipv4', 'ipv6', 'subnet', 'subnet6', 'automatic_provision', 'rest_enabled', - 'web_management_enabled') + 'web_management_enabled', 'get_radius_key_value') # LOCAL EMAILS diff --git a/preferences/admin.py b/preferences/admin.py index 5ca90095..2658a94e 100644 --- a/preferences/admin.py +++ b/preferences/admin.py @@ -37,7 +37,8 @@ from .models import ( MailContact, AssoOption, MailMessageOption, - HomeOption + HomeOption, + RadiusKey ) @@ -86,6 +87,11 @@ class HomeOptionAdmin(VersionAdmin): pass +class RadiusKeyAdmin(VersionAdmin): + """Class radiuskey""" + pass + + admin.site.register(OptionalUser, OptionalUserAdmin) admin.site.register(OptionalMachine, OptionalMachineAdmin) admin.site.register(OptionalTopologie, OptionalTopologieAdmin) @@ -93,5 +99,7 @@ admin.site.register(GeneralOption, GeneralOptionAdmin) admin.site.register(HomeOption, HomeOptionAdmin) admin.site.register(Service, ServiceAdmin) admin.site.register(MailContact, MailContactAdmin) +admin.site.register(Reminder, ReminderAdmin) +admin.site.register(RadiusKey, RadiusKeyAdmin) admin.site.register(AssoOption, AssoOptionAdmin) admin.site.register(MailMessageOption, MailMessageOptionAdmin) diff --git a/preferences/forms.py b/preferences/forms.py index 73731750..549bc016 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -39,6 +39,8 @@ from .models import ( HomeOption, Service, MailContact + Reminder, + RadiusKey ) @@ -242,8 +244,33 @@ class DelServiceForm(Form): else: self.fields['services'].queryset = Service.objects.all() -class MailContactForm(FormRevMixin, ModelForm): - """Edit and add contact email adress""" + +class RadiusKeyForm(FormRevMixin, ModelForm): + """Edition, ajout de clef radius""" + members = forms.ModelMultipleChoiceField( + Switch.objects.all(), + required=False + ) + + class Meta: + model = RadiusKey + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(RadiusKeyForm, self).__init__(*args, prefix=prefix, **kwargs) + instance = kwargs.get('instance', None) + if instance: + self.initial['members'] = Switch.objects.filter(radius_key=instance) + + def save(self, commit=True): + instance = super().save(commit) + instance.switch_set = self.cleaned_data['members'] + return instance + + +class MailContactForm(ModelForm): + """Edition, ajout d'adresse de contact""" class Meta: model = MailContact fields = '__all__' diff --git a/preferences/migrations/0047_auto_20180711_0015.py b/preferences/migrations/0047_auto_20180711_0015.py new file mode 100644 index 00000000..4100bf89 --- /dev/null +++ b/preferences/migrations/0047_auto_20180711_0015.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-07-10 22:15 +from __future__ import unicode_literals + +from django.db import migrations, models +import re2o.aes_field +import re2o.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0046_merge_20180710_1533'), + ] + + operations = [ + migrations.CreateModel( + name='RadiusKey', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('radius_key', re2o.aes_field.AESEncryptedField(help_text='Clef radius', max_length=255)), + ('comment', models.CharField(blank=True, help_text='Commentaire de cette clef', max_length=255, null=True)), + ('default_switch', models.BooleanField(default=True, help_text='Clef par défaut des switchs', unique=True)), + ], + options={ + 'permissions': (('view_radiuskey', 'Peut voir un objet radiuskey'),), + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.AlterField( + model_name='optionaluser', + name='gpg_fingerprint', + field=models.BooleanField(default=True), + ), + migrations.AlterField( + model_name='optionaluser', + name='is_tel_mandatory', + field=models.BooleanField(default=True), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index cb89a361..14ab9a52 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -35,6 +35,11 @@ from django.utils.translation import ugettext_lazy as _ import machines.models from re2o.mixins import AclMixin +<<<<<<< HEAD +======= +from re2o.aes_field import AESEncryptedField +from datetime import timedelta +>>>>>>> 3d881c4f... Gestion de la clef radius, et serialisation class PreferencesModel(models.Model): @@ -240,6 +245,63 @@ def optionaltopologie_post_save(**kwargs): topologie_pref.set_in_cache() +class RadiusKey(AclMixin, models.Model): + """Class of a radius key""" + radius_key = AESEncryptedField( + max_length=255, + help_text="Clef radius" + ) + comment = models.CharField( + max_length=255, + null=True, + blank=True, + help_text="Commentaire de cette clef" + ) + default_switch = models.BooleanField( + default=True, + unique=True, + help_text= "Clef par défaut des switchs" + ) + + class Meta: + permissions = ( + ("view_radiuskey", "Peut voir un objet radiuskey"), + ) + + +class Reminder(AclMixin, models.Model): + """Options pour les mails de notification de fin d'adhésion. + Days: liste des nombres de jours pour lesquells un mail est envoyé + optionalMessage: message additionel pour le mail + """ + PRETTY_NAME="Options pour le mail de fin d'adhésion" + + days = models.IntegerField( + default=7, + unique=True, + help_text="Délais entre le mail et la fin d'adhésion" + ) + message = models.CharField( + max_length=255, + default="", + null=True, + blank=True, + help_text="Message affiché spécifiquement pour ce rappel" + ) + + class Meta: + permissions = ( + ("view_reminder", "Peut voir un objet reminder"), + ) + + def users_to_remind(self): + from re2o.utils import all_has_access + date = timezone.now().replace(minute=0,hour=0) + futur_date = date + timedelta(days=self.days) + users = all_has_access(futur_date).exclude(pk__in = all_has_access(futur_date + timedelta(days=1))) + return users + + class GeneralOption(AclMixin, PreferencesModel): """Options générales : nombre de resultats par page, nom du site, temps où les liens sont valides""" diff --git a/preferences/templates/preferences/aff_radiuskey.html b/preferences/templates/preferences/aff_radiuskey.html new file mode 100644 index 00000000..6aca740d --- /dev/null +++ b/preferences/templates/preferences/aff_radiuskey.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 radiuskey in radiuskey_list %} + + + + + + + {% endfor %} +
ClefCommentaireClef par default des switchs
{{ radiuskey.radius_key }}{{ radiuskey.comment }}{{ radiuskey.default_switch }} + {% can_edit radiuskey %} + {% include 'buttons/edit.html' with href='preferences:edit-radiuskey' id=radiuskey.id %} + {% acl_end %} + {% include 'buttons/history.html' with href='preferences:history' name='radiuskey' id=radiuskey.id %} +
+ diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 6a499969..ccdfa4f0 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -118,6 +118,38 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "VLAN for machines rejected by RADIUS" %} {{ topologieoptions.vlan_decision_nok }} + + Placement sur ce vlan par default en cas de rejet + {{ topologieoptions.vlan_decision_nok }} + + + +
Configuration des switches
+ + + + + + + + + + + +
Web management, activé si provision automatique{{ topologieoptions.switchs_web_management }}Rest management, activé si provision auto{{ topologieoptions.switchs_rest_management }}
Plage d'ip de management des switchs{{ topologieoptions.switchs_ip_type }}
+ +
Clef radius
+ {% can_create RadiusKey%} + Ajouter une clef radius + {% acl_end %} + {% include "preferences/aff_radiuskey.html" with radiuskey_list=radiuskey_list %} + +
{% if topologieoptions.provisioned_switchs %}Provision de la config des switchs{% else %}Provision de la config des switchs{% endif%}
+ + + + +
Switchs configurés automatiquement{{ topologieoptions.provisioned_switchs|join:", " }}

{% trans "General preferences" %}

diff --git a/preferences/templates/preferences/preferences.html b/preferences/templates/preferences/preferences.html index 447df487..8ae9b6bc 100644 --- a/preferences/templates/preferences/preferences.html +++ b/preferences/templates/preferences/preferences.html @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load i18n %} +{% load massive_bootstrap_form %} {% block title %}{% trans "Preferences" %}{% endblock %} @@ -37,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% csrf_token %} {% if preferenceform %} - {% bootstrap_form preferenceform %} + {% massive_bootstrap_form preferenceform 'members' %} {% endif %} {% bootstrap_button action_name button_type="submit" icon='ok' button_class='btn-success' %}
diff --git a/preferences/urls.py b/preferences/urls.py index a89fcdf3..fc45c735 100644 --- a/preferences/urls.py +++ b/preferences/urls.py @@ -80,5 +80,19 @@ urlpatterns = [ name='edit-mailcontact' ), url(r'^del_mailcontact/$', views.del_mailcontact, name='del-mailcontact'), + url(r'^add_reminder/$', views.add_reminder, name='add-reminder'), + url( + r'^edit_reminder/(?P[0-9]+)$', + views.edit_reminder, + name='edit-reminder' + ), + url(r'^del_reminder/$', views.del_reminder, name='del-reminder'), + url(r'^add_radiuskey/$', views.add_radiuskey, name='add-radiuskey'), + url( + r'^edit_radiuskey/(?P[0-9]+)$', + views.edit_radiuskey, + name='edit-radiuskey' + ), + url(r'^del_radiuskey/$', views.del_radiuskey, name='del-radiuskey'), url(r'^$', views.display_options, name='display-options'), ] diff --git a/preferences/views.py b/preferences/views.py index 559cdfef..cc12177a 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -43,8 +43,16 @@ from reversion import revisions as reversion from re2o.views import form from re2o.acl import can_create, can_edit, can_delete_set, can_view_all +<<<<<<< HEAD from .forms import ( ServiceForm, DelServiceForm, MailContactForm, DelMailContactForm +======= +from .forms import MailContactForm, DelMailContactForm +from .forms import ( + ServiceForm, + ReminderForm, + RadiusKeyForm +>>>>>>> 3d881c4f... Gestion de la clef radius, et serialisation ) from .models import ( Service, @@ -55,7 +63,13 @@ from .models import ( MailMessageOption, GeneralOption, OptionalTopologie, +<<<<<<< HEAD HomeOption +======= + HomeOption, + Reminder, + RadiusKey +>>>>>>> 3d881c4f... Gestion de la clef radius, et serialisation ) from . import models from . import forms @@ -76,6 +90,11 @@ def display_options(request): mailmessageoptions, _created = MailMessageOption.objects.get_or_create() service_list = Service.objects.all() mailcontact_list = MailContact.objects.all() +<<<<<<< HEAD +======= + reminder_list = Reminder.objects.all() + radiuskey_list = RadiusKey.objects.all() +>>>>>>> 3d881c4f... Gestion de la clef radius, et serialisation return form({ 'useroptions': useroptions, 'machineoptions': machineoptions, @@ -85,7 +104,13 @@ def display_options(request): 'homeoptions': homeoptions, 'mailmessageoptions': mailmessageoptions, 'service_list': service_list, +<<<<<<< HEAD 'mailcontact_list': mailcontact_list +======= + 'reminder_list': reminder_list, + 'mailcontact_list': mailcontact_list, + 'radiuskey_list' : radiuskey_list, +>>>>>>> 3d881c4f... Gestion de la clef radius, et serialisation }, 'preferences/display_preferences.html', request) @@ -198,6 +223,69 @@ def del_service(request, instances): @login_required +<<<<<<< HEAD +======= +@can_delete(Reminder) +def del_reminder(request, reminder_instance, **_kwargs): + """Destruction d'un reminder""" + if request.method == "POST": + reminder_instance.delete() + messages.success(request, "Le reminder a été détruit") + return redirect(reverse('preferences:display-options')) + return form( + {'objet': reminder_instance, 'objet_name': 'reminder'}, + 'preferences/delete.html', + request + ) + + +@login_required +@can_create(RadiusKey) +def add_radiuskey(request): + """Ajout d'une clef radius""" + radiuskey = RadiusKeyForm(request.POST or None) + if radiuskey.is_valid(): + radiuskey.save() + messages.success(request, "Cette clef a été ajouté") + return redirect(reverse('preferences:display-options')) + return form( + {'preferenceform': radiuskey, 'action_name': 'Ajouter'}, + 'preferences/preferences.html', + request + ) + +@can_edit(RadiusKey) +def edit_radiuskey(request, radiuskey_instance, **_kwargs): + """Edition des clefs radius""" + radiuskey = RadiusKeyForm(request.POST or None, instance=radiuskey_instance) + if radiuskey.is_valid(): + radiuskey.save() + messages.success(request, "Radiuskey modifié") + return redirect(reverse('preferences:display-options')) + return form( + {'preferenceform': radiuskey, 'action_name': 'Editer'}, + 'preferences/preferences.html', + request + ) + + +@login_required +@can_delete(RadiusKey) +def del_radiuskey(request, radiuskey_instance, **_kwargs): + """Destruction d'un radiuskey""" + if request.method == "POST": + radiuskey_instance.delete() + messages.success(request, "La radiuskey a été détruite") + return redirect(reverse('preferences:display-options')) + return form( + {'objet': radiuskey_instance, 'objet_name': 'radiuskey'}, + 'preferences/delete.html', + request + ) + + +@login_required +>>>>>>> 3d881c4f... Gestion de la clef radius, et serialisation @can_create(MailContact) def add_mailcontact(request): """Add a contact email adress.""" diff --git a/topologie/migrations/0070_switch_radius_key.py b/topologie/migrations/0070_switch_radius_key.py new file mode 100644 index 00000000..b3d31b1f --- /dev/null +++ b/topologie/migrations/0070_switch_radius_key.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-07-10 22:20 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0047_auto_20180711_0015'), + ('topologie', '0069_switch_automatic_provision'), + ] + + operations = [ + migrations.AddField( + model_name='switch', + name='radius_key', + field=models.ForeignKey(blank=True, help_text='Clef radius du switch', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.RadiusKey'), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index e1d017be..e2c53427 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -49,6 +49,7 @@ from django.db import transaction from django.utils.translation import ugettext_lazy as _ from reversion import revisions as reversion +from preferences.models import OptionalTopologie, RadiusKey from machines.models import Machine, regen from re2o.mixins import AclMixin, RevMixin @@ -228,6 +229,13 @@ class Switch(AclMixin, Machine): null=True, on_delete=models.SET_NULL, ) + radius_key = models.ForeignKey( + 'preferences.RadiusKey', + blank=True, + null=True, + on_delete=models.PROTECT, + help_text="Clef radius du switch" + ) class Meta: unique_together = ('stack', 'stack_member_id') @@ -295,7 +303,18 @@ class Switch(AclMixin, Machine): @cached_property def get_name(self): return self.name or self.main_interface().domain.name - + + @cached_property + def get_radius_key(self): + return self.radius_key or RadiusKey.objects.filter(default_switch=True).first() + + @cached_property + def get_radius_key_value(self): + if self.get_radius_key: + return self.get_radius_key.radius_key + else: + return None + @cached_property def rest_enabled(self): return OptionalTopologie.get_cached_value('switchs_rest_management') or self.automatic_provision From 76183df6a8c0f850c321c29ad3dc171cef07142f Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 11 Jul 2018 01:37:08 +0200 Subject: [PATCH 15/29] Gestion automatique des switchs, affichage plus clair --- preferences/models.py | 8 +++++++- .../templates/preferences/display_preferences.html | 12 +++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/preferences/models.py b/preferences/models.py index 14ab9a52..68acf69e 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -225,12 +225,18 @@ class OptionalTopologie(AclMixin, PreferencesModel): help_text="Plage d'ip de management des switchs" ) - @cached_property def provisioned_switchs(self): + """Liste des switches provisionnés""" from topologie.models import Switch return Switch.objects.filter(automatic_provision=True) + @cached_property + def provision_switchs_enabled(self): + """Return true if all settings are ok : switchs on automatic provision, + ip_type""" + return bool(self.provisioned_switchs and self.switchs_ip_type) + class Meta: permissions = ( ("view_optionaltopologie", _("Can view the topology options")), diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index ccdfa4f0..4ab358c8 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -132,9 +132,19 @@ with this program; if not, write to the Free Software Foundation, Inc., Rest management, activé si provision auto {{ topologieoptions.switchs_rest_management }} + + + + +
{% if topologieoptions.provision_switchs_enabled %}Provision de la config des switchs{% else %}Provision de la config des switchs{% endif%}
+ + + + + - +
Switchs configurés automatiquement{{ topologieoptions.provisioned_switchs|join:", " }} {% if topologieoptions.provisioned_switchs %} OK{% else %}Manquant{% endif %}
Plage d'ip de management des switchs{{ topologieoptions.switchs_ip_type }}{{ topologieoptions.switchs_ip_type }} {% if topologieoptions.switchs_ip_type %} OK{% else %}Manquant{% endif %}
From 73610c34179a5d383634fb6de46fd8041ad2498b Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 11 Jul 2018 01:40:06 +0200 Subject: [PATCH 16/29] Methode str pour les clefs radius --- preferences/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/preferences/models.py b/preferences/models.py index 68acf69e..ff29241d 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -274,6 +274,9 @@ class RadiusKey(AclMixin, models.Model): ("view_radiuskey", "Peut voir un objet radiuskey"), ) + def __str__(self): + return "Clef radius " + str(self.id) + " " + str(self.comment) + class Reminder(AclMixin, models.Model): """Options pour les mails de notification de fin d'adhésion. From b4af276b146f88e4e6b96a512fc63cdf13fda5e8 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 11 Jul 2018 02:19:29 +0200 Subject: [PATCH 17/29] Stockage des creds d'identification des switchs --- preferences/admin.py | 8 ++- preferences/forms.py | 28 +++++++- .../migrations/0048_switchmanagementcred.py | 30 ++++++++ preferences/models.py | 27 ++++++- .../preferences/aff_switchmanagementcred.html | 47 ++++++++++++ .../preferences/display_preferences.html | 21 ++++-- preferences/urls.py | 7 ++ preferences/views.py | 71 +++++++++++++------ .../0071_switch_management_creds.py | 22 ++++++ topologie/models.py | 28 +++++++- 10 files changed, 260 insertions(+), 29 deletions(-) create mode 100644 preferences/migrations/0048_switchmanagementcred.py create mode 100644 preferences/templates/preferences/aff_switchmanagementcred.html create mode 100644 topologie/migrations/0071_switch_management_creds.py diff --git a/preferences/admin.py b/preferences/admin.py index 2658a94e..57f39595 100644 --- a/preferences/admin.py +++ b/preferences/admin.py @@ -38,7 +38,8 @@ from .models import ( AssoOption, MailMessageOption, HomeOption, - RadiusKey + RadiusKey, + SwitchManagementCred ) @@ -91,6 +92,10 @@ class RadiusKeyAdmin(VersionAdmin): """Class radiuskey""" pass +class SwitchManagementCredAdmin(VersionAdmin): + """Class managementcred for switch""" + pass + admin.site.register(OptionalUser, OptionalUserAdmin) admin.site.register(OptionalMachine, OptionalMachineAdmin) @@ -101,5 +106,6 @@ admin.site.register(Service, ServiceAdmin) admin.site.register(MailContact, MailContactAdmin) admin.site.register(Reminder, ReminderAdmin) admin.site.register(RadiusKey, RadiusKeyAdmin) +admin.site.register(SwitchManagementCred, SwitchManagementCredAdmin) admin.site.register(AssoOption, AssoOptionAdmin) admin.site.register(MailMessageOption, MailMessageOptionAdmin) diff --git a/preferences/forms.py b/preferences/forms.py index 549bc016..976070a2 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -40,7 +40,8 @@ from .models import ( Service, MailContact Reminder, - RadiusKey + RadiusKey, + SwitchManagementCred ) @@ -269,6 +270,31 @@ class RadiusKeyForm(FormRevMixin, ModelForm): return instance +class SwitchManagementCredForm(FormRevMixin, ModelForm): + """Edition, ajout de creds de management pour gestion + et interface rest des switchs""" + members = forms.ModelMultipleChoiceField( + Switch.objects.all(), + required=False + ) + + class Meta: + model = SwitchManagementCred + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(SwitchManagementCredForm, self).__init__(*args, prefix=prefix, **kwargs) + instance = kwargs.get('instance', None) + if instance: + self.initial['members'] = Switch.objects.filter(management_creds=instance) + + def save(self, commit=True): + instance = super().save(commit) + instance.switch_set = self.cleaned_data['members'] + return instance + + class MailContactForm(ModelForm): """Edition, ajout d'adresse de contact""" class Meta: diff --git a/preferences/migrations/0048_switchmanagementcred.py b/preferences/migrations/0048_switchmanagementcred.py new file mode 100644 index 00000000..9221b115 --- /dev/null +++ b/preferences/migrations/0048_switchmanagementcred.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-07-10 23:57 +from __future__ import unicode_literals + +from django.db import migrations, models +import re2o.aes_field +import re2o.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0047_auto_20180711_0015'), + ] + + operations = [ + migrations.CreateModel( + name='SwitchManagementCred', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('management_id', models.CharField(help_text='Login du switch', max_length=63)), + ('management_pass', re2o.aes_field.AESEncryptedField(help_text='Mot de passe', max_length=63)), + ('default_switch', models.BooleanField(default=True, help_text='Creds par défaut des switchs', unique=True)), + ], + options={ + 'permissions': (('view_switchmanagementcred', 'Peut voir un objet switchmanagementcred'),), + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index ff29241d..65db5d97 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -235,7 +235,7 @@ class OptionalTopologie(AclMixin, PreferencesModel): def provision_switchs_enabled(self): """Return true if all settings are ok : switchs on automatic provision, ip_type""" - return bool(self.provisioned_switchs and self.switchs_ip_type) + return bool(self.provisioned_switchs and self.switchs_ip_type and SwitchManagementCred.objects.filter(default_switch=True).exists()) class Meta: permissions = ( @@ -278,6 +278,31 @@ class RadiusKey(AclMixin, models.Model): return "Clef radius " + str(self.id) + " " + str(self.comment) +class SwitchManagementCred(AclMixin, models.Model): + """Class of a management creds of a switch, for rest management""" + management_id = models.CharField( + max_length=63, + help_text="Login du switch" + ) + management_pass = AESEncryptedField( + max_length=63, + help_text="Mot de passe" + ) + default_switch = models.BooleanField( + default=True, + unique=True, + help_text= "Creds par défaut des switchs" + ) + + class Meta: + permissions = ( + ("view_switchmanagementcred", "Peut voir un objet switchmanagementcred"), + ) + + def __str__(self): + return "Identifiant " + str(self.management_id) + + class Reminder(AclMixin, models.Model): """Options pour les mails de notification de fin d'adhésion. Days: liste des nombres de jours pour lesquells un mail est envoyé diff --git a/preferences/templates/preferences/aff_switchmanagementcred.html b/preferences/templates/preferences/aff_switchmanagementcred.html new file mode 100644 index 00000000..2f03edf8 --- /dev/null +++ b/preferences/templates/preferences/aff_switchmanagementcred.html @@ -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 %} +{% load acl %} + + + + + + + + + + {% for switchmanagementcred in switchmanagementcred_list %} + + + + + + {% endfor %} +
IdentifiantCreds par default des switchs
{{ switchmanagementcred.management_id }}{{ switchmanagementcred.default_switch }} + {% can_edit switchmanagementcred %} + {% include 'buttons/edit.html' with href='preferences:edit-switchmanagementcred' id=switchmanagementcred.id %} + {% acl_end %} + {% include 'buttons/history.html' with href='preferences:history' name='switchmanagementcred' id=switchmanagementcred.id %} +
+ diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 4ab358c8..85c4c5cd 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -124,7 +124,13 @@ with this program; if not, write to the Free Software Foundation, Inc., -
Configuration des switches
+
Clef radius
+ {% can_create RadiusKey%} +
Ajouter une clef radius + {% acl_end %} + {% include "preferences/aff_radiuskey.html" with radiuskey_list=radiuskey_list %} + +

Configuration des switches

@@ -148,11 +154,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Web management, activé si provision automatique
-
Clef radius
- {% can_create RadiusKey%} - Ajouter une clef radius +
Creds de management des switchs
+ {% can_create SwitchManagementCred%} + Ajouter un id/mdp de management switch {% acl_end %} - {% include "preferences/aff_radiuskey.html" with radiuskey_list=radiuskey_list %} +

+

+ {% if switchmanagementcred_list %} OK{% else %}Manquant{% endif %} + {% include "preferences/aff_switchmanagementcred.html" with switchmanagementcred_list=switchmanagementcred_list %} + +
{% if topologieoptions.provisioned_switchs %}Provision de la config des switchs{% else %}Provision de la config des switchs{% endif%}
diff --git a/preferences/urls.py b/preferences/urls.py index fc45c735..87e2f0ae 100644 --- a/preferences/urls.py +++ b/preferences/urls.py @@ -94,5 +94,12 @@ urlpatterns = [ name='edit-radiuskey' ), url(r'^del_radiuskey/$', views.del_radiuskey, name='del-radiuskey'), + url(r'^add_switchmanagementcred/$', views.add_switchmanagementcred, name='add-switchmanagementcred'), + url( + r'^edit_switchmanagementcred/(?P[0-9]+)$', + views.edit_switchmanagementcred, + name='edit-switchmanagementcred' + ), + url(r'^del_switchmanagementcred/$', views.del_switchmanagementcred, name='del-switchmanagementcred'), url(r'^$', views.display_options, name='display-options'), ] diff --git a/preferences/views.py b/preferences/views.py index cc12177a..726e430e 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -43,16 +43,12 @@ from reversion import revisions as reversion from re2o.views import form from re2o.acl import can_create, can_edit, can_delete_set, can_view_all -<<<<<<< HEAD -from .forms import ( - ServiceForm, DelServiceForm, MailContactForm, DelMailContactForm -======= from .forms import MailContactForm, DelMailContactForm from .forms import ( ServiceForm, ReminderForm, - RadiusKeyForm ->>>>>>> 3d881c4f... Gestion de la clef radius, et serialisation + RadiusKeyForm, + SwitchManagementCredForm ) from .models import ( Service, @@ -63,13 +59,10 @@ from .models import ( MailMessageOption, GeneralOption, OptionalTopologie, -<<<<<<< HEAD - HomeOption -======= HomeOption, Reminder, - RadiusKey ->>>>>>> 3d881c4f... Gestion de la clef radius, et serialisation + RadiusKey, + SwitchManagementCred ) from . import models from . import forms @@ -90,11 +83,9 @@ def display_options(request): mailmessageoptions, _created = MailMessageOption.objects.get_or_create() service_list = Service.objects.all() mailcontact_list = MailContact.objects.all() -<<<<<<< HEAD -======= reminder_list = Reminder.objects.all() radiuskey_list = RadiusKey.objects.all() ->>>>>>> 3d881c4f... Gestion de la clef radius, et serialisation + switchmanagementcred_list = SwitchManagementCred.objects.all() return form({ 'useroptions': useroptions, 'machineoptions': machineoptions, @@ -104,13 +95,11 @@ def display_options(request): 'homeoptions': homeoptions, 'mailmessageoptions': mailmessageoptions, 'service_list': service_list, -<<<<<<< HEAD 'mailcontact_list': mailcontact_list -======= 'reminder_list': reminder_list, 'mailcontact_list': mailcontact_list, 'radiuskey_list' : radiuskey_list, ->>>>>>> 3d881c4f... Gestion de la clef radius, et serialisation + 'switchmanagementcred_list': switchmanagementcred_list, }, 'preferences/display_preferences.html', request) @@ -223,8 +212,6 @@ def del_service(request, instances): @login_required -<<<<<<< HEAD -======= @can_delete(Reminder) def del_reminder(request, reminder_instance, **_kwargs): """Destruction d'un reminder""" @@ -285,7 +272,51 @@ def del_radiuskey(request, radiuskey_instance, **_kwargs): @login_required ->>>>>>> 3d881c4f... Gestion de la clef radius, et serialisation +@can_create(SwitchManagementCred) +def add_switchmanagementcred(request): + """Ajout de creds de management""" + switchmanagementcred = SwitchManagementCredForm(request.POST or None) + if switchmanagementcred.is_valid(): + switchmanagementcred.save() + messages.success(request, "Ces creds ont été ajoutés") + return redirect(reverse('preferences:display-options')) + return form( + {'preferenceform': switchmanagementcred, 'action_name': 'Ajouter'}, + 'preferences/preferences.html', + request + ) + +@can_edit(SwitchManagementCred) +def edit_switchmanagementcred(request, switchmanagementcred_instance, **_kwargs): + """Edition des creds de management""" + switchmanagementcred = SwitchManagementCredForm(request.POST or None, instance=switchmanagementcred_instance) + if switchmanagementcred.is_valid(): + switchmanagementcred.save() + messages.success(request, "Creds de managament modifié") + return redirect(reverse('preferences:display-options')) + return form( + {'preferenceform': switchmanagementcred, 'action_name': 'Editer'}, + 'preferences/preferences.html', + request + ) + + +@login_required +@can_delete(SwitchManagementCred) +def del_switchmanagementcred(request, switchmanagementcred_instance, **_kwargs): + """Destruction d'un switchmanagementcred""" + if request.method == "POST": + switchmanagementcred_instance.delete() + messages.success(request, "Ce switchmanagementcred a été détruit") + return redirect(reverse('preferences:display-options')) + return form( + {'objet': switchmanagementcred_instance, 'objet_name': 'switchmanagementcred'}, + 'preferences/delete.html', + request + ) + + +@login_required @can_create(MailContact) def add_mailcontact(request): """Add a contact email adress.""" diff --git a/topologie/migrations/0071_switch_management_creds.py b/topologie/migrations/0071_switch_management_creds.py new file mode 100644 index 00000000..d9eeb774 --- /dev/null +++ b/topologie/migrations/0071_switch_management_creds.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-07-10 23:57 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0048_switchmanagementcred'), + ('topologie', '0070_switch_radius_key'), + ] + + operations = [ + migrations.AddField( + model_name='switch', + name='management_creds', + field=models.ForeignKey(blank=True, help_text='Identifiant de management de ce switch', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.SwitchManagementCred'), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index e2c53427..8076abb7 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -49,7 +49,11 @@ from django.db import transaction from django.utils.translation import ugettext_lazy as _ from reversion import revisions as reversion -from preferences.models import OptionalTopologie, RadiusKey +from preferences.models import ( + OptionalTopologie, + RadiusKey, + SwitchManagementCred +) from machines.models import Machine, regen from re2o.mixins import AclMixin, RevMixin @@ -236,6 +240,13 @@ class Switch(AclMixin, Machine): on_delete=models.PROTECT, help_text="Clef radius du switch" ) + management_creds = models.ForeignKey( + 'preferences.SwitchManagementCred', + blank=True, + null=True, + on_delete=models.PROTECT, + help_text="Identifiant de management de ce switch" + ) class Meta: unique_together = ('stack', 'stack_member_id') @@ -306,15 +317,30 @@ class Switch(AclMixin, Machine): @cached_property def get_radius_key(self): + """Retourne l'objet de la clef radius de ce switch""" return self.radius_key or RadiusKey.objects.filter(default_switch=True).first() @cached_property def get_radius_key_value(self): + """Retourne la valeur en str de la clef radius, none si il n'y en a pas""" if self.get_radius_key: return self.get_radius_key.radius_key else: return None + @cached_property + def get_management_cred(self): + """Retourne l'objet des creds de managament de ce switch""" + return self.management_creds or SwitchManagementCred.objects.filter(default_switch=True).first() + + @cached_property + def get_management_cred_value(self): + """Retourne un dict des creds de management du switch""" + if self.get_management_cred: + return {'id': self.get_management_cred.management_id, 'pass': self.get_management_cred.management_pass} + else: + return None + @cached_property def rest_enabled(self): return OptionalTopologie.get_cached_value('switchs_rest_management') or self.automatic_provision From ca2e0172a1be6b8ffbf735c13b8e13beeb16d63b Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 11 Jul 2018 02:21:08 +0200 Subject: [PATCH 18/29] Serialisation pour export vers re2o-services --- api/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/serializers.py b/api/serializers.py index 454ca954..4605b79b 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -751,7 +751,7 @@ class SwitchPortSerializer(serializers.ModelSerializer): model = topologie.Switch fields = ('short_name', 'model', 'switchbay', 'ports', 'ipv4', 'ipv6', 'subnet', 'subnet6', 'automatic_provision', 'rest_enabled', - 'web_management_enabled', 'get_radius_key_value') + 'web_management_enabled', 'get_radius_key_value', 'get_management_cred_value') # LOCAL EMAILS From c12b3575dd8a6ae18a5672b41c31e46a16d0aee6 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 11 Jul 2018 03:14:56 +0200 Subject: [PATCH 19/29] Bricoles d'affichage --- .../templates/preferences/aff_radiuskey.html | 7 ++++ .../preferences/aff_switchmanagementcred.html | 7 ++++ preferences/templates/preferences/delete.html | 40 +++++++++++++++++++ preferences/urls.py | 4 +- preferences/views.py | 16 ++++++-- 5 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 preferences/templates/preferences/delete.html diff --git a/preferences/templates/preferences/aff_radiuskey.html b/preferences/templates/preferences/aff_radiuskey.html index 6aca740d..9d9980fa 100644 --- a/preferences/templates/preferences/aff_radiuskey.html +++ b/preferences/templates/preferences/aff_radiuskey.html @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., + @@ -37,10 +38,16 @@ with this program; if not, write to the Free Software Foundation, Inc., + diff --git a/preferences/templates/preferences/aff_switchmanagementcred.html b/preferences/templates/preferences/aff_switchmanagementcred.html index 2f03edf8..d69a287a 100644 --- a/preferences/templates/preferences/aff_switchmanagementcred.html +++ b/preferences/templates/preferences/aff_switchmanagementcred.html @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., + @@ -35,10 +36,16 @@ with this program; if not, write to the Free Software Foundation, Inc., + diff --git a/preferences/templates/preferences/delete.html b/preferences/templates/preferences/delete.html new file mode 100644 index 00000000..ce277647 --- /dev/null +++ b/preferences/templates/preferences/delete.html @@ -0,0 +1,40 @@ +{% 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 %}Création et modification de machines{% endblock %} + +{% block content %} + + + {% csrf_token %} +

Attention, voulez-vous vraiment supprimer cet objet {{ objet_name }} ( {{ objet }} ) ?

+ {% bootstrap_button "Confirmer" button_type="submit" icon="trash" %} + +
+
+
+{% endblock %} diff --git a/preferences/urls.py b/preferences/urls.py index 87e2f0ae..89d0254c 100644 --- a/preferences/urls.py +++ b/preferences/urls.py @@ -93,13 +93,13 @@ urlpatterns = [ views.edit_radiuskey, name='edit-radiuskey' ), - url(r'^del_radiuskey/$', views.del_radiuskey, name='del-radiuskey'), + url(r'^del_radiuskey/(?P[0-9]+)$', views.del_radiuskey, name='del-radiuskey'), url(r'^add_switchmanagementcred/$', views.add_switchmanagementcred, name='add-switchmanagementcred'), url( r'^edit_switchmanagementcred/(?P[0-9]+)$', views.edit_switchmanagementcred, name='edit-switchmanagementcred' ), - url(r'^del_switchmanagementcred/$', views.del_switchmanagementcred, name='del-switchmanagementcred'), + url(r'^del_switchmanagementcred/(?P[0-9]+)$', views.del_switchmanagementcred, name='del-switchmanagementcred'), url(r'^$', views.display_options, name='display-options'), ] diff --git a/preferences/views.py b/preferences/views.py index 726e430e..181434ea 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -261,8 +261,12 @@ def edit_radiuskey(request, radiuskey_instance, **_kwargs): def del_radiuskey(request, radiuskey_instance, **_kwargs): """Destruction d'un radiuskey""" if request.method == "POST": - radiuskey_instance.delete() - messages.success(request, "La radiuskey a été détruite") + try: + radiuskey_instance.delete() + messages.success(request, "La radiuskey a été détruite") + except ProtectedError: + messages.error(request, "Erreur la\ + clef ne peut être supprimé, elle est affectée à des switchs") return redirect(reverse('preferences:display-options')) return form( {'objet': radiuskey_instance, 'objet_name': 'radiuskey'}, @@ -306,8 +310,12 @@ def edit_switchmanagementcred(request, switchmanagementcred_instance, **_kwargs) def del_switchmanagementcred(request, switchmanagementcred_instance, **_kwargs): """Destruction d'un switchmanagementcred""" if request.method == "POST": - switchmanagementcred_instance.delete() - messages.success(request, "Ce switchmanagementcred a été détruit") + try: + switchmanagementcred_instance.delete() + messages.success(request, "Ces creds ont été détruits") + except ProtectedError: + messages.error(request, "Erreur ces\ + creds ne peuvent être supprimés, ils sont affectés à des switchs") return redirect(reverse('preferences:display-options')) return form( {'objet': switchmanagementcred_instance, 'objet_name': 'switchmanagementcred'}, From 6c41f903989aa6ed02c9cdc4841df27d41709fa3 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 11 Jul 2018 16:44:07 +0200 Subject: [PATCH 20/29] N'affiche pas la clef radius en clair dans le menu preferences --- preferences/templates/preferences/aff_radiuskey.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/preferences/templates/preferences/aff_radiuskey.html b/preferences/templates/preferences/aff_radiuskey.html index 9d9980fa..073aeea2 100644 --- a/preferences/templates/preferences/aff_radiuskey.html +++ b/preferences/templates/preferences/aff_radiuskey.html @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Clef Commentaire Clef par default des switchsClef utilisée par les switchs
{{ radiuskey.radius_key }} {{ radiuskey.comment }} {{ radiuskey.default_switch }}{{ radiuskey.switch_set.all|join:", " }} {% can_edit radiuskey %} {% include 'buttons/edit.html' with href='preferences:edit-radiuskey' id=radiuskey.id %} {% acl_end %} + {% can_delete radiuskey %} + + + + {% acl_end %} {% include 'buttons/history.html' with href='preferences:history' name='radiuskey' id=radiuskey.id %}
Identifiant Creds par default des switchsUtilisé pour les switchs
{{ switchmanagementcred.management_id }} {{ switchmanagementcred.default_switch }}{{ switchmanagementcred.switch_set.all|join:", " }} {% can_edit switchmanagementcred %} {% include 'buttons/edit.html' with href='preferences:edit-switchmanagementcred' id=switchmanagementcred.id %} {% acl_end %} + {% can_delete switchmanagementcred %} + + + + {% acl_end %} {% include 'buttons/history.html' with href='preferences:history' name='switchmanagementcred' id=switchmanagementcred.id %}
- + @@ -35,7 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% for radiuskey in radiuskey_list %} - + From ae0f134cc7f40dc0f3570c0c807692305fa0bc8a Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 11 Jul 2018 19:46:13 +0200 Subject: [PATCH 21/29] =?UTF-8?q?Cr=C3=A9e=20sp=C3=A9cific=20role,=20l'uti?= =?UTF-8?q?lise=20pour=20get=20l'ip=20du=20serveur=20des=20config=20switch?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/serializers.py | 2 +- .../migrations/0094_role_specific_role.py | 20 +++++++++++++++++++ machines/models.py | 13 ++++++++++++ preferences/models.py | 18 ++++++++++++++++- .../preferences/display_preferences.html | 4 ++++ 5 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 machines/migrations/0094_role_specific_role.py diff --git a/api/serializers.py b/api/serializers.py index 4605b79b..f06e1b92 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -696,7 +696,7 @@ class RoleSerializer(NamespacedHMSerializer): class Meta: model = machines.Role - fields = ('role_type', 'servers') + fields = ('role_type', 'servers', 'specific_role') class VlanPortSerializer(NamespacedHMSerializer): diff --git a/machines/migrations/0094_role_specific_role.py b/machines/migrations/0094_role_specific_role.py new file mode 100644 index 00000000..73cade7b --- /dev/null +++ b/machines/migrations/0094_role_specific_role.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-07-11 16:49 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0093_merge_20180710_0226'), + ] + + operations = [ + migrations.AddField( + model_name='role', + name='specific_role', + field=models.CharField(blank=True, choices=[('dhcp-server', 'dhcp-server'), ('switch-conf-server', 'switch-conf-server'), ('dns-recursif-server', 'dns-recursif-server'), ('ntp-server', 'ntp-server'), ('radius-server', 'radius-server'), ('ntp-server', 'ntp-server'), ('log-server', 'log-server'), ('ldap-master-server', 'ldap-master-server'), ('ldap-backup-server', 'ldap-backup-server'), ('smtp-server', 'smtp-server'), ('postgresql-server', 'postgresql-server'), ('mysql-server', 'mysql-server'), ('sql-client', 'sql-client'), ('gateway', 'gateway')], max_length=32, null=True), + ), + ] diff --git a/machines/models.py b/machines/models.py index 212d65e8..d5cdc5a1 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1652,6 +1652,19 @@ class Role(RevMixin, AclMixin, models.Model): machine__interface__role=cls.objects.filter(specific_role=roletype) ) + @classmethod + def get_instance(cls, machineid, *_args, **_kwargs): + """Get the Machine instance with machineid. + :param userid: The id + :return: The user + """ + return cls.objects.get(pk=machineid) + + @classmethod + def interface_for_roletype(cls, roletype): + """Return interfaces for a roletype""" + return Interface.objects.filter(role=cls.objects.filter(specific_role=roletype)) + def save(self, *args, **kwargs): super(Role, self).save(*args, **kwargs) diff --git a/preferences/models.py b/preferences/models.py index 65db5d97..c1d19a23 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -231,11 +231,27 @@ class OptionalTopologie(AclMixin, PreferencesModel): from topologie.models import Switch return Switch.objects.filter(automatic_provision=True) + @cached_property + def switchs_management_interface(self): + """Return the ip of the interface that the switch have to contact to get it's config""" + if self.switchs_ip_type: + from machines.models import Role, Interface + return Interface.objects.filter(machine__interface__in=Role.interface_for_roletype("switch-conf-server")).filter(type__ip_type=self.switchs_ip_type).first() + else: + return None + + @cached_property + def switchs_management_interface_ip(self): + """Same, but return the ipv4""" + if not self.switchs_management_interface: + return None + return self.switchs_management_interface.ipv4 + @cached_property def provision_switchs_enabled(self): """Return true if all settings are ok : switchs on automatic provision, ip_type""" - return bool(self.provisioned_switchs and self.switchs_ip_type and SwitchManagementCred.objects.filter(default_switch=True).exists()) + return bool(self.provisioned_switchs and self.switchs_ip_type and SwitchManagementCred.objects.filter(default_switch=True).exists() and self.switchs_management_interface_ip) class Meta: permissions = ( diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 85c4c5cd..5ea1f0e6 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -152,6 +152,10 @@ with this program; if not, write to the Free Software Foundation, Inc., + + + +
ClefId Clef Commentaire Clef par default des switchs Clef utilisée par les switchs
{{ radiuskey.radius_key }}{{ radiuskey.id }} {{ radiuskey.comment }} {{ radiuskey.default_switch }} {{ radiuskey.switch_set.all|join:", " }}Plage d'ip de management des switchs {{ topologieoptions.switchs_ip_type }} {% if topologieoptions.switchs_ip_type %} OK{% else %}Manquant{% endif %}
Serveur des config des switchs{{ topologieoptions.switchs_management_interface }} {% if topologieoptions.switchs_management_interface %} - {{ topologieoptions.switchs_management_interface_ip }} OK{% else %}Manquant{% endif %}
Creds de management des switchs
From 3c22ea4e89338a3bbf3305ae108df73e04cdf408 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Thu, 12 Jul 2018 01:12:46 +0200 Subject: [PATCH 22/29] Export de l'ensemble des ip du switch --- api/serializers.py | 2 +- machines/models.py | 2 +- topologie/models.py | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index f06e1b92..f5e52837 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -750,7 +750,7 @@ class SwitchPortSerializer(serializers.ModelSerializer): class Meta: model = topologie.Switch fields = ('short_name', 'model', 'switchbay', 'ports', 'ipv4', 'ipv6', - 'subnet', 'subnet6', 'automatic_provision', 'rest_enabled', + 'interfaces_subnet', 'interfaces6_subnet', 'automatic_provision', 'rest_enabled', 'web_management_enabled', 'get_radius_key_value', 'get_management_cred_value') # LOCAL EMAILS diff --git a/machines/models.py b/machines/models.py index d5cdc5a1..040043a1 100644 --- a/machines/models.py +++ b/machines/models.py @@ -377,7 +377,7 @@ class IpType(RevMixin, AclMixin, models.Model): 'netmask' : 'ffff:ffff:ffff:ffff::', 'netmask_cidr' : str(self.prefix_v6_length), 'vlan': str(self.vlan), - 'vlan_id': str(self.vlan.vlan_id) + 'vlan_id': self.vlan.vlan_id } else: return None diff --git a/topologie/models.py b/topologie/models.py index 8076abb7..28216819 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -367,13 +367,14 @@ class Switch(AclMixin, Machine): return str(self.main_interface().ipv6().first()) @cached_property - def subnet(self): - """ Return the subnet of the management ip""" - return self.main_interface().type.ip_type.ip_set_full_info + def interfaces_subnet(self): + """Return dict ip:subnet for all ip of the switch""" + return dict((str(interface.ipv4), interface.type.ip_type.ip_set_full_info) for interface in self.interface_set.all()) @cached_property - def subnet6(self): - return self.main_interface().type.ip_type.ip6_set_full_info + def interfaces6_subnet(self): + """Return dict ip6:subnet for all ipv6 of the switch""" + return dict((str(interface.ipv6().first()), interface.type.ip_type.ip6_set_full_info) for interface in self.interface_set.all()) def __str__(self): return str(self.get_name) From 8a22dc1e0294bf1b2dcc7377d637bfac0e92dd16 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Thu, 12 Jul 2018 17:33:26 +0200 Subject: [PATCH 23/29] Mode de provision sftp --- api/serializers.py | 5 +- preferences/models.py | 53 +++++++++++++++++-- .../preferences/display_preferences.html | 12 +++++ 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index f5e52837..f5fd1fa2 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -353,7 +353,10 @@ class OptionalTopologieSerializer(NamespacedHMSerializer): class Meta: model = preferences.OptionalTopologie fields = ('radius_general_policy', 'vlan_decision_ok', - 'vlan_decision_nok') + 'vlan_decision_nok', 'switchs_ip_type', 'switchs_web_management', + 'switchs_web_management_ssl', 'switchs_rest_management', + 'switchs_management_utils', 'switchs_management_interface_ip', + 'provision_switchs_enabled', 'switchs_provision', 'switchs_management_sftp_creds') class GeneralOptionSerializer(NamespacedHMSerializer): diff --git a/preferences/models.py b/preferences/models.py index c1d19a23..5c2d58ba 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -35,11 +35,8 @@ from django.utils.translation import ugettext_lazy as _ import machines.models from re2o.mixins import AclMixin -<<<<<<< HEAD -======= from re2o.aes_field import AESEncryptedField from datetime import timedelta ->>>>>>> 3d881c4f... Gestion de la clef radius, et serialisation class PreferencesModel(models.Model): @@ -185,6 +182,10 @@ class OptionalTopologie(AclMixin, PreferencesModel): (MACHINE, _("On the IP range's VLAN of the machine")), (DEFINED, _("Preset in 'VLAN for machines accepted by RADIUS'")), ) + CHOICE_PROVISION = ( + ('sftp', 'sftp'), + ('tftp', 'tftp'), + ) radius_general_policy = models.CharField( max_length=32, @@ -224,6 +225,24 @@ class OptionalTopologie(AclMixin, PreferencesModel): null=True, help_text="Plage d'ip de management des switchs" ) + switchs_provision = models.CharField( + max_length=32, + choices=CHOICE_PROVISION, + default='tftp', + help_text="Mode de récupération des confs par les switchs" + ) + sftp_login = models.CharField( + max_length=32, + null=True, + blank=True, + help_text="Login sftp des switchs" + ) + sftp_pass = AESEncryptedField( + max_length=63, + null=True, + blank=True, + help_text="Mot de passe sftp" + ) @cached_property def provisioned_switchs(self): @@ -247,11 +266,37 @@ class OptionalTopologie(AclMixin, PreferencesModel): return None return self.switchs_management_interface.ipv4 + @cached_property + def switchs_management_sftp_creds(self): + """Credentials des switchs pour provion sftp""" + if self.sftp_login and self.sftp_pass: + return {'login' : self.sftp_login, 'pass' : self.sftp_pass} + else: + return None + + @cached_property + def switchs_management_utils(self): + """Used for switch_conf, return a list of ip on vlans""" + from machines.models import Role, Ipv6List, Interface + def return_ips_dict(interfaces): + return {'ipv4' : [str(interface.ipv4) for interface in interfaces], 'ipv6' : Ipv6List.objects.filter(interface__in=interfaces).values_list('ipv6', flat=True)} + + ntp_servers = Role.all_interfaces_for_roletype("ntp-server").filter(type__ip_type=self.switchs_ip_type) + log_servers = Role.all_interfaces_for_roletype("log-server").filter(type__ip_type=self.switchs_ip_type) + radius_servers = Role.all_interfaces_for_roletype("radius-server").filter(type__ip_type=self.switchs_ip_type) + dhcp_servers = Role.all_interfaces_for_roletype("dhcp-server") + subnet = None + subnet6 = None + if self.switchs_ip_type: + subnet = self.switchs_ip_type.ip_set_full_info + subnet6 = self.switchs_ip_type.ip6_set_full_info + return {'ntp_servers': return_ips_dict(ntp_servers), 'log_servers': return_ips_dict(log_servers), 'radius_servers': return_ips_dict(radius_servers), 'dhcp_servers': return_ips_dict(dhcp_servers), 'subnet': subnet, 'subnet6': subnet6} + @cached_property def provision_switchs_enabled(self): """Return true if all settings are ok : switchs on automatic provision, ip_type""" - return bool(self.provisioned_switchs and self.switchs_ip_type and SwitchManagementCred.objects.filter(default_switch=True).exists() and self.switchs_management_interface_ip) + return bool(self.provisioned_switchs and self.switchs_ip_type and SwitchManagementCred.objects.filter(default_switch=True).exists() and self.switchs_management_interface_ip and bool(self.switchs_provision != 'sftp' or self.switchs_management_sftp_creds)) class Meta: permissions = ( diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 5ea1f0e6..0cba4d78 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -156,6 +156,18 @@ with this program; if not, write to the Free Software Foundation, Inc., Serveur des config des switchs {{ topologieoptions.switchs_management_interface }} {% if topologieoptions.switchs_management_interface %} - {{ topologieoptions.switchs_management_interface_ip }} OK{% else %}Manquant{% endif %} + + Mode de provision des switchs + {{ topologieoptions.switchs_provision }} + + + Mode TFTP + OK + + + Mode SFTP + {% if topologieoptions.switchs_management_sftp_creds %} OK{% else %}Creds manquants{% endif %} +
Creds de management des switchs
From bd58c259eeb86ac1399149ee5b648432783203f7 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Thu, 12 Jul 2018 17:38:15 +0200 Subject: [PATCH 24/29] Et la migration pour sftp --- .../migrations/0049_auto_20180712_1713.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 preferences/migrations/0049_auto_20180712_1713.py diff --git a/preferences/migrations/0049_auto_20180712_1713.py b/preferences/migrations/0049_auto_20180712_1713.py new file mode 100644 index 00000000..e47f547d --- /dev/null +++ b/preferences/migrations/0049_auto_20180712_1713.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-07-12 15:13 +from __future__ import unicode_literals + +from django.db import migrations, models +import re2o.aes_field + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0048_switchmanagementcred'), + ] + + operations = [ + migrations.AddField( + model_name='optionaltopologie', + name='sftp_login', + field=models.CharField(blank=True, help_text='Login sftp des switchs', max_length=32, null=True), + ), + migrations.AddField( + model_name='optionaltopologie', + name='sftp_pass', + field=re2o.aes_field.AESEncryptedField(blank=True, help_text='Mot de passe sftp', max_length=63, null=True), + ), + migrations.AddField( + model_name='optionaltopologie', + name='switchs_provision', + field=models.CharField(choices=[('sftp', 'sftp'), ('tftp', 'tftp')], default='tftp', help_text='Mode de récupération des confs par les switchs', max_length=32), + ), + ] From b44021a98ba20f73714fdb2973ee19ddae3fe332 Mon Sep 17 00:00:00 2001 From: detraz Date: Wed, 19 Sep 2018 23:24:03 +0200 Subject: [PATCH 25/29] Fix bugs et simplifie les migrations de switchs --- .../migrations/0092_auto_20180708_2018.py | 25 ----- .../migrations/0093_merge_20180710_0226.py | 16 --- .../migrations/0094_role_specific_role.py | 20 ---- ...707_2040.py => 0095_auto_20180919_2225.py} | 14 ++- preferences/admin.py | 6 +- preferences/forms.py | 17 ++- ...naltopologie_switchs_web_management_ssl.py | 20 ---- .../migrations/0046_merge_20180710_1533.py | 16 --- .../migrations/0047_auto_20180711_0015.py | 40 ------- .../migrations/0048_switchmanagementcred.py | 30 ------ .../migrations/0049_auto_20180712_1713.py | 31 ------ .../migrations/0051_auto_20180919_2225.py | 102 ++++++++++++++++++ preferences/views.py | 50 ++++++++- .../migrations/0063_auto_20180919_2225.py | 32 ++++++ .../migrations/0068_modelswitch_firmware.py | 20 ---- .../migrations/0070_switch_radius_key.py | 22 ---- .../0071_switch_management_creds.py | 22 ---- 17 files changed, 212 insertions(+), 271 deletions(-) delete mode 100644 machines/migrations/0092_auto_20180708_2018.py delete mode 100644 machines/migrations/0093_merge_20180710_0226.py delete mode 100644 machines/migrations/0094_role_specific_role.py rename machines/migrations/{0091_auto_20180707_2040.py => 0095_auto_20180919_2225.py} (59%) delete mode 100644 preferences/migrations/0045_optionaltopologie_switchs_web_management_ssl.py delete mode 100644 preferences/migrations/0046_merge_20180710_1533.py delete mode 100644 preferences/migrations/0047_auto_20180711_0015.py delete mode 100644 preferences/migrations/0048_switchmanagementcred.py delete mode 100644 preferences/migrations/0049_auto_20180712_1713.py create mode 100644 preferences/migrations/0051_auto_20180919_2225.py create mode 100644 topologie/migrations/0063_auto_20180919_2225.py delete mode 100644 topologie/migrations/0068_modelswitch_firmware.py delete mode 100644 topologie/migrations/0070_switch_radius_key.py delete mode 100644 topologie/migrations/0071_switch_management_creds.py diff --git a/machines/migrations/0092_auto_20180708_2018.py b/machines/migrations/0092_auto_20180708_2018.py deleted file mode 100644 index b4f41c14..00000000 --- a/machines/migrations/0092_auto_20180708_2018.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-07-08 18:18 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('machines', '0091_auto_20180707_2040'), - ] - - operations = [ - migrations.AddField( - model_name='vlan', - name='igmp', - field=models.BooleanField(default=False, help_text='Gestion multicast v4'), - ), - migrations.AddField( - model_name='vlan', - name='mld', - field=models.BooleanField(default=False, help_text='Gestion multicast v6'), - ), - ] diff --git a/machines/migrations/0093_merge_20180710_0226.py b/machines/migrations/0093_merge_20180710_0226.py deleted file mode 100644 index c3347890..00000000 --- a/machines/migrations/0093_merge_20180710_0226.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-07-10 00:26 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('machines', '0092_auto_20180708_2018'), - ('machines', '0083_remove_duplicate_rights'), - ] - - operations = [ - ] diff --git a/machines/migrations/0094_role_specific_role.py b/machines/migrations/0094_role_specific_role.py deleted file mode 100644 index 73cade7b..00000000 --- a/machines/migrations/0094_role_specific_role.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-07-11 16:49 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('machines', '0093_merge_20180710_0226'), - ] - - operations = [ - migrations.AddField( - model_name='role', - name='specific_role', - field=models.CharField(blank=True, choices=[('dhcp-server', 'dhcp-server'), ('switch-conf-server', 'switch-conf-server'), ('dns-recursif-server', 'dns-recursif-server'), ('ntp-server', 'ntp-server'), ('radius-server', 'radius-server'), ('ntp-server', 'ntp-server'), ('log-server', 'log-server'), ('ldap-master-server', 'ldap-master-server'), ('ldap-backup-server', 'ldap-backup-server'), ('smtp-server', 'smtp-server'), ('postgresql-server', 'postgresql-server'), ('mysql-server', 'mysql-server'), ('sql-client', 'sql-client'), ('gateway', 'gateway')], max_length=32, null=True), - ), - ] diff --git a/machines/migrations/0091_auto_20180707_2040.py b/machines/migrations/0095_auto_20180919_2225.py similarity index 59% rename from machines/migrations/0091_auto_20180707_2040.py rename to machines/migrations/0095_auto_20180919_2225.py index a2aea3a6..66c082ff 100644 --- a/machines/migrations/0091_auto_20180707_2040.py +++ b/machines/migrations/0095_auto_20180919_2225.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-07-07 18:40 +# Generated by Django 1.10.7 on 2018-09-19 20:25 from __future__ import unicode_literals from django.db import migrations, models @@ -8,7 +8,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('machines', '0090_auto_20180625_1706'), + ('machines', '0094_auto_20180815_1918'), ] operations = [ @@ -27,4 +27,14 @@ class Migration(migrations.Migration): name='dhcpv6_snooping', field=models.BooleanField(default=False), ), + migrations.AddField( + model_name='vlan', + name='igmp', + field=models.BooleanField(default=False, help_text='Gestion multicast v4'), + ), + migrations.AddField( + model_name='vlan', + name='mld', + field=models.BooleanField(default=False, help_text='Gestion multicast v6'), + ), ] diff --git a/preferences/admin.py b/preferences/admin.py index 57f39595..b7b171d0 100644 --- a/preferences/admin.py +++ b/preferences/admin.py @@ -39,7 +39,8 @@ from .models import ( MailMessageOption, HomeOption, RadiusKey, - SwitchManagementCred + SwitchManagementCred, + Reminder ) @@ -96,6 +97,9 @@ class SwitchManagementCredAdmin(VersionAdmin): """Class managementcred for switch""" pass +class ReminderAdmin(VersionAdmin): + """Class reminder for switch""" + pass admin.site.register(OptionalUser, OptionalUserAdmin) admin.site.register(OptionalMachine, OptionalMachineAdmin) diff --git a/preferences/forms.py b/preferences/forms.py index 976070a2..81a12261 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -38,11 +38,12 @@ from .models import ( MailMessageOption, HomeOption, Service, - MailContact + MailContact, Reminder, RadiusKey, - SwitchManagementCred + SwitchManagementCred, ) +from topologie.models import Switch class EditOptionalUserForm(ModelForm): @@ -245,11 +246,21 @@ class DelServiceForm(Form): else: self.fields['services'].queryset = Service.objects.all() +class ReminderForm(FormRevMixin, ModelForm): + """Edition, ajout de services sur la page d'accueil""" + class Meta: + model = Reminder + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(ReminderForm, self).__init__(*args, prefix=prefix, **kwargs) + class RadiusKeyForm(FormRevMixin, ModelForm): """Edition, ajout de clef radius""" members = forms.ModelMultipleChoiceField( - Switch.objects.all(), + queryset=Switch.objects.all(), required=False ) diff --git a/preferences/migrations/0045_optionaltopologie_switchs_web_management_ssl.py b/preferences/migrations/0045_optionaltopologie_switchs_web_management_ssl.py deleted file mode 100644 index 47633c05..00000000 --- a/preferences/migrations/0045_optionaltopologie_switchs_web_management_ssl.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-07-09 21:50 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('preferences', '0044_auto_20180709_2237'), - ] - - operations = [ - migrations.AddField( - model_name='optionaltopologie', - name='switchs_web_management_ssl', - field=models.BooleanField(default=False, help_text='Web management ssl. Assurez-vous que un certif est installé sur le switch !'), - ), - ] diff --git a/preferences/migrations/0046_merge_20180710_1533.py b/preferences/migrations/0046_merge_20180710_1533.py deleted file mode 100644 index 13ffa0d3..00000000 --- a/preferences/migrations/0046_merge_20180710_1533.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-07-10 13:33 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('preferences', '0045_remove_unused_payment_fields'), - ('preferences', '0045_optionaltopologie_switchs_web_management_ssl'), - ] - - operations = [ - ] diff --git a/preferences/migrations/0047_auto_20180711_0015.py b/preferences/migrations/0047_auto_20180711_0015.py deleted file mode 100644 index 4100bf89..00000000 --- a/preferences/migrations/0047_auto_20180711_0015.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-07-10 22:15 -from __future__ import unicode_literals - -from django.db import migrations, models -import re2o.aes_field -import re2o.mixins - - -class Migration(migrations.Migration): - - dependencies = [ - ('preferences', '0046_merge_20180710_1533'), - ] - - operations = [ - migrations.CreateModel( - name='RadiusKey', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('radius_key', re2o.aes_field.AESEncryptedField(help_text='Clef radius', max_length=255)), - ('comment', models.CharField(blank=True, help_text='Commentaire de cette clef', max_length=255, null=True)), - ('default_switch', models.BooleanField(default=True, help_text='Clef par défaut des switchs', unique=True)), - ], - options={ - 'permissions': (('view_radiuskey', 'Peut voir un objet radiuskey'),), - }, - bases=(re2o.mixins.AclMixin, models.Model), - ), - migrations.AlterField( - model_name='optionaluser', - name='gpg_fingerprint', - field=models.BooleanField(default=True), - ), - migrations.AlterField( - model_name='optionaluser', - name='is_tel_mandatory', - field=models.BooleanField(default=True), - ), - ] diff --git a/preferences/migrations/0048_switchmanagementcred.py b/preferences/migrations/0048_switchmanagementcred.py deleted file mode 100644 index 9221b115..00000000 --- a/preferences/migrations/0048_switchmanagementcred.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-07-10 23:57 -from __future__ import unicode_literals - -from django.db import migrations, models -import re2o.aes_field -import re2o.mixins - - -class Migration(migrations.Migration): - - dependencies = [ - ('preferences', '0047_auto_20180711_0015'), - ] - - operations = [ - migrations.CreateModel( - name='SwitchManagementCred', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('management_id', models.CharField(help_text='Login du switch', max_length=63)), - ('management_pass', re2o.aes_field.AESEncryptedField(help_text='Mot de passe', max_length=63)), - ('default_switch', models.BooleanField(default=True, help_text='Creds par défaut des switchs', unique=True)), - ], - options={ - 'permissions': (('view_switchmanagementcred', 'Peut voir un objet switchmanagementcred'),), - }, - bases=(re2o.mixins.AclMixin, models.Model), - ), - ] diff --git a/preferences/migrations/0049_auto_20180712_1713.py b/preferences/migrations/0049_auto_20180712_1713.py deleted file mode 100644 index e47f547d..00000000 --- a/preferences/migrations/0049_auto_20180712_1713.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-07-12 15:13 -from __future__ import unicode_literals - -from django.db import migrations, models -import re2o.aes_field - - -class Migration(migrations.Migration): - - dependencies = [ - ('preferences', '0048_switchmanagementcred'), - ] - - operations = [ - migrations.AddField( - model_name='optionaltopologie', - name='sftp_login', - field=models.CharField(blank=True, help_text='Login sftp des switchs', max_length=32, null=True), - ), - migrations.AddField( - model_name='optionaltopologie', - name='sftp_pass', - field=re2o.aes_field.AESEncryptedField(blank=True, help_text='Mot de passe sftp', max_length=63, null=True), - ), - migrations.AddField( - model_name='optionaltopologie', - name='switchs_provision', - field=models.CharField(choices=[('sftp', 'sftp'), ('tftp', 'tftp')], default='tftp', help_text='Mode de récupération des confs par les switchs', max_length=32), - ), - ] diff --git a/preferences/migrations/0051_auto_20180919_2225.py b/preferences/migrations/0051_auto_20180919_2225.py new file mode 100644 index 00000000..f776a9a6 --- /dev/null +++ b/preferences/migrations/0051_auto_20180919_2225.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-09-19 20:25 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import re2o.aes_field +import re2o.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0095_auto_20180919_2225'), + ('preferences', '0050_auto_20180818_1329'), + ] + + operations = [ + migrations.CreateModel( + name='RadiusKey', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('radius_key', re2o.aes_field.AESEncryptedField(help_text='Clef radius', max_length=255)), + ('comment', models.CharField(blank=True, help_text='Commentaire de cette clef', max_length=255, null=True)), + ('default_switch', models.BooleanField(default=True, help_text='Clef par défaut des switchs', unique=True)), + ], + options={ + 'permissions': (('view_radiuskey', 'Peut voir un objet radiuskey'),), + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.CreateModel( + name='Reminder', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('days', models.IntegerField(default=7, help_text="Délais entre le mail et la fin d'adhésion", unique=True)), + ('message', models.CharField(blank=True, default='', help_text='Message affiché spécifiquement pour ce rappel', max_length=255, null=True)), + ], + options={ + 'permissions': (('view_reminder', 'Peut voir un objet reminder'),), + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.CreateModel( + name='SwitchManagementCred', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('management_id', models.CharField(help_text='Login du switch', max_length=63)), + ('management_pass', re2o.aes_field.AESEncryptedField(help_text='Mot de passe', max_length=63)), + ('default_switch', models.BooleanField(default=True, help_text='Creds par défaut des switchs', unique=True)), + ], + options={ + 'permissions': (('view_switchmanagementcred', 'Peut voir un objet switchmanagementcred'),), + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.AddField( + model_name='optionaltopologie', + name='sftp_login', + field=models.CharField(blank=True, help_text='Login sftp des switchs', max_length=32, null=True), + ), + migrations.AddField( + model_name='optionaltopologie', + name='sftp_pass', + field=re2o.aes_field.AESEncryptedField(blank=True, help_text='Mot de passe sftp', max_length=63, null=True), + ), + migrations.AddField( + model_name='optionaltopologie', + name='switchs_ip_type', + field=models.OneToOneField(blank=True, help_text="Plage d'ip de management des switchs", null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.IpType'), + ), + migrations.AddField( + model_name='optionaltopologie', + name='switchs_provision', + field=models.CharField(choices=[('sftp', 'sftp'), ('tftp', 'tftp')], default='tftp', help_text='Mode de récupération des confs par les switchs', max_length=32), + ), + migrations.AddField( + model_name='optionaltopologie', + name='switchs_rest_management', + field=models.BooleanField(default=False, help_text='Rest management, activé si provision auto'), + ), + migrations.AddField( + model_name='optionaltopologie', + name='switchs_web_management', + field=models.BooleanField(default=False, help_text='Web management, activé si provision automatique'), + ), + migrations.AddField( + model_name='optionaltopologie', + name='switchs_web_management_ssl', + field=models.BooleanField(default=False, help_text='Web management ssl. Assurez-vous que un certif est installé sur le switch !'), + ), + migrations.AlterField( + model_name='mailmessageoption', + name='welcome_mail_en', + field=models.TextField(default='', help_text='Mail de bienvenue en anglais'), + ), + migrations.AlterField( + model_name='mailmessageoption', + name='welcome_mail_fr', + field=models.TextField(default='', help_text='Mail de bienvenue en français'), + ), + ] diff --git a/preferences/views.py b/preferences/views.py index 181434ea..e9f10d53 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -41,7 +41,7 @@ from django.utils.translation import ugettext as _ from reversion import revisions as reversion from re2o.views import form -from re2o.acl import can_create, can_edit, can_delete_set, can_view_all +from re2o.acl import can_create, can_edit, can_delete_set, can_view_all, can_delete from .forms import MailContactForm, DelMailContactForm from .forms import ( @@ -95,9 +95,8 @@ def display_options(request): 'homeoptions': homeoptions, 'mailmessageoptions': mailmessageoptions, 'service_list': service_list, - 'mailcontact_list': mailcontact_list - 'reminder_list': reminder_list, 'mailcontact_list': mailcontact_list, + 'reminder_list': reminder_list, 'radiuskey_list' : radiuskey_list, 'switchmanagementcred_list': switchmanagementcred_list, }, 'preferences/display_preferences.html', request) @@ -210,6 +209,51 @@ def del_service(request, instances): request ) +@login_required +@can_create(Reminder) +def add_reminder(request): + """Ajout d'un service de la page d'accueil""" + reminder = ReminderForm(request.POST or None, request.FILES or None) + if service.is_valid(): + with transaction.atomic(), reversion.create_revision(): + reminder.save() + reversion.set_user(request.user) + reversion.set_comment("Creation") + messages.success(request, _("The service was added.")) + return redirect(reverse('preferences:display-options')) + return form( + {'preferenceform': service, 'action_name': _("Add a service")}, + 'preferences/preferences.html', + request + ) + +@login_required +@can_edit(Reminder) +def edit_reminder(request, service_instance, **_kwargs): + """Edition des services affichés sur la page d'accueil""" + reminder = ReminderForm( + request.POST or None, + request.FILES or None, + instance=reminder_instance + ) + if reminder.is_valid(): + with transaction.atomic(), reversion.create_revision(): + reminder.save() + reversion.set_user(request.user) + reversion.set_comment( + "Field(s) edited: %s" % ', '.join( + field for field in reminder.changed_data + ) + ) + messages.success(request, _("The service was edited.")) + return redirect(reverse('preferences:display-options')) + return form( + {'preferenceform': service, 'action_name': _("Edit")}, + 'preferences/preferences.html', + request + ) + + @login_required @can_delete(Reminder) diff --git a/topologie/migrations/0063_auto_20180919_2225.py b/topologie/migrations/0063_auto_20180919_2225.py new file mode 100644 index 00000000..45228340 --- /dev/null +++ b/topologie/migrations/0063_auto_20180919_2225.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-09-19 20:25 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0051_auto_20180919_2225'), + ('topologie', '0062_auto_20180815_1918'), + ] + + operations = [ + migrations.AddField( + model_name='modelswitch', + name='firmware', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='switch', + name='management_creds', + field=models.ForeignKey(blank=True, help_text='Identifiant de management de ce switch', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.SwitchManagementCred'), + ), + migrations.AddField( + model_name='switch', + name='radius_key', + field=models.ForeignKey(blank=True, help_text='Clef radius du switch', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.RadiusKey'), + ), + ] diff --git a/topologie/migrations/0068_modelswitch_firmware.py b/topologie/migrations/0068_modelswitch_firmware.py deleted file mode 100644 index 8596f58f..00000000 --- a/topologie/migrations/0068_modelswitch_firmware.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-07-08 19:56 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('topologie', '0067_auto_20180701_0016'), - ] - - operations = [ - migrations.AddField( - model_name='modelswitch', - name='firmware', - field=models.CharField(blank=True, max_length=255, null=True), - ), - ] diff --git a/topologie/migrations/0070_switch_radius_key.py b/topologie/migrations/0070_switch_radius_key.py deleted file mode 100644 index b3d31b1f..00000000 --- a/topologie/migrations/0070_switch_radius_key.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-07-10 22:20 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('preferences', '0047_auto_20180711_0015'), - ('topologie', '0069_switch_automatic_provision'), - ] - - operations = [ - migrations.AddField( - model_name='switch', - name='radius_key', - field=models.ForeignKey(blank=True, help_text='Clef radius du switch', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.RadiusKey'), - ), - ] diff --git a/topologie/migrations/0071_switch_management_creds.py b/topologie/migrations/0071_switch_management_creds.py deleted file mode 100644 index d9eeb774..00000000 --- a/topologie/migrations/0071_switch_management_creds.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2018-07-10 23:57 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('preferences', '0048_switchmanagementcred'), - ('topologie', '0070_switch_radius_key'), - ] - - operations = [ - migrations.AddField( - model_name='switch', - name='management_creds', - field=models.ForeignKey(blank=True, help_text='Identifiant de management de ce switch', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.SwitchManagementCred'), - ), - ] From eded66beb07a3049e2bdaeb931a160ee7b750ba1 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Mon, 9 Jul 2018 21:05:50 +0200 Subject: [PATCH 26/29] =?UTF-8?q?Gestion=20des=20switchs=20=C3=A0=20provis?= =?UTF-8?q?ioner=20automatiquement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- preferences/forms.py | 18 +++++++++++++++-- preferences/models.py | 1 + .../0064_switch_automatic_provision.py | 20 +++++++++++++++++++ topologie/models.py | 4 ++++ 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 topologie/migrations/0064_switch_automatic_provision.py diff --git a/preferences/forms.py b/preferences/forms.py index 81a12261..5933107c 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -45,7 +45,6 @@ from .models import ( ) from topologie.models import Switch - class EditOptionalUserForm(ModelForm): """Formulaire d'édition des options de l'user. (solde, telephone..)""" class Meta: @@ -96,7 +95,14 @@ class EditOptionalMachineForm(ModelForm): class EditOptionalTopologieForm(ModelForm): - """Options de topologie, formulaire d'edition (vlan par default etc)""" + """Options de topologie, formulaire d'edition (vlan par default etc) + On rajoute un champ automatic provision switchs pour gérer facilement + l'ajout de switchs au provisionning automatique""" + automatic_provision_switchs = forms.ModelMultipleChoiceField( + Switch.objects.all(), + required=False + ) + class Meta: model = OptionalTopologie fields = '__all__' @@ -114,6 +120,14 @@ class EditOptionalTopologieForm(ModelForm): self.fields['vlan_decision_nok'].label = _("VLAN for machines rejected" " by RADIUS") + self.initial['automatic_provision_switchs'] = Switch.objects.filter(automatic_provision=True) + + def save(self, commit=True): + instance = super().save(commit) + Switch.objects.all().update(automatic_provision=False) + self.cleaned_data['automatic_provision_switchs'].update(automatic_provision=True) + return instance + class EditGeneralOptionForm(ModelForm): """Options générales (affichages de résultats de recherche, etc)""" diff --git a/preferences/models.py b/preferences/models.py index 5c2d58ba..31d382aa 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -34,6 +34,7 @@ from django.forms import ValidationError from django.utils.translation import ugettext_lazy as _ import machines.models + from re2o.mixins import AclMixin from re2o.aes_field import AESEncryptedField from datetime import timedelta diff --git a/topologie/migrations/0064_switch_automatic_provision.py b/topologie/migrations/0064_switch_automatic_provision.py new file mode 100644 index 00000000..d8d280cf --- /dev/null +++ b/topologie/migrations/0064_switch_automatic_provision.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-09-20 16:28 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0063_auto_20180919_2225'), + ] + + operations = [ + migrations.AddField( + model_name='switch', + name='automatic_provision', + field=models.BooleanField(default=False, help_text='Provision automatique de ce switch'), + ), + ] diff --git a/topologie/models.py b/topologie/models.py index 28216819..840d9c70 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -247,6 +247,10 @@ class Switch(AclMixin, Machine): on_delete=models.PROTECT, help_text="Identifiant de management de ce switch" ) + automatic_provision = models.BooleanField( + default=False, + help_text='Provision automatique de ce switch', + ) class Meta: unique_together = ('stack', 'stack_member_id') From 1ffb481aef728f396cab61f3028c3331ac384ba9 Mon Sep 17 00:00:00 2001 From: Antoine Vintache Date: Thu, 20 Sep 2018 21:25:33 +0200 Subject: [PATCH 27/29] =?UTF-8?q?Ajout=20de=20l'affichage=20des=20switchs?= =?UTF-8?q?=20dans=20la=20liste=20des=20mod=C3=A8les=20de=20switchs.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- topologie/templates/topologie/aff_model_switch.html | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/topologie/templates/topologie/aff_model_switch.html b/topologie/templates/topologie/aff_model_switch.html index 7fd1b37c..fc49331f 100644 --- a/topologie/templates/topologie/aff_model_switch.html +++ b/topologie/templates/topologie/aff_model_switch.html @@ -38,7 +38,8 @@ with this program; if not, write to the Free Software Foundation, Inc., Firmware {% trans "Switch constructor" as tr_constructor %} {% include "buttons/sort.html" with prefix='model-switch' col='constructor' text=tr_constructor %} - + {% trans "Switches" %} + {% for model_switch in model_switch_list %} @@ -46,6 +47,13 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ model_switch.reference }} {{model_switch.firmware}} {{ model_switch.constructor }} + + {% for switch in model_switch.switch_set.all %} + + {{ switch }} + + {% endfor %} + {% can_edit model_switch %} From 6652e33d8caba5a6e6a9af19ae8067fb848d08e0 Mon Sep 17 00:00:00 2001 From: Antoine Vintache Date: Mon, 24 Sep 2018 19:55:13 +0200 Subject: [PATCH 28/29] Modification , utilisation de history button --- preferences/templates/preferences/aff_radiuskey.html | 3 ++- .../templates/preferences/aff_switchmanagementcred.html | 3 ++- topologie/templates/topologie/aff_vlanoptions.html | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/preferences/templates/preferences/aff_radiuskey.html b/preferences/templates/preferences/aff_radiuskey.html index 073aeea2..0d58efe3 100644 --- a/preferences/templates/preferences/aff_radiuskey.html +++ b/preferences/templates/preferences/aff_radiuskey.html @@ -22,6 +22,7 @@ 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 %} +{% load logs_extra %} @@ -48,7 +49,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} - {% include 'buttons/history.html' with href='preferences:history' name='radiuskey' id=radiuskey.id %} + {% history_button radiuskey %} {% endfor %} diff --git a/preferences/templates/preferences/aff_switchmanagementcred.html b/preferences/templates/preferences/aff_switchmanagementcred.html index d69a287a..ef8b0143 100644 --- a/preferences/templates/preferences/aff_switchmanagementcred.html +++ b/preferences/templates/preferences/aff_switchmanagementcred.html @@ -22,6 +22,7 @@ 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 %} +{% load logs_extra %}
@@ -46,7 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} - {% include 'buttons/history.html' with href='preferences:history' name='switchmanagementcred' id=switchmanagementcred.id %} + {% history_button switchmanagementcred %} {% endfor %} diff --git a/topologie/templates/topologie/aff_vlanoptions.html b/topologie/templates/topologie/aff_vlanoptions.html index 7684e951..3810cb7f 100644 --- a/topologie/templates/topologie/aff_vlanoptions.html +++ b/topologie/templates/topologie/aff_vlanoptions.html @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load acl %} +{% load logs_extra %}
@@ -51,7 +52,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% can_edit vlan %} {% include 'buttons/edit.html' with href='topologie:edit-vlanoptions' id=vlan.id %} {% acl_end %} - {% include 'buttons/history.html' with href='machines:history' name='vlan' id=vlan.id %} + {% history_button vlan %} {% endfor %} From 42d2f804701d7a3191423494d977f7b4666475a6 Mon Sep 17 00:00:00 2001 From: detraz Date: Mon, 24 Sep 2018 20:01:20 +0200 Subject: [PATCH 29/29] Fix divers --- api/serializers.py | 1 + topologie/models.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/api/serializers.py b/api/serializers.py index f5fd1fa2..94144b40 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -349,6 +349,7 @@ class OptionalMachineSerializer(NamespacedHMSerializer): class OptionalTopologieSerializer(NamespacedHMSerializer): """Serialize `preferences.models.OptionalTopologie` objects. """ + switchs_management_interface_ip = serializers.CharField() class Meta: model = preferences.OptionalTopologie diff --git a/topologie/models.py b/topologie/models.py index 840d9c70..d02fce40 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -552,7 +552,7 @@ class Port(AclMixin, RevMixin, models.Model): return port_profile else: nothing_profile, _created = PortProfile.objects.get_or_create( - profile_default='nothing', + profil_default='nothing', name='nothing', radius_type='NO' )