Browse Source

Converted IPAddress.interface to a GenericForeignKey

Jeremy Stretch 7 years ago
parent
commit
5930a64203

+ 1 - 1
netbox/dcim/migrations/0042_device_cluster.py

@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Generated by Django 1.11.4 on 2017-08-16 21:06
+# Generated by Django 1.11.4 on 2017-08-18 19:46
 from __future__ import unicode_literals
 
 from django.db import migrations, models

+ 5 - 0
netbox/dcim/models.py

@@ -1175,6 +1175,11 @@ class Interface(models.Model):
         help_text="This interface is used only for out-of-band management"
     )
     description = models.CharField(max_length=100, blank=True)
+    ip_addresses = GenericRelation(
+        to='ipam.IPAddress',
+        content_type_field='interface_type',
+        object_id_field='interface_id'
+    )
 
     objects = InterfaceQuerySet.as_manager()
 

+ 5 - 1
netbox/ipam/api/views.py

@@ -134,7 +134,11 @@ class PrefixViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 #
 
 class IPAddressViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
-    queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device', 'nat_inside')
+    queryset = IPAddress.objects.select_related(
+        'vrf__tenant', 'tenant', 'nat_inside'
+    ).prefetch_related(
+        'interface__device'
+    )
     serializer_class = serializers.IPAddressSerializer
     write_serializer_class = serializers.WritableIPAddressSerializer
     filter_class = filters.IPAddressFilter

+ 18 - 18
netbox/ipam/fixtures/ipam.json

@@ -70,7 +70,7 @@
         "family": 4,
         "address": "10.0.255.1/32",
         "vrf": null,
-        "interface": 3,
+        "interface_id": 3,
         "nat_inside": null,
         "description": ""
     }
@@ -84,7 +84,7 @@
         "family": 4,
         "address": "169.254.254.1/31",
         "vrf": null,
-        "interface": 4,
+        "interface_id": 4,
         "nat_inside": null,
         "description": ""
     }
@@ -98,7 +98,7 @@
         "family": 4,
         "address": "10.0.255.2/32",
         "vrf": null,
-        "interface": 185,
+        "interface_id": 185,
         "nat_inside": null,
         "description": ""
     }
@@ -112,7 +112,7 @@
         "family": 4,
         "address": "169.254.1.1/31",
         "vrf": null,
-        "interface": 213,
+        "interface_id": 213,
         "nat_inside": null,
         "description": ""
     }
@@ -126,7 +126,7 @@
         "family": 4,
         "address": "10.0.254.1/24",
         "vrf": null,
-        "interface": 12,
+        "interface_id": 12,
         "nat_inside": null,
         "description": ""
     }
@@ -140,7 +140,7 @@
         "family": 4,
         "address": "10.15.21.1/31",
         "vrf": null,
-        "interface": 218,
+        "interface_id": 218,
         "nat_inside": null,
         "description": ""
     }
@@ -154,7 +154,7 @@
         "family": 4,
         "address": "10.15.21.2/31",
         "vrf": null,
-        "interface": 9,
+        "interface_id": 9,
         "nat_inside": null,
         "description": ""
     }
@@ -168,7 +168,7 @@
         "family": 4,
         "address": "10.15.22.1/31",
         "vrf": null,
-        "interface": 8,
+        "interface_id": 8,
         "nat_inside": null,
         "description": ""
     }
@@ -182,7 +182,7 @@
         "family": 4,
         "address": "10.15.20.1/31",
         "vrf": null,
-        "interface": 7,
+        "interface_id": 7,
         "nat_inside": null,
         "description": ""
     }
@@ -196,7 +196,7 @@
         "family": 4,
         "address": "10.16.20.1/31",
         "vrf": null,
-        "interface": 216,
+        "interface_id": 216,
         "nat_inside": null,
         "description": ""
     }
@@ -210,7 +210,7 @@
         "family": 4,
         "address": "10.15.22.2/31",
         "vrf": null,
-        "interface": 206,
+        "interface_id": 206,
         "nat_inside": null,
         "description": ""
     }
@@ -224,7 +224,7 @@
         "family": 4,
         "address": "10.16.22.1/31",
         "vrf": null,
-        "interface": 217,
+        "interface_id": 217,
         "nat_inside": null,
         "description": ""
     }
@@ -238,7 +238,7 @@
         "family": 4,
         "address": "10.16.22.2/31",
         "vrf": null,
-        "interface": 205,
+        "interface_id": 205,
         "nat_inside": null,
         "description": ""
     }
@@ -252,7 +252,7 @@
         "family": 4,
         "address": "10.16.20.2/31",
         "vrf": null,
-        "interface": 211,
+        "interface_id": 211,
         "nat_inside": null,
         "description": ""
     }
@@ -266,7 +266,7 @@
         "family": 4,
         "address": "10.15.22.2/31",
         "vrf": null,
-        "interface": 212,
+        "interface_id": 212,
         "nat_inside": null,
         "description": ""
     }
@@ -280,7 +280,7 @@
         "family": 4,
         "address": "10.0.254.2/32",
         "vrf": null,
-        "interface": 188,
+        "interface_id": 188,
         "nat_inside": null,
         "description": ""
     }
@@ -294,7 +294,7 @@
         "family": 4,
         "address": "169.254.1.1/31",
         "vrf": null,
-        "interface": 200,
+        "interface_id": 200,
         "nat_inside": null,
         "description": ""
     }
@@ -308,7 +308,7 @@
         "family": 4,
         "address": "169.254.1.2/31",
         "vrf": null,
-        "interface": 194,
+        "interface_id": 194,
         "nat_inside": null,
         "description": ""
     }

+ 44 - 0
netbox/ipam/migrations/0019_ipaddress_interface_to_gfk.py

@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-08-18 19:31
+from __future__ import unicode_literals
+
+from django.contrib.contenttypes.models import ContentType
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+def set_interface_type(apps, schema_editor):
+    """
+    Set the interface_type field to 'Interface' for all IP addresses assigned to an Interface.
+    """
+    Interface = apps.get_model('dcim', 'Interface')
+    interface_type = ContentType.objects.get_for_model(Interface)
+    IPAddress = apps.get_model('ipam', 'IPAddress')
+    IPAddress.objects.filter(interface__isnull=False).update(interface_type=interface_type)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('ipam', '0018_remove_service_uniqueness_constraint'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='ipaddress',
+            name='interface_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.ContentType'),
+        ),
+        migrations.RunPython(set_interface_type),
+        migrations.AlterField(
+            model_name='IPAddress',
+            name='interface',
+            field=models.PositiveIntegerField(blank=True, null=True),
+        ),
+        migrations.RenameField(
+            model_name='IPAddress',
+            old_name='interface',
+            new_name='interface_id',
+        )
+    ]

+ 12 - 3
netbox/ipam/models.py

@@ -2,10 +2,12 @@ from __future__ import unicode_literals
 import netaddr
 
 from django.conf import settings
-from django.contrib.contenttypes.fields import GenericRelation
+from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
+from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
 from django.core.validators import MaxValueValidator, MinValueValidator
 from django.db import models
+from django.db.models import Q
 from django.db.models.expressions import RawSQL
 from django.urls import reverse
 from django.utils.encoding import python_2_unicode_compatible
@@ -407,8 +409,15 @@ class IPAddress(CreatedUpdatedModel, CustomFieldModel):
     role = models.PositiveSmallIntegerField(
         'Role', choices=IPADDRESS_ROLE_CHOICES, blank=True, null=True, help_text='The functional role of this IP'
     )
-    interface = models.ForeignKey(Interface, related_name='ip_addresses', on_delete=models.CASCADE, blank=True,
-                                  null=True)
+    interface_type = models.ForeignKey(
+        to=ContentType,
+        on_delete=models.PROTECT,
+        limit_choices_to=Q(app_label='dcim', model='interface') | Q(app_label='virtualization', model='vminterface'),
+        blank=True,
+        null=True
+    )
+    interface_id = models.PositiveIntegerField(blank=True, null=True)
+    interface = GenericForeignKey('interface_type', 'interface_id')
     nat_inside = models.OneToOneField('self', related_name='nat_outside', on_delete=models.SET_NULL, blank=True,
                                       null=True, verbose_name='NAT (Inside)',
                                       help_text="The IP for which this address is the \"outside\" IP")

+ 12 - 6
netbox/ipam/views.py

@@ -594,7 +594,11 @@ class PrefixBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
 #
 
 class IPAddressListView(ObjectListView):
-    queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device', 'nat_inside')
+    queryset = IPAddress.objects.select_related(
+        'vrf__tenant', 'tenant', 'nat_inside'
+    ).prefetch_related(
+        'interface__device'
+    )
     filter = filters.IPAddressFilter
     filter_form = forms.IPAddressFilterForm
     table = tables.IPAddressDetailTable
@@ -605,7 +609,7 @@ class IPAddressView(View):
 
     def get(self, request, pk):
 
-        ipaddress = get_object_or_404(IPAddress.objects.select_related('interface__device'), pk=pk)
+        ipaddress = get_object_or_404(IPAddress.objects.select_related('vrf__tenant', 'tenant'), pk=pk)
 
         # Parent prefixes table
         parent_prefixes = Prefix.objects.filter(
@@ -622,12 +626,14 @@ class IPAddressView(View):
         ).exclude(
             pk=ipaddress.pk
         ).select_related(
-            'interface__device', 'nat_inside'
+            'nat_inside'
+        ).prefetch_related(
+            'interface__device'
         )
         duplicate_ips_table = tables.IPAddressTable(list(duplicate_ips), orderable=False)
 
         # Related IP table
-        related_ips = IPAddress.objects.select_related(
+        related_ips = IPAddress.objects.prefetch_related(
             'interface__device'
         ).exclude(
             address=str(ipaddress.address)
@@ -681,7 +687,7 @@ class IPAddressBulkImportView(PermissionRequiredMixin, BulkImportView):
 class IPAddressBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'ipam.change_ipaddress'
     cls = IPAddress
-    queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device')
+    queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant').prefetch_related('interface__device')
     filter = filters.IPAddressFilter
     table = tables.IPAddressTable
     form = forms.IPAddressBulkEditForm
@@ -691,7 +697,7 @@ class IPAddressBulkEditView(PermissionRequiredMixin, BulkEditView):
 class IPAddressBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_ipaddress'
     cls = IPAddress
-    queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device')
+    queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant').prefetch_related('interface__device')
     filter = filters.IPAddressFilter
     table = tables.IPAddressTable
     default_return_url = 'ipam:ipaddress_list'

+ 4 - 3
netbox/virtualization/migrations/0001_initial.py

@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Generated by Django 1.11.4 on 2017-08-16 21:06
+# Generated by Django 1.11.4 on 2017-08-18 19:46
 from __future__ import unicode_literals
 
 import dcim.fields
@@ -13,9 +13,9 @@ class Migration(migrations.Migration):
     initial = True
 
     dependencies = [
-        ('tenancy', '0003_unicode_literals'),
         ('dcim', '0041_napalm_integration'),
-        ('ipam', '0018_remove_service_uniqueness_constraint'),
+        ('ipam', '0019_ipaddress_interface_to_gfk'),
+        ('tenancy', '0003_unicode_literals'),
     ]
 
     operations = [
@@ -89,6 +89,7 @@ class Migration(migrations.Migration):
                 ('virtual_machine', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interfaces', to='virtualization.VirtualMachine')),
             ],
             options={
+                'verbose_name': 'VM interface',
                 'ordering': ['virtual_machine', 'name'],
             },
         ),

+ 5 - 0
netbox/virtualization/models.py

@@ -221,6 +221,11 @@ class VMInterface(models.Model):
         max_length=100,
         blank=True
     )
+    ip_addresses = GenericRelation(
+        to='ipam.IPAddress',
+        content_type_field='interface_type',
+        object_id_field='interface_id'
+    )
 
     class Meta:
         ordering = ['virtual_machine', 'name']