Jeremy Stretch 9 лет назад
Родитель
Сommit
f33269e50b

+ 9 - 2
netbox/dcim/api/serializers.py

@@ -70,8 +70,8 @@ class WritableSiteSerializer(serializers.ModelSerializer):
     class Meta:
         model = Site
         fields = [
-            'id', 'name', 'slug', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address', 'contact_name',
-            'contact_phone', 'contact_email', 'comments',
+            'id', 'name', 'slug', 'region', 'tenant', 'facility', 'asn', 'physical_address', 'shipping_address',
+            'contact_name', 'contact_phone', 'contact_email', 'comments',
         ]
 
 
@@ -171,6 +171,13 @@ class RackReservationSerializer(serializers.ModelSerializer):
         fields = ['id', 'rack', 'units', 'created', 'user', 'description']
 
 
+class WritableRackReservationSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = RackReservation
+        fields = ['id', 'rack', 'units', 'user', 'description']
+
+
 #
 # Manufacturers
 #

+ 3 - 2
netbox/dcim/api/views.py

@@ -114,9 +114,10 @@ class RackViewSet(WritableSerializerMixin, CustomFieldModelViewSet):
 # Rack reservations
 #
 
-class RackReservationViewSet(ModelViewSet):
-    queryset = RackReservation.objects.all()
+class RackReservationViewSet(WritableSerializerMixin, ModelViewSet):
+    queryset = RackReservation.objects.select_related('rack')
     serializer_class = serializers.RackReservationSerializer
+    write_serializer_class = serializers.WritableRackReservationSerializer
     filter_class = filters.RackReservationFilter
 
 

+ 1066 - 0
netbox/dcim/tests/test_api.py

@@ -0,0 +1,1066 @@
+from rest_framework import status
+from rest_framework.test import APITestCase
+
+from django.contrib.auth.models import User
+from django.urls import reverse
+
+from dcim.models import (
+    ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, DeviceType, InterfaceTemplate, Manufacturer,
+    PowerPortTemplate, PowerOutletTemplate, Rack, RackGroup, RackReservation, RackRole, Region, Site,
+)
+from users.models import Token
+
+
+class RegionTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.region1 = Region.objects.create(name='Test Region 1', slug='test-region-1')
+        self.region2 = Region.objects.create(name='Test Region 2', slug='test-region-2')
+        self.region3 = Region.objects.create(name='Test Region 3', slug='test-region-3')
+
+    def test_get_region(self):
+
+        url = reverse('dcim-api:region-detail', kwargs={'pk': self.region1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.region1.name)
+
+    def test_list_regions(self):
+
+        url = reverse('dcim-api:region-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_region(self):
+
+        data = {
+            'name': 'Test Region 4',
+            'slug': 'test-region-4',
+        }
+
+        url = reverse('dcim-api:region-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(Region.objects.count(), 4)
+        region4 = Region.objects.get(pk=response.data['id'])
+        self.assertEqual(region4.name, data['name'])
+        self.assertEqual(region4.slug, data['slug'])
+
+    def test_update_region(self):
+
+        data = {
+            'name': 'Test Region X',
+            'slug': 'test-region-x',
+        }
+
+        url = reverse('dcim-api:region-detail', kwargs={'pk': self.region1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(Region.objects.count(), 3)
+        region1 = Region.objects.get(pk=response.data['id'])
+        self.assertEqual(region1.name, data['name'])
+        self.assertEqual(region1.slug, data['slug'])
+
+    def test_delete_region(self):
+
+        url = reverse('dcim-api:region-detail', kwargs={'pk': self.region1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(Region.objects.count(), 2)
+
+
+class SiteTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.region1 = Region.objects.create(name='Test Region 1', slug='test-region-1')
+        self.region2 = Region.objects.create(name='Test Region 2', slug='test-region-2')
+        self.site1 = Site.objects.create(region=self.region1, name='Test Site 1', slug='test-site-1')
+        self.site2 = Site.objects.create(region=self.region1, name='Test Site 2', slug='test-site-2')
+        self.site3 = Site.objects.create(region=self.region1, name='Test Site 3', slug='test-site-3')
+
+    def test_get_site(self):
+
+        url = reverse('dcim-api:site-detail', kwargs={'pk': self.site1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.site1.name)
+
+    def test_list_sites(self):
+
+        url = reverse('dcim-api:site-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_site(self):
+
+        data = {
+            'name': 'Test Site 4',
+            'slug': 'test-site-4',
+            'region': self.region1.pk,
+        }
+
+        url = reverse('dcim-api:site-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(Site.objects.count(), 4)
+        site4 = Site.objects.get(pk=response.data['id'])
+        self.assertEqual(site4.name, data['name'])
+        self.assertEqual(site4.slug, data['slug'])
+        self.assertEqual(site4.region_id, data['region'])
+
+    def test_update_site(self):
+
+        data = {
+            'name': 'Test Site X',
+            'slug': 'test-site-x',
+            'region': self.region2.pk,
+        }
+
+        url = reverse('dcim-api:site-detail', kwargs={'pk': self.site1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(Site.objects.count(), 3)
+        site1 = Site.objects.get(pk=response.data['id'])
+        self.assertEqual(site1.name, data['name'])
+        self.assertEqual(site1.slug, data['slug'])
+        self.assertEqual(site1.region_id, data['region'])
+
+    def test_delete_site(self):
+
+        url = reverse('dcim-api:site-detail', kwargs={'pk': self.site1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(Site.objects.count(), 2)
+
+
+class RackGroupTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.site1 = Site.objects.create(name='Test Site 1', slug='test-site-1')
+        self.site2 = Site.objects.create(name='Test Site 2', slug='test-site-2')
+        self.rackgroup1 = RackGroup.objects.create(site=self.site1, name='Test Rack Group 1', slug='test-rack-group-1')
+        self.rackgroup2 = RackGroup.objects.create(site=self.site1, name='Test Rack Group 2', slug='test-rack-group-2')
+        self.rackgroup3 = RackGroup.objects.create(site=self.site1, name='Test Rack Group 3', slug='test-rack-group-3')
+
+    def test_get_rackgroup(self):
+
+        url = reverse('dcim-api:rackgroup-detail', kwargs={'pk': self.rackgroup1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.rackgroup1.name)
+
+    def test_list_rackgroups(self):
+
+        url = reverse('dcim-api:rackgroup-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_rackgroup(self):
+
+        data = {
+            'name': 'Test Rack Group 4',
+            'slug': 'test-rack-group-4',
+            'site': self.site1.pk,
+        }
+
+        url = reverse('dcim-api:rackgroup-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(RackGroup.objects.count(), 4)
+        rackgroup4 = RackGroup.objects.get(pk=response.data['id'])
+        self.assertEqual(rackgroup4.name, data['name'])
+        self.assertEqual(rackgroup4.slug, data['slug'])
+        self.assertEqual(rackgroup4.site_id, data['site'])
+
+    def test_update_rackgroup(self):
+
+        data = {
+            'name': 'Test Rack Group X',
+            'slug': 'test-rack-group-x',
+            'site': self.site2.pk,
+        }
+
+        url = reverse('dcim-api:rackgroup-detail', kwargs={'pk': self.rackgroup1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(RackGroup.objects.count(), 3)
+        rackgroup1 = RackGroup.objects.get(pk=response.data['id'])
+        self.assertEqual(rackgroup1.name, data['name'])
+        self.assertEqual(rackgroup1.slug, data['slug'])
+        self.assertEqual(rackgroup1.site_id, data['site'])
+
+    def test_delete_rackgroup(self):
+
+        url = reverse('dcim-api:rackgroup-detail', kwargs={'pk': self.rackgroup1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(RackGroup.objects.count(), 2)
+
+
+class RackRoleTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.rackrole1 = RackRole.objects.create(name='Test Rack Role 1', slug='test-rack-role-1', color='ff0000')
+        self.rackrole2 = RackRole.objects.create(name='Test Rack Role 2', slug='test-rack-role-2', color='00ff00')
+        self.rackrole3 = RackRole.objects.create(name='Test Rack Role 3', slug='test-rack-role-3', color='0000ff')
+
+    def test_get_rackrole(self):
+
+        url = reverse('dcim-api:rackrole-detail', kwargs={'pk': self.rackrole1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.rackrole1.name)
+
+    def test_list_rackroles(self):
+
+        url = reverse('dcim-api:rackrole-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_rackrole(self):
+
+        data = {
+            'name': 'Test Rack Role 4',
+            'slug': 'test-rack-role-4',
+            'color': 'ffff00',
+        }
+
+        url = reverse('dcim-api:rackrole-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(RackRole.objects.count(), 4)
+        rackrole1 = RackRole.objects.get(pk=response.data['id'])
+        self.assertEqual(rackrole1.name, data['name'])
+        self.assertEqual(rackrole1.slug, data['slug'])
+        self.assertEqual(rackrole1.color, data['color'])
+
+    def test_update_rackrole(self):
+
+        data = {
+            'name': 'Test Rack Role X',
+            'slug': 'test-rack-role-x',
+            'color': 'ffff00',
+        }
+
+        url = reverse('dcim-api:rackrole-detail', kwargs={'pk': self.rackrole1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(RackRole.objects.count(), 3)
+        rackrole1 = RackRole.objects.get(pk=response.data['id'])
+        self.assertEqual(rackrole1.name, data['name'])
+        self.assertEqual(rackrole1.slug, data['slug'])
+        self.assertEqual(rackrole1.color, data['color'])
+
+    def test_delete_rackrole(self):
+
+        url = reverse('dcim-api:rackrole-detail', kwargs={'pk': self.rackrole1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(RackRole.objects.count(), 2)
+
+
+class RackTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.site1 = Site.objects.create(name='Test Site 1', slug='test-site-1')
+        self.site2 = Site.objects.create(name='Test Site 2', slug='test-site-2')
+        self.rackgroup1 = RackGroup.objects.create(site=self.site1, name='Test Rack Group 1', slug='test-rack-group-1')
+        self.rackgroup2 = RackGroup.objects.create(site=self.site2, name='Test Rack Group 2', slug='test-rack-group-2')
+        self.rackrole1 = RackRole.objects.create(name='Test Rack Role 1', slug='test-rack-role-1', color='ff0000')
+        self.rackrole2 = RackRole.objects.create(name='Test Rack Role 2', slug='test-rack-role-2', color='00ff00')
+        self.rack1 = Rack.objects.create(
+            site=self.site1, group=self.rackgroup1, role=self.rackrole1, name='Test Rack 1'
+        )
+        self.rack2 = Rack.objects.create(
+            site=self.site1, group=self.rackgroup1, role=self.rackrole1, name='Test Rack 2'
+        )
+        self.rack3 = Rack.objects.create(
+            site=self.site1, group=self.rackgroup1, role=self.rackrole1, name='Test Rack 3'
+        )
+
+    def test_get_rack(self):
+
+        url = reverse('dcim-api:rack-detail', kwargs={'pk': self.rack1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.rack1.name)
+
+    def test_list_racks(self):
+
+        url = reverse('dcim-api:rack-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_rack(self):
+
+        data = {
+            'name': 'Test Rack 4',
+            'site': self.site1.pk,
+            'group': self.rackgroup1.pk,
+            'role': self.rackrole1.pk,
+        }
+
+        url = reverse('dcim-api:rack-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(Rack.objects.count(), 4)
+        rack4 = Rack.objects.get(pk=response.data['id'])
+        self.assertEqual(rack4.name, data['name'])
+        self.assertEqual(rack4.site_id, data['site'])
+        self.assertEqual(rack4.group_id, data['group'])
+        self.assertEqual(rack4.role_id, data['role'])
+
+    def test_update_rack(self):
+
+        data = {
+            'name': 'Test Rack X',
+            'site': self.site2.pk,
+            'group': self.rackgroup2.pk,
+            'role': self.rackrole2.pk,
+        }
+
+        url = reverse('dcim-api:rack-detail', kwargs={'pk': self.rack1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(Rack.objects.count(), 3)
+        rack1 = Rack.objects.get(pk=response.data['id'])
+        self.assertEqual(rack1.name, data['name'])
+        self.assertEqual(rack1.site_id, data['site'])
+        self.assertEqual(rack1.group_id, data['group'])
+        self.assertEqual(rack1.role_id, data['role'])
+
+    def test_delete_rack(self):
+
+        url = reverse('dcim-api:rack-detail', kwargs={'pk': self.rack1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(Rack.objects.count(), 2)
+
+
+class RackReservationTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.user1 = user
+        self.site1 = Site.objects.create(name='Test Site 1', slug='test-site-1')
+        self.rack1 = Rack.objects.create(site=self.site1, name='Test Rack 1')
+        self.rackreservation1 = RackReservation.objects.create(
+            rack=self.rack1, units=[1, 2, 3], user=user, description='First reservation',
+        )
+        self.rackreservation2 = RackReservation.objects.create(
+            rack=self.rack1, units=[4, 5, 6], user=user, description='Second reservation',
+        )
+        self.rackreservation3 = RackReservation.objects.create(
+            rack=self.rack1, units=[7, 8, 9], user=user, description='Third reservation',
+        )
+
+    def test_get_rackreservation(self):
+
+        url = reverse('dcim-api:rackreservation-detail', kwargs={'pk': self.rackreservation1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['id'], self.rackreservation1.pk)
+
+    def test_list_rackreservations(self):
+
+        url = reverse('dcim-api:rackreservation-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_rackreservation(self):
+
+        data = {
+            'rack': self.rack1.pk,
+            'units': [10, 11, 12],
+            'user': self.user1.pk,
+            'description': 'Fourth reservation',
+        }
+
+        url = reverse('dcim-api:rackreservation-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(RackReservation.objects.count(), 4)
+        rackreservation4 = RackReservation.objects.get(pk=response.data['id'])
+        self.assertEqual(rackreservation4.rack_id, data['rack'])
+        self.assertEqual(rackreservation4.units, data['units'])
+        self.assertEqual(rackreservation4.user_id, data['user'])
+        self.assertEqual(rackreservation4.description, data['description'])
+
+    def test_update_rackreservation(self):
+
+        data = {
+            'rack': self.rack1.pk,
+            'units': [10, 11, 12],
+            'user': self.user1.pk,
+            'description': 'Modified reservation',
+        }
+
+        url = reverse('dcim-api:rackreservation-detail', kwargs={'pk': self.rackreservation1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(RackReservation.objects.count(), 3)
+        rackreservation1 = RackReservation.objects.get(pk=response.data['id'])
+        self.assertEqual(rackreservation1.units, data['units'])
+        self.assertEqual(rackreservation1.description, data['description'])
+
+    def test_delete_rackreservation(self):
+
+        url = reverse('dcim-api:rackreservation-detail', kwargs={'pk': self.rackreservation1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(RackReservation.objects.count(), 2)
+
+
+class ManufacturerTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.manufacturer1 = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
+        self.manufacturer2 = Manufacturer.objects.create(name='Test Manufacturer 2', slug='test-manufacturer-2')
+        self.manufacturer3 = Manufacturer.objects.create(name='Test Manufacturer 3', slug='test-manufacturer-3')
+
+    def test_get_manufacturer(self):
+
+        url = reverse('dcim-api:manufacturer-detail', kwargs={'pk': self.manufacturer1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.manufacturer1.name)
+
+    def test_list_manufacturers(self):
+
+        url = reverse('dcim-api:manufacturer-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_manufacturer(self):
+
+        data = {
+            'name': 'Test Manufacturer 4',
+            'slug': 'test-manufacturer-4',
+        }
+
+        url = reverse('dcim-api:manufacturer-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(Manufacturer.objects.count(), 4)
+        manufacturer4 = Manufacturer.objects.get(pk=response.data['id'])
+        self.assertEqual(manufacturer4.name, data['name'])
+        self.assertEqual(manufacturer4.slug, data['slug'])
+
+    def test_update_manufacturer(self):
+
+        data = {
+            'name': 'Test Manufacturer X',
+            'slug': 'test-manufacturer-x',
+        }
+
+        url = reverse('dcim-api:manufacturer-detail', kwargs={'pk': self.manufacturer1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(Manufacturer.objects.count(), 3)
+        manufacturer1 = Manufacturer.objects.get(pk=response.data['id'])
+        self.assertEqual(manufacturer1.name, data['name'])
+        self.assertEqual(manufacturer1.slug, data['slug'])
+
+    def test_delete_manufacturer(self):
+
+        url = reverse('dcim-api:manufacturer-detail', kwargs={'pk': self.manufacturer1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(Manufacturer.objects.count(), 2)
+
+
+class DeviceTypeTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.manufacturer1 = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
+        self.manufacturer2 = Manufacturer.objects.create(name='Test Manufacturer 2', slug='test-manufacturer-2')
+        self.devicetype1 = DeviceType.objects.create(
+            manufacturer=self.manufacturer1, model='Test Device Type 1', slug='test-device-type-1'
+        )
+        self.devicetype2 = DeviceType.objects.create(
+            manufacturer=self.manufacturer1, model='Test Device Type 2', slug='test-device-type-2'
+        )
+        self.devicetype3 = DeviceType.objects.create(
+            manufacturer=self.manufacturer1, model='Test Device Type 3', slug='test-device-type-3'
+        )
+
+    def test_get_devicetype(self):
+
+        url = reverse('dcim-api:devicetype-detail', kwargs={'pk': self.devicetype1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['model'], self.devicetype1.model)
+
+    def test_list_devicetypes(self):
+
+        url = reverse('dcim-api:devicetype-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_devicetype(self):
+
+        data = {
+            'manufacturer': self.manufacturer1.pk,
+            'model': 'Test Device Type 4',
+            'slug': 'test-device-type-4',
+        }
+
+        url = reverse('dcim-api:devicetype-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(DeviceType.objects.count(), 4)
+        devicetype4 = DeviceType.objects.get(pk=response.data['id'])
+        self.assertEqual(devicetype4.manufacturer_id, data['manufacturer'])
+        self.assertEqual(devicetype4.model, data['model'])
+        self.assertEqual(devicetype4.slug, data['slug'])
+
+    def test_update_devicetype(self):
+
+        data = {
+            'manufacturer': self.manufacturer2.pk,
+            'model': 'Test Device Type X',
+            'slug': 'test-device-type-x',
+        }
+
+        url = reverse('dcim-api:devicetype-detail', kwargs={'pk': self.devicetype1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(DeviceType.objects.count(), 3)
+        devicetype1 = DeviceType.objects.get(pk=response.data['id'])
+        self.assertEqual(devicetype1.manufacturer_id, data['manufacturer'])
+        self.assertEqual(devicetype1.model, data['model'])
+        self.assertEqual(devicetype1.slug, data['slug'])
+
+    def test_delete_devicetype(self):
+
+        url = reverse('dcim-api:devicetype-detail', kwargs={'pk': self.devicetype1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(DeviceType.objects.count(), 2)
+
+
+class ConsolePortTemplateTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
+        self.devicetype = DeviceType.objects.create(
+            manufacturer=self.manufacturer, model='Test Device Type 1', slug='test-device-type-1'
+        )
+        self.consoleporttemplate1 = ConsolePortTemplate.objects.create(
+            device_type=self.devicetype, name='Test CP Template 1'
+        )
+        self.consoleporttemplate2 = ConsolePortTemplate.objects.create(
+            device_type=self.devicetype, name='Test CP Template 2'
+        )
+        self.consoleporttemplate3 = ConsolePortTemplate.objects.create(
+            device_type=self.devicetype, name='Test CP Template 3'
+        )
+
+    def test_get_consoleporttemplate(self):
+
+        url = reverse('dcim-api:consoleporttemplate-detail', kwargs={'pk': self.consoleporttemplate1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.consoleporttemplate1.name)
+
+    def test_list_consoleporttemplates(self):
+
+        url = reverse('dcim-api:consoleporttemplate-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_consoleporttemplate(self):
+
+        data = {
+            'device_type': self.devicetype.pk,
+            'name': 'Test CP Template 4',
+        }
+
+        url = reverse('dcim-api:consoleporttemplate-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(ConsolePortTemplate.objects.count(), 4)
+        consoleporttemplate4 = ConsolePortTemplate.objects.get(pk=response.data['id'])
+        self.assertEqual(consoleporttemplate4.device_type_id, data['device_type'])
+        self.assertEqual(consoleporttemplate4.name, data['name'])
+
+    def test_update_consoleporttemplate(self):
+
+        data = {
+            'device_type': self.devicetype.pk,
+            'name': 'Test CP Template X',
+        }
+
+        url = reverse('dcim-api:consoleporttemplate-detail', kwargs={'pk': self.consoleporttemplate1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(ConsolePortTemplate.objects.count(), 3)
+        consoleporttemplate1 = ConsolePortTemplate.objects.get(pk=response.data['id'])
+        self.assertEqual(consoleporttemplate1.name, data['name'])
+
+    def test_delete_consoleporttemplate(self):
+
+        url = reverse('dcim-api:consoleporttemplate-detail', kwargs={'pk': self.consoleporttemplate1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(ConsolePortTemplate.objects.count(), 2)
+
+
+class ConsoleServerPortTemplateTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
+        self.devicetype = DeviceType.objects.create(
+            manufacturer=self.manufacturer, model='Test Device Type 1', slug='test-device-type-1'
+        )
+        self.consoleserverporttemplate1 = ConsoleServerPortTemplate.objects.create(
+            device_type=self.devicetype, name='Test CSP Template 1'
+        )
+        self.consoleserverporttemplate2 = ConsoleServerPortTemplate.objects.create(
+            device_type=self.devicetype, name='Test CSP Template 2'
+        )
+        self.consoleserverporttemplate3 = ConsoleServerPortTemplate.objects.create(
+            device_type=self.devicetype, name='Test CSP Template 3'
+        )
+
+    def test_get_consoleserverporttemplate(self):
+
+        url = reverse('dcim-api:consoleserverporttemplate-detail', kwargs={'pk': self.consoleserverporttemplate1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.consoleserverporttemplate1.name)
+
+    def test_list_consoleserverporttemplates(self):
+
+        url = reverse('dcim-api:consoleserverporttemplate-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_consoleserverporttemplate(self):
+
+        data = {
+            'device_type': self.devicetype.pk,
+            'name': 'Test CSP Template 4',
+        }
+
+        url = reverse('dcim-api:consoleserverporttemplate-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(ConsoleServerPortTemplate.objects.count(), 4)
+        consoleserverporttemplate4 = ConsoleServerPortTemplate.objects.get(pk=response.data['id'])
+        self.assertEqual(consoleserverporttemplate4.device_type_id, data['device_type'])
+        self.assertEqual(consoleserverporttemplate4.name, data['name'])
+
+    def test_update_consoleserverporttemplate(self):
+
+        data = {
+            'device_type': self.devicetype.pk,
+            'name': 'Test CSP Template X',
+        }
+
+        url = reverse('dcim-api:consoleserverporttemplate-detail', kwargs={'pk': self.consoleserverporttemplate1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(ConsoleServerPortTemplate.objects.count(), 3)
+        consoleserverporttemplate1 = ConsoleServerPortTemplate.objects.get(pk=response.data['id'])
+        self.assertEqual(consoleserverporttemplate1.name, data['name'])
+
+    def test_delete_consoleserverporttemplate(self):
+
+        url = reverse('dcim-api:consoleserverporttemplate-detail', kwargs={'pk': self.consoleserverporttemplate1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(ConsoleServerPortTemplate.objects.count(), 2)
+
+
+class PowerPortTemplateTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
+        self.devicetype = DeviceType.objects.create(
+            manufacturer=self.manufacturer, model='Test Device Type 1', slug='test-device-type-1'
+        )
+        self.powerporttemplate1 = PowerPortTemplate.objects.create(
+            device_type=self.devicetype, name='Test PP Template 1'
+        )
+        self.powerporttemplate2 = PowerPortTemplate.objects.create(
+            device_type=self.devicetype, name='Test PP Template 2'
+        )
+        self.powerporttemplate3 = PowerPortTemplate.objects.create(
+            device_type=self.devicetype, name='Test PP Template 3'
+        )
+
+    def test_get_powerporttemplate(self):
+
+        url = reverse('dcim-api:powerporttemplate-detail', kwargs={'pk': self.powerporttemplate1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.powerporttemplate1.name)
+
+    def test_list_powerporttemplates(self):
+
+        url = reverse('dcim-api:powerporttemplate-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_powerporttemplate(self):
+
+        data = {
+            'device_type': self.devicetype.pk,
+            'name': 'Test PP Template 4',
+        }
+
+        url = reverse('dcim-api:powerporttemplate-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(PowerPortTemplate.objects.count(), 4)
+        powerporttemplate4 = PowerPortTemplate.objects.get(pk=response.data['id'])
+        self.assertEqual(powerporttemplate4.device_type_id, data['device_type'])
+        self.assertEqual(powerporttemplate4.name, data['name'])
+
+    def test_update_powerporttemplate(self):
+
+        data = {
+            'device_type': self.devicetype.pk,
+            'name': 'Test PP Template X',
+        }
+
+        url = reverse('dcim-api:powerporttemplate-detail', kwargs={'pk': self.powerporttemplate1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(PowerPortTemplate.objects.count(), 3)
+        powerporttemplate1 = PowerPortTemplate.objects.get(pk=response.data['id'])
+        self.assertEqual(powerporttemplate1.name, data['name'])
+
+    def test_delete_powerporttemplate(self):
+
+        url = reverse('dcim-api:powerporttemplate-detail', kwargs={'pk': self.powerporttemplate1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(PowerPortTemplate.objects.count(), 2)
+
+
+class PowerOutletTemplateTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
+        self.devicetype = DeviceType.objects.create(
+            manufacturer=self.manufacturer, model='Test Device Type 1', slug='test-device-type-1'
+        )
+        self.poweroutlettemplate1 = PowerOutletTemplate.objects.create(
+            device_type=self.devicetype, name='Test PO Template 1'
+        )
+        self.poweroutlettemplate2 = PowerOutletTemplate.objects.create(
+            device_type=self.devicetype, name='Test PO Template 2'
+        )
+        self.poweroutlettemplate3 = PowerOutletTemplate.objects.create(
+            device_type=self.devicetype, name='Test PO Template 3'
+        )
+
+    def test_get_poweroutlettemplate(self):
+
+        url = reverse('dcim-api:poweroutlettemplate-detail', kwargs={'pk': self.poweroutlettemplate1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.poweroutlettemplate1.name)
+
+    def test_list_poweroutlettemplates(self):
+
+        url = reverse('dcim-api:poweroutlettemplate-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_poweroutlettemplate(self):
+
+        data = {
+            'device_type': self.devicetype.pk,
+            'name': 'Test PO Template 4',
+        }
+
+        url = reverse('dcim-api:poweroutlettemplate-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(PowerOutletTemplate.objects.count(), 4)
+        poweroutlettemplate4 = PowerOutletTemplate.objects.get(pk=response.data['id'])
+        self.assertEqual(poweroutlettemplate4.device_type_id, data['device_type'])
+        self.assertEqual(poweroutlettemplate4.name, data['name'])
+
+    def test_update_poweroutlettemplate(self):
+
+        data = {
+            'device_type': self.devicetype.pk,
+            'name': 'Test PO Template X',
+        }
+
+        url = reverse('dcim-api:poweroutlettemplate-detail', kwargs={'pk': self.poweroutlettemplate1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(PowerOutletTemplate.objects.count(), 3)
+        poweroutlettemplate1 = PowerOutletTemplate.objects.get(pk=response.data['id'])
+        self.assertEqual(poweroutlettemplate1.name, data['name'])
+
+    def test_delete_poweroutlettemplate(self):
+
+        url = reverse('dcim-api:poweroutlettemplate-detail', kwargs={'pk': self.poweroutlettemplate1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(PowerOutletTemplate.objects.count(), 2)
+
+
+class InterfaceTemplateTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
+        self.devicetype = DeviceType.objects.create(
+            manufacturer=self.manufacturer, model='Test Device Type 1', slug='test-device-type-1'
+        )
+        self.interfacetemplate1 = InterfaceTemplate.objects.create(
+            device_type=self.devicetype, name='Test Interface Template 1'
+        )
+        self.interfacetemplate2 = InterfaceTemplate.objects.create(
+            device_type=self.devicetype, name='Test Interface Template 2'
+        )
+        self.interfacetemplate3 = InterfaceTemplate.objects.create(
+            device_type=self.devicetype, name='Test Interface Template 3'
+        )
+
+    def test_get_interfacetemplate(self):
+
+        url = reverse('dcim-api:interfacetemplate-detail', kwargs={'pk': self.interfacetemplate1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.interfacetemplate1.name)
+
+    def test_list_interfacetemplates(self):
+
+        url = reverse('dcim-api:interfacetemplate-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_interfacetemplate(self):
+
+        data = {
+            'device_type': self.devicetype.pk,
+            'name': 'Test Interface Template 4',
+        }
+
+        url = reverse('dcim-api:interfacetemplate-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(InterfaceTemplate.objects.count(), 4)
+        interfacetemplate4 = InterfaceTemplate.objects.get(pk=response.data['id'])
+        self.assertEqual(interfacetemplate4.device_type_id, data['device_type'])
+        self.assertEqual(interfacetemplate4.name, data['name'])
+
+    def test_update_interfacetemplate(self):
+
+        data = {
+            'device_type': self.devicetype.pk,
+            'name': 'Test Interface Template X',
+        }
+
+        url = reverse('dcim-api:interfacetemplate-detail', kwargs={'pk': self.interfacetemplate1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(InterfaceTemplate.objects.count(), 3)
+        interfacetemplate1 = InterfaceTemplate.objects.get(pk=response.data['id'])
+        self.assertEqual(interfacetemplate1.name, data['name'])
+
+    def test_delete_interfacetemplate(self):
+
+        url = reverse('dcim-api:interfacetemplate-detail', kwargs={'pk': self.interfacetemplate1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(InterfaceTemplate.objects.count(), 2)
+
+
+class DeviceBayTemplateTest(APITestCase):
+
+    def setUp(self):
+
+        user = User.objects.create(username='testuser', is_superuser=True)
+        token = Token.objects.create(user=user)
+        self.header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token.key)}
+
+        self.manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
+        self.devicetype = DeviceType.objects.create(
+            manufacturer=self.manufacturer, model='Test Device Type 1', slug='test-device-type-1'
+        )
+        self.devicebaytemplate1 = DeviceBayTemplate.objects.create(
+            device_type=self.devicetype, name='Test Device Bay Template 1'
+        )
+        self.devicebaytemplate2 = DeviceBayTemplate.objects.create(
+            device_type=self.devicetype, name='Test Device Bay Template 2'
+        )
+        self.devicebaytemplate3 = DeviceBayTemplate.objects.create(
+            device_type=self.devicetype, name='Test Device Bay Template 3'
+        )
+
+    def test_get_devicebaytemplate(self):
+
+        url = reverse('dcim-api:devicebaytemplate-detail', kwargs={'pk': self.devicebaytemplate1.pk})
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['name'], self.devicebaytemplate1.name)
+
+    def test_list_devicebaytemplates(self):
+
+        url = reverse('dcim-api:devicebaytemplate-list')
+        response = self.client.get(url, **self.header)
+
+        self.assertEqual(response.data['count'], 3)
+
+    def test_create_devicebaytemplate(self):
+
+        data = {
+            'device_type': self.devicetype.pk,
+            'name': 'Test Device Bay Template 4',
+        }
+
+        url = reverse('dcim-api:devicebaytemplate-list')
+        response = self.client.post(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(DeviceBayTemplate.objects.count(), 4)
+        devicebaytemplate4 = DeviceBayTemplate.objects.get(pk=response.data['id'])
+        self.assertEqual(devicebaytemplate4.device_type_id, data['device_type'])
+        self.assertEqual(devicebaytemplate4.name, data['name'])
+
+    def test_update_devicebaytemplate(self):
+
+        data = {
+            'device_type': self.devicetype.pk,
+            'name': 'Test Device Bay Template X',
+        }
+
+        url = reverse('dcim-api:devicebaytemplate-detail', kwargs={'pk': self.devicebaytemplate1.pk})
+        response = self.client.put(url, data, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(DeviceBayTemplate.objects.count(), 3)
+        devicebaytemplate1 = DeviceBayTemplate.objects.get(pk=response.data['id'])
+        self.assertEqual(devicebaytemplate1.name, data['name'])
+
+    def test_delete_devicebaytemplate(self):
+
+        url = reverse('dcim-api:devicebaytemplate-detail', kwargs={'pk': self.devicebaytemplate1.pk})
+        response = self.client.delete(url, **self.header)
+
+        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+        self.assertEqual(DeviceBayTemplate.objects.count(), 2)

+ 0 - 661
netbox/dcim/tests/test_apis.py

@@ -1,661 +0,0 @@
-import json
-from rest_framework import status
-from rest_framework.test import APITestCase
-
-from django.conf import settings
-
-
-class SiteTest(APITestCase):
-
-    fixtures = [
-        'dcim',
-        'ipam',
-        'extras',
-    ]
-
-    standard_fields = [
-        'id',
-        'name',
-        'slug',
-        'region',
-        'tenant',
-        'facility',
-        'asn',
-        'physical_address',
-        'shipping_address',
-        'contact_name',
-        'contact_phone',
-        'contact_email',
-        'comments',
-        'custom_fields',
-        'count_prefixes',
-        'count_vlans',
-        'count_racks',
-        'count_devices',
-        'count_circuits'
-    ]
-
-    nested_fields = [
-        'id',
-        'name',
-        'slug'
-    ]
-
-    rack_fields = [
-        'id',
-        'name',
-        'facility_id',
-        'display_name',
-        'site',
-        'group',
-        'tenant',
-        'role',
-        'type',
-        'width',
-        'u_height',
-        'desc_units',
-        'comments',
-        'custom_fields',
-    ]
-
-    graph_fields = [
-        'name',
-        'embed_url',
-        'embed_link',
-    ]
-
-    def test_get_list(self, endpoint='/{}api/dcim/sites/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for i in content:
-            self.assertEqual(
-                sorted(i.keys()),
-                sorted(self.standard_fields),
-            )
-
-    def test_get_detail(self, endpoint='/{}api/dcim/sites/1/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(
-            sorted(content.keys()),
-            sorted(self.standard_fields),
-        )
-
-    def test_get_site_list_graphs(self, endpoint='/{}api/dcim/sites/1/graphs/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for i in json.loads(response.content.decode('utf-8')):
-            self.assertEqual(
-                sorted(i.keys()),
-                sorted(self.graph_fields),
-            )
-
-
-class RackTest(APITestCase):
-    fixtures = [
-        'dcim',
-        'ipam'
-    ]
-
-    nested_fields = [
-        'id',
-        'name',
-        'facility_id',
-        'display_name'
-    ]
-
-    standard_fields = [
-        'id',
-        'name',
-        'facility_id',
-        'display_name',
-        'site',
-        'group',
-        'tenant',
-        'role',
-        'type',
-        'width',
-        'u_height',
-        'desc_units',
-        'comments',
-        'custom_fields',
-    ]
-
-    detail_fields = [
-        'id',
-        'name',
-        'facility_id',
-        'display_name',
-        'site',
-        'group',
-        'tenant',
-        'role',
-        'type',
-        'width',
-        'u_height',
-        'desc_units',
-        'reservations',
-        'comments',
-        'custom_fields',
-        'front_units',
-        'rear_units'
-    ]
-
-    def test_get_list(self, endpoint='/{}api/dcim/racks/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for i in content:
-            self.assertEqual(
-                sorted(i.keys()),
-                sorted(self.standard_fields),
-            )
-            self.assertEqual(
-                sorted(i.get('site').keys()),
-                sorted(SiteTest.nested_fields),
-            )
-
-    def test_get_detail(self, endpoint='/{}api/dcim/racks/1/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(
-            sorted(content.keys()),
-            sorted(self.detail_fields),
-        )
-        self.assertEqual(
-            sorted(content.get('site').keys()),
-            sorted(SiteTest.nested_fields),
-        )
-
-
-class ManufacturersTest(APITestCase):
-
-    fixtures = [
-        'dcim',
-        'ipam'
-    ]
-
-    standard_fields = [
-        'id',
-        'name',
-        'slug',
-    ]
-
-    nested_fields = standard_fields
-
-    def test_get_list(self, endpoint='/{}api/dcim/manufacturers/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for i in content:
-            self.assertEqual(
-                sorted(i.keys()),
-                sorted(self.standard_fields),
-            )
-
-    def test_get_detail(self, endpoint='/{}api/dcim/manufacturers/1/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(
-            sorted(content.keys()),
-            sorted(self.standard_fields),
-        )
-
-
-class DeviceTypeTest(APITestCase):
-
-    fixtures = ['dcim', 'ipam']
-
-    standard_fields = [
-        'id',
-        'manufacturer',
-        'model',
-        'slug',
-        'part_number',
-        'u_height',
-        'is_full_depth',
-        'interface_ordering',
-        'is_console_server',
-        'is_pdu',
-        'is_network_device',
-        'subdevice_role',
-        'comments',
-        'custom_fields',
-        'instance_count',
-    ]
-
-    nested_fields = [
-        'id',
-        'manufacturer',
-        'model',
-        'slug'
-    ]
-
-    def test_get_list(self, endpoint='/{}api/dcim/device-types/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for i in content:
-            self.assertEqual(
-                sorted(i.keys()),
-                sorted(self.standard_fields),
-            )
-
-    def test_detail_list(self, endpoint='/{}api/dcim/device-types/1/'.format(settings.BASE_PATH)):
-        # TODO: details returns list view.
-        # response = self.client.get(endpoint)
-        # content = json.loads(response.content.decode('utf-8'))
-        # self.assertEqual(response.status_code, status.HTTP_200_OK)
-        # self.assertEqual(
-        #     sorted(content.keys()),
-        #     sorted(self.standard_fields),
-        # )
-        # self.assertEqual(
-        #     sorted(content.get('manufacturer').keys()),
-        #     sorted(ManufacturersTest.nested_fields),
-        # )
-        pass
-
-
-class DeviceRolesTest(APITestCase):
-
-    fixtures = ['dcim', 'ipam']
-
-    standard_fields = ['id', 'name', 'slug', 'color']
-
-    nested_fields = ['id', 'name', 'slug']
-
-    def test_get_list(self, endpoint='/{}api/dcim/device-roles/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for i in content:
-            self.assertEqual(
-                sorted(i.keys()),
-                sorted(self.standard_fields),
-            )
-
-    def test_get_detail(self, endpoint='/{}api/dcim/device-roles/1/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(
-            sorted(content.keys()),
-            sorted(self.standard_fields),
-        )
-
-
-class PlatformsTest(APITestCase):
-
-    fixtures = ['dcim', 'ipam']
-
-    standard_fields = ['id', 'name', 'slug', 'rpc_client']
-
-    nested_fields = ['id', 'name', 'slug']
-
-    def test_get_list(self, endpoint='/{}api/dcim/platforms/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for i in content:
-            self.assertEqual(
-                sorted(i.keys()),
-                sorted(self.standard_fields),
-            )
-
-    def test_get_detail(self, endpoint='/{}api/dcim/platforms/1/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(
-            sorted(content.keys()),
-            sorted(self.standard_fields),
-        )
-
-
-class DeviceTest(APITestCase):
-
-    fixtures = ['dcim', 'ipam']
-
-    standard_fields = [
-        'id',
-        'name',
-        'display_name',
-        'device_type',
-        'device_role',
-        'tenant',
-        'platform',
-        'serial',
-        'asset_tag',
-        'site',
-        'rack',
-        'position',
-        'face',
-        'parent_device',
-        'status',
-        'primary_ip',
-        'primary_ip4',
-        'primary_ip6',
-        'comments',
-        'custom_fields',
-    ]
-
-    nested_fields = ['id', 'name', 'display_name']
-
-    def test_get_list(self, endpoint='/{}api/dcim/devices/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for device in content:
-            self.assertEqual(
-                sorted(device.keys()),
-                sorted(self.standard_fields),
-            )
-            self.assertEqual(
-                sorted(device.get('device_type')),
-                sorted(DeviceTypeTest.nested_fields),
-            )
-            self.assertEqual(
-                sorted(device.get('device_role')),
-                sorted(DeviceRolesTest.nested_fields),
-            )
-            if device.get('platform'):
-                self.assertEqual(
-                    sorted(device.get('platform')),
-                    sorted(PlatformsTest.nested_fields),
-                )
-            self.assertEqual(
-                sorted(device.get('rack')),
-                sorted(RackTest.nested_fields),
-            )
-
-    def test_get_list_flat(self, endpoint='/{}api/dcim/devices/?format=json_flat'.format(settings.BASE_PATH)):
-
-        flat_fields = [
-            'asset_tag',
-            'comments',
-            'device_role_id',
-            'device_role_name',
-            'device_role_slug',
-            'device_type_id',
-            'device_type_manufacturer_id',
-            'device_type_manufacturer_name',
-            'device_type_manufacturer_slug',
-            'device_type_model',
-            'device_type_slug',
-            'display_name',
-            'face',
-            'id',
-            'name',
-            'parent_device',
-            'platform_id',
-            'platform_name',
-            'platform_slug',
-            'position',
-            'primary_ip_address',
-            'primary_ip_family',
-            'primary_ip_id',
-            'primary_ip4_address',
-            'primary_ip4_family',
-            'primary_ip4_id',
-            'primary_ip6',
-            'site_id',
-            'site_name',
-            'site_slug',
-            'rack_display_name',
-            'rack_facility_id',
-            'rack_id',
-            'rack_name',
-            'serial',
-            'status',
-            'tenant',
-        ]
-
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        device = content[0]
-        self.assertEqual(
-            sorted(device.keys()),
-            sorted(flat_fields),
-        )
-
-    def test_get_detail(self, endpoint='/{}api/dcim/devices/1/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(
-            sorted(content.keys()),
-            sorted(self.standard_fields),
-        )
-
-
-class ConsoleServerPortsTest(APITestCase):
-
-    fixtures = ['dcim', 'ipam']
-
-    standard_fields = ['id', 'device', 'name', 'connected_console']
-
-    nested_fields = ['id', 'device', 'name']
-
-    def test_get_list(self, endpoint='/{}api/dcim/devices/9/console-server-ports/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for console_port in content:
-            self.assertEqual(
-                sorted(console_port.keys()),
-                sorted(self.standard_fields),
-            )
-            self.assertEqual(
-                sorted(console_port.get('device')),
-                sorted(DeviceTest.nested_fields),
-            )
-
-
-class ConsolePortsTest(APITestCase):
-    fixtures = ['dcim', 'ipam']
-
-    standard_fields = ['id', 'device', 'name', 'cs_port', 'connection_status']
-
-    nested_fields = ['id', 'device', 'name']
-
-    def test_get_list(self, endpoint='/{}api/dcim/devices/1/console-ports/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for console_port in content:
-            self.assertEqual(
-                sorted(console_port.keys()),
-                sorted(self.standard_fields),
-            )
-            self.assertEqual(
-                sorted(console_port.get('device')),
-                sorted(DeviceTest.nested_fields),
-            )
-            self.assertEqual(
-                sorted(console_port.get('cs_port')),
-                sorted(ConsoleServerPortsTest.nested_fields),
-            )
-
-    def test_get_detail(self, endpoint='/{}api/dcim/console-ports/1/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(
-            sorted(content.keys()),
-            sorted(self.standard_fields),
-        )
-        self.assertEqual(
-            sorted(content.get('device')),
-            sorted(DeviceTest.nested_fields),
-        )
-
-
-class PowerPortsTest(APITestCase):
-    fixtures = ['dcim', 'ipam']
-
-    standard_fields = ['id', 'device', 'name', 'power_outlet', 'connection_status']
-
-    nested_fields = ['id', 'device', 'name']
-
-    def test_get_list(self, endpoint='/{}api/dcim/devices/1/power-ports/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for i in content:
-            self.assertEqual(
-                sorted(i.keys()),
-                sorted(self.standard_fields),
-            )
-            self.assertEqual(
-                sorted(i.get('device')),
-                sorted(DeviceTest.nested_fields),
-            )
-
-    def test_get_detail(self, endpoint='/{}api/dcim/power-ports/1/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(
-            sorted(content.keys()),
-            sorted(self.standard_fields),
-        )
-        self.assertEqual(
-            sorted(content.get('device')),
-            sorted(DeviceTest.nested_fields),
-        )
-
-
-class PowerOutletsTest(APITestCase):
-    fixtures = ['dcim', 'ipam']
-
-    standard_fields = ['id', 'device', 'name', 'connected_port']
-
-    nested_fields = ['id', 'device', 'name']
-
-    def test_get_list(self, endpoint='/{}api/dcim/devices/11/power-outlets/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for i in content:
-            self.assertEqual(
-                sorted(i.keys()),
-                sorted(self.standard_fields),
-            )
-            self.assertEqual(
-                sorted(i.get('device')),
-                sorted(DeviceTest.nested_fields),
-            )
-
-
-class InterfaceTest(APITestCase):
-    fixtures = ['dcim', 'ipam', 'extras']
-
-    standard_fields = [
-        'id',
-        'device',
-        'name',
-        'form_factor',
-        'lag',
-        'mac_address',
-        'mgmt_only',
-        'description',
-        'is_connected'
-    ]
-
-    nested_fields = ['id', 'device', 'name']
-
-    detail_fields = [
-        'id',
-        'device',
-        'name',
-        'form_factor',
-        'lag',
-        'mac_address',
-        'mgmt_only',
-        'description',
-        'is_connected',
-        'connected_interface'
-    ]
-
-    connection_fields = [
-        'id',
-        'interface_a',
-        'interface_b',
-        'connection_status',
-    ]
-
-    def test_get_list(self, endpoint='/{}api/dcim/devices/1/interfaces/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for i in content:
-            self.assertEqual(
-                sorted(i.keys()),
-                sorted(self.standard_fields),
-            )
-            self.assertEqual(
-                sorted(i.get('device')),
-                sorted(DeviceTest.nested_fields),
-            )
-
-    def test_get_detail(self, endpoint='/{}api/dcim/interfaces/1/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(
-            sorted(content.keys()),
-            sorted(self.detail_fields),
-        )
-        self.assertEqual(
-            sorted(content.get('device')),
-            sorted(DeviceTest.nested_fields),
-        )
-
-    def test_get_graph_list(self, endpoint='/{}api/dcim/interfaces/1/graphs/'.format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        for i in content:
-            self.assertEqual(
-                sorted(i.keys()),
-                sorted(SiteTest.graph_fields),
-            )
-
-    def test_get_interface_connections(self, endpoint='/{}api/dcim/interface-connections/4/'
-                                       .format(settings.BASE_PATH)):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(
-            sorted(content.keys()),
-            sorted(self.connection_fields),
-        )
-
-
-class RelatedConnectionsTest(APITestCase):
-
-    fixtures = ['dcim', 'ipam']
-
-    standard_fields = [
-        'device',
-        'console-ports',
-        'power-ports',
-        'interfaces',
-    ]
-
-    def test_get_list(self, endpoint=('/{}api/dcim/related-connections/?peer-device=test1-edge1&peer-interface=xe-0/0/3'
-                                      .format(settings.BASE_PATH))):
-        response = self.client.get(endpoint)
-        content = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
-        self.assertEqual(
-            sorted(content.keys()),
-            sorted(self.standard_fields),
-        )

+ 8 - 1
netbox/utilities/api.py

@@ -62,7 +62,14 @@ class ChoiceFieldSerializer(Field):
     Represent a ChoiceField as (value, label).
     """
     def __init__(self, choices, **kwargs):
-        self._choices = {k: v for k, v in choices}
+        self._choices = dict()
+        for k, v in choices:
+            # Unpack grouped choices
+            if type(v) in [list, tuple]:
+                for k2, v2 in v:
+                    self._choices[k2] = v2
+            else:
+                self._choices[k] = v
         super(ChoiceFieldSerializer, self).__init__(**kwargs)
 
     def to_representation(self, obj):