From bcfa760cf92e64c40d017749ae94f266faab0963 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 31 Jan 2019 13:47:24 -0500 Subject: [PATCH] Closes #2805: Allow null route distinguisher for VRFs --- CHANGELOG.md | 1 + docs/core-functionality/ipam.md | 2 +- .../ipam/migrations/0024_vrf_allow_null_rd.py | 18 ++++++++++++ netbox/ipam/models.py | 2 ++ netbox/ipam/tests/test_api.py | 29 ++++++++++++------- 5 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 netbox/ipam/migrations/0024_vrf_allow_null_rd.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 81f122587..59c90565b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ v2.5.5 (FUTURE) ## Enhancements +* [#2805](https://github.com/digitalocean/netbox/issues/2805) - Allow null route distinguisher for VRFs * [#2809](https://github.com/digitalocean/netbox/issues/2809) - Remove VRF child prefixes table; link to main prefixes view * [#2825](https://github.com/digitalocean/netbox/issues/2825) - Include directly connected device for front/rear ports diff --git a/docs/core-functionality/ipam.md b/docs/core-functionality/ipam.md index 05b613da2..cd95c40e6 100644 --- a/docs/core-functionality/ipam.md +++ b/docs/core-functionality/ipam.md @@ -83,7 +83,7 @@ An IP address can be designated as the network address translation (NAT) inside A VRF object in NetBox represents a virtual routing and forwarding (VRF) domain. Each VRF is essentially a separate routing table. VRFs are commonly used to isolate customers or organizations from one another within a network, or to route overlapping address space (e.g. multiple instances of the 10.0.0.0/8 space). -Each VRF is assigned a unique name and route distinguisher (RD). The RD is expected to take one of the forms prescribed in [RFC 4364](https://tools.ietf.org/html/rfc4364#section-4.2), however its formatting is not strictly enforced. +Each VRF is assigned a unique name and an optional route distinguisher (RD). The RD is expected to take one of the forms prescribed in [RFC 4364](https://tools.ietf.org/html/rfc4364#section-4.2), however its formatting is not strictly enforced. Each prefix and IP address may be assigned to one (and only one) VRF. If you have a prefix or IP address which exists in multiple VRFs, you will need to create a separate instance of it in NetBox for each VRF. Any IP prefix or address not assigned to a VRF is said to belong to the "global" table. diff --git a/netbox/ipam/migrations/0024_vrf_allow_null_rd.py b/netbox/ipam/migrations/0024_vrf_allow_null_rd.py new file mode 100644 index 000000000..611644f6c --- /dev/null +++ b/netbox/ipam/migrations/0024_vrf_allow_null_rd.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.5 on 2019-01-31 18:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ipam', '0023_change_logging'), + ] + + operations = [ + migrations.AlterField( + model_name='vrf', + name='rd', + field=models.CharField(blank=True, max_length=21, null=True, unique=True), + ), + ] diff --git a/netbox/ipam/models.py b/netbox/ipam/models.py index a14e1c7ed..7c879595f 100644 --- a/netbox/ipam/models.py +++ b/netbox/ipam/models.py @@ -29,6 +29,8 @@ class VRF(ChangeLoggedModel, CustomFieldModel): rd = models.CharField( max_length=21, unique=True, + blank=True, + null=True, verbose_name='Route distinguisher' ) tenant = models.ForeignKey( diff --git a/netbox/ipam/tests/test_api.py b/netbox/ipam/tests/test_api.py index d57cb728f..3f4555b55 100644 --- a/netbox/ipam/tests/test_api.py +++ b/netbox/ipam/tests/test_api.py @@ -16,7 +16,7 @@ class VRFTest(APITestCase): self.vrf1 = VRF.objects.create(name='Test VRF 1', rd='65000:1') self.vrf2 = VRF.objects.create(name='Test VRF 2', rd='65000:2') - self.vrf3 = VRF.objects.create(name='Test VRF 3', rd='65000:3') + self.vrf3 = VRF.objects.create(name='Test VRF 3') # No RD def test_get_vrf(self): @@ -44,19 +44,26 @@ class VRFTest(APITestCase): def test_create_vrf(self): - data = { - 'name': 'Test VRF 4', - 'rd': '65000:4', - } + data_list = [ + # VRF with RD + { + 'name': 'Test VRF 4', + 'rd': '65000:4', + }, + # VRF without RD + { + 'name': 'Test VRF 5', + } + ] url = reverse('ipam-api:vrf-list') - response = self.client.post(url, data, format='json', **self.header) - self.assertHttpStatus(response, status.HTTP_201_CREATED) - self.assertEqual(VRF.objects.count(), 4) - vrf4 = VRF.objects.get(pk=response.data['id']) - self.assertEqual(vrf4.name, data['name']) - self.assertEqual(vrf4.rd, data['rd']) + for data in data_list: + response = self.client.post(url, data, format='json', **self.header) + self.assertHttpStatus(response, status.HTTP_201_CREATED) + vrf = VRF.objects.get(pk=response.data['id']) + self.assertEqual(vrf.name, data['name']) + self.assertEqual(vrf.rd, data['rd'] if 'rd' in data else None) def test_create_vrf_bulk(self):