Просмотр исходного кода

Fixes #2305: Make VLAN fields optional when creating a VM interface via the API

Jeremy Stretch 7 лет назад
Родитель
Сommit
24520717e4
2 измененных файлов с 184 добавлено и 9 удалено
  1. 16 9
      netbox/virtualization/api/serializers.py
  2. 168 0
      netbox/virtualization/tests/test_api.py

+ 16 - 9
netbox/virtualization/api/serializers.py

@@ -4,12 +4,12 @@ from rest_framework import serializers
 from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
 
 from dcim.api.serializers import NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedSiteSerializer
-from dcim.constants import IFACE_MODE_CHOICES
+from dcim.constants import IFACE_FF_CHOICES, IFACE_FF_VIRTUAL, IFACE_MODE_CHOICES
 from dcim.models import Interface
 from extras.api.customfields import CustomFieldModelSerializer
 from ipam.models import IPAddress, VLAN
 from tenancy.api.serializers import NestedTenantSerializer
-from utilities.api import ChoiceField, ValidatedModelSerializer, WritableNestedSerializer
+from utilities.api import ChoiceField, SerializedPKRelatedField, ValidatedModelSerializer, WritableNestedSerializer
 from virtualization.constants import VM_STATUS_CHOICES
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 
@@ -135,7 +135,7 @@ class NestedVirtualMachineSerializer(WritableNestedSerializer):
 #
 
 # Cannot import ipam.api.serializers.NestedVLANSerializer due to circular dependency
-class InterfaceVLANSerializer(serializers.ModelSerializer):
+class InterfaceVLANSerializer(WritableNestedSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='ipam-api:vlan-detail')
 
     class Meta:
@@ -143,17 +143,24 @@ class InterfaceVLANSerializer(serializers.ModelSerializer):
         fields = ['id', 'url', 'vid', 'name', 'display_name']
 
 
-class InterfaceSerializer(serializers.ModelSerializer):
+class InterfaceSerializer(TaggitSerializer, ValidatedModelSerializer):
     virtual_machine = NestedVirtualMachineSerializer()
-    mode = ChoiceField(choices=IFACE_MODE_CHOICES)
-    untagged_vlan = InterfaceVLANSerializer()
-    tagged_vlans = InterfaceVLANSerializer(many=True)
+    form_factor = ChoiceField(choices=IFACE_FF_CHOICES, default=IFACE_FF_VIRTUAL, required=False)
+    mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False)
+    untagged_vlan = InterfaceVLANSerializer(required=False, allow_null=True)
+    tagged_vlans = SerializedPKRelatedField(
+        queryset=VLAN.objects.all(),
+        serializer=InterfaceVLANSerializer,
+        required=False,
+        many=True
+    )
+    tags = TagListSerializerField(required=False)
 
     class Meta:
         model = Interface
         fields = [
-            'id', 'name', 'virtual_machine', 'enabled', 'mac_address', 'mtu', 'mode', 'untagged_vlan', 'tagged_vlans',
-            'description',
+            'id', 'virtual_machine', 'name', 'form_factor', 'enabled', 'mtu', 'mac_address', 'description', 'mode',
+            'untagged_vlan', 'tagged_vlans', 'tags',
         ]
 
 

+ 168 - 0
netbox/virtualization/tests/test_api.py

@@ -3,6 +3,9 @@ from __future__ import unicode_literals
 from django.urls import reverse
 from rest_framework import status
 
+from dcim.constants import IFACE_FF_VIRTUAL, IFACE_MODE_TAGGED
+from dcim.models import Interface
+from ipam.models import VLAN
 from utilities.testing import APITestCase
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 
@@ -390,3 +393,168 @@ class VirtualMachineTest(APITestCase):
 
         self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
         self.assertEqual(VirtualMachine.objects.count(), 2)
+
+
+class InterfaceTest(APITestCase):
+
+    def setUp(self):
+
+        super(InterfaceTest, self).setUp()
+
+        clustertype = ClusterType.objects.create(name='Test Cluster Type 1', slug='test-cluster-type-1')
+        cluster = Cluster.objects.create(name='Test Cluster 1', type=clustertype)
+        self.virtualmachine = VirtualMachine.objects.create(cluster=cluster, name='Test VM 1')
+        self.interface1 = Interface.objects.create(
+            virtual_machine=self.virtualmachine,
+            name='Test Interface 1',
+            form_factor=IFACE_FF_VIRTUAL
+        )
+        self.interface2 = Interface.objects.create(
+            virtual_machine=self.virtualmachine,
+            name='Test Interface 2',
+            form_factor=IFACE_FF_VIRTUAL
+        )
+        self.interface3 = Interface.objects.create(
+            virtual_machine=self.virtualmachine,
+            name='Test Interface 3',
+            form_factor=IFACE_FF_VIRTUAL
+        )
+
+        self.vlan1 = VLAN.objects.create(name="Test VLAN 1", vid=1)
+        self.vlan2 = VLAN.objects.create(name="Test VLAN 2", vid=2)
+        self.vlan3 = VLAN.objects.create(name="Test VLAN 3", vid=3)
+
+    def test_get_interface(self):
+
+        url = reverse('virtualization-api:interface-detail', kwargs={'pk': self.interface1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.interface1.name)
+
+    def test_list_interfaces(self):
+
+        url = reverse('virtualization-api:interface-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_interface(self):
+
+        data = {
+            'virtual_machine': self.virtualmachine.pk,
+            'name': 'Test Interface 4',
+        }
+
+        url = reverse('virtualization-api:interface-list')
+        response = self.client.post(url, data, format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_201_CREATED)
+        self.assertEqual(Interface.objects.count(), 4)
+        interface4 = Interface.objects.get(pk=response.data['id'])
+        self.assertEqual(interface4.virtual_machine_id, data['virtual_machine'])
+        self.assertEqual(interface4.name, data['name'])
+
+    def test_create_interface_with_802_1q(self):
+
+        data = {
+            'virtual_machine': self.virtualmachine.pk,
+            'name': 'Test Interface 4',
+            'mode': IFACE_MODE_TAGGED,
+            'untagged_vlan': self.vlan3.id,
+            'tagged_vlans': [self.vlan1.id, self.vlan2.id],
+        }
+
+        url = reverse('virtualization-api:interface-list')
+        response = self.client.post(url, data, format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_201_CREATED)
+        self.assertEqual(Interface.objects.count(), 4)
+        self.assertEqual(response.data['virtual_machine']['id'], data['virtual_machine'])
+        self.assertEqual(response.data['name'], data['name'])
+        self.assertEqual(response.data['untagged_vlan']['id'], data['untagged_vlan'])
+        self.assertEqual([v['id'] for v in response.data['tagged_vlans']], data['tagged_vlans'])
+
+    def test_create_interface_bulk(self):
+
+        data = [
+            {
+                'virtual_machine': self.virtualmachine.pk,
+                'name': 'Test Interface 4',
+            },
+            {
+                'virtual_machine': self.virtualmachine.pk,
+                'name': 'Test Interface 5',
+            },
+            {
+                'virtual_machine': self.virtualmachine.pk,
+                'name': 'Test Interface 6',
+            },
+        ]
+
+        url = reverse('virtualization-api:interface-list')
+        response = self.client.post(url, data, format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_201_CREATED)
+        self.assertEqual(Interface.objects.count(), 6)
+        self.assertEqual(response.data[0]['name'], data[0]['name'])
+        self.assertEqual(response.data[1]['name'], data[1]['name'])
+        self.assertEqual(response.data[2]['name'], data[2]['name'])
+
+    def test_create_interface_802_1q_bulk(self):
+
+        data = [
+            {
+                'virtual_machine': self.virtualmachine.pk,
+                'name': 'Test Interface 4',
+                'mode': IFACE_MODE_TAGGED,
+                'untagged_vlan': self.vlan2.id,
+                'tagged_vlans': [self.vlan1.id],
+            },
+            {
+                'virtual_machine': self.virtualmachine.pk,
+                'name': 'Test Interface 5',
+                'mode': IFACE_MODE_TAGGED,
+                'untagged_vlan': self.vlan2.id,
+                'tagged_vlans': [self.vlan1.id],
+            },
+            {
+                'virtual_machine': self.virtualmachine.pk,
+                'name': 'Test Interface 6',
+                'mode': IFACE_MODE_TAGGED,
+                'untagged_vlan': self.vlan2.id,
+                'tagged_vlans': [self.vlan1.id],
+            },
+        ]
+
+        url = reverse('virtualization-api:interface-list')
+        response = self.client.post(url, data, format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_201_CREATED)
+        self.assertEqual(Interface.objects.count(), 6)
+        for i in range(0, 3):
+            self.assertEqual(response.data[i]['name'], data[i]['name'])
+            self.assertEqual([v['id'] for v in response.data[i]['tagged_vlans']], data[i]['tagged_vlans'])
+            self.assertEqual(response.data[i]['untagged_vlan']['id'], data[i]['untagged_vlan'])
+
+    def test_update_interface(self):
+
+        data = {
+            'virtual_machine': self.virtualmachine.pk,
+            'name': 'Test Interface X',
+        }
+
+        url = reverse('virtualization-api:interface-detail', kwargs={'pk': self.interface1.pk})
+        response = self.client.put(url, data, format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+        self.assertEqual(Interface.objects.count(), 3)
+        interface1 = Interface.objects.get(pk=response.data['id'])
+        self.assertEqual(interface1.name, data['name'])
+
+    def test_delete_interface(self):
+
+        url = reverse('dcim-api:interface-detail', kwargs={'pk': self.interface1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(Interface.objects.count(), 2)