diff --git a/docs/configuration/optional-settings.md b/docs/configuration/optional-settings.md index a9f42394d78..fc8c13ea067 100644 --- a/docs/configuration/optional-settings.md +++ b/docs/configuration/optional-settings.md @@ -47,9 +47,17 @@ In order to send email, NetBox needs an email server configured. The following i --- +# ENFORCE_GLOBAL_UNIQUE + +Default: False + +Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce unique IP space within the global table (all prefixes and IP addresses not assigned to a VRF), set `ENFORCE_GLOBAL_UNIQUE` to True. + +--- + ## LOGIN_REQUIRED -Default: False, +Default: False Setting this to True will permit only authenticated users to access any part of NetBox. By default, anonymous users are permitted to access most data in NetBox (excluding secrets) but not make any changes. diff --git a/netbox/ipam/api/serializers.py b/netbox/ipam/api/serializers.py index e632111abff..144ea548214 100644 --- a/netbox/ipam/api/serializers.py +++ b/netbox/ipam/api/serializers.py @@ -12,7 +12,7 @@ class VRFSerializer(serializers.ModelSerializer): class Meta: model = VRF - fields = ['id', 'name', 'rd', 'description'] + fields = ['id', 'name', 'rd', 'enforce_unique', 'description'] class VRFNestedSerializer(VRFSerializer): diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index eb7d39e1d25..607ba1254d0 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -25,7 +25,7 @@ class VRFForm(forms.ModelForm, BootstrapMixin): class Meta: model = VRF - fields = ['name', 'rd', 'description'] + fields = ['name', 'rd', 'enforce_unique', 'description'] labels = { 'rd': "RD", } @@ -38,7 +38,7 @@ class VRFFromCSVForm(forms.ModelForm): class Meta: model = VRF - fields = ['name', 'rd', 'description'] + fields = ['name', 'rd', 'enforce_unique', 'description'] class VRFImportForm(BulkImportForm, BootstrapMixin): diff --git a/netbox/ipam/migrations/0002_vrf_add_enforce_unique.py b/netbox/ipam/migrations/0002_vrf_add_enforce_unique.py new file mode 100644 index 00000000000..373e93d8032 --- /dev/null +++ b/netbox/ipam/migrations/0002_vrf_add_enforce_unique.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-07-14 19:34 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ipam', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='vrf', + name='enforce_unique', + field=models.BooleanField(default=True, help_text=b'Prevent duplicate prefixes/IP addresses within this VRF', verbose_name=b'Enforce unique space'), + ), + ] diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index e37e21bb3ef..447557b4eb7 100644 --- a/netbox/ipam/models.py +++ b/netbox/ipam/models.py @@ -1,5 +1,6 @@ from netaddr import IPNetwork, cidr_merge +from django.conf import settings from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse from django.core.validators import MaxValueValidator, MinValueValidator @@ -45,6 +46,8 @@ class VRF(CreatedUpdatedModel): """ name = models.CharField(max_length=50) rd = models.CharField(max_length=21, unique=True, verbose_name='Route distinguisher') + enforce_unique = models.BooleanField(default=True, verbose_name='Enforce unique space', + help_text="Prevent duplicate prefixes/IP addresses within this VRF") description = models.CharField(max_length=100, blank=True) class Meta: @@ -309,6 +312,21 @@ class IPAddress(CreatedUpdatedModel): def get_absolute_url(self): return reverse('ipam:ipaddress', args=[self.pk]) + def clean(self): + + # Enforce unique IP space if applicable + if self.vrf and self.vrf.enforce_unique: + duplicate_ips = IPAddress.objects.filter(vrf=self.vrf, address__net_host=str(self.address.ip))\ + .exclude(pk=self.pk) + if duplicate_ips: + raise ValidationError("Duplicate IP address found in VRF {}: {}".format(self.vrf, + duplicate_ips.first())) + elif not self.vrf and settings.ENFORCE_GLOBAL_UNIQUE: + duplicate_ips = IPAddress.objects.filter(vrf=None, address__net_host=str(self.address.ip))\ + .exclude(pk=self.pk) + if duplicate_ips: + raise ValidationError("Duplicate IP address found in global table: {}".format(duplicate_ips.first())) + def save(self, *args, **kwargs): if self.address: # Infer address family from IPAddress object diff --git a/netbox/netbox/configuration.example.py b/netbox/netbox/configuration.example.py index 745bdfaafc1..603327c6ec1 100644 --- a/netbox/netbox/configuration.example.py +++ b/netbox/netbox/configuration.example.py @@ -82,3 +82,7 @@ BANNER_BOTTOM = '' # When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to # prefer IPv4 instead. PREFER_IPV4 = False + +# Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce unique IP space within the global table +# (all prefixes and IP addresses not assigned to a VRF), set ENFORCE_GLOBAL_UNIQUE to True. +ENFORCE_GLOBAL_UNIQUE = False diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 3f7606c227e..d4723709451 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -41,6 +41,7 @@ SHORT_DATETIME_FORMAT = getattr(configuration, 'SHORT_DATETIME_FORMAT', 'Y-m-d H BANNER_TOP = getattr(configuration, 'BANNER_TOP', False) BANNER_BOTTOM = getattr(configuration, 'BANNER_BOTTOM', False) PREFER_IPV4 = getattr(configuration, 'PREFER_IPV4', False) +ENFORCE_GLOBAL_UNIQUE = getattr(configuration, 'ENFORCE_GLOBAL_UNIQUE', False) CSRF_TRUSTED_ORIGINS = ALLOWED_HOSTS # Attempt to import LDAP configuration if it has been defined diff --git a/netbox/templates/ipam/vrf.html b/netbox/templates/ipam/vrf.html index e0af3038aee..e3ce30c3b79 100644 --- a/netbox/templates/ipam/vrf.html +++ b/netbox/templates/ipam/vrf.html @@ -30,6 +30,16 @@ Route Distinguisher {{ vrf.rd }} + + Enforce Uniqueness + + {% if vrf.enforce_unique %} + + {% else %} + + {% endif %} + + Description diff --git a/netbox/templates/ipam/vrf_import.html b/netbox/templates/ipam/vrf_import.html index b852be6def5..ce16181c4ad 100644 --- a/netbox/templates/ipam/vrf_import.html +++ b/netbox/templates/ipam/vrf_import.html @@ -38,6 +38,11 @@ Route distinguisher 65000:123456 + + Enforce uniqueness + Prevent duplicate prefixes/IP addresses + True + Description Short description (optional) @@ -46,7 +51,7 @@

Example

-
Customer_ABC,65000:123456,Native VRF for customer ABC
+
Customer_ABC,65000:123456,True,Native VRF for customer ABC
{% endblock %}