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

"arista.eos.eos_acls" idempotency is not working correctly #512

Open
ivanchakarov opened this issue Jan 25, 2024 · 3 comments · May be fixed by #564
Open

"arista.eos.eos_acls" idempotency is not working correctly #512

ivanchakarov opened this issue Jan 25, 2024 · 3 comments · May be fixed by #564

Comments

@ivanchakarov
Copy link

SUMMARY

I'm trying to deploy simple access-lists to an Arista switch:

ip access-list SNMP-ACCESS
   10 permit ip host 10.10.10.5 any
ip access-list SSH-ACCESS
   10 permit ip any any

For which I'm using the following playbook:

---
- name: Test-play
  hosts: lab_sw
  
  tasks:
    - name: Configure ACLs
      arista.eos.eos_acls:
        config:
          - afi: ipv4
            acls:
              - name: SSH-ACCESS
                aces:
                  - sequence: 10
                    grant: permit
                    protocol: ip
                    source:
                      any: true
                    destination:
                      any: true
              - name: SNMP-ACCESS
                aces:
                  - sequence: 10
                    grant: permit
                    protocol: ip
                    source:
                      host: 10.10.10.5
                    destination:
                      any: true

The initial run completes successfully, and the ACLs are deployed. Unfortunately, if I rerun the playbook, the access lists get broken.

As you can see on the below output, the "before" and "after" do not match, and a change is made. The entry in "SSH-ACCESS" gets deleted. This is not the expected behavior since no changes are desired and Ansible should identify that.

changed: [VBOX-SWITCH1] => {
    "after": [
        {
            "acls": [
                {
                    "aces": [
                        {
                            "destination": {
                                "any": true
                            },
                            "grant": "permit",
                            "protocol": "ip",
                            "sequence": 10,
                            "source": {
                                "host": "10.10.10.5"
                            }
                        }
                    ],
                    "name": "SNMP-ACCESS"
                },
                {
                    "name": "SSH-ACCESS"
                }
            ],
            "afi": "ipv4"
        }
    ],
    "before": [
        {
            "acls": [
                {
                    "aces": [
                        {
                            "destination": {
                                "any": true
                            },
                            "grant": "permit",
                            "protocol": "ip",
                            "sequence": 10,
                            "source": {
                                "host": "10.10.10.5"
                            }
                        }
                    ],
                    "name": "SNMP-ACCESS"
                },
                {
                    "aces": [
                        {
                            "destination": {
                                "any": true
                            },
                            "grant": "permit",
                            "protocol": "ip",
                            "sequence": 10,
                            "source": {
                                "any": true
                            }
                        }
                    ],
                    "name": "SSH-ACCESS"
                }
            ],
            "afi": "ipv4"
        }
    ],
    "changed": true,
    "commands": [
        "ip access-list SSH-ACCESS",
        "no 10"
    ]

If I rerun it one more time the issue get fixed but in a weird way - check the applied by Ansible commands - there is one unnecessary "no 10":

changed: [VBOX-SWITCH1] => {
    "after": [
        {
            "acls": [
                {
                    "aces": [
                        {
                            "destination": {
                                "any": true
                            },
                            "grant": "permit",
                            "protocol": "ip",
                            "sequence": 10,
                            "source": {
                                "host": "10.10.10.5"
                            }
                        }
                    ],
                    "name": "SNMP-ACCESS"
                },
                {
                    "aces": [
                        {
                            "destination": {
                                "any": true
                            },
                            "grant": "permit",
                            "protocol": "ip",
                            "sequence": 10,
                            "source": {
                                "any": true
                            }
                        }
                    ],
                    "name": "SSH-ACCESS"
                }
            ],
            "afi": "ipv4"
        }
    ],
    "before": [
        {
            "acls": [
                {
                    "aces": [
                        {
                            "destination": {
                                "any": true
                            },
                            "grant": "permit",
                            "protocol": "ip",
                            "sequence": 10,
                            "source": {
                                "host": "10.10.10.5"
                            }
                        }
                    ],
                    "name": "SNMP-ACCESS"
                },
                {
                    "name": "SSH-ACCESS"
                }
            ],
            "afi": "ipv4"
        }
    ],
    "changed": true,
    "commands": [
        "ip access-list SSH-ACCESS",
        "no 10",
        "10 permit ip any any"
    ]

Another run repeats the same behavior.

My assumption is that during the check for differences before/after the names of the two ACLs are not compared but only their entries (in my case the two ACLs have seq 10). If I change the sequence number in the second access list from 10 to 20 the issue is not observed. Another evidence for this theory is that if I create another playbook and include an access list with different name but same entries, Ansible reports that no changes need to be done, and the new ACL is not configured.

ISSUE TYPE
  • Bug Report
COMPONENT NAME

arista.eos.eos_acls

ANSIBLE VERSION
ansible [core 2.15.8]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/ichakarov/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /home/ichakarov/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] (/usr/bin/python3)
  jinja version = 3.0.3
  libyaml = True
COLLECTION VERSION
Collection                    Version
----------------------------- -------
ansible.netcommon             6.0.0  
ansible.utils                 3.0.0  
arista.eos                    7.0.0  
CONFIGURATION
CONFIG_FILE() = /etc/ansible/ansible.cfg
OS / ENVIRONMENT
Arista vEOS-lab
Software image version: 4.31.1F
STEPS TO REPRODUCE

Run the following play book 2-3 times:

---
- name: Test-play
  hosts: lab_sw
  
  tasks:
    - name: Configure ACLs
      arista.eos.eos_acls:
        config:
          - afi: ipv4
            acls:
              - name: SSH-ACCESS
                aces:
                  - sequence: 10
                    grant: permit
                    protocol: ip
                    source:
                      any: true
                    destination:
                      any: true
              - name: SNMP-ACCESS
                aces:
                  - sequence: 10
                    grant: permit
                    protocol: ip
                    source:
                      host: 10.10.10.5
                    destination:
                      any: true
EXPECTED RESULTS

On the second run (and every next one), no changes have to me made on the end device.

ACTUAL RESULTS

On the second run, Ansible does not properly identify the differences before/after (there aren't any) and make changes on the first ACL in the playbook:

    "commands": [
        "ip access-list SSH-ACCESS",
        "no 10"
    ]
@bewing
Copy link

bewing commented Dec 24, 2024

Stumbled across this issue while troubleshooting something similar. I can confirm the above behavior still exists in ansible core 2.15.13 + arista.eos 10.0.1

Note: Changing state to replaced alters the behavior -- seq 10 is not removed, but it is still listed as changed:

changed: [clab-ceos-ceos1] => {
    "after": [
        {
            "acls": [
                {
                    "aces": [
                        {
                            "destination": {
                                "any": true
                            },
                            "grant": "permit",
                            "protocol": "ip",
                            "sequence": 10,
                            "source": {
                                "host": "10.10.10.5"
                            }
                        }
                    ],
                    "name": "SNMP-ACCESS"
                },
                {
                    "aces": [
                        {
                            "destination": {
                                "any": true
                            },
                            "grant": "permit",
                            "protocol": "ip",
                            "sequence": 10,
                            "source": {
                                "any": true
                            }
                        }
                    ],
                    "name": "SSH-ACCESS"
                }
            ],
            "afi": "ipv4"
        }
    ],
    "before": [
        {
            "acls": [
                {
                    "aces": [
                        {
                            "destination": {
                                "any": true
                            },
                            "grant": "permit",
                            "protocol": "ip",
                            "sequence": 10,
                            "source": {
                                "host": "10.10.10.5"
                            }
                        }
                    ],
                    "name": "SNMP-ACCESS"
                },
                {
                    "aces": [
                        {
                            "destination": {
                                "any": true
                            },
                            "grant": "permit",
                            "protocol": "ip",
                            "sequence": 10,
                            "source": {
                                "any": true
                            }
                        }
                    ],
                    "name": "SSH-ACCESS"
                }
            ],
            "afi": "ipv4"
        }
    ],
    "changed": true,
    "commands": [
        "ip access-list SSH-ACCESS",
        "10 permit ip any any"
    ]
}

so while the config becomes idempotent, you can't tell, because it's always marked as changed.

As a very interesting sidebar to this, the following playbook:

- name: Network Getting Started First Playbook
  gather_facts: false
  hosts: all
  tasks:
    - name: push ACL 1
      arista.eos.eos_acls:
        state: replaced
        config:
          - afi: ipv4
            acls:
            - name: TEST-LIST-1
              aces:
              - sequence: "10"
                remark: "test"
              - sequence: "20"
                grant: permit
                log: true
                destination:
                  any: true
                  port_protocol:
                    eq: "https"
                protocol: tcp
                source:
                  subnet_address: 192.0.2.0/24
    - name: push ACL 2
      arista.eos.eos_acls:
        state: replaced
        config:
          - afi: ipv4
            acls:
            - name: TEST-LIST-2
              aces:
              - sequence: "10"
                remark: "test"
              - sequence: "20"
                grant: permit
                log: true
                destination:
                  any: true
                  port_protocol:
                    eq: "https"
                protocol: tcp
                source:
                  subnet_address: 192.0.2.0/24

produces one changed task, and one unchanged task:

ok: [clab-ceos-ceos1] => {
    "before": [
        {
            "acls": [
                {
                    "aces": [
                        {
                            "remark": "test",
                            "sequence": 10
                        },
                        {
                            "destination": {
                                "any": true,
                                "port_protocol": {
                                    "eq": "https"
                                }
                            },
                            "grant": "permit",
                            "log": true,
                            "protocol": "tcp",
                            "sequence": 20,
                            "source": {
                                "subnet_address": "192.0.2.0/24"
                            }
                        }
                    ],
                    "name": "TEST-LIST-1"
                },
                {
                    "aces": [
                        {
                            "remark": "test",
                            "sequence": 10
                        },
                        {
                            "destination": {
                                "any": true,
                                "port_protocol": {
                                    "eq": "https"
                                }
                            },
                            "grant": "permit",
                            "log": true,
                            "protocol": "tcp",
                            "sequence": 20,
                            "source": {
                                "subnet_address": "192.0.2.0/24"
                            }
                        }
                    ],
                    "name": "TEST-LIST-2"
                }
            ],
            "afi": "ipv4"
        }
    ],
    "changed": false,
    "commands": []
}
changed: [clab-ceos-ceos1] => {
    "after": [
        {
            "acls": [
                {
                    "aces": [
                        {
                            "remark": "test",
                            "sequence": 10
                        },
                        {
                            "destination": {
                                "any": true,
                                "port_protocol": {
                                    "eq": "https"
                                }
                            },
                            "grant": "permit",
                            "log": true,
                            "protocol": "tcp",
                            "sequence": 20,
                            "source": {
                                "subnet_address": "192.0.2.0/24"
                            }
                        }
                    ],
                    "name": "TEST-LIST-1"
                },
                {
                    "aces": [
                        {
                            "remark": "test",
                            "sequence": 10
                        },
                        {
                            "destination": {
                                "any": true,
                                "port_protocol": {
                                    "eq": "https"
                                }
                            },
                            "grant": "permit",
                            "log": true,
                            "protocol": "tcp",
                            "sequence": 20,
                            "source": {
                                "subnet_address": "192.0.2.0/24"
                            }
                        }
                    ],
                    "name": "TEST-LIST-2"
                }
            ],
            "afi": "ipv4"
        }
    ],
    "before": [
        {
            "acls": [
                {
                    "aces": [
                        {
                            "remark": "test",
                            "sequence": 10
                        },
                        {
                            "destination": {
                                "any": true,
                                "port_protocol": {
                                    "eq": "https"
                                }
                            },
                            "grant": "permit",
                            "log": true,
                            "protocol": "tcp",
                            "sequence": 20,
                            "source": {
                                "subnet_address": "192.0.2.0/24"
                            }
                        }
                    ],
                    "name": "TEST-LIST-1"
                },
                {
                    "aces": [
                        {
                            "remark": "test",
                            "sequence": 10
                        },
                        {
                            "destination": {
                                "any": true,
                                "port_protocol": {
                                    "eq": "https"
                                }
                            },
                            "grant": "permit",
                            "log": true,
                            "protocol": "tcp",
                            "sequence": 20,
                            "source": {
                                "subnet_address": "192.0.2.0/24"
                            }
                        }
                    ],
                    "name": "TEST-LIST-2"
                }
            ],
            "afi": "ipv4"
        }
    ],
    "changed": true,
    "commands": [
        "ip access-list TEST-LIST-2",
        "10 remark test",
        "20 permit tcp 192.0.2.0/24 any eq https log"
    ]
}

so definitely a bug somewhere in the parsing related to whether or not an ACL is the first ACL configured on the host.

@bewing
Copy link

bewing commented Dec 24, 2024

As a workaround, you can leverage ACL rendering and an idempotent config push to ensure the status of individual ACLs on a device:

- name: Set two ACLs idempotently
  gather_facts: false
  hosts: all
  tasks:
    - name: Render ACL 1
      register: acl_1
      arista.eos.eos_acls:
        state: rendered
        config:
          - afi: ipv4
            acls:
            - name: TEST-LIST-1
              aces:
              - sequence: "10"
                remark: "test"
              - sequence: "20"
                grant: permit
                log: true
                destination:
                  any: true
                  port_protocol:
                    eq: "https"
                protocol: tcp
                source:
                  subnet_address: 192.0.2.0/24
    - name: Render ACL 2
      register: acl_2
      arista.eos.eos_acls:
        state: rendered
        config:
          - afi: ipv4
            acls:
            - name: TEST-LIST-2
              aces:
              - sequence: "10"
                remark: "test"
              - sequence: "20"
                grant: permit
                log: true
                destination:
                  any: true
                  port_protocol:
                    eq: "https"
                protocol: tcp
                source:
                  subnet_address: 192.0.3.0/24
    - name: Push ACL 1
      arista.eos.eos_config:
        lines: "{{ ['no ip access-list TEST-LIST-1'] + acl_1['rendered'] }}"
        match: none
        save_when: changed
    - name: Push ACL 2
      arista.eos.eos_config:
        lines: "{{ ['no ip access-list TEST-LIST-2'] + acl_2['rendered'] }}"
        match: none
        save_when: changed
PLAY [Set two ACLs idempotently] *************************************************************************************************************

TASK [Render ACL 1] ***************************************************************************************************************************************
ok: [clab-ceos-ceos1]

TASK [Render ACL 2] ***************************************************************************************************************************************
ok: [clab-ceos-ceos1]

TASK [Push ACL 1] *****************************************************************************************************************************************
[WARNING]: To ensure idempotency and correct diff the input configuration lines should be similar to how they appear if present in the running
configuration on device
changed: [clab-ceos-ceos1]

TASK [Push ACL 2] *****************************************************************************************************************************************
changed: [clab-ceos-ceos1]

PLAY RECAP ************************************************************************************************************************************************
clab-ceos-ceos1            : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

:: 2nd run ::
PLAY [Set two ACLs idempotently] *************************************************************************************************************

TASK [Render ACL 1] ***************************************************************************************************************************************
ok: [clab-ceos-ceos1]

TASK [Render ACL 2] ***************************************************************************************************************************************
ok: [clab-ceos-ceos1]

TASK [Push ACL 1] *****************************************************************************************************************************************
ok: [clab-ceos-ceos1]

TASK [Push ACL 2] *****************************************************************************************************************************************
ok: [clab-ceos-ceos1]

PLAY RECAP ************************************************************************************************************************************************
clab-ceos-ceos1            : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Note that due to how arista.eos.eos_config does parent matching, you should NOT try to use it to push more than one ACL at once, as testing showed that repeated lines with different parents would be omitted.

bewing added a commit to bewing/arista.eos that referenced this issue Dec 30, 2024
bewing added a commit to bewing/arista.eos that referenced this issue Dec 30, 2024
Only check for missing wanted ACLs after processing all had ACLs, not
just the first one.  Addresses ansible-collections#512
@bewing bewing linked a pull request Dec 30, 2024 that will close this issue
@bewing
Copy link

bewing commented Dec 30, 2024

I didn't add a specific test case in the PR for the original commit, which I think is actually addressed by #563, but did test and confirm a fix for the issue I was experiencing with a replaced operation.

bewing added a commit to bewing/arista.eos that referenced this issue Dec 30, 2024
bewing added a commit to bewing/arista.eos that referenced this issue Dec 30, 2024
Only check for missing wanted ACLs after processing all had ACLs, not
just the first one.  Addresses ansible-collections#512
bewing added a commit to bewing/arista.eos that referenced this issue Dec 30, 2024
bewing added a commit to bewing/arista.eos that referenced this issue Dec 30, 2024
Only check for missing wanted ACLs after processing all had ACLs, not
just the first one.  Addresses ansible-collections#512
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants