Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

soa-mname incorrectly typed as integer #117

Closed
martydingo opened this issue Dec 23, 2021 · 18 comments
Closed

soa-mname incorrectly typed as integer #117

martydingo opened this issue Dec 23, 2021 · 18 comments

Comments

@martydingo
Copy link

martydingo commented Dec 23, 2021

Hiya there,

Currently I'm writing a python script to interpolate between the PowerDNS API and the Netbox API, while troubleshooting some API call issues, looking at the Netbox API documentation, adding zones via an API call via POST specifies the following

soa_mname 
integer
title: SOA MName

The MNAME should be a string, not an integer, as per the MNAME listed in https://www.cloudflare.com/en-gb/learning/dns/dns-records/dns-soa-record/

There would be no circumstances an SOA MNAME contained within the rrset returned would ever be an integer, so this type should be changed.

@martydingo
Copy link
Author

Actually, looking through Netbox itself, it's a dropdown of nameservers, not an integer as described in the image below.

image

@peteeckel
Copy link
Contributor

I can confirm that it should be a serialised nameserver object, and actually it is for me:

zone-export

Are you using the latest version of netbox-dns (I assume you are, but there was a change a while ago that fixed exactly what you're reporting):

diff --git a/netbox_dns/api/serializers.py b/netbox_dns/api/serializers.py
index 8c71598..2efcd15 100644
--- a/netbox_dns/api/serializers.py
+++ b/netbox_dns/api/serializers.py
[...] 
 
 
@@ -59,6 +74,12 @@ class ZoneSerializer(PrimaryModelSerializer):
     nameservers = NestedNameServerSerializer(
         many=True, read_only=False, required=False, help_text="Nameservers for the zone"
     )
+    soa_mname = NestedNameServerSerializer(
+        many=False,
+        read_only=False,
+        required=False,
+        help_text="Primary nameserver for the zone",
+    )

@peteeckel
Copy link
Contributor

peteeckel commented Dec 23, 2021

Am I correct in assuming that you are trying to import zones from PowerDNS to Netbox DNS?

You need to create the NameServer object for the name server specified in soa_mname before creating a Zone object that references it. Then you can create zones that reference a nameserver by name like that (generated code):

import requests
import json

def send_request():
    try:
        response = requests.post(
            url="https://192.168.106.105/api/plugins/netbox-dns/zones/",
            headers={
                "Authorization": "Token 75512f5ac45684348ba51271c9611f3eb07aa198",
                "Content-Type": "application/json; charset=utf-8",
            },
            data=json.dumps({
                "soa_expire": "1800",
                "soa_rname": "hostmaster.example.com",
                "soa_minimum": "7200",
                "name": "zone10.example.com",
                "soa_serial_auto": True,
                "soa_mname": {
                    "name": "ns2.example.com"
                },
                "soa_retry": "3600",
                "default_ttl": "86400",
                "soa_ttl": "3600",
                "soa_refresh": "86400"
            })
        )
        print('Response HTTP Status Code: {status_code}'.format(
            status_code=response.status_code))
        print('Response HTTP Response Body: {content}'.format(
            content=response.content))
    except requests.exceptions.RequestException:
        print('HTTP Request failed')

I just tried it with the following result:

HTTP/1.1 201 Created
Date: Thu, 23 Dec 2021 13:59:43 GMT
Server: gunicorn
Content-Type: application/json
Location: http://192.168.106.105/api/plugins/netbox-dns/zones/15/
Vary: Accept,Cookie,Origin
Allow: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
API-Version: 3.1
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
X-Frame-Options: SAMEORIGIN
Content-Length: 579
Connection: close

{
    "id":15,
    "url":"http://192.168.106.105/api/plugins/netbox-dns/zones/15/",
    "name":"zone7.example.com",
    "display":"zone7.example.com",
    "nameservers":[],
    "status":"active",
    "tags":[],
    "created":"2021-12-23",
    "last_updated":"2021-12-23T13:59:43.407360Z",
    "default_ttl":86400,
    "soa_ttl":3600,
    "soa_mname":{
        "id":2,
        "url":"http://192.168.106.105/api/plugins/netbox-dns/nameservers/2/",
        "display":"ns2.example.com",
        "name":"ns2.example.com"
    },
    "soa_rname":"hostmaster.example.com",
    "soa_serial":1640267984,
    "soa_serial_auto":true,
    "soa_refresh":86400,
    "soa_retry":3600,
    "soa_expire":1800,
    "soa_minimum":7200
}

Maybe this helps.

I found, however, a restriction: Currently it isn't possible to populate the nameservers field via the API. This seems to be a restriction with the standard serialiser, so there needs to be a specific create() method to be implemented. The response from Netbox is quite useful:

{
    "error": "The `.create()` method does not support writable nested fields by default.\nWrite an explicit `.create()` method for serializer `netbox_dns.api.serializers.ZoneSerializer`, or set `read_only=True` on nested serializer fields.", 
    "exception": "AssertionError", 
    "netbox_version": "3.1.1", 
    "python_version": "3.8.6"
}

[edited out obvious nonsense :-)]

@peteeckel
Copy link
Contributor

I must admit that I don't have the slightest idea from where the Netbox API documentation tool gets the information that the soa_mname should be an integer.

The serializer has the right definition:

    soa_mname = NestedNameServerSerializer(
        many=False,
        read_only=False,
        required=False,
        help_text="Primary nameserver for the zone",
    )

The model has it as well (wouldn't work otherwise, which always is a good indication):

    soa_mname = models.ForeignKey(
        NameServer,
        related_name="zones_soa",
        verbose_name="SOA MName",
        on_delete=models.PROTECT,
        blank=False,
        null=False,
    )

At the moment I just can't figure out where that comes from. But I can assure you that it's wrong ... I'll continue to try to find it, but don't hold your breath.

@martydingo
Copy link
Author

martydingo commented Dec 24, 2021

Thanks very much for going into so much detail into this issue.

It's an odd one, with the API reporting this as an integer. Netbox API is running 3.1 according to the title of the API docs, and Netbox reports v3.1.2.

The HTML added is as so, if it helps:

<tr class="property-row required"><td><!-- react-text: 13140 -->soa_mname<!-- /react-text --><span class="star">*</span></td><td><span class="model"><span class="prop"><span class="prop-type">integer</span><span class="property primitive"><br><!-- react-text: 13148 -->title<!-- /react-text --><!-- react-text: 13149 -->: <!-- /react-text --><!-- react-text: 13150 -->SOA MName<!-- /react-text --></span></span></span></td></tr>

I'm trying to push the following:

{
    "name": "dingo.zone",
    "soa_mname": {
        "name": "b.dns.dingo.management"
    },
    "soa_rname": "admin.dingo.zone.",
    "soa_ttl": 3600,
    "soa_serial": 1627142029,
    "soa_refresh": 10800,
    "soa_retry": 3600,
    "soa_expire": 604800,
    "soa_minimum": 3600
}

To which I keep seeing:

{"soa_mname":["This field cannot be null."]}

My code has worked for importing other objects into Netbox, so I'm unsure why it keeps thinking soa_mname is null.

@martydingo
Copy link
Author

That nameserver is also already configured on Netbox

{
    "count": 2,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 1,
            "url": "http://10.84.0.2/api/plugins/netbox-dns/nameservers/1/",
            "display": "a.dns.dingo.management",
            "name": "a.dns.dingo.management",
            "tags": [],
            "created": "2021-12-21",
            "last_updated": "2021-12-21T15:27:02.308147Z"
        },
        {
            "id": 2,
            "url": "http://10.84.0.2/api/plugins/netbox-dns/nameservers/2/",
            "display": "b.dns.dingo.management",
            "name": "b.dns.dingo.management",
            "tags": [],
            "created": "2021-12-21",
            "last_updated": "2021-12-21T15:27:07.138644Z"
        }
    ]
}

@peteeckel
Copy link
Contributor

peteeckel commented Dec 24, 2021

Hi,

could there be something wrong with your error handling code? I just tried to push the same data you used to my instance, and I also get a null value error - but not on soa_mname, but on default_ttl:

{
    "name": "dingo.zone",
    "soa_mname": {
      "name": "b.dns.dingo.management"
    },
    "soa_rname": "admin.dingo.zone.",
    "soa_ttl": "3600",
    "soa_serial": "1627142029",
    "soa_refresh": "10800",
    "soa_retry": "3600",
    "soa_expire": "604800",
    "soa_minimum": "3600",
}
{
    "error": "null value in column \"default_ttl\" violates not-null constraint\nDETAIL:  Failing row contains (2021-12-24, 2021-12-24 11:26:50.229393+00, {}, 2, dingo.zone, active, null, 604800, 3600, 3, 10800, 3600, admin.dingo.zone., 1640345211, 3600, t).\n", 
    "exception": "IntegrityError", 
    "netbox_version": "3.1.1", 
    "python_version": "3.8.6"
}

Could you please try adding default_ttl as well?

@peteeckel
Copy link
Contributor

Just to be on the safe side I just retried with Netbox 3.1.2 (my test environment was still on 3.1.1). Same result - with default_ttl it works.

@martydingo
Copy link
Author

With these assortment of small differences (default_ttl being required, soa_mname reporting as an integer) I believe there's going to be an issue with my environment, it's the only thing I can explain this with. It's almost as of I have content from different versions merged in with each other.

{'name': 'home.dingo.services', 'default_ttl': 3600, 'nameservers': [{'name': 'b.dns.dingo.management'}, {'name': 'a.dns.dingo.management'}], 'soa_mname': {'name': 'a.dns.dingo.management'}, 'soa_rname': 'admin.home.dingo.services.', 'soa_ttl': 3600, 'soa_serial': 1637495034, 'soa_refresh': 10800, 'soa_retry': 3600, 'soa_expire': 604800, 'soa_minimum': 3600}
{"soa_mname":["This field cannot be null."]}

I'll continue to investigate on my end.

@martydingo
Copy link
Author

martydingo commented Dec 24, 2021

Oh yeah this is definitely most likely a layer 8 problem with myself. I added a json.dumps to the API call, and then I see

{
    "name": "home.dingo.services",
    "default_ttl": 3600,
    "nameservers": [
        {
            "name": "a.dns.dingo.management"
        },
        {
            "name": "b.dns.dingo.management"
        }
    ],
    "soa_mname": {
        "name": "b.dns.dingo.management"
    },
    "soa_rname": "admin.home.dingo.services.",
    "soa_ttl": 3600,
    "soa_serial": 1637495034,
    "soa_refresh": 10800,
    "soa_retry": 3600,
    "soa_expire": 604800,
    "soa_minimum": 3600
}
{"detail":"Unsupported media type \"\" in request."}

@peteeckel
Copy link
Contributor

The "Unsupported media type" could be a result of a missing "Content-Type: application/json" in your request - did you include that?

@peteeckel
Copy link
Contributor

Hi @martydingo, don't try to fix the issue with the incorrect documentation of the soa_mname parameter in your environment, it is most likely not related to the problem you have submitting a new zone.

I see the incorrect documentation as in a functioning environment as well, and it seems to have its cause somewhere in the Code that automatically generates the API documentation or in the way netbox-dns presents its interface.

@martydingo
Copy link
Author

Interesting, I had put this on hold as time is not-so-spare.

Happy to test any fixes.

@peteeckel
Copy link
Contributor

@martydingo: It would be great if you could try with a correct "Content-Type:" header. As far as I understand that's your real issue, while the incorrect documentation is a nuisance (I agree) but not a show-stopper.

@martydingo
Copy link
Author

Okay cool, will do and will update.

@peteeckel
Copy link
Contributor

@martydingo: Did you find the time to try it again? As I said it works for me. Can you also try with pynetbox?

@peteeckel
Copy link
Contributor

@martydingo: I just checked some other object types in NetBox (not NetBox DNS), and the incorrect classification of object types in POST requests as integer can be found there as well.

Look, for instance, at the GET request for /dcim/devices, and the corresponding POST request in the NetBox API documentation. For the GET request, you'll find a NestedDeviceRole object for the device_role field in the results of the API call. For the POST request, the object type is integer, just as with NetBox DNS-Objects (e.g. Record or Zone objects).

Screenshot 2022-08-20 at 16 28 43

Screenshot 2022-08-20 at 16 29 05

So it seems the incorrect type in the documentation is, if any, a bug in NetBox, not NetBox DNS.

@hatsat32: I suggest closing this issue as we can't do anything about it.

@peteeckel
Copy link
Contributor

With NetBox 3.5 and the new OpenAPI framework this mirepesentation in the API documentation is finally resolved:

Screenshot 2023-04-28 at 13 42 05

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants