From e5b55357ceb0bd9c549564122f4b19baa0ddf011 Mon Sep 17 00:00:00 2001 From: John-Magne Bredal Date: Fri, 24 Aug 2018 11:09:40 +0200 Subject: [PATCH] Make vlan and prefix endpoints writable (Fixes #1758) --- python/nav/web/api/v1/serializers.py | 46 +++++++++++++++++ python/nav/web/api/v1/views.py | 4 +- tests/integration/api_test.py | 75 +++++++++++++++++++++++++++- 3 files changed, 121 insertions(+), 4 deletions(-) diff --git a/python/nav/web/api/v1/serializers.py b/python/nav/web/api/v1/serializers.py index a2ec8b154c..41e8b9bb3f 100644 --- a/python/nav/web/api/v1/serializers.py +++ b/python/nav/web/api/v1/serializers.py @@ -257,17 +257,63 @@ class Meta(object): class VlanSerializer(serializers.ModelSerializer): """Serializer for the vlan model""" + VALID_NET_TYPES = ["scope", "reserved"] + class Meta(object): model = manage.Vlan fields = '__all__' + def validate_net_type(self, value): + """Validate net_type + + :type value: nav.models.manage.NetType + """ + if value.id not in VlanSerializer.VALID_NET_TYPES: + raise serializers.ValidationError( + "net_type must be {}".format( + ' or '.join(VlanSerializer.VALID_NET_TYPES))) + return value + class PrefixSerializer(serializers.ModelSerializer): """Serializer for prefix model""" + + usages = serializers.PrimaryKeyRelatedField( + many=True, read_only=False, required=False, + queryset=manage.Usage.objects.all()) + class Meta(object): model = manage.Prefix fields = '__all__' + def update(self, instance, validated_data): + if 'usages' in validated_data: + new_usages = set(u.id for u in validated_data.pop('usages')) + current_usages = set(u.id for u in instance.usages.all()) + to_add = new_usages - current_usages + to_delete = current_usages - new_usages + + for usage in to_add: + manage.PrefixUsage( + prefix=instance, + usage=manage.Usage.objects.get(pk=usage) + ).save() + + manage.PrefixUsage.objects.filter( + prefix=instance, usage__in=list(to_delete)).delete() + return super(PrefixSerializer, self).update(instance, validated_data) + + def create(self, validated_data): + usages = validated_data.pop('usages', []) + instance = super(PrefixSerializer, self).create(validated_data) + for usage in usages: + manage.PrefixUsage( + prefix=instance, + usage=usage + ).save() + + return instance + class PrefixUsageSerializer(serializers.Serializer): """Serializer for prefix usage queries""" diff --git a/python/nav/web/api/v1/views.py b/python/nav/web/api/v1/views.py index 74ebd80315..5bf531e95a 100644 --- a/python/nav/web/api/v1/views.py +++ b/python/nav/web/api/v1/views.py @@ -578,7 +578,7 @@ def get_queryset(self): return queryset -class VlanViewSet(NAVAPIMixin, viewsets.ReadOnlyModelViewSet): +class VlanViewSet(NAVAPIMixin, viewsets.ModelViewSet): """Lists all vlans. Search @@ -601,7 +601,7 @@ class VlanViewSet(NAVAPIMixin, viewsets.ReadOnlyModelViewSet): search_fields = ['net_ident', 'description'] -class PrefixViewSet(NAVAPIMixin, viewsets.ReadOnlyModelViewSet): +class PrefixViewSet(NAVAPIMixin, viewsets.ModelViewSet): """Lists all prefixes. Filters diff --git a/tests/integration/api_test.py b/tests/integration/api_test.py index 5832cdbf40..72375d4418 100644 --- a/tests/integration/api_test.py +++ b/tests/integration/api_test.py @@ -37,6 +37,12 @@ 'room': { 'id': 'blapp', 'location': 'mylocation' + }, + 'vlan': { + 'net_type': 'scope', + }, + 'prefix': { + 'net_address': '158.38.240.0/25' } } @@ -61,7 +67,7 @@ def test_allowed_endpoints(db, api_client, token, serializer_models, name, url): @pytest.mark.parametrize("endpoint", - ['account', 'location', 'room']) + ['account', 'location', 'room', 'vlan']) def test_delete(db, api_client, token, endpoint): create_token_endpoint(token, endpoint) response_create = create(api_client, endpoint, TEST_DATA.get(endpoint)) @@ -77,7 +83,7 @@ def test_delete(db, api_client, token, endpoint): @pytest.mark.parametrize("endpoint", - ['account', 'netbox', 'location', 'room']) + ['account', 'netbox', 'location', 'room', 'vlan']) def test_create(db, api_client, token, endpoint): create_token_endpoint(token, endpoint) response = create(api_client, endpoint, TEST_DATA.get(endpoint)) @@ -208,6 +214,69 @@ def test_delete_room_wrong_room(db, api_client, token): assert response.status_code == 404 +def test_validate_vlan(db, api_client, token): + endpoint = 'vlan' + create_token_endpoint(token, 'vlan') + testdata = dict(TEST_DATA.get(endpoint)) + testdata.update({'net_type': 'core'}) + response = create(api_client, endpoint, testdata) + + print(response) + assert response.status_code == 400 + + +def prepare_prefix_test(db, api_client, token): + token.endpoints = { + 'prefix': ENDPOINTS.get('prefix'), + 'vlan': ENDPOINTS.get('vlan') + } + token.save() + testdata = dict(TEST_DATA.get('prefix')) + + vlan_response = create(api_client, 'vlan', TEST_DATA.get('vlan')) + vlan = json.loads(vlan_response.content.decode('utf-8')) + testdata.update({'vlan': vlan.get('id')}) + return testdata + + +def test_create_prefix(db, api_client, token): + endpoint = 'prefix' + testdata = prepare_prefix_test(db, api_client, token) + response = create(api_client, endpoint, testdata) + + print(response) + assert response.status_code == 201 + + +def test_create_prefix_with_usage(db, api_client, token, serializer_models): + endpoint = 'prefix' + testdata = prepare_prefix_test(db, api_client, token) + testdata.update({ + 'usages': ['ans'] + }) + + response = create(api_client, endpoint, testdata) + json_response = json.loads(response.content.decode('utf-8')) + assert json_response.get('usages') == ['ans'] + + +def test_update_prefix_remove_usage(db, api_client, token, serializer_models): + endpoint = 'prefix' + testdata = prepare_prefix_test(db, api_client, token) + testdata.update({ + 'usages': ['ans', 'student'] + }) + response = create(api_client, endpoint, testdata) + prefix = json.loads(response.content.decode('utf-8')) + + testdata.update({ + 'usages': ['ans'] + }) + response = update(api_client, endpoint, prefix.get('id'), testdata) + json_response = json.loads(response.content.decode('utf-8')) + assert json_response.get('usages') == ['ans'] + + # Helpers def create_token_endpoint(token, name): @@ -285,3 +354,5 @@ def serializer_models(): end_time=INFINITY).save() admin = profiles.Account.objects.get(login='admin') auditlog.LogEntry.add_log_entry(admin, verb='verb', template='asd') + manage.Usage(id='ans', description='Ansatte').save() + manage.Usage(id='student', description='Studenter').save()