diff --git a/.jenkins b/.jenkins index b0eaa86bc..ff8ec98af 100644 --- a/.jenkins +++ b/.jenkins @@ -78,7 +78,7 @@ spec: """ } // finally, kick off tox to run the entire test suite - sh 'tox' + sh 'tox -v' } } } diff --git a/docker-compose.yml b/docker-compose.yml index 788cce251..b6a9c5611 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,10 +49,14 @@ services: environment: POSTGRES_PASSWORD: "12345" POSTGRES_DB: netbox + ports: + - 5432:5432 volumes: - netbox-postgres-data:/var/lib/postgresql/data redis: image: redis:5-alpine + ports: + - 6379:6379 volumes: - netbox-redis-data:/data redis-cache: diff --git a/netbox/dcim/migrations/0123_region_custom_field_data.py b/netbox/dcim/migrations/0123_region_custom_field_data.py new file mode 100644 index 000000000..38fe2ebb6 --- /dev/null +++ b/netbox/dcim/migrations/0123_region_custom_field_data.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.7 on 2021-07-14 14:28 + +import django.core.serializers.json +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0122_standardize_name_length'), + ] + + operations = [ + migrations.AddField( + model_name='region', + name='custom_field_data', + field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), + ), + ] diff --git a/netbox/dcim/models/sites.py b/netbox/dcim/models/sites.py index 923b33124..7641e8899 100644 --- a/netbox/dcim/models/sites.py +++ b/netbox/dcim/models/sites.py @@ -25,8 +25,8 @@ __all__ = ( # Regions # -@extras_features('export_templates', 'webhooks') -class Region(MPTTModel, ChangeLoggedModel): +@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks') +class Region(MPTTModel, ChangeLoggedModel, CustomFieldModel): """ Sites can be grouped within geographic Regions. """ diff --git a/netbox/extras/tests/test_filters.py b/netbox/extras/tests/test_filters.py index 7bcd83e81..2e9df3b15 100644 --- a/netbox/extras/tests/test_filters.py +++ b/netbox/extras/tests/test_filters.py @@ -38,7 +38,7 @@ class ExportTemplateTestCase(TestCase): self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_content_type(self): - params = {'content_type': ContentType.objects.get(model='site').pk} + params = {'content_type': ContentType.objects.get(model='rack').pk} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) diff --git a/netbox/users/tests/test_filters.py b/netbox/users/tests/test_filters.py index c3774927c..7130fa03e 100644 --- a/netbox/users/tests/test_filters.py +++ b/netbox/users/tests/test_filters.py @@ -189,4 +189,4 @@ class ObjectPermissionTestCase(TestCase): def test_object_types(self): object_types = ContentType.objects.filter(model__in=['site', 'rack']) params = {'object_types': [object_types[0].pk, object_types[1].pk]} - self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) diff --git a/netbox/vapor/api/serializers.py b/netbox/vapor/api/serializers.py index 523a71eb3..b72a740f3 100644 --- a/netbox/vapor/api/serializers.py +++ b/netbox/vapor/api/serializers.py @@ -8,9 +8,8 @@ from dcim.api.nested_serializers import ( NestedInterfaceSerializer, NestedCableSerializer, ) -from dcim.api.serializers import InterfaceConnectionSerializer from dcim.choices import InterfaceTypeChoices, InterfaceModeChoices -from dcim.models import Interface +from dcim.models import Interface, Cable from ipam.api.nested_serializers import NestedPrefixSerializer from ipam.models import VLAN, Prefix from tenancy.api.nested_serializers import NestedTenantGroupSerializer @@ -98,6 +97,14 @@ class NestedVLANInterfaceSerializer(WritableNestedSerializer): fields = ['id', 'url', 'device', 'name', 'cable', 'type', 'untagged_vlan', 'tagged_vlans'] +class NestedVaporCableSerializer(serializers.ModelSerializer): + url = serializers.HyperlinkedIdentityField(view_name='dcim-api:cable-detail') + + class Meta: + model = Cable + fields = ['id', 'url', 'label', 'status'] + + class CableTerminationSerializer(serializers.ModelSerializer): cable_peer_type = serializers.SerializerMethodField(read_only=True) cable_peer = serializers.SerializerMethodField(read_only=True) @@ -172,15 +179,15 @@ class InterfaceSerializer(TaggedObjectSerializer, CableTerminationSerializer, Co required=False, many=True ) - cable = NestedCableSerializer(read_only=True) + cable = NestedVaporCableSerializer(read_only=True) count_ipaddresses = serializers.IntegerField(read_only=True) class Meta: model = Interface fields = [ 'id', 'url', 'device', 'name', 'type', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only', - 'description', 'connected_endpoint_type', 'connected_endpoint', 'connected_endpoint_reachable', 'cable', 'mode', - 'untagged_vlan', 'tagged_vlans', 'tags', 'count_ipaddresses', + 'description', 'connected_endpoint_type', 'connected_endpoint', 'connected_endpoint_reachable', 'cable', + 'mode', 'untagged_vlan', 'tagged_vlans', 'tags', 'count_ipaddresses', ] ref_name = 'VaporInterfaceSerializer' diff --git a/netbox/vapor/tests/test_api.py b/netbox/vapor/tests/test_api.py deleted file mode 100644 index 838bc2ac8..000000000 --- a/netbox/vapor/tests/test_api.py +++ /dev/null @@ -1,97 +0,0 @@ -from dcim.models import ( - Cable, - Device, - DeviceRole, - DeviceType, - Interface, - Manufacturer, - Site, -) -from django.urls import reverse -from rest_framework import status -from tenancy.models import Tenant as Customer -from utilities.testing import APITestCase, create_test_user - - -class VaporTestCustomers(APITestCase): - - @classmethod - def setUpTestData(cls): - cls.customer1 = Customer.objects.create(name='Test Customer 1', slug='test-customer-1') - - def test_get_a_customer(self): - """ Inspect a single customer """ - url = reverse('vapor-api:tenant-detail', kwargs={'pk': self.customer1.pk}) - response = self.client.get(url, **self.header) - self.assertEqual(response.data['name'], self.customer1.name) - - def test_get_customers(self): - """ List all customers """ - url = reverse('vapor-api:tenant-list') - response = self.client.get(url, **self.header) - self.assertEqual(response.data['results'][0]['name'], self.customer1.name) - - def test_create_customer(self): - """ Post and create a customer """ - data = { - 'name': 'Test Customer 2', - 'slug': 'test-customer-2' - } - - url = reverse('vapor-api:tenant-list') - - response = self.client.post(url, data, format='json', **self.header) - self.assertHttpStatus(response, status.HTTP_201_CREATED) - self.assertEqual(Customer.objects.count(), 2) - - custo = Customer.objects.get(pk=response.data['id']) - self.assertEqual(custo.name, data['name']) - self.assertEqual(custo.slug, data['slug']) - - -class VaporTestInterfaces(APITestCase): - - @classmethod - def setUpTestData(cls): - - cls.site1 = Site.objects.create(name='test', slug='test') - cls.manufacturer1 = Manufacturer.objects.create(name='Vapor', slug='vapor') - cls.devicetype1 = DeviceType.objects.create( - model='chamber-locker', - slug='chamber-locker', - manufacturer=cls.manufacturer1 - ) - cls.devicerole1 = DeviceRole.objects.create(name='locker', slug='locker') - cls.customer1 = Customer.objects.create(name='Test Customer 1', slug='test-customer-1') - cls.device1 = Device.objects.create( - name='network-locker', - device_role=cls.devicerole1, - device_type=cls.devicetype1, - site=cls.site1, - tenant=cls.customer1, - ) - cls.device2 = Device.objects.create( - name='network-locker2', - device_role=cls.devicerole1, - device_type=cls.devicetype1, - site=cls.site1, - ) - cls.interface1 = Interface.objects.create(name='e1', device=cls.device1) - cls.interface2 = Interface.objects.create(name='xe-0/0/0', device=cls.device2) - - cls.cable = Cable(termination_a=cls.interface1, termination_b=cls.interface2) - cls.cable.save() - - def test_get_interfaces(self): - url = reverse('vapor-api:interface-list') - response = self.client.get(url, **self.header) - self.assertEqual(response.data['count'], 2) - - def test_get_customers(self): - """ Inspect a single customers interfaces """ - base_url = reverse('vapor-api:interface-list') - query = {'customer': self.customer1.slug} - url = '{}?{}'.format(base_url, '&'.join(['{}={}'.format(k, v) for k, v in query.items()])) - response = self.client.get(url, **self.header) - self.assertEqual(response.data['count'], 1) - self.assertEqual(response.data['results'][0]['name'], self.interface1.name)