TheNetworkGuy / netbox-zabbix-sync

Python script to synchronise Netbox devices to Zabbix.
MIT License
109 stars 27 forks source link

Add/delete host fails with proxy contexts added #61

Closed Kage1 closed 1 month ago

Kage1 commented 1 month ago

We are testing the Netbox to Zabbix 7.0 integration and adding a host via the script using the standard snmpv2 that it defaults to works great. If I try to use a config context to assign an agent interface and/or set the host to be monitored via a proxy the script throws errors and exits.

Sometimes I get a log output but mostly I get netbox-zabbix-sync exited with code 0 with nothing else helpful.

Here's the rendered context that is being apply by a tag:

{
    "zabbix": {
        "interface_port": 10500,
        "interface_type": 1
    }
}

Rendered config then using the proxy, redacted servername

{
    "zabbix": {
        "proxy": "servername"
    }
}

Template is being applied by a custom parameter and for now is set to ICMP Ping:

Zabbix template ICMP Ping

Kage1 commented 1 month ago

When combining contexts it is rending properly with all three lines showing up under the zabbix group.

TheNetworkGuy commented 1 month ago

Thanks for creating this issue. I haven't tested the script on Zabbix 7 yet. I'll try to replicate your issue today or this week.

TheNetworkGuy commented 1 month ago

Can you post a bit more details / post the log output of the script? Can you run the script using -v to enable debugging?

Although i am able to replicate some stuff by myself. For instance i get an error when trying to set a proxy. My guess is that this comes from using the outdated library instead of zabbix-utils (see issue #52 for more details.). Especially since Zabbix 7 introduced several proxy changes.

2024-06-10 15:59:40,921 - Netbox-Zabbix-sync - ERROR - Couldn't add JunHost1, Zabbix returned ('Error -32602: Invalid params., Invalid parameter "/1/proxyid": value must be 0.', -32602).
Traceback (most recent call last):
  File "C:\Data\netbox-zabbix-sync\netbox_zabbix_sync.py", line 641, in createInZabbix
    host = self.zabbix.host.create(host=self.name,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Data\venv\Lib\site-packages\pyzabbix\api.py", line 278, in __call__
    return self._parent.do_request(self._method, args or kwargs)["result"]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Data\venv\Lib\site-packages\pyzabbix\api.py", line 249, in do_request
    raise ZabbixAPIException(
pyzabbix.api.ZabbixAPIException: ('Error -32602: Invalid params., Invalid parameter "/1/proxyid": value must be 0.', -32602)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Data\netbox-zabbix-sync\netbox_zabbix_sync.py", line 1013, in <module>
    main(args)
  File "C:\Data\netbox-zabbix-sync\netbox_zabbix_sync.py", line 211, in main
    device.createInZabbix(zabbix_groups, zabbix_templates,
  File "C:\Data\netbox-zabbix-sync\netbox_zabbix_sync.py", line 655, in createInZabbix
    raise SyncExternalError(e) from e
TypeError: exception causes must derive from BaseException

I woud like to get more information from your side as well to validate that we are hitting the same error. Even though the script could exit with exit(0), this doesn't mean that the script didn't catch any errors and log those to stderr or the sync.log file.

Kage1 commented 1 month ago

All I can get out of the container is: netbox-zabbix-sync exited with code 0

The container is not out putting any other logs. Any suggestions?

Kage1 commented 1 month ago

I finally was able to get some additional info out of it and I think I may have stumbled across another bug. I have multiple tags defined in the config and when I commented all out except the one I'm using for the proxy it finally ran with an output.

netbox-zabbix-sync  | 2024-06-10 11:28:40,073 - Netbox-Zabbix-sync - DEBUG - Found group TestGroup1 for host TestDevice.
netbox-zabbix-sync  | 2024-06-10 11:28:40,073 - Netbox-Zabbix-sync - DEBUG - Found template ICMP Ping Lab for host TestDevice.
netbox-zabbix-sync  | 2024-06-10 11:28:40,074 - Netbox-Zabbix-sync - DEBUG - Found proxy TestProxy for TestDevice.
netbox-zabbix-sync  | 2024-06-10 11:28:40,177 - Netbox-Zabbix-sync - DEBUG - Device TestDevice: hostname in-sync.
netbox-zabbix-sync  | 2024-06-10 11:28:40,177 - Netbox-Zabbix-sync - DEBUG - Device TestDevice: template ICMP Ping Lab is present in Zabbix.
netbox-zabbix-sync  | 2024-06-10 11:28:40,177 - Netbox-Zabbix-sync - DEBUG - Device TestDevice: template(s) in-sync.
netbox-zabbix-sync  | 2024-06-10 11:28:40,178 - Netbox-Zabbix-sync - DEBUG - Device TestDevice: hostgroup in-sync.
netbox-zabbix-sync  | 2024-06-10 11:28:40,178 - Netbox-Zabbix-sync - DEBUG - Device TestDevice: status in-sync.
netbox-zabbix-sync  | 2024-06-10 11:28:40,178 - Netbox-Zabbix-sync - WARNING - Device TestDevice: proxy OUT of sync.
netbox-zabbix-sync  | 2024-06-10 11:28:40,346 - Netbox-Zabbix-sync - ERROR - Zabbix returned the following error: ('Error -32602: Invalid params., Invalid parameter "/1/proxyid": value must be 0.', -32602).
netbox-zabbix-sync  | Traceback (most recent call last):
netbox-zabbix-sync  |   File "/opt/netbox-zabbix/netbox_zabbix_sync.py", line 687, in updateZabbixHost
netbox-zabbix-sync  |     self.zabbix.host.update(hostid=self.zabbix_id, **kwargs)
netbox-zabbix-sync  |   File "/usr/local/lib/python3.12/site-packages/pyzabbix/api.py", line 278, in __call__
netbox-zabbix-sync  |     return self._parent.do_request(self._method, args or kwargs)["result"]
netbox-zabbix-sync  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-zabbix-sync  |   File "/usr/local/lib/python3.12/site-packages/pyzabbix/api.py", line 249, in do_request
netbox-zabbix-sync  |     raise ZabbixAPIException(
netbox-zabbix-sync  | pyzabbix.api.ZabbixAPIException: ('Error -32602: Invalid params., Invalid parameter "/1/proxyid": value must be 0.', -32602)
netbox-zabbix-sync  |
netbox-zabbix-sync  | During handling of the above exception, another exception occurred:
netbox-zabbix-sync  |
netbox-zabbix-sync  | Traceback (most recent call last):
netbox-zabbix-sync  |   File "/opt/netbox-zabbix/netbox_zabbix_sync.py", line 1013, in <module>
netbox-zabbix-sync  |     main(args)
netbox-zabbix-sync  |   File "/opt/netbox-zabbix/netbox_zabbix_sync.py", line 207, in main
netbox-zabbix-sync  |     device.ConsistencyCheck(zabbix_groups, zabbix_templates,
netbox-zabbix-sync  |   File "/opt/netbox-zabbix/netbox_zabbix_sync.py", line 772, in ConsistencyCheck
netbox-zabbix-sync  |     self.updateZabbixHost(proxyid=self.zbxproxy)
netbox-zabbix-sync  |   File "/opt/netbox-zabbix/netbox_zabbix_sync.py", line 691, in updateZabbixHost
netbox-zabbix-sync  |     raise SyncExternalError(e) from e
netbox-zabbix-sync  | TypeError: exception causes must derive from BaseException
netbox-zabbix-sync exited with code 1

When I commented out all of the tag filters except for one I finally started getting something more than exited code 0.

# Default device filter, only get devices which have a name in Netbox:
nb_device_filter = {"name__n": "null"}
nb_device_filter = {"tag": "zpx1"}
nb_device_filter = {"tag": "za"}
nb_device_filter = {"tag": "zb"}
nb_device_filter = {"tag": "z1"}
nb_device_filter = {"tag": "z2"}
# Default device filter, only get devices which have a name in Netbox:
nb_device_filter = {"name__n": "null"}
#nb_device_filter = {"tag": "zpx1"}
nb_device_filter = {"tag": "za"}
#nb_device_filter = {"tag": "zb"}
#nb_device_filter = {"tag": "z1"}
#nb_device_filter = {"tag": "z2"}
Kage1 commented 1 month ago

As I was looking at they py code I think I see what's going on. The script is only expecting a single filter defined for nb_device_filter and the last one defined wins. In my case I was not using that tag yet on my test device so the script was doing exactly what it was told to do, since it couldn't find anything todo it exited.

My main goal was to define all of the tags that could be valid for a netbox device so I would only need to add a single tag to a device in Netbox. This would add the correct context to the device and the script would also pick it up for a sync operation.

TheNetworkGuy commented 1 month ago

Okay so a couple of things are happening here. First off you are correct that the script crashes with setting the proxy on Zabbix 7.0. I need to work on this in order to fix this. You can see this in your log: netbox-zabbix-sync | 2024-06-10 11:28:40,346 - Netbox-Zabbix-sync - ERROR - Zabbix returned the following error: ('Error -32602: Invalid params., Invalid parameter "/1/proxyid": value must be 0.', -32602). This is because of the recent changes being made with proxy logic (proxy groups / seperate proxy's) in 7.0 :) Second part is your filter. You are correct that only one variable is collected by the script. So if you want to filter using a logical OR filter you will need to set the filter to something like this: nb_device_filter = {"tag": ["zb", "zpx1", "z2"]}

If you want to test the script for its full functionallity you can either:

Kage1 commented 1 month ago

I'm good with waiting. In the mean time I can test the tag code you noted above to ensure that is working.

Side note, I did test adding a device with the agent interface (no proxy) and it worked as expected. So right now it looks like the only thing broken are the proxy connections.

TheNetworkGuy commented 1 month ago

Glad to hear! Looking forward to check if the tag filtering works as expected :)

Kage1 commented 1 month ago

So if you want to filter using a logical OR filter you will need to set the filter to something like this: nb_device_filter = {"tag": ["zb", "zpx1", "z2"]}

I tested the code to utilize multiple tags and the script exited code 0, I'm guessing it's ignoring all of the tags defined this way.

I removed the Zabbix ID from the netbox device and deleted the device from Zabbix, set the tag in the config.py to nb_device_filter = {"tag": "za"} and the script added the device to Zabbix with the agent as the monitoring interface w/o an issue and set the ID in Netbox.

TheNetworkGuy commented 1 month ago

Seems that with tag filtering the Netbox API sets the operation to a logical AND instead of OR :( More information here: https://demo.netbox.dev/static/docs/rest-api/filtering/

For example with the following filter i only get devices which have both tags applied. nb_device_filter = {"tag": ["z1", "z2"]}

Kage1 commented 1 month ago

Seems that with tag filtering the Netbox API sets the operation to a logical AND instead of OR :( More information here: https://demo.netbox.dev/static/docs/rest-api/filtering/

For example with the following filter i only get devices which have both tags applied. `nb_device_filter = {"tag": ["z1", "z2"]

I'll fall back to my original plan, I'll have a single tag that the script will look for to add it to Zabbix then have another tag(s) to define proxy/agent types. A little messy, but still doable.

Here's a Netbox thread confirming the same thing we are discussing here. If a logical "OR" is needed then it would need to be done client side. https://github.com/netbox-community/netbox/discussions/11703

TheNetworkGuy commented 1 month ago

A lot of work has been performed in order to add support for Zabbix proxies in the branch zabbix_utils. I think that i've handled this work for the most part. Only updating documentation and then its ready for main.

TheNetworkGuy commented 1 month ago

Code has been updated for Zabbix 7 proxy and proxy group support. I see that the container is not being updated yet due to some linter errors but once i've fixed those then you can pull the latest release of the container.

TheNetworkGuy commented 1 month ago

The container has been updated. Please check the readme at the section of proxy's to see the changes. Would love to hear if you face any issues or encounter any bugs.

Kage1 commented 1 month ago

Success!

Updated the context to include the agent interface, proxy and proxy_group config. Both compiled in netbox successfully. I did originally miss the comma between the proxy and proxy_group statement causing netbox to say invalid JSON, but that was quickly fixed.

{
    "zabbix": {
        "interface_port": 10500,
        "interface_type": 1,
        "proxy": "myawsomeproxy",
        "proxy_group": "myawsomgroup"
    }
}

Ran the sync and the host was created and added the proxy group as the monitoring source.

netbox-zabbix-sync  | 2024-06-12 09:14:21,404 - Netbox-Zabbix-sync - DEBUG - Found group myawsomgroup for host host_to_monitor.
netbox-zabbix-sync  | 2024-06-12 09:14:21,404 - Netbox-Zabbix-sync - DEBUG - Found template ICMP Ping myawsomgroup for host host_to_monitor.
netbox-zabbix-sync  | 2024-06-12 09:14:22,360 - Netbox-Zabbix-sync - INFO - Created host host_to_monitor in Zabbix.
netbox-zabbix-sync exited with code 0

Would it be possible to add a debug log noting which proxy and/or proxy group was applied?

TheNetworkGuy commented 1 month ago

Good suggestion, i think this was already present before i made a new function for the proxy setup. And if not, we already have this type of logging for hostgroups and templates. I've updated the logging and properly formatted a lot of other logs so that the style matches.

TheNetworkGuy commented 1 month ago

Feel free to re-open this issue but for now i'll close it since the proxy config is working and debugging has been properly optimized as well.