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

zabbix_host hasn't the idempotence when a host has two interfaces or more #253

Closed
sky-joker opened this issue Nov 1, 2020 · 9 comments · Fixed by #309
Closed

zabbix_host hasn't the idempotence when a host has two interfaces or more #253

sky-joker opened this issue Nov 1, 2020 · 9 comments · Fixed by #309
Labels
bug Something isn't working module The issue or pull request is related to Zabbix module

Comments

@sky-joker
Copy link
Contributor

SUMMARY

After I executed twice a playbook that has a host with two interfaces or more, I checked the changed always occurs.

ISSUE TYPE
  • Bug Report
COMPONENT NAME

plugins/modules/zabbix_host.py

ANSIBLE VERSION
# ansible --version
ansible 2.10.2
CONFIGURATION
# ansible-config dump --only-changed
OS / ENVIRONMENT / Zabbix Version
  • Zabbix 5.0
  • zabbix-api 0.5.4
STEPS TO REPRODUCE

I created the following playbook, and I executed.

---
- name: Example playbook
  hosts: localhost
  gather_facts: no
  tasks:
    - name: Operation a zabbix host
      zabbix_host:
        server_url: "{{ zabbix_server_url }}"
        login_user: "{{ zabbix_login_user }}"
        login_password: "{{ zabbix_login_password }}"
        force: false
        host_name: example01
        host_groups:
          - Linux servers
        link_templates:
          - Template App Apache by Zabbix agent
        interfaces:
          - type: 1
            useip: 0
            main: 1
            dns: 10.1.1.1
          - type: 1
            useip: 1
            ip: 192.168.100.1
            main: 0
            dns: 10.2.1.1
        status: enabled
        state: present
EXPECTED RESULTS

The changed occurs only one time when the playbook executes two times in a row.

ACTUAL RESULTS

The changed always occurs.

@sky-joker sky-joker added bug Something isn't working module The issue or pull request is related to Zabbix module labels Nov 1, 2020
@D3DeFi
Copy link
Contributor

D3DeFi commented Nov 13, 2020

I believe this is happening because there is no elegant way on how to check if user provided interfaces match those present on the host. Right now, zabbix_host depends on the ordering being the same, maybe that have changed in Zabbix 5.0 and interfaces are not always returned in the same order?

Only alternative I was able to come up with is basically doing hash of IP + PORT or DNS + PORT, but then if one of those parameters should change for the interface we would once again need to rely on ordering. So no idea how to solve this.

Any chance this is duplicate of #244 ?

For example this does not work:

...
        interfaces:
          - type: 1
            useip: 0
            main: 1
            dns: 10.1.1.1
          - type: 1
            useip: 1
            ip: 192.168.100.1
            main: 0
            dns: 10.2.1.1

then re-runing with ordering switched

...
        interfaces:
          - type: 1
            useip: 1
            ip: 192.168.100.1
            main: 0
            dns: 10.2.1.1
          - type: 1
            useip: 0
            main: 1
            dns: 10.1.1.1

@D3DeFi
Copy link
Contributor

D3DeFi commented Dec 25, 2020

I am still not sure how to fix this and we may as well end up with documenting this as a known issue. Only reliable piece of information from hostinterface is hostinterfaceid. However, we cannot expect user to provide this as a parameter to the ansible module so our only option is to match interfaces of a given type by how are they ordered. And then also documenting that interfaces must be ordered in the same way as they are supposed to be present on the zabbix host itself.

@sky-joker
Copy link
Contributor Author

sky-joker commented Jan 2, 2021

@D3DeFi

I'm sorry for the late response.

I am still not sure how to fix this and we may as well end up with documenting this as a known issue.

I think so too.
It’s a good idea to document and respond first.

I think maybe #244 is a different issue.

@D3DeFi
Copy link
Contributor

D3DeFi commented Jan 7, 2021

Maybe not related, but we now also getting:

TASK [test_zabbix_host : test: change interface settings (remove one)] *********
fatal: [testhost]: FAILED! => {"changed": false, "msg": "Failed to update host ExampleHost: ('Error -32602: Invalid params., Host cannot have more than one default interface of the same type. while sending {\"jsonrpc\": \"2.0\", \"method\": \"hostinterface.update\", \"params\": {\"type\": 1, \"main\": 1, \"useip\": 1, \"ip\": \"10.1.1.1\", \"dns\": \"\", \"port\": \"10050\", \"details\": {}, \"interfaceid\": \"8\"}, \"auth\": \"41095ab42eb2dc666f80068c79a2ed91\", \"id\": 9}', -32602)"}

https://github.com/ansible-collections/community.zabbix/pull/305/checks?check_run_id=1661558334 For Zabbix 5.2 py37
https://github.com/ansible-collections/community.zabbix/runs/1661585811?check_suite_focus=true For Zabbix 5.0 py37
https://github.com/ansible-collections/community.zabbix/runs/1661684241?check_suite_focus=true For Zabbix 5.2 py27

This seems to happen at random (e.g. interfaces are returned in a wrong order) and our code doesn't match them, thus creating
additional main interface.

@D3DeFi
Copy link
Contributor

D3DeFi commented Jan 7, 2021

I was now able to confirm my theory in https://github.com/ansible-collections/community.zabbix/runs/1662114652
I also think that #244 is related with this issue, however have slightly different behavior due to force=false.

With the standard set of host interfaces from our integration tests:

    interfaces:
      - type: 1
        main: 1
        useip: 1
        ip: 10.1.1.1
        dns: ""
        port: "10050"
      - type: 1
        main: 0
        useip: 1
        ip: 10.1.1.1
        dns: ""
        port: "{$MACRO}"
      - type: 4
        main: 1
        useip: 1
        ip: 10.1.1.1
        dns: ""
        port: "12345"

In the failing run of tests, the resulting host interface objects was returned in the following order by Zabbix server:

                "hostinterfaces": [
                    {
                        "details": [],
                        "dns": "",
                        "hostid": "10378",
                        "interfaceid": "8",
                        "ip": "10.1.1.1",
                        "main": "0",
                        "port": "{$MACRO}",
                        "type": "1",
                        "useip": "1"
                    },
                    {
                        "details": [],
                        "dns": "",
                        "hostid": "10378",
                        "interfaceid": "9",
                        "ip": "10.1.1.1",
                        "main": "1",
                        "port": "12345",
                        "type": "4",
                        "useip": "1"
                    },
                    {
                        "details": [],
                        "dns": "",
                        "hostid": "10378",
                        "interfaceid": "7",
                        "ip": "10.1.1.1",
                        "main": "1",
                        "port": "10050",
                        "type": "1",
                        "useip": "1"
                    }
                ],

This eventually leads to a wrong pairing of user provided interface and existing interface on the host with the logic from the module:

                for interface in interfaces:
                    flag = False
                    interface_str = interface
                    for exist_interface in exist_interface_list:
                        interface_type = int(interface['type'])
                        exist_interface_type = int(exist_interface['type'])
                        if interface_type == exist_interface_type:
                            # update
                            interface_str['interfaceid'] = exist_interface['interfaceid']

Lastly, the error. It is trying to update interfaceid=8 with values form interfaceid=7 (as that is the first one in playbook):

fatal: [testhost]: FAILED! => {"changed": false, "msg": "Failed to update host ExampleHost: (u'Error -32602: Invalid params., Host cannot have more than one default interface of the same type. while sending {\"params\": {\"interfaceid\": \"8\", \"ip\": \"10.1.1.1\", \"useip\": 1, \"details\": {}, \"dns\": \"\", \"main\": 1, \"type\": 1, \"port\": \"10050\"}, \"jsonrpc\": \"2.0\", \"method\": \"hostinterface.update\", \"auth\": \"f19dcbe828cc5cf97bebd0bf4008d02c\", \"id\": 9}', -32602)"}

I am not sure whether this can be considered a bug in Zabbix API as we normally should rely on the interfaceid and not the ordering of result, but in our use case we cannot possibly ask user to provide hostinterfaceid as the suboption to the interfaces parameter.

Hence I think we should always order list of existing interfaces by interfaceid before passing it to the host.update_host function. However, I worry that I haven't think through all of the implication this change may cause, but I think it is a good compromise to expect that user defined interfaces are in the same order in every rerun of the playbook.

@sky-joker does anything comes to your mind that can get broken if we always sort existing interfaces by their IDs before using that list for comparison? PR on its way

@D3DeFi
Copy link
Contributor

D3DeFi commented Jan 7, 2021

Fix I have in mind is in the last commit in #309 - I can create separate PR if you think this is a way to go @sky-joker

@sky-joker
Copy link
Contributor Author

sky-joker commented Jan 11, 2021

I seem this is a more complex issue.
I confirmed that the changed also always occurs when a host has one interface only.
I used a playbook is below in Zabbix 4.4 and 5.0.

---
- name: Example playbook
  hosts: localhost
  gather_facts: no
  tasks:
    - name: Operation a zabbix host
      zabbix_host:
        server_url: "{{ zabbix_server_url }}"
        login_user: "{{ zabbix_login_user }}"
        login_password: "{{ zabbix_login_password }}"
        force: false
        host_name: example01
        host_groups:
          - Linux servers
        link_templates:
          - Template App Apache by Zabbix agent
        interfaces:
          - type: 1
            useip: 0
            main: 1
            dns: 10.1.1.1
        status: enabled
        state: present

Sorry, to be honest, I'm confused...
But, I think to sort the interfaces by an id makes sense.
If you do PR of the interfaces sort, I will merge it.

@D3DeFi
Copy link
Contributor

D3DeFi commented Jan 11, 2021

Honestly this doesn't make sense, with just one interface this error should not happen at all. I am as confused as you are right now.

I have repurposed #309 as a PR for this.

Edit: Also I seem to be not able to reproduce it on my side with just one interface and force=no, no matter how I try. You are able to reproduce this error 100%? Can you get me list of hostinterfaces on the host when the error occurs? E.g. what is returned by API from exist_interfaces = host._zapi.hostinterface.get({'output': 'extend', 'hostids': host_id})

Edit2: Is your 5.0 Zabbix server 5.0.7 or different version? Also my python is 3.8.5, zabbix-api 0.5.4 and community.zabbix I am running from the latest changes on main branch

@nerijus
Copy link

nerijus commented Sep 13, 2021

This issue reappeared with zabbix 5.4.4.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working module The issue or pull request is related to Zabbix module
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants