peteeckel / netbox-plugin-dns

NetBox DNS is a NetBox plugin for managing DNS data.
https://pypi.org/project/netbox-plugin-dns
MIT License
197 stars 17 forks source link

HELP: import netbox-dns custom fields from ansible #244

Closed shakin89 closed 6 months ago

shakin89 commented 6 months ago

Didn't want to open a new issue because this is help wanted question. I would like to import netbox-dns custom fields from ansible. This is my variable dict

ip_addresses:
  - address: 172.30.10.5/32
    assigned_object:
      device: pve01 
      name: eth0
    dns_name: pve01.mgmt.mine.xxx
    custom_fields:
      ipaddress_dns_record_name: pve01
      ipaddress_dns_zone_id: 
        name: mgmt.mine.xxx

but when i use netbox ip_address module i always get an error. This is my task

- name: Assign IP Addresses
  netbox.netbox.netbox_ip_address:
    netbox_url: "{{ vault_netbox_api }}"
    netbox_token: "{{ vault_netbox_token }}"
    data: "{{ ip }}"
    state: present
  loop: "{{ ip_addresses }}"
  loop_control:
    loop_var: "ip"
    label: "{{ ip.assigned_object.device }} - {{ ip.assigned_object.name }} => {{ ip.address }}"

But i get always this error:

The full traceback is:
Traceback (most recent call last):
  File "/opt/semaphore/.ansible/tmp/ansible-tmp-1713634456.5706835-1574565-155929368590099/AnsiballZ_netbox_ip_address.py", line 107, in <module>
    _ansiballz_main()
  File "/opt/semaphore/.ansible/tmp/ansible-tmp-1713634456.5706835-1574565-155929368590099/AnsiballZ_netbox_ip_address.py", line 99, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/opt/semaphore/.ansible/tmp/ansible-tmp-1713634456.5706835-1574565-155929368590099/AnsiballZ_netbox_ip_address.py", line 47, in invoke_module
    runpy.run_module(mod_name='ansible_collections.netbox.netbox.plugins.modules.netbox_ip_address', init_globals=dict(_module_fqn='ansible_collections.netbox.netbox.plugins.modules.netbox_ip_address', _modlib_path=modlib_path),
  File "<frozen runpy>", line 226, in run_module
  File "<frozen runpy>", line 98, in _run_module_code
  File "<frozen runpy>", line 88, in _run_code
  File "/tmp/ansible_netbox.netbox.netbox_ip_address_payload_9m9uuikr/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/modules/netbox_ip_address.py", line 372, in <module>
  File "/tmp/ansible_netbox.netbox.netbox_ip_address_payload_9m9uuikr/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/modules/netbox_ip_address.py", line 368, in main
  File "/tmp/ansible_netbox.netbox.netbox_ip_address_payload_9m9uuikr/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_ipam.py", line 231, in run
  File "/tmp/ansible_netbox.netbox.netbox_ip_address_payload_9m9uuikr/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_ipam.py", line 44, in _handle_state_new_present
  File "/tmp/ansible_netbox.netbox.netbox_ip_address_payload_9m9uuikr/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_utils.py", line 1481, in _ensure_object_exists
  File "/tmp/ansible_netbox.netbox.netbox_ip_address_payload_9m9uuikr/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_utils.py", line 1460, in _update_netbox_object
  File "/usr/lib/python3/dist-packages/pynetbox/core/response.py", line 587, in update
    return self.save()
           ^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pynetbox/core/response.py", line 551, in save
    updates = self.updates()
              ^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pynetbox/core/response.py", line 528, in updates
    diff = self._diff()
           ^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pynetbox/core/response.py", line 504, in _diff
    current = Hashabledict({fmt_dict(k, v) for k, v in self.serialize().items()})
                                                       ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pynetbox/core/response.py", line 479, in serialize
    ret[i] = flatten_custom(current_val)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pynetbox/core/response.py", line 61, in flatten_custom
    return {
           ^
  File "/usr/lib/python3/dist-packages/pynetbox/core/response.py", line 62, in <dictcomp>
    k: v if not isinstance(v, dict) else v["value"] for k, v in custom_dict.items()
                                         ~^^^^^^^^^
KeyError: 'value'
failed: [localhost] (item=pve02 - eth0 => 172.30.10.6/32) => {
    "ansible_loop_var": "ip",
    "changed": false,
    "ip": {
        "address": "172.30.10.6/32",
        "assigned_object": {
            "device": "pve02",
            "name": "eth0"
        },
        "custom_fields": {
            "ipaddress_dns_record_name": "pve02",
            "ipaddress_dns_zone_id": {
                "name": "mgmt.mine.xx"
            }
        },
        "dns_name": "pve02.mgmt.mine.xx"
    },
    "module_stderr": "Traceback (most recent call last):\n  File \"/opt/semaphore/.ansible/tmp/ansible-tmp-1713634456.5706835-1574565-155929368590099/AnsiballZ_netbox_ip_address.py\", line 107, in <module>\n    _ansiballz_main()\n  File \"/opt/semaphore/.ansible/tmp/ansible-tmp-1713634456.5706835-1574565-155929368590099/AnsiballZ_netbox_ip_address.py\", line 99, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/opt/semaphore/.ansible/tmp/ansible-tmp-1713634456.5706835-1574565-155929368590099/AnsiballZ_netbox_ip_address.py\", line 47, in invoke_module\n    runpy.run_module(mod_name='ansible_collections.netbox.netbox.plugins.modules.netbox_ip_address', init_globals=dict(_module_fqn='ansible_collections.netbox.netbox.plugins.modules.netbox_ip_address', _modlib_path=modlib_path),\n  File \"<frozen runpy>\", line 226, in run_module\n  File \"<frozen runpy>\", line 98, in _run_module_code\n  File \"<frozen runpy>\", line 88, in _run_code\n  File \"/tmp/ansible_netbox.netbox.netbox_ip_address_payload_9m9uuikr/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/modules/netbox_ip_address.py\", line 372, in <module>\n  File \"/tmp/ansible_netbox.netbox.netbox_ip_address_payload_9m9uuikr/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/modules/netbox_ip_address.py\", line 368, in main\n  File \"/tmp/ansible_netbox.netbox.netbox_ip_address_payload_9m9uuikr/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_ipam.py\", line 231, in run\n  File \"/tmp/ansible_netbox.netbox.netbox_ip_address_payload_9m9uuikr/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_ipam.py\", line 44, in _handle_state_new_present\n  File \"/tmp/ansible_netbox.netbox.netbox_ip_address_payload_9m9uuikr/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_utils.py\", line 1481, in _ensure_object_exists\n  File \"/tmp/ansible_netbox.netbox.netbox_ip_address_payload_9m9uuikr/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_utils.py\", line 1460, in _update_netbox_object\n  File \"/usr/lib/python3/dist-packages/pynetbox/core/response.py\", line 587, in update\n    return self.save()\n           ^^^^^^^^^^^\n  File \"/usr/lib/python3/dist-packages/pynetbox/core/response.py\", line 551, in save\n    updates = self.updates()\n              ^^^^^^^^^^^^^^\n  File \"/usr/lib/python3/dist-packages/pynetbox/core/response.py\", line 528, in updates\n    diff = self._diff()\n           ^^^^^^^^^^^^\n  File \"/usr/lib/python3/dist-packages/pynetbox/core/response.py\", line 504, in _diff\n    current = Hashabledict({fmt_dict(k, v) for k, v in self.serialize().items()})\n                                                       ^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3/dist-packages/pynetbox/core/response.py\", line 479, in serialize\n    ret[i] = flatten_custom(current_val)\n             ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3/dist-packages/pynetbox/core/response.py\", line 61, in flatten_custom\n    return {\n           ^\n  File \"/usr/lib/python3/dist-packages/pynetbox/core/response.py\", line 62, in <dictcomp>\n    k: v if not isinstance(v, dict) else v[\"value\"] for k, v in custom_dict.items()\n                                         ~^^^^^^^^^\nKeyError: 'value'\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
    "rc": 1
}

I do not understand how i should express the dns_zone_id field. If i write it in this way

ip_addresses:
  - address: 172.30.10.5/32
    assigned_object:
      device: pve01 
      name: eth0
    dns_name: pve01.mgmt.mine.xxx
    custom_fields:
      ipaddress_dns_record_name: pve01
      ipaddress_dns_zone_id: 
        - name: mgmt.mine.xxx

i get

The full traceback is:
Traceback (most recent call last):
  File "/opt/semaphore/.ansible/tmp/ansible-tmp-1713635105.1304758-1576907-6834066123150/AnsiballZ_netbox_ip_address.py", line 107, in <module>
    _ansiballz_main()
  File "/opt/semaphore/.ansible/tmp/ansible-tmp-1713635105.1304758-1576907-6834066123150/AnsiballZ_netbox_ip_address.py", line 99, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/opt/semaphore/.ansible/tmp/ansible-tmp-1713635105.1304758-1576907-6834066123150/AnsiballZ_netbox_ip_address.py", line 47, in invoke_module
    runpy.run_module(mod_name='ansible_collections.netbox.netbox.plugins.modules.netbox_ip_address', init_globals=dict(_module_fqn='ansible_collections.netbox.netbox.plugins.modules.netbox_ip_address', _modlib_path=modlib_path),
  File "<frozen runpy>", line 226, in run_module
  File "<frozen runpy>", line 98, in _run_module_code
  File "<frozen runpy>", line 88, in _run_code
  File "/tmp/ansible_netbox.netbox.netbox_ip_address_payload_rli4fhr6/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/modules/netbox_ip_address.py", line 372, in <module>
  File "/tmp/ansible_netbox.netbox.netbox_ip_address_payload_rli4fhr6/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/modules/netbox_ip_address.py", line 368, in main
  File "/tmp/ansible_netbox.netbox.netbox_ip_address_payload_rli4fhr6/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_ipam.py", line 231, in run
  File "/tmp/ansible_netbox.netbox.netbox_ip_address_payload_rli4fhr6/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_ipam.py", line 44, in _handle_state_new_present
  File "/tmp/ansible_netbox.netbox.netbox_ip_address_payload_rli4fhr6/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_utils.py", line 1481, in _ensure_object_exists
  File "/tmp/ansible_netbox.netbox.netbox_ip_address_payload_rli4fhr6/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_utils.py", line 1460, in _update_netbox_object
  File "/usr/lib/python3/dist-packages/pynetbox/core/response.py", line 587, in update
    return self.save()
           ^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pynetbox/core/response.py", line 560, in save
    if req.patch(updates):
       ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pynetbox/core/query.py", line 424, in patch
    return self._make_call(verb="patch", data=data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/pynetbox/core/query.py", line 284, in _make_call
    raise RequestError(req)
pynetbox.core.query.RequestError: The request failed with code 400 Bad Request: {'custom_fields': ["Unknown related object(s): [{'name': 'mgmt.mine.xxx'}]"]}
failed: [localhost] (item=pve01 - eth0 => 172.30.10.5/32) => {
    "ansible_loop_var": "ip",
    "changed": false,
    "ip": {
        "address": "172.30.10.5/32",
        "assigned_object": {
            "device": "pve01",
            "name": "eth0"
        },
        "custom_fields": {
            "ipaddress_dns_record_name": "pve01",
            "ipaddress_dns_zone_id": [
                {
                    "name": "mgmt.mine.xxx"
                }
            ]
        },
        "dns_name": "pve01.mgmt.mine.xxx"
    },
    "module_stderr": "Traceback (most recent call last):\n  File \"/opt/semaphore/.ansible/tmp/ansible-tmp-1713635105.1304758-1576907-6834066123150/AnsiballZ_netbox_ip_address.py\", line 107, in <module>\n    _ansiballz_main()\n  File \"/opt/semaphore/.ansible/tmp/ansible-tmp-1713635105.1304758-1576907-6834066123150/AnsiballZ_netbox_ip_address.py\", line 99, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/opt/semaphore/.ansible/tmp/ansible-tmp-1713635105.1304758-1576907-6834066123150/AnsiballZ_netbox_ip_address.py\", line 47, in invoke_module\n    runpy.run_module(mod_name='ansible_collections.netbox.netbox.plugins.modules.netbox_ip_address', init_globals=dict(_module_fqn='ansible_collections.netbox.netbox.plugins.modules.netbox_ip_address', _modlib_path=modlib_path),\n  File \"<frozen runpy>\", line 226, in run_module\n  File \"<frozen runpy>\", line 98, in _run_module_code\n  File \"<frozen runpy>\", line 88, in _run_code\n  File \"/tmp/ansible_netbox.netbox.netbox_ip_address_payload_rli4fhr6/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/modules/netbox_ip_address.py\", line 372, in <module>\n  File \"/tmp/ansible_netbox.netbox.netbox_ip_address_payload_rli4fhr6/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/modules/netbox_ip_address.py\", line 368, in main\n  File \"/tmp/ansible_netbox.netbox.netbox_ip_address_payload_rli4fhr6/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_ipam.py\", line 231, in run\n  File \"/tmp/ansible_netbox.netbox.netbox_ip_address_payload_rli4fhr6/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_ipam.py\", line 44, in _handle_state_new_present\n  File \"/tmp/ansible_netbox.netbox.netbox_ip_address_payload_rli4fhr6/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_utils.py\", line 1481, in _ensure_object_exists\n  File \"/tmp/ansible_netbox.netbox.netbox_ip_address_payload_rli4fhr6/ansible_netbox.netbox.netbox_ip_address_payload.zip/ansible_collections/netbox/netbox/plugins/module_utils/netbox_utils.py\", line 1460, in _update_netbox_object\n  File \"/usr/lib/python3/dist-packages/pynetbox/core/response.py\", line 587, in update\n    return self.save()\n           ^^^^^^^^^^^\n  File \"/usr/lib/python3/dist-packages/pynetbox/core/response.py\", line 560, in save\n    if req.patch(updates):\n       ^^^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3/dist-packages/pynetbox/core/query.py\", line 424, in patch\n    return self._make_call(verb=\"patch\", data=data)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3/dist-packages/pynetbox/core/query.py\", line 284, in _make_call\n    raise RequestError(req)\npynetbox.core.query.RequestError: The request failed with code 400 Bad Request: {'custom_fields': [\"Unknown related object(s): [{'name': 'mgmt.mine.xxx'}]\"]}\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
    "rc": 1
}

Is it only allowed to have numerical id? Thanks in advance for any help. I appreciate your great work.

peteeckel commented 6 months ago

I'm afraid that's a limitation of custom fields in NetBox.

A NetBox custom field referencing an object expects that object's id as its value. There does not seem a mechanism to refer to a different attribute instead.

So you would need to pull a list of NetBox DNS zones in Ansible at the beginning of the code section and store that list (or dictionary, keyed by name) in a variable. Then you could use indirect addressing to refer to the id.