4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AnsibleのaclsモジュールでACL設定変更を自動化してみた

Posted at

はじめに

以前の記事で、CSV形式のACL要件資料とJinja2テンプレートから「名前付き拡張ACL」のConfigファイルを生成し、設定変更を行ってみました。

AnsibleでACL Config作成&設定変更を自動化してみた

一方その後、Ansible Collectionでxxx_aclsモジュールがリリースされ、Configを用意しなくても、オプション内でIPアドレスやポート番号を指定すれば、名前付き・番号付き、標準・拡張ACLのConfig作成と設定変更が出来るようになりました。

[Ansible] cisco.ios collection 0.0.3 リリース、ACL や OSPF などのモジュールなどが追加

本記事は、Cisco IOS、NX-OS、ASA用の以下モジュールで動作確認した時のメモです。

cisco.ios.ios_acls – ACLs resource module
cisco.nxos.nxos_acls – ACLs resource module
cisco.asa.asa_acls – Access-Lists resource module

1. 用意した環境

1-1. Ansible

CentOS上にAnsible2.10.1をインストールの上、Ansible Galaxyから以下のCollectionをインストールしました。

$ ansible-galaxy collection list

# /home/centos/.ansible/collections/ansible_collections
Collection        Version   
----------------- ----------
ansible.netcommon 1.5.1-dev9
cisco.asa         1.0.5-dev4
cisco.ios         1.3.1-dev5
cisco.nxos        1.4.1-dev9

1-2. NW機器

Cisco CML-P 2.1の仮想環境上に、IOS機器iosvl2-0 iosvl2-1、NX-OS機器nxos-0、ASA機器asav-1を同一セグメント内に構築し、外部環境のCentOSとext-conn-0経由で接続しました。

image.png

2. ACL要件資料

各OS機器で、名前付き拡張ACLと番号付き拡張ACLをそれぞれ3要件ずつ、計18要件を追加してみます。

  • acl_requests4.csv
    image.png
acl_requests4.csv(生データ)
hostname,change,acl_type,acl_name,seq_no,action,protocol,src_addr,src_oper,src_port,dest_addr,dest_oper,dest_port,modifier,description
iosvl2-0,add,name_extended,TEST1,110,deny,udp,192.168.1.1/32,,,192.168.2.1/32,eq,ntp,,ACL_TEST1
iosvl2-0,add,name_extended,TEST1,120,deny,tcp,192.168.1.2/32,gt,1023,192.168.2.0/24,eq,www,,
iosvl2-0,add,name_extended,TEST1,130,permit,ip,192.168.1.0/24,,,192.168.2.0/24,,,log,
iosvl2-0,add,number_extended,100,110,deny,udp,any,,,any,eq,ntp,,ACL_100
iosvl2-0,add,number_extended,100,120,deny,tcp,,gt,1023,,eq,www,,
iosvl2-0,add,number_extended,100,130,permit,ip,any,,,192.168.2.0/24,,,log,
nxos-0,add,name_extended,TEST1,110,deny,udp,192.168.1.1/32,,,192.168.2.1/32,eq,ntp,,ACL_TEST1
nxos-0,add,name_extended,TEST1,120,deny,tcp,192.168.1.2/32,range,1024-65535,192.168.2.0/24,eq,www,,
nxos-0,add,name_extended,TEST1,130,permit,ip,192.168.1.0/24,,,192.168.2.0/24,,,log,
nxos-0,add,number_extended,100,110,deny,udp,any,,,any,eq,ntp,,ACL_100
nxos-0,add,number_extended,100,120,deny,tcp,,range,1024-65535,,eq,www,,
nxos-0,add,number_extended,100,130,permit,ip,any,,,192.168.2.0/24,,,log,
asav-1,add,name_extended,TEST1,1,deny,udp,192.168.1.1/32,,,192.168.2.1/32,eq,ntp,,ACL_TEST1
asav-1,add,name_extended,TEST1,2,deny,tcp,192.168.1.2/32,gt,1023,192.168.2.0/24,eq,www,,
asav-1,add,name_extended,TEST1,3,permit,ip,192.168.1.0/24,,,192.168.2.0/24,,,log,
asav-1,add,number_extended,100,1,deny,udp,any,,,any,eq,ntp,,ACL_100
asav-1,add,number_extended,100,2,deny,tcp,,gt,1023,,eq,www,,
asav-1,add,number_extended,100,3,permit,ip,any,,,192.168.2.0/24,,,log,

各項目の内容は以下の通りです。

項目名 内容
hostname 対象機器のホスト名
change 設定変更種別 例:add(追加)、delete(削除)
acl_type ACL種別 例:name_extended(名前付き拡張)、name_standard(名前付き標準)、number_extended(番号付き拡張)、number_standard(番号付き標準)※今回の実装対象はname_extended、number_extendedのみ
acl_name ACL名 or ACL番号
seq_no シーケンス番号
action 通信許可 or 拒否 例:permit、deny
protocol プロトコル 例:ip、tcp、udp
src_addr 送信元アドレス アドレス/プレフィックス長の形式で指定
src_oper 送信元演算子(任意) 例:eq、gt、lt、range
src_port 送信元ポート番号(任意) rangeの場合、FromとToをハイフンで区切って指定
dest_addr 宛先アドレス アドレス/プレフィックス長の形式で指定
dest_oper 宛先演算子(任意) 例:eq、neq、gt、lt、range
dest_port 宛先ポート番号(任意) rangeの場合、FromとToをハイフンで区切って指定

3. Playbook

各タスクの内容は以下の通りです。

  1. read_csvモジュールを使い、ACL要件資料の各行を[{ "hostname": "xx", "acl_name": "aa", ~ }, { "hostname": "yy", "acl_name": "bb", ~ }, ~]のように、リスト内の要素として定義。
  2. debugモジュールで、タスク1のリストを表示。
  3. set_factモジュールで、上記のリストを変数aclsに格納。ここまでは機器毎の実行は不要なためrun_once: yesで1回のみ実行。
  4. templateモジュールで、変数aclsと後述のJinja2テンプレートをレンダリングし、xxx_aclsモジュールのアーギュメントとして読み込めるJSON形式に変換してファイル出力。
  5. include_varsモジュールで、タスク4のJSONファイルを読み込み、変数acl_optionに格納。
  6. debugモジュールで、タスク5の内容を表示。
  7. ~9. cisco.ios.ios_acls cisco.nxos.nxos_acls cisco.asa.asa_aclsモジュールで、各OS機器のACL Config生成と設定変更を実施。configstateアーギュメントは、タスク5の変数をループで読み取り。
playbook_acl_extend.yml
---

- hosts: cisco
  gather_facts: no

  vars:
    template: acl_module_template3.j2
    acl_list: acl_requests4.csv
    acl_option_file: acl_option_{{ inventory_hostname }}.json

  tasks:
    - name: read acl requests from CSV file and return a list   # (1)
      read_csv:
        path: "{{ acl_list }}"
      run_once: yes
      register: acl_requests

    - name: display acl requests   # (2)
      debug:
        msg: "{{ acl_requests.list }}"
      run_once: yes

    - name: set acl requests   # (3)
      set_fact:
        acls: "{{ acl_requests.list }}"
      run_once: yes

    - name: build template   # (4)
      template:
        src: "{{ template }}"
        dest: "{{ acl_option_file }}"

    - name: set acl options   # (5)
      include_vars:
        file: "{{ acl_option_file }}"
        name: acl_option

    - name: display acl options   # (6)
      debug:
        msg: "{{ acl_option }}"

    - name: merge acl config (ios)   # (7)
      cisco.ios.ios_acls:
        config: "{{ item.config }}"
        state: "{{ item.state }}"
      loop: "{{ acl_option.acl_config }}"
      when: ansible_network_os == 'cisco.ios.ios'

    - name: merge acl config (nxos)   # (8)
      cisco.nxos.nxos_acls:
        config: "{{ item.config }}"
        state: "{{ item.state }}"
      loop: "{{ acl_option.acl_config }}"
      when: ansible_network_os == 'cisco.nxos.nxos'

    - name: merge acl config (asa)   # (9)
      cisco.asa.asa_acls:
        config: "{{ item.config[0] }}"
        state: "{{ item.state }}"
      loop: "{{ acl_option.acl_config }}"
      when: ansible_network_os == 'cisco.asa.asa'

4. Jinja2テンプレート

詳細は割愛しますが、大まかなポイントは以下の通りです。

acl_module_template3.j2
{
    "acl_config": [
    {% for item in acls -%}
        {% if item.hostname == inventory_hostname and item.change == 'add' -%}
            {% set src_prefix = item.src_addr | ansible.netcommon.ipaddr('prefix') -%}
            {% set dest_prefix = item.dest_addr | ansible.netcommon.ipaddr('prefix') -%}
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            {% if item.dest_addr == '' or item.dest_addr == 'any' -%}
                                            "any": yes,
                                            {% elif dest_prefix == 32 -%}
                                            "host": "{{ item.dest_addr | ansible.netcommon.ipaddr('network') }}",
                                            {% elif dest_prefix < 32 -%}
                                            "address": "{{ item.dest_addr | ansible.netcommon.ipaddr('network') }}",
                                            {% if ansible_network_os == 'cisco.asa.asa' -%}
                                            "netmask": "{{ item.dest_addr | ansible.netcommon.ipaddr('netmask') }}",
                                            {% else -%}
                                            "wildcard_bits": "{{ item.dest_addr | ansible.netcommon.ipaddr('wildcard') }}",
                                            {% endif -%}
                                            {% endif -%}
                                            {% if item.dest_oper == 'range' -%}                                        
                                            "port_protocol": {
                                                "range": {
                                                    {% if ansible_network_os == 'cisco.nxos.nxos' -%}
                                                    "end": "{{ item.dest_port.split('-')[1] }}",
                                                    "start": "{{ item.dest_port.split('-')[0] }}"
                                                    {% else -%}
                                                    "end": {{ item.dest_port.split('-')[1] }},
                                                    "start": {{ item.dest_port.split('-')[0] }}
                                                    {% endif -%}
                                                }
                                            }
                                            {% elif item.dest_oper != '' and item.dest_oper != 'any' -%}
                                            "port_protocol": {
                                                "{{ item.dest_oper }}": "{{ item.dest_port }}"
                                            }
                                            {% endif -%}
                                        },
                                        "grant": "{{ item.action }}",
                                        "protocol": "{{ item.protocol }}",
                                        {% if ansible_network_os == 'cisco.asa.asa' -%}
                                        "line": {{ item.seq_no }},
                                        {% else -%}   
                                        "sequence": {{ item.seq_no }},
                                        {% endif -%}
                                        "source": {
                                            {% if item.src_addr == '' or item.src_addr == 'any' -%}
                                            "any": yes,
                                            {% elif src_prefix == 32 -%}
                                            "host": "{{ item.src_addr | ansible.netcommon.ipaddr('network') }}",
                                            {% elif src_prefix < 32 -%}
                                            "address": "{{ item.src_addr | ansible.netcommon.ipaddr('network') }}",
                                            {% if ansible_network_os == 'cisco.asa.asa' -%}
                                            "netmask": "{{ item.src_addr | ansible.netcommon.ipaddr('netmask') }}",
                                            {% else -%}
                                            "wildcard_bits": "{{ item.src_addr | ansible.netcommon.ipaddr('wildcard') }}",
                                            {% endif -%}
                                            {% endif -%}
                                            {% if item.src_oper == 'range' -%}                                        
                                            "port_protocol": {
                                                "range": {
                                                    {% if ansible_network_os == 'cisco.nxos.nxos' -%}
                                                    "end": "{{ item.src_port.split('-')[1] }}",
                                                    "start": "{{ item.src_port.split('-')[0] }}"
                                                    {% else -%}
                                                    "end": {{ item.src_port.split('-')[1] }},
                                                    "start": {{ item.src_port.split('-')[0] }}
                                                    {% endif -%}
                                                }
                                            }
                                            {% elif item.src_oper != '' and item.src_oper != 'any' -%}
                                            "port_protocol": {
                                                "{{ item.src_oper }}": "{{ item.src_port }}"
                                            }
                                            {% endif -%}
                                        }
                                    }
                                ],
                                {% if ansible_network_os != 'cisco.nxos.nxos' -%}
                                "acl_type": "{{ item.acl_type | regex_replace('^(name|number)_(.*)$', '\\2') }}",
                                {% endif -%}
                                "name": "{{ item.acl_name }}"
                            }
                        ],
                        {% if ansible_network_os != 'cisco.asa.asa' -%}
                        "afi": "ipv4"
                        {% endif -%}
                    }
                ],
                "state": {% if item.change == 'add' -%}"merged"{% elif item.change == 'delete' -%}"deleted"{% endif -%}
            },
        {% endif -%}
    {% endfor -%}
    ]
}
  • テンプレートのフォーマットはJSONを使用。当初、Playbookのフォーマットと合わせてYAMLで作っていましたが、余分なWhitespaceがYAML内でインデントとして扱われてしまう可能性を考慮し、途中で修正しました:sweat_smile:

  • 冒頭の{% if item.hostname == inventory_hostname and item.change == 'add' -%}で、ACL追加対象が自分自身の要件のみレンダリングを行い、機器毎にファイルacl_option_{{ inventory_hostname }}.jsonが生成されるようにした。※inventory_hostname=Ansible実行対象のホスト名

  • 1つの機器で要件が複数ある場合、リスト形式で要素を追加していき、Playbook内で読み込んだ時にループで回せるようにした。

  • IPアドレス(ホスト/ネットワーク/any)、ポート番号(eq/neq/gt/lt/range)、OS(IOS/NX-OS/ASA)で利用するアーギュメントやデータ型が異なるため、テンプレート内で条件分岐させた。
    例えば、IOS/NX-OSでは、送信元/宛先ネットワークをワイルドカードマスク用のアーギュメントwildcard_bitsで指定するが、ASAではネットマスク用のアーギュメントnetmaskで指定する。

5. 実行結果

こちらも詳細は割愛しますが、3台ともFailせず完了しています。もう1台の、Inventoryファイルでは指定したものの、ACL設定要件がなかったiosvl2-1も、changed=0で完了しています。

実行結果
$ ansible-playbook -i inventory_cml.ini playbook_acl_extend.yml

PLAY [cisco] *****************************************************************************************************

TASK [read acl requests from CSV file and return a list] *********************************************************
ok: [iosvl2-0]

TASK [display acl requests] **************************************************************************************
ok: [iosvl2-0] => {
    "msg": [
        {
            "acl_name": "TEST1",
            "acl_type": "name_extended",
            "action": "deny",
            "change": "add",
            "description": "ACL_TEST1",
            "dest_addr": "192.168.2.1/32",
            "dest_oper": "eq",
            "dest_port": "ntp",
            "hostname": "iosvl2-0",
            "modifier": "",
            "protocol": "udp",
            "seq_no": "110",
            "src_addr": "192.168.1.1/32",
            "src_oper": "",
            "src_port": ""
        },
        {
            "acl_name": "TEST1",
            "acl_type": "name_extended",
            "action": "deny",
            "change": "add",
            "description": "",
            "dest_addr": "192.168.2.0/24",
            "dest_oper": "eq",
            "dest_port": "www",
            "hostname": "iosvl2-0",
            "modifier": "",
            "protocol": "tcp",
            "seq_no": "120",
            "src_addr": "192.168.1.2/32",
            "src_oper": "gt",
            "src_port": "1023"
        },
        {
            "acl_name": "TEST1",
            "acl_type": "name_extended",
            "action": "permit",
            "change": "add",
            "description": "",
            "dest_addr": "192.168.2.0/24",
            "dest_oper": "",
            "dest_port": "",
            "hostname": "iosvl2-0",
            "modifier": "log",
            "protocol": "ip",
            "seq_no": "130",
            "src_addr": "192.168.1.0/24",
            "src_oper": "",
            "src_port": ""
        },
        {
            "acl_name": "100",
            "acl_type": "number_extended",
            "action": "deny",
            "change": "add",
            "description": "ACL_100",
            "dest_addr": "any",
            "dest_oper": "eq",
            "dest_port": "ntp",
            "hostname": "iosvl2-0",
            "modifier": "",
            "protocol": "udp",
            "seq_no": "110",
            "src_addr": "any",
            "src_oper": "",
            "src_port": ""
        },
        {
            "acl_name": "100",
            "acl_type": "number_extended",
            "action": "deny",
            "change": "add",
            "description": "",
            "dest_addr": "",
            "dest_oper": "eq",
            "dest_port": "www",
            "hostname": "iosvl2-0",
            "modifier": "",
            "protocol": "tcp",
            "seq_no": "120",
            "src_addr": "",
            "src_oper": "gt",
            "src_port": "1023"
        },
        {
            "acl_name": "100",
            "acl_type": "number_extended",
            "action": "permit",
            "change": "add",
            "description": "",
            "dest_addr": "192.168.2.0/24",
            "dest_oper": "",
            "dest_port": "",
            "hostname": "iosvl2-0",
            "modifier": "log",
            "protocol": "ip",
            "seq_no": "130",
            "src_addr": "any",
            "src_oper": "",
            "src_port": ""
        },
        {
            "acl_name": "TEST1",
            "acl_type": "name_extended",
            "action": "deny",
            "change": "add",
            "description": "ACL_TEST1",
            "dest_addr": "192.168.2.1/32",
            "dest_oper": "eq",
            "dest_port": "ntp",
            "hostname": "nxos-0",
            "modifier": "",
            "protocol": "udp",
            "seq_no": "110",
            "src_addr": "192.168.1.1/32",
            "src_oper": "",
            "src_port": ""
        },
        {
            "acl_name": "TEST1",
            "acl_type": "name_extended",
            "action": "deny",
            "change": "add",
            "description": "",
            "dest_addr": "192.168.2.0/24",
            "dest_oper": "eq",
            "dest_port": "www",
            "hostname": "nxos-0",
            "modifier": "",
            "protocol": "tcp",
            "seq_no": "120",
            "src_addr": "192.168.1.2/32",
            "src_oper": "range",
            "src_port": "1024-65535"
        },
        {
            "acl_name": "TEST1",
            "acl_type": "name_extended",
            "action": "permit",
            "change": "add",
            "description": "",
            "dest_addr": "192.168.2.0/24",
            "dest_oper": "",
            "dest_port": "",
            "hostname": "nxos-0",
            "modifier": "log",
            "protocol": "ip",
            "seq_no": "130",
            "src_addr": "192.168.1.0/24",
            "src_oper": "",
            "src_port": ""
        },
        {
            "acl_name": "100",
            "acl_type": "number_extended",
            "action": "deny",
            "change": "add",
            "description": "ACL_100",
            "dest_addr": "any",
            "dest_oper": "eq",
            "dest_port": "ntp",
            "hostname": "nxos-0",
            "modifier": "",
            "protocol": "udp",
            "seq_no": "110",
            "src_addr": "any",
            "src_oper": "",
            "src_port": ""
        },
        {
            "acl_name": "100",
            "acl_type": "number_extended",
            "action": "deny",
            "change": "add",
            "description": "",
            "dest_addr": "",
            "dest_oper": "eq",
            "dest_port": "www",
            "hostname": "nxos-0",
            "modifier": "",
            "protocol": "tcp",
            "seq_no": "120",
            "src_addr": "",
            "src_oper": "range",
            "src_port": "1024-65535"
        },
        {
            "acl_name": "100",
            "acl_type": "number_extended",
            "action": "permit",
            "change": "add",
            "description": "",
            "dest_addr": "192.168.2.0/24",
            "dest_oper": "",
            "dest_port": "",
            "hostname": "nxos-0",
            "modifier": "log",
            "protocol": "ip",
            "seq_no": "130",
            "src_addr": "any",
            "src_oper": "",
            "src_port": ""
        },
        {
            "acl_name": "TEST1",
            "acl_type": "name_extended",
            "action": "deny",
            "change": "add",
            "description": "ACL_TEST1",
            "dest_addr": "192.168.2.1/32",
            "dest_oper": "eq",
            "dest_port": "ntp",
            "hostname": "asav-1",
            "modifier": "",
            "protocol": "udp",
            "seq_no": "1",
            "src_addr": "192.168.1.1/32",
            "src_oper": "",
            "src_port": ""
        },
        {
            "acl_name": "TEST1",
            "acl_type": "name_extended",
            "action": "deny",
            "change": "add",
            "description": "",
            "dest_addr": "192.168.2.0/24",
            "dest_oper": "eq",
            "dest_port": "www",
            "hostname": "asav-1",
            "modifier": "",
            "protocol": "tcp",
            "seq_no": "2",
            "src_addr": "192.168.1.2/32",
            "src_oper": "gt",
            "src_port": "1023"
        },
        {
            "acl_name": "TEST1",
            "acl_type": "name_extended",
            "action": "permit",
            "change": "add",
            "description": "",
            "dest_addr": "192.168.2.0/24",
            "dest_oper": "",
            "dest_port": "",
            "hostname": "asav-1",
            "modifier": "log",
            "protocol": "ip",
            "seq_no": "3",
            "src_addr": "192.168.1.0/24",
            "src_oper": "",
            "src_port": ""
        },
        {
            "acl_name": "100",
            "acl_type": "number_extended",
            "action": "deny",
            "change": "add",
            "description": "ACL_100",
            "dest_addr": "any",
            "dest_oper": "eq",
            "dest_port": "ntp",
            "hostname": "asav-1",
            "modifier": "",
            "protocol": "udp",
            "seq_no": "1",
            "src_addr": "any",
            "src_oper": "",
            "src_port": ""
        },
        {
            "acl_name": "100",
            "acl_type": "number_extended",
            "action": "deny",
            "change": "add",
            "description": "",
            "dest_addr": "",
            "dest_oper": "eq",
            "dest_port": "www",
            "hostname": "asav-1",
            "modifier": "",
            "protocol": "tcp",
            "seq_no": "2",
            "src_addr": "",
            "src_oper": "gt",
            "src_port": "1023"
        },
        {
            "acl_name": "100",
            "acl_type": "number_extended",
            "action": "permit",
            "change": "add",
            "description": "",
            "dest_addr": "192.168.2.0/24",
            "dest_oper": "",
            "dest_port": "",
            "hostname": "asav-1",
            "modifier": "log",
            "protocol": "ip",
            "seq_no": "3",
            "src_addr": "any",
            "src_oper": "",
            "src_port": ""
        }
    ]
}

TASK [set acl requests] ******************************************************************************************
ok: [iosvl2-0]

TASK [build template] ********************************************************************************************
[WARNING]: The value '' is not a valid IP address or network, passing this value to ipaddr filter might result in
breaking change in future.
[WARNING]: The value '' is not a valid IP address or network, passing this value to ipaddr filter might result in
breaking change in future.
[WARNING]: The value '' is not a valid IP address or network, passing this value to ipaddr filter might result in
breaking change in future.
ok: [iosvl2-1]
changed: [nxos-0]
changed: [asav-1]
changed: [iosvl2-0]

TASK [set acl options] *******************************************************************************************
ok: [iosvl2-0]
ok: [nxos-0]
ok: [asav-1]
ok: [iosvl2-1]

TASK [display acl options] ***************************************************************************************
ok: [nxos-0] => {
    "msg": {
        "acl_config": [
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "host": "192.168.2.1",
                                            "port_protocol": {
                                                "eq": "ntp"
                                            }
                                        },
                                        "grant": "deny",
                                        "protocol": "udp",
                                        "sequence": 110,
                                        "source": {
                                            "host": "192.168.1.1"
                                        }
                                    }
                                ],
                                "name": "TEST1"
                            }
                        ],
                        "afi": "ipv4"
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "address": "192.168.2.0",
                                            "port_protocol": {
                                                "eq": "www"
                                            },
                                            "wildcard_bits": "0.0.0.255"
                                        },
                                        "grant": "deny",
                                        "protocol": "tcp",
                                        "sequence": 120,
                                        "source": {
                                            "host": "192.168.1.2",
                                            "port_protocol": {
                                                "range": {
                                                    "end": "65535",
                                                    "start": "1024"
                                                }
                                            }
                                        }
                                    }
                                ],
                                "name": "TEST1"
                            }
                        ],
                        "afi": "ipv4"
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "address": "192.168.2.0",
                                            "wildcard_bits": "0.0.0.255"
                                        },
                                        "grant": "permit",
                                        "protocol": "ip",
                                        "sequence": 130,
                                        "source": {
                                            "address": "192.168.1.0",
                                            "wildcard_bits": "0.0.0.255"
                                        }
                                    }
                                ],
                                "name": "TEST1"
                            }
                        ],
                        "afi": "ipv4"
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "any": true,
                                            "port_protocol": {
                                                "eq": "ntp"
                                            }
                                        },
                                        "grant": "deny",
                                        "protocol": "udp",
                                        "sequence": 110,
                                        "source": {
                                            "any": true
                                        }
                                    }
                                ],
                                "name": "100"
                            }
                        ],
                        "afi": "ipv4"
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "any": true,
                                            "port_protocol": {
                                                "eq": "www"
                                            }
                                        },
                                        "grant": "deny",
                                        "protocol": "tcp",
                                        "sequence": 120,
                                        "source": {
                                            "any": true,
                                            "port_protocol": {
                                                "range": {
                                                    "end": "65535",
                                                    "start": "1024"
                                                }
                                            }
                                        }
                                    }
                                ],
                                "name": "100"
                            }
                        ],
                        "afi": "ipv4"
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "address": "192.168.2.0",
                                            "wildcard_bits": "0.0.0.255"
                                        },
                                        "grant": "permit",
                                        "protocol": "ip",
                                        "sequence": 130,
                                        "source": {
                                            "any": true
                                        }
                                    }
                                ],
                                "name": "100"
                            }
                        ],
                        "afi": "ipv4"
                    }
                ],
                "state": "merged"
            }
        ]
    }
}
ok: [asav-1] => {
    "msg": {
        "acl_config": [
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "host": "192.168.2.1",
                                            "port_protocol": {
                                                "eq": "ntp"
                                            }
                                        },
                                        "grant": "deny",
                                        "line": 1,
                                        "protocol": "udp",
                                        "source": {
                                            "host": "192.168.1.1"
                                        }
                                    }
                                ],
                                "acl_type": "extended",
                                "name": "TEST1"
                            }
                        ]
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "address": "192.168.2.0",
                                            "netmask": "255.255.255.0",
                                            "port_protocol": {
                                                "eq": "www"
                                            }
                                        },
                                        "grant": "deny",
                                        "line": 2,
                                        "protocol": "tcp",
                                        "source": {
                                            "host": "192.168.1.2",
                                            "port_protocol": {
                                                "gt": "1023"
                                            }
                                        }
                                    }
                                ],
                                "acl_type": "extended",
                                "name": "TEST1"
                            }
                        ]
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "address": "192.168.2.0",
                                            "netmask": "255.255.255.0"
                                        },
                                        "grant": "permit",
                                        "line": 3,
                                        "protocol": "ip",
                                        "source": {
                                            "address": "192.168.1.0",
                                            "netmask": "255.255.255.0"
                                        }
                                    }
                                ],
                                "acl_type": "extended",
                                "name": "TEST1"
                            }
                        ]
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "any": true,
                                            "port_protocol": {
                                                "eq": "ntp"
                                            }
                                        },
                                        "grant": "deny",
                                        "line": 1,
                                        "protocol": "udp",
                                        "source": {
                                            "any": true
                                        }
                                    }
                                ],
                                "acl_type": "extended",
                                "name": "100"
                            }
                        ]
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "any": true,
                                            "port_protocol": {
                                                "eq": "www"
                                            }
                                        },
                                        "grant": "deny",
                                        "line": 2,
                                        "protocol": "tcp",
                                        "source": {
                                            "any": true,
                                            "port_protocol": {
                                                "gt": "1023"
                                            }
                                        }
                                    }
                                ],
                                "acl_type": "extended",
                                "name": "100"
                            }
                        ]
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "address": "192.168.2.0",
                                            "netmask": "255.255.255.0"
                                        },
                                        "grant": "permit",
                                        "line": 3,
                                        "protocol": "ip",
                                        "source": {
                                            "any": true
                                        }
                                    }
                                ],
                                "acl_type": "extended",
                                "name": "100"
                            }
                        ]
                    }
                ],
                "state": "merged"
            }
        ]
    }
}
ok: [iosvl2-0] => {
    "msg": {
        "acl_config": [
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "host": "192.168.2.1",
                                            "port_protocol": {
                                                "eq": "ntp"
                                            }
                                        },
                                        "grant": "deny",
                                        "protocol": "udp",
                                        "sequence": 110,
                                        "source": {
                                            "host": "192.168.1.1"
                                        }
                                    }
                                ],
                                "acl_type": "extended",
                                "name": "TEST1"
                            }
                        ],
                        "afi": "ipv4"
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "address": "192.168.2.0",
                                            "port_protocol": {
                                                "eq": "www"
                                            },
                                            "wildcard_bits": "0.0.0.255"
                                        },
                                        "grant": "deny",
                                        "protocol": "tcp",
                                        "sequence": 120,
                                        "source": {
                                            "host": "192.168.1.2",
                                            "port_protocol": {
                                                "gt": "1023"
                                            }
                                        }
                                    }
                                ],
                                "acl_type": "extended",
                                "name": "TEST1"
                            }
                        ],
                        "afi": "ipv4"
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "address": "192.168.2.0",
                                            "wildcard_bits": "0.0.0.255"
                                        },
                                        "grant": "permit",
                                        "protocol": "ip",
                                        "sequence": 130,
                                        "source": {
                                            "address": "192.168.1.0",
                                            "wildcard_bits": "0.0.0.255"
                                        }
                                    }
                                ],
                                "acl_type": "extended",
                                "name": "TEST1"
                            }
                        ],
                        "afi": "ipv4"
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "any": true,
                                            "port_protocol": {
                                                "eq": "ntp"
                                            }
                                        },
                                        "grant": "deny",
                                        "protocol": "udp",
                                        "sequence": 110,
                                        "source": {
                                            "any": true
                                        }
                                    }
                                ],
                                "acl_type": "extended",
                                "name": "100"
                            }
                        ],
                        "afi": "ipv4"
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "any": true,
                                            "port_protocol": {
                                                "eq": "www"
                                            }
                                        },
                                        "grant": "deny",
                                        "protocol": "tcp",
                                        "sequence": 120,
                                        "source": {
                                            "any": true,
                                            "port_protocol": {
                                                "gt": "1023"
                                            }
                                        }
                                    }
                                ],
                                "acl_type": "extended",
                                "name": "100"
                            }
                        ],
                        "afi": "ipv4"
                    }
                ],
                "state": "merged"
            },
            {
                "config": [
                    {
                        "acls": [
                            {
                                "aces": [
                                    {
                                        "destination": {
                                            "address": "192.168.2.0",
                                            "wildcard_bits": "0.0.0.255"
                                        },
                                        "grant": "permit",
                                        "protocol": "ip",
                                        "sequence": 130,
                                        "source": {
                                            "any": true
                                        }
                                    }
                                ],
                                "acl_type": "extended",
                                "name": "100"
                            }
                        ],
                        "afi": "ipv4"
                    }
                ],
                "state": "merged"
            }
        ]
    }
}
ok: [iosvl2-1] => {
    "msg": {
        "acl_config": []
    }
}

TASK [merge acl config (ios)] ************************************************************************************
skipping: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'host': '192.168.2.1', 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'sequence': 110, 'source': {'host': '192.168.1.1'}}], 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255', 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'sequence': 120, 'source': {'host': '192.168.1.2', 'port_protocol': {'range': {'end': '65535', 'start': '1024'}}}}], 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255'}, 'grant': 'permit', 'protocol': 'ip', 'sequence': 130, 'source': {'address': '192.168.1.0', 'wildcard_bits': '0.0.0.255'}}], 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'sequence': 110, 'source': {'any': True}}], 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'sequence': 120, 'source': {'any': True, 'port_protocol': {'range': {'end': '65535', 'start': '1024'}}}}], 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255'}, 'grant': 'permit', 'protocol': 'ip', 'sequence': 130, 'source': {'any': True}}], 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'host': '192.168.2.1', 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'line': 1, 'source': {'host': '192.168.1.1'}}], 'acl_type': 'extended', 'name': 'TEST1'}]}], 'state': 'merged'}) 
skipping: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'netmask': '255.255.255.0', 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'line': 2, 'source': {'host': '192.168.1.2', 'port_protocol': {'gt': '1023'}}}], 'acl_type': 'extended', 'name': 'TEST1'}]}], 'state': 'merged'}) 
skipping: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'netmask': '255.255.255.0'}, 'grant': 'permit', 'protocol': 'ip', 'line': 3, 'source': {'address': '192.168.1.0', 'netmask': '255.255.255.0'}}], 'acl_type': 'extended', 'name': 'TEST1'}]}], 'state': 'merged'}) 
skipping: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'line': 1, 'source': {'any': True}}], 'acl_type': 'extended', 'name': '100'}]}], 'state': 'merged'}) 
skipping: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'line': 2, 'source': {'any': True, 'port_protocol': {'gt': '1023'}}}], 'acl_type': 'extended', 'name': '100'}]}], 'state': 'merged'}) 
skipping: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'netmask': '255.255.255.0'}, 'grant': 'permit', 'protocol': 'ip', 'line': 3, 'source': {'any': True}}], 'acl_type': 'extended', 'name': '100'}]}], 'state': 'merged'}) 
changed: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'host': '192.168.2.1', 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'sequence': 110, 'source': {'host': '192.168.1.1'}}], 'acl_type': 'extended', 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'})
changed: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255', 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'sequence': 120, 'source': {'host': '192.168.1.2', 'port_protocol': {'gt': '1023'}}}], 'acl_type': 'extended', 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'})
changed: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255'}, 'grant': 'permit', 'protocol': 'ip', 'sequence': 130, 'source': {'address': '192.168.1.0', 'wildcard_bits': '0.0.0.255'}}], 'acl_type': 'extended', 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'})
changed: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'sequence': 110, 'source': {'any': True}}], 'acl_type': 'extended', 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'})
changed: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'sequence': 120, 'source': {'any': True, 'port_protocol': {'gt': '1023'}}}], 'acl_type': 'extended', 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'})
changed: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255'}, 'grant': 'permit', 'protocol': 'ip', 'sequence': 130, 'source': {'any': True}}], 'acl_type': 'extended', 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'})

TASK [merge acl config (nxos)] ***********************************************************************************
skipping: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'host': '192.168.2.1', 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'sequence': 110, 'source': {'host': '192.168.1.1'}}], 'acl_type': 'extended', 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255', 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'sequence': 120, 'source': {'host': '192.168.1.2', 'port_protocol': {'gt': '1023'}}}], 'acl_type': 'extended', 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255'}, 'grant': 'permit', 'protocol': 'ip', 'sequence': 130, 'source': {'address': '192.168.1.0', 'wildcard_bits': '0.0.0.255'}}], 'acl_type': 'extended', 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'sequence': 110, 'source': {'any': True}}], 'acl_type': 'extended', 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'sequence': 120, 'source': {'any': True, 'port_protocol': {'gt': '1023'}}}], 'acl_type': 'extended', 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255'}, 'grant': 'permit', 'protocol': 'ip', 'sequence': 130, 'source': {'any': True}}], 'acl_type': 'extended', 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'host': '192.168.2.1', 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'line': 1, 'source': {'host': '192.168.1.1'}}], 'acl_type': 'extended', 'name': 'TEST1'}]}], 'state': 'merged'}) 
skipping: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'netmask': '255.255.255.0', 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'line': 2, 'source': {'host': '192.168.1.2', 'port_protocol': {'gt': '1023'}}}], 'acl_type': 'extended', 'name': 'TEST1'}]}], 'state': 'merged'}) 
skipping: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'netmask': '255.255.255.0'}, 'grant': 'permit', 'protocol': 'ip', 'line': 3, 'source': {'address': '192.168.1.0', 'netmask': '255.255.255.0'}}], 'acl_type': 'extended', 'name': 'TEST1'}]}], 'state': 'merged'}) 
skipping: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'line': 1, 'source': {'any': True}}], 'acl_type': 'extended', 'name': '100'}]}], 'state': 'merged'}) 
skipping: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'line': 2, 'source': {'any': True, 'port_protocol': {'gt': '1023'}}}], 'acl_type': 'extended', 'name': '100'}]}], 'state': 'merged'}) 
skipping: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'netmask': '255.255.255.0'}, 'grant': 'permit', 'protocol': 'ip', 'line': 3, 'source': {'any': True}}], 'acl_type': 'extended', 'name': '100'}]}], 'state': 'merged'}) 
changed: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'host': '192.168.2.1', 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'sequence': 110, 'source': {'host': '192.168.1.1'}}], 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'})
changed: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255', 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'sequence': 120, 'source': {'host': '192.168.1.2', 'port_protocol': {'range': {'end': '65535', 'start': '1024'}}}}], 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'})
changed: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255'}, 'grant': 'permit', 'protocol': 'ip', 'sequence': 130, 'source': {'address': '192.168.1.0', 'wildcard_bits': '0.0.0.255'}}], 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'})
changed: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'sequence': 110, 'source': {'any': True}}], 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'})
changed: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'sequence': 120, 'source': {'any': True, 'port_protocol': {'range': {'end': '65535', 'start': '1024'}}}}], 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'})
changed: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255'}, 'grant': 'permit', 'protocol': 'ip', 'sequence': 130, 'source': {'any': True}}], 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'})

TASK [merge acl config (asa)] ************************************************************************************
skipping: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'host': '192.168.2.1', 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'sequence': 110, 'source': {'host': '192.168.1.1'}}], 'acl_type': 'extended', 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255', 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'sequence': 120, 'source': {'host': '192.168.1.2', 'port_protocol': {'gt': '1023'}}}], 'acl_type': 'extended', 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'host': '192.168.2.1', 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'sequence': 110, 'source': {'host': '192.168.1.1'}}], 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255', 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'sequence': 120, 'source': {'host': '192.168.1.2', 'port_protocol': {'range': {'end': '65535', 'start': '1024'}}}}], 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255'}, 'grant': 'permit', 'protocol': 'ip', 'sequence': 130, 'source': {'address': '192.168.1.0', 'wildcard_bits': '0.0.0.255'}}], 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'sequence': 110, 'source': {'any': True}}], 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255'}, 'grant': 'permit', 'protocol': 'ip', 'sequence': 130, 'source': {'address': '192.168.1.0', 'wildcard_bits': '0.0.0.255'}}], 'acl_type': 'extended', 'name': 'TEST1'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'sequence': 110, 'source': {'any': True}}], 'acl_type': 'extended', 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'sequence': 120, 'source': {'any': True, 'port_protocol': {'gt': '1023'}}}], 'acl_type': 'extended', 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'sequence': 120, 'source': {'any': True, 'port_protocol': {'range': {'end': '65535', 'start': '1024'}}}}], 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [iosvl2-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255'}, 'grant': 'permit', 'protocol': 'ip', 'sequence': 130, 'source': {'any': True}}], 'acl_type': 'extended', 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
skipping: [nxos-0] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'wildcard_bits': '0.0.0.255'}, 'grant': 'permit', 'protocol': 'ip', 'sequence': 130, 'source': {'any': True}}], 'name': '100'}], 'afi': 'ipv4'}], 'state': 'merged'}) 
changed: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'host': '192.168.2.1', 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'line': 1, 'source': {'host': '192.168.1.1'}}], 'acl_type': 'extended', 'name': 'TEST1'}]}], 'state': 'merged'})
changed: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'netmask': '255.255.255.0', 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'line': 2, 'source': {'host': '192.168.1.2', 'port_protocol': {'gt': '1023'}}}], 'acl_type': 'extended', 'name': 'TEST1'}]}], 'state': 'merged'})
changed: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'netmask': '255.255.255.0'}, 'grant': 'permit', 'protocol': 'ip', 'line': 3, 'source': {'address': '192.168.1.0', 'netmask': '255.255.255.0'}}], 'acl_type': 'extended', 'name': 'TEST1'}]}], 'state': 'merged'})
changed: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'ntp'}}, 'grant': 'deny', 'protocol': 'udp', 'line': 1, 'source': {'any': True}}], 'acl_type': 'extended', 'name': '100'}]}], 'state': 'merged'})
changed: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'any': True, 'port_protocol': {'eq': 'www'}}, 'grant': 'deny', 'protocol': 'tcp', 'line': 2, 'source': {'any': True, 'port_protocol': {'gt': '1023'}}}], 'acl_type': 'extended', 'name': '100'}]}], 'state': 'merged'})
changed: [asav-1] => (item={'config': [{'acls': [{'aces': [{'destination': {'address': '192.168.2.0', 'netmask': '255.255.255.0'}, 'grant': 'permit', 'protocol': 'ip', 'line': 3, 'source': {'any': True}}], 'acl_type': 'extended', 'name': '100'}]}], 'state': 'merged'})

PLAY RECAP *******************************************************************************************************
asav-1                     : ok=4    changed=2    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   
iosvl2-0                   : ok=7    changed=2    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   
iosvl2-1                   : ok=3    changed=0    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0   
nxos-0                     : ok=4    changed=2    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   

6. Config確認

6-1. IOS機器

6行のACL要件に対し、4行しか設定されていませんでした。入っていないのは名前付きACLの120番と番号付きACLの130番です。

show_run
ip access-list extended TEST1
 deny   udp host 192.168.1.1 host 192.168.2.1 eq ntp
 permit ip 192.168.1.0 0.0.0.255 192.168.2.0 0.0.0.255
!
access-list 100 deny   udp any any eq ntp
access-list 100 deny   tcp any gt 1023 any eq www
show_ip_access-list
iosvl2-0#sh ip access-list
Extended IP access list 100
    110 deny udp any any eq ntp
    120 deny tcp any gt 1023 any eq www
Extended IP access list TEST1
    110 deny udp host 192.168.1.1 host 192.168.2.1 eq ntp
    130 permit ip 192.168.1.0 0.0.0.255 192.168.2.0 0.0.0.255

切り分けしたところ、送信元と宛先でアーギュメント指定方法が異なり、かつ宛先がネットワークだとうまく行きませんでした。(前者はホスト/ネットワーク、後者はany/ネットワークの混在。)
以下URLのcisco.iosのConfig生成用コードを見ると、送信元と宛先共通の関数の中で、本来**config_data[type]とすべきところが、**config_data["source"]となっていました。ここを修正したら6行出力されました。
GitHub - cisco.ios/plugins/module_utils/network/ios/rm_templates/acls.py

また、ここには記載していませんが、ポート番号をrange指定するケースでも、Configを生成する処理に失敗し、Failedになりました。上記URLのコードを見る限り、rangeは未実装のようです。

現状Configにコマンドip access-list TEST1のみが設定され、エントリが1行も存在しない状態でPlaybookを実行したケースでもFailedとなりました。

6-2. NX-OS機器

問題なく設定されていました。

show_run
ip access-list 100
  110 deny udp any any eq ntp
  120 deny tcp any range 1024 65535 any eq www
  130 permit ip any 192.168.2.0/24
ip access-list TEST1
  110 deny udp 192.168.1.1/32 192.168.2.1/32 eq ntp
  120 deny tcp 192.168.1.2/32 range 1024 65535 192.168.2.0/24 eq www
  130 permit ip 192.168.1.0/24 192.168.2.0/24

6-3. ASA機器

今回の要件では特に問題ありませんでしたが、IOS機器と同様range指定がFailedとなりました。

show_run
access-list TEST1 extended deny udp host 192.168.1.1 host 192.168.2.1 eq ntp
access-list TEST1 extended deny tcp host 192.168.1.2 gt 1023 192.168.2.0 255.255.255.0 eq www
access-list TEST1 extended permit ip 192.168.1.0 255.255.255.0 192.168.2.0 255.255.255.0
access-list 100 extended deny udp any any eq ntp
access-list 100 extended deny tcp any gt 1023 any eq www
access-list 100 extended permit ip any 192.168.2.0 255.255.255.0

7. 冪等性確認

同じPlaybookを再度実行したところ、IOS、NX-OS機器の一部要件、ASA機器の全要件でchangedになり、冪等性が担保されませんでした。
どのようなConfigが再投入されているか、xxx_aclsモジュールの実行結果をregister変数に格納し、変数内のcommandsを確認してみました。

7-1. IOS機器

同じシーケンス番号の既存Configを削除しようとしているようですが、コマンドが途切れています。もしこれが既存Configとして扱われているとしたら、追加Configと異なるので再投入されてしまうのも説明が付く気がします。

"ip access-list extended TEST1",
"no 120 deny tcp host 192.168.1.2",
"120 deny tcp host 192.168.1.2 gt 1023 192.168.2.0 0.0.0.255 eq www"

"ip access-list extended 100",
"no 120 deny tcp any",
"120 deny tcp any gt 1023 any eq www"

7-2. NX-OS機器

既存ConfigはIPアドレス/プレフィックスで表示され、追加Configはhost ホストアドレスもしくはネットワークアドレス ワイルドカードの形で表示されるせいかと思いましたが、名前付き拡張ACLの110番の冪等性が担保される理由が付きませんね。謎ですw

"ip access-list TEST1",
"no 120",
"120 deny tcp host 192.168.1.2 range 1024 65535 192.168.2.0 0.0.0.255 eq www"

"ip access-list TEST1",
"no 130",
"130 permit ip 192.168.1.0 0.0.0.255 192.168.2.0 0.0.0.255"

"ip access-list 100",
"no 120",
"120 deny tcp any range 1024 65535 any eq www"

"ip access-list 100",
"no 130",
"130 permit ip any 192.168.2.0 0.0.0.255"

7-3. ASA機器

既存Configの削除は行われず、新規の扱いでした。これも謎ですw

"access-list TEST1 line 1 extended deny udp host 192.168.1.1 host 192.168.2.1 eq ntp"
"access-list TEST1 line 2 extended deny tcp host 192.168.1.2 gt 1023 192.168.2.0 255.255.255.0 eq www"
(省略)

最後に

ACL設定変更のような単純作業とAnsibleは相性が良く、かつxxx_aclsはサポートするアーギュメントの種類が多いため、かなり有用なモジュールだと思います。ただ、今回の動作確認でいくつか想定通りの結果にならない点もあったので、本番作業で使えるのはもう少し先かなと感じました。今回見つかったバグは、GitHubにIssueを上げたいと思います。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?