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):