Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Netbox ansible custom_fields are not idempotent #805

Closed
DevFinGitGovSecChatOps opened this issue Jun 24, 2022 · 9 comments
Closed
Labels
bug Something isn't working status: accepted

Comments

@DevFinGitGovSecChatOps
Copy link

DevFinGitGovSecChatOps commented Jun 24, 2022

Ansible NetBox Collection version

3.7.1

Ansible version

ansible [core 2.13.1]
  config file = None
  configured module search path = ['/home/user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /builds/<redacted-path>/.ve/lib64/python3.8/site-packages/ansible
  ansible collection location = /home/user/.ansible/collections:/usr/share/ansible/collections
  executable location = /builds/<redacted-path>/.ve/bin/ansible
  python version = 3.8.13 (default, Apr  5 2022, 17:15:15) [GCC 9.1.1 20190605 (Red Hat 9.1.1-2)]
  jinja version = 3.1.2
  libyaml = True

NetBox version

  • v3.2.0
  • v3.2.5

Python version

3.8

Steps to Reproduce

ansible-playbook main.playbook --diff

Note:
cf1 and cf2 custom_fields are defined in netbox but not used in this run

- name: "Test SoT stuff"
  connection: local
  hosts: localhost
  gather_facts: false
  tasks:
    - name: "Setup prefix"
      netbox.netbox.netbox_prefix:
        netbox_url: "{{ netbox_url }}"
        netbox_token: "{{ netbox_token }}"
        data:
          prefix: "1.2.3.0/24"
          status: "Active"
          custom_fields:
            cf3: "something"
            cf4: "something_else"

Expected Behavior

When running a playbook with custom_fields, I would expect the modules to be idempotent.
In this example I am defining two custom_fields in a prefix that has four custom_fields defined in netbox.

If nothing changed, I would expect the diff to be unchanged.

TASK [Setup prefix] ************************************************************
ok: [localhost] => (item={'prefix': '1.2.3.0/24', 'status': 'Active', 'custom_fields': {'cf3': 'something', 'cf4': 'something_else'}})
PLAY RECAP *********************************************************************

Observed Behavior

However, instead of returning an "OK" Status, the run suggests there have been changes.

TASK [Setup prefix] ************************************************************
--- before
+++ after
@@ -1,5 +1,3 @@
custom_fields:
-  cf1: null
-  cf2: null
   cf3: something
   cf4: something_else
changed: [localhost] => (item={'prefix': '1.2.3.0/24', 'status': 'Active', 'custom_fields': {'cf3': 'something', 'cf4': 'something_else'}})
PLAY RECAP *********************************************************************

This is suboptimal, since I want to run a pipeline that checks for changes in netbox. It is hard to focus on what really changed, since the custom_fields suggest a change where none happened.

@DevFinGitGovSecChatOps DevFinGitGovSecChatOps added the bug Something isn't working label Jun 24, 2022
@DevFinGitGovSecChatOps
Copy link
Author

Also a change is logged without a diff. This shouldn't be triggered as well I think.

grafik

@DevFinGitGovSecChatOps
Copy link
Author

DevFinGitGovSecChatOps commented Jun 24, 2022

Another observered behaviour:
When I change one value of a custom_field of an object, the changelog will show in the diff, that all of them changed.

In this example I only changed cf3

grafik

@sc68cal
Copy link
Contributor

sc68cal commented Jun 27, 2022

I can also +1 this bug, there are custom fields that I set in interfaces and it results in Ansible marking it as a changed item.

@rodvand
Copy link
Contributor

rodvand commented Jun 27, 2022

I think there's another issue for this as well. Not sure how to tackle it - any suggestions on logic to handle this?

@sc68cal
Copy link
Contributor

sc68cal commented Jun 27, 2022

Another observered behaviour: When I change one value of a custom_field of an object, the changelog will show in the diff, that all of them changed.

In this example I only changed cf3

I think this behavior is correct, since custom_fields is a dictionary, and you changed one of the key/value pairs inside it, so the whole dictionary did change.

@DevFinGitGovSecChatOps
Copy link
Author

Another observered behaviour: When I change one value of a custom_field of an object, the changelog will show in the diff, that all of them changed.
In this example I only changed cf3

I think this behavior is correct, since custom_fields is a dictionary, and you changed one of the key/value pairs inside it, so the whole dictionary did change.

I'd argue that the values outside of custom_fields are also dicts, yet they get validated correctly as seen in this example:
grafik

The difference seems to be that in the module fields the diff will check all key-value pairs seperately but in the custom_fields it only checks for any change in the dict.

Might a solution be to iterate through the values in a custom_field when looking for changed values instead of all custom_fields as a whole?

@sc68cal
Copy link
Contributor

sc68cal commented Jun 29, 2022

I'd argue that the values outside of custom_fields are also dicts,

Unfortunately, that is not correct. Fields like description are CharFields while CustomFields are much more complex.

@DevFinGitGovSecChatOps
Copy link
Author

DevFinGitGovSecChatOps commented Jul 1, 2022

Sorry, I didn't express myself correctly. What I ment to say is (maybe I'm wrong?) that the content of data: is a dict:

data:
  prefix: "1.2.3.0/24"
  status: "Active"
  description: "Something"

And the custom_fields are a dict in a dict:

data:
  prefix: "1.2.3.0/24"
  status: "Active"
  description: "Something"
  custom_fields:
    cf3: "something"
    cf4: "something_else"

I wanted to say that there already seems to be a mechanism to enumerate through a dict and correctly check for diffs. Maybe that mechanism can be applied to enumerate through the values of the custom_fields (dict in dict) as well?

@cfiehe
Copy link
Contributor

cfiehe commented Oct 10, 2022

I have created a pull request that should fix the issue described here. The main problem is, that fields, which are unset, are compared with the target state, which does not contain those fields. You can see the issue here:

TASK [netbox : vm | Ensure virtual machine 'Test Virtual Machine'] *************
--- before
+++ after
@@ -1,48 +1,9 @@
 {
     "custom_fields": {
-        "VM_NetworkAdapter_1": null,
-        "VM_NetworkAdapter_1_IP": null,
-        "VM_NetworkAdapter_2": null,
-        "VM_NetworkAdapter_2_IP": null,
-        "VM_NetworkAdapter_3": null,
-        "VM_NetworkAdapter_3_IP": null,
-        "VM_NetworkAdapter_4": null,
-        "VM_NetworkAdapter_4_IP": null,
-        "VM_NetworkAdapter_5": null,
-        "VM_NetworkAdapter_5_IP": null,
-        "VM_application": null,
-        "VM_costcentre": null,
-        "VM_costunit": null,
         "VM_critical": true,
-        "VM_folder": null,
-        "VM_guestosfullname": null,
         "VM_ha_type": "self",
-        "VM_id": null,
-        "VM_impact": null,
-        "VM_licence": null,
-        "VM_orga": null,
-        "VM_owner": "LOC",
         "VM_priority": "ASAP",
-        "VM_prodtype": "Prod",
-        "VM_site": null,
-        "VM_siteaffinity": null,
-        "VM_sla": "99.50%",
-        "VM_team": null,
-        "VM_uuid": null,
-        "VMupdater_add": null,
-        "VMupdater_off": null,
-        "VMupdater_upd": null,
-        "monitoring_hostgroups": null,
-        "monitoring_notification_mail": null,
-        "monitoring_notification_mattermost": null,
-        "monitoring_notification_sms": null,
-        "monitoring_notification_ticket": null,
-        "monitoring_serviceset_app_vm": null,
-        "monitoring_serviceset_app_vm_priority": null,
-        "monitoring_serviceset_os_vm": null,
-        "monitoring_serviceset_os_vm_priority": null,
-        "monitoring_zone": null,
-        "vm_osversion": null
+        "VM_prodtype": "Prod"
     }
 }

We have only one change here VM_prodtype: Prod, but Ansible says that all unset values of the current state have not equivalent counterpart in the target state and must be removed. That is actually true, so the idea is only to include values in the current state which have no null value. In this case we compare values that have a "real" value either in the current state or in the target state and the issue is gone.

With the fix we get only this:

TASK [netbox : vm | Ensure virtual machine 'Test Virtual Machine'] *************
--- before
+++ after
@@ -5,7 +5,7 @@
         "VM_ha_type": "self",
         "VM_owner": "LOC",
         "VM_priority": "ASAP",
+        "VM_prodtype": "Prod",
         "VM_sla": "99.50%",
     }

And all following runs give you:

TASK [netbox : vm | Ensure virtual machine 'Test Virtual Machine'] *************
ok: [localhost]

@rodvand rodvand closed this as completed Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working status: accepted
Projects
None yet
Development

No branches or pull requests

4 participants