netbox-community / netbox

The premier source of truth powering network automation. Open source under Apache 2. Try NetBox Cloud free: https://netboxlabs.com/free-netbox-cloud/
http://netboxlabs.com/oss/netbox/
Apache License 2.0
15.76k stars 2.54k forks source link

Adding Interface vie API wont accept null values for not required fields after update to 3.5.1 #12533

Closed LoSaMe closed 1 year ago

LoSaMe commented 1 year ago

NetBox version

v3.5.1

Python version

3.8

Steps to Reproduce

We utilize a custom script to add new devices to NetBox, which collects necessary information and creates corresponding objects via the NetBox API. However, we encountered an issue after upgrading our instance from version 3.4.1 to 3.5.1 (worked before). Below is an example Powershell code that is responsible for creating interfaces.

$uri = "https://XXX/api/dcim/interfaces/"
$Token = "XXX"
$device = 377

# disable SSL check
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
    public bool CheckValidationResult(
        ServicePoint srvPoint, X509Certificate certificate,
        WebRequest request, int certificateProblem) {
        return true;
    }
}
"@

$Post = @{
    Uri             = $uri
    Method          = "POST"
    Headers         = @{
        Authorization = 'Token ' + $Token
    }
    UseBasicParsing = $true
    ContentType     = 'application/json'
    body            = @{
        display                       = "test"
        device                        = $device 
        vdcs                          = @()
        module                        = $null
        name                          = "test"
        label                         = ""
        type                          = "virtual"
        enabled                       = $true
        parent                        = $null
        bridge                        = $null
        lag                           = $null
        mtu                           = $null
        mac_address                   = $null
        speed                         = $null
        duplex                        = $null
        wwn                           = $null
        mgmt_only                     = $false
        description                   = "test"
        mode                          = $null
        rf_role                       = $null
        rf_channel                    = $null
        poe_mode                      = $null
        poe_type                      = $null
        rf_channel_frequency          = $null
        rf_channel_width              = $null
        tx_power                      = $null
        untagged_vlan                 = $null
        tagged_vlans                  = @()
        mark_connected                = $false
        cable                         = $null
        cable_end                     = ""
        wireless_link                 = $null
        link_peers                    = @()
        link_peers_type               = $null
        wireless_lans                 = @()
        vrf                           = $null
        l2vpn_termination             = $null
        connected_endpoints           = $null
        connected_endpoints_type      = $null
        connected_endpoints_reachable = $null
        tags                          = @()
        custom_fields                 = @{}
    } | ConvertTo-Json
}
$response = Invoke-WebRequest @Post

Same results with a Python script:

import requests
import json
from requests.packages.urllib3.exceptions import InsecureRequestWarning

# disable SSL check
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

uri = "https://XXX/api/dcim/interfaces/"
Token = "XXX"
device = 377

headers = {
    "Authorization": f"Token {Token}",
    "Content-Type": "application/json",
}

body = {
    "display": "test",
    "device": device,
    "vdcs": [],
    "module": None,
    "name": "test",
    "label": "",
    "type": "virtual",
    "enabled": True,
    "parent": None,
    "bridge": None,
    "lag": None,
    "mtu": None,
    "mac_address": None,
    "speed": None,
    "duplex": None,
    "wwn": None,
    "mgmt_only": False,
    "description": "test",
    "mode": None,
    "rf_role": None,
    "rf_channel": None,
    "poe_mode": None,
    "poe_type": None,
    "rf_channel_frequency": None,
    "rf_channel_width": None,
    "tx_power": None,
    "untagged_vlan": None,
    "tagged_vlans": [],
    "mark_connected": False,
    "cable": None,
    "cable_end": "",
    "wireless_link": None,
    "link_peers": [],
    "link_peers_type": None,
    "wireless_lans": [],
    "vrf": None,
    "l2vpn_termination": None,
    "connected_endpoints": None,
    "connected_endpoints_type": None,
    "connected_endpoints_reachable": None,
    "tags": [],
    "custom_fields": {},
}

response = requests.post(
    url=uri,
    headers=headers,
    data=json.dumps(body),
    verify=False,
)

# To access the response content, use response.content
# To access the response status code, use response.status_code
# To access the response headers, use response.headers

Expected Behavior

Add the Interface as defined.

Observed Behavior

The following error occurs when the script attempts to add a interface: {"wnn": ["This field may not be null.""]}

If a default value for "wwn" such as "0000000000000000" is added to the script, then the script will output:

{"error": "null value in column \"mode\" violates not-null 
constraint\nDETAIL:  Failing row contains (2023-05-09 14:46:51.778691+00, 
2023-05-09 14:46:51.778711+00, {\"Position\": null}, 2449, LAN-Team1, , , f, 
t, null, null, null, 9999999999999999LAN-Team000001............, lag, f, null, 
null, 442, null, null, null, 00:00:00:00:00:00:00:00, null, null, null, null, 
null, null, null, null, null, null, 50000000, null, null, ).\n", "exception": 
"IntegrityError", "netbox_version": "3.5.1", "python_version": "3.8.10"}

Adding an interface via GUI is not a problem (only the required fields are filled)

jsenecal commented 1 year ago

Hi @LoSaMe what happens if you simply omit the fields in your POST ?

LoSaMe commented 1 year ago

Thank you for the fast answer @jsenecal . Omitting all null values resolved the problem. Simple but effective :)

tomasz-c commented 1 year ago

It seems to me that this is not the final solution to this problem. For example, if you want to change 802.1Q Mode from Access to null using the API, this is currently not possible.

jcollie commented 1 year ago

Leaving out values isn't a solution for people that don't directly control what's sent in the HTTP request, like users of pynetbox.

jeremystretch commented 1 year ago

@tomasz-c @jcollie would either of you like to volunteer for this one?

DanSheps commented 1 year ago

I don't know if this is a bug. mode does not take null, it does take "" however.

Likely some of the other columns are the same as well.

srfwx commented 1 year ago

I ran into the same issue after upgrading I was previously setting interface mode to None (which used to work). Using "" instead does indeed solves the issue.