meraki / dashboard-api-ansible

dashboard-api-ansible
https://meraki.github.io/dashboard-api-ansible/
GNU General Public License v3.0
13 stars 3 forks source link

cisco.meraki.meraki_mx_vlan is not idempotent when dhcp_options is supplied #4

Closed dwebr closed 1 year ago

dwebr commented 1 year ago

Meraki Collection

cisco.meraki 2.15.3

Ansible and Python

ansible [core 2.14.10] python version = 3.9.15 (main, Oct 22 2022, 11:54:42) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] (/opt/netadmin/ve/meraki3.9.15/bin/python) jinja version = 3.1.2 libyaml = True

Source File For Changes - vlan_change_test_ansible_2.json

[
    {
        "net_name": "01-Test-Ansible",
        "vlan_id": 11,
        "vlan_name": "Local_Office_Wired_Wireless",
        "vlan_subnet": "192.168.1.0/24",
        "vlan_appliance_ip": "192.168.1.1",
        "new_dhcp_options": [
            {
                "code": "242",
                "type": "text",
                "value": "HTTPSRVR=10.10.10.70,TLSSRVR=10.10.10.70,DOT1X=1,SIG=2"
            }
        ]
    }
]

Playbook - meraki_update_list_vlan_dhcp_options.yaml

---

- name: "PLAY 1: Update Meraki VLAN DHCP Options"
  hosts: meraki
  vars:
    meraki_headers:
      Content-Type: "applicaiton/json"
      X-Cisco-Meraki-API-Key: "{{ meraki_key }}"
    input: "{{ lookup('file','changes/vlan_change_test_ansible_2.json') | from_json }}"
  gather_facts: false
  connection: local

  tasks:
    - name: "DEBUG: Display the data from the input file"
      ansible.builtin.debug:
        msg: "{{ vlan }}"
      loop: "{{ input }}"
      loop_control:
        loop_var: vlan

    - name: "TASK 1 - Query for Current VLAN Information"
      cisco.meraki.meraki_mx_vlan:
        auth_key: "{{ meraki_key }}"
        state: query
        org_id: "{{ meraki_org_id }}"
        net_name: "{{ vlan.net_name }}"
        vlan_id: "{{ vlan.vlan_id }}"
      loop: "{{ input }}"
      loop_control:
        loop_var: vlan
      register: current_data_results
      when: vlan.net_name is defined

    - name: "DEBUG: current_data from TASK 1"
      ansible.builtin.debug:
        msg: "{{ current_data_results }}"
      when: vlan_dvlan_basics_results is defined

    - name: "TASK 2: Update VLAN Basics Using Input Data"
      cisco.meraki.meraki_mx_vlan:
        auth_key: "{{ meraki_key }}"
        state: present
        org_id: "{{ meraki_org_id }}"
        net_name: "{{ vlan.net_name }}"
        vlan_id: "{{ vlan.vlan_id }}"
        name: "{{ vlan.vlan_name }}"
        subnet: "{{ vlan.vlan_subnet }}"
        appliance_ip: "{{ vlan.vlan_appliance_ip }}"
      loop: "{{ input }}"
      loop_control:
        loop_var: vlan
      register: vlan_basics_results
      when: vlan.net_name is defined

    - name: "DEBUG: vlan_basics_results from TASK 2"
      ansible.builtin.debug:
        msg: "{{ vlan_basics_results }}"
      when: vlan_basics_results is defined

    - name: "TASK 3: Update VLAN and DHCP Options Usng Input Data"
      cisco.meraki.meraki_mx_vlan:
        auth_key: "{{ meraki_key }}"
        state: present
        org_id: "{{ meraki_org_id }}"
        net_name: "{{ vlan.net_name }}"
        vlan_id: "{{ vlan.vlan_id }}"
        name: "{{ vlan.vlan_name }}"
        subnet: "{{ vlan.vlan_subnet }}"
        appliance_ip: "{{ vlan.vlan_appliance_ip }}"
        dhcp_options: "{{ vlan.new_dhcp_options }}"
      loop: "{{ input }}"
      loop_control:
        loop_var: vlan
      register: vlan_dhcp_results
      when: vlan.net_name is defined

    - name: "DEBUG: vlan_dhcp_results from TASK 3"
      ansible.builtin.debug:
        msg: 
          - "{{ vlan_dhcp_results }}"
      when: vlan_dhcp_results is defined

Playbook Run when NO change to dhcp_options specified in json file to what is currently configured

The expected result should be no change. NOTE that TASK 2 is successful but only specifies the vlan basics and no dhcp_options


PLAY [PLAY 1: Update Meraki VLAN DHCP Options] ********************************************************************************************************************

TASK [DEBUG: Display the data from the input file] **** ok: [meraki_prod] => (item={'net_name': '01-Test-Ansible', 'vlan_id': 11, 'vlan_name': 'Local_Office_Wired_Wireless', 'vlan_subnet': '192.168.1.0/24', 'vlan_appliance_ip': '192.168.1.1', 'new_dhcp_options': [{'code': '242', 'type': 'text', 'value': 'HTTPSRVR=10.10.10.70,TLSSRVR=10.10.10.70,DOT1X=1,SIG=2'}]}) => { "msg": { "net_name": "01-Test-Ansible", "new_dhcp_options": [ { "code": "242", "type": "text", "value": "HTTPSRVR=10.10.10.70,TLSSRVR=10.10.10.70,DOT1X=1,SIG=2" } ], "vlan_appliance_ip": "192.168.1.1", "vlan_id": 11, "vlan_name": "Local_Office_Wired_Wireless", "vlan_subnet": "192.168.1.0/24" } }

TASK [TASK 1 - Query for Current VLAN Information] **** ok: [meraki_prod] => (item={'net_name': '01-Test-Ansible', 'vlan_id': 11, 'vlan_name': 'Local_Office_Wired_Wireless', 'vlan_subnet': '192.168.1.0/24', 'vlan_appliance_ip': '192.168.1.1', 'new_dhcp_options': [{'code': '242', 'type': 'text', 'value': 'HTTPSRVR=10.10.10.70,TLSSRVR=10.10.10.70,DOT1X=1,SIG=2'}]})

TASK [TASK 2: Update VLAN Basics Using Input Data] **** ok: [meraki_prod] => (item={'net_name': '01-Test-Ansible', 'vlan_id': 11, 'vlan_name': 'Local_Office_Wired_Wireless', 'vlan_subnet': '192.168.1.0/24', 'vlan_appliance_ip': '192.168.1.1', 'new_dhcp_options': [{'code': '242', 'type': 'text', 'value': 'HTTPSRVR=10.10.10.70,TLSSRVR=10.10.10.70,DOT1X=1,SIG=2'}]})

TASK [DEBUG: vlan_basics_results from TASK 2] ***** ok: [meraki_prod] => { "msg": { "changed": false, "msg": "All items completed", "results": [ { "ansible_loop_var": "vlan", "changed": false, "data": { "appliance_ip": "192.168.1.1", "dhcp_boot_options_enabled": false, "dhcp_handling": "Run a DHCP server", "dhcp_lease_time": "1 day", "dhcp_options": [ { "code": "242", "type": "text", "value": "HTTPSRVR=10.10.10.70,TLSSRVR=10.10.10.70,DOT1X=1,SIG=2" } ], "dns_nameservers": "upstream_dns", "fixed_ip_assignments": {}, "group_policy_id": "100", "id": 11, "interface_id": "622622648487113032", "ipv6": { "enabled": false }, "mandatory_dhcp": { "enabled": false }, "name": "Local_Office_Wired_Wireless", "network_id": "L_622622648484030932", "reserved_ip_ranges": [ { "comment": "Network-Reserved", "end": "192.168.1.20", "start": "192.168.1.1" } ], "subnet": "192.168.1.0/24" }, "failed": false, "invocation": { "module_args": { "appliance_ip": "192.168.1.1", "auth_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "dhcp_boot_filename": null, "dhcp_boot_next_server": null, "dhcp_boot_options_enabled": null, "dhcp_handling": null, "dhcp_lease_time": null, "dhcp_options": null, "dhcp_relay_server_ips": null, "dns_nameservers": null, "fixed_ip_assignments": null, "follow_redirects": "all", "host": "api.meraki.com", "internal_error_retry_time": 60, "name": "Local_Office_Wired_Wireless", "net_id": null, "net_name": "01-Test-Ansible", "org_id": "211212", "org_name": null, "output_format": "snakecase", "output_level": "normal", "protocol": "https", "rate_limit_retry_time": 165, "reserved_ip_range": null, "state": "present", "subnet": "192.168.1.0/24", "timeout": 30, "use_https": true, "use_proxy": false, "validate_certs": true, "vlan_id": 11, "vpn_nat_subnet": null } }, "response": "OK (unknown bytes)", "status": 200, "vlan": { "net_name": "01-Test-Ansible", "new_dhcp_options": [ { "code": "242", "type": "text", "value": "HTTPSRVR=10.10.10.70,TLSSRVR=10.10.10.70,DOT1X=1,SIG=2" } ], "vlan_appliance_ip": "192.168.1.1", "vlan_id": 11, "vlan_name": "Local_Office_Wired_Wireless", "vlan_subnet": "192.168.1.0/24" } } ], "skipped": false } }

TASK [TASK 3: Update VLAN and DHCP Options Usng Input Data] *** An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TypeError: 'NoneType' object is not subscriptable failed: [meraki_prod] (item={'net_name': '01-Test-Ansible', 'vlan_id': 11, 'vlan_name': 'Local_Office_Wired_Wireless', 'vlan_subnet': '192.168.1.0/24', 'vlan_appliance_ip': '192.168.1.1', 'new_dhcp_options': [{'code': '242', 'type': 'text', 'value': 'HTTPSRVR=10.10.10.70,TLSSRVR=10.10.10.70,DOT1X=1,SIG=2'}]}) => {"ansible_loop_var": "vlan", "changed": false, "module_stderr": "Traceback (most recent call last):\n File \"/tmp/ansible-tmp-1695138802.6366746-19975-23514250263467/AnsiballZ_meraki_mx_vlan.py\", line 107, in \n _ansiballz_main()\n File \"/tmp/ansible-tmp-1695138802.6366746-19975-23514250263467/AnsiballZ_meraki_mx_vlan.py\", line 99, in _ansiballz_main\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n File \"/tmp/ansible-tmp-1695138802.6366746-19975-23514250263467/AnsiballZ_meraki_mx_vlan.py\", line 47, in invoke_module\n runpy.run_module(mod_name='ansible_collections.cisco.meraki.plugins.modules.meraki_mx_vlan', init_globals=dict(_module_fqn='ansible_collections.cisco.meraki.plugins.modules.meraki_mx_vlan', _modlib_path=modlib_path),\n File \"/opt/netadmin/pyenv/versions/3.9.15/lib/python3.9/runpy.py\", line 225, in run_module\n return _run_module_code(code, init_globals, run_name, mod_spec)\n File \"/opt/netadmin/pyenv/versions/3.9.15/lib/python3.9/runpy.py\", line 97, in _run_module_code\n _run_code(code, mod_globals, init_globals,\n File \"/opt/netadmin/pyenv/versions/3.9.15/lib/python3.9/runpy.py\", line 87, in _run_code\n exec(code, run_globals)\n File \"/tmp/ansible_cisco.meraki.meraki_mx_vlan_payload_h62hojy7/ansible_cisco.meraki.meraki_mx_vlan_payload.zip/ansible_collections/cisco/meraki/plugins/modules/meraki_mx_vlan.py\", line 585, in \n File \"/tmp/ansible_cisco.meraki.meraki_mx_vlan_payload_h62hojy7/ansible_cisco.meraki.meraki_mx_vlan_payload.zip/ansible_collections/cisco/meraki/plugins/modules/meraki_mx_vlan.py\", line 562, in main\n File \"/tmp/ansible_cisco.meraki.meraki_mx_vlan_payload_h62hojy7/ansible_cisco.meraki.meraki_mx_vlan_payload.zip/ansible_collections/cisco/meraki/plugins/module_utils/network/meraki/meraki.py\", line 205, in generate_diff\nTypeError: 'NoneType' object is not subscriptable\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1, "vlan": {"net_name": "01-Test-Ansible", "new_dhcp_options": [{"code": "242", "type": "text", "value": "HTTPSRVR=10.10.10.70,TLSSRVR=10.10.10.70,DOT1X=1,SIG=2"}], "vlan_appliance_ip": "192.168.1.1", "vlan_id": 11, "vlan_name": "Local_Office_Wired_Wireless", "vlan_subnet": "192.168.1.0/24"}}

PLAY RECAP **** meraki_prod : ok=4 changed=0 unreachable=0 failed=1 skipped=1 rescued=0 ignored=0

# Playbook run when json file does contain a change to the DHCP option

PLAY [PLAY 1: Update Meraki VLAN DHCP Options] ****

TASK [DEBUG: Display the data from the input file] **** ok: [meraki_prod] => (item={'net_name': '01-Test-Ansible', 'vlan_id': 11, 'vlan_name': 'Local_Office_Wired_Wireless', 'vlan_subnet': '192.168.1.0/24', 'vlan_appliance_ip': '192.168.1.1', 'new_dhcp_options': [{'code': '242', 'type': 'text', 'value': 'HTTPSRVR=10.10.10.100,TLSSRVR=10.10.10.100,DOT1X=1,SIG=2'}]}) => { "msg": { "net_name": "01-Test-Ansible", "new_dhcp_options": [ { "code": "242", "type": "text", "value": "HTTPSRVR=10.10.10.100,TLSSRVR=10.10.10.100,DOT1X=1,SIG=2" } ], "vlan_appliance_ip": "192.168.1.1", "vlan_id": 11, "vlan_name": "Local_Office_Wired_Wireless", "vlan_subnet": "192.168.1.0/24" } }

TASK [TASK 1 - Query for Current VLAN Information] **** ok: [meraki_prod] => (item={'net_name': '01-Test-Ansible', 'vlan_id': 11, 'vlan_name': 'Local_Office_Wired_Wireless', 'vlan_subnet': '192.168.1.0/24', 'vlan_appliance_ip': '192.168.1.1', 'new_dhcp_options': [{'code': '242', 'type': 'text', 'value': 'HTTPSRVR=10.10.10.100,TLSSRVR=10.10.10.100,DOT1X=1,SIG=2'}]})

TASK [TASK 2: Update VLAN Basics Using Input Data] **** ok: [meraki_prod] => (item={'net_name': '01-Test-Ansible', 'vlan_id': 11, 'vlan_name': 'Local_Office_Wired_Wireless', 'vlan_subnet': '192.168.1.0/24', 'vlan_appliance_ip': '192.168.1.1', 'new_dhcp_options': [{'code': '242', 'type': 'text', 'value': 'HTTPSRVR=10.10.10.100,TLSSRVR=10.10.10.100,DOT1X=1,SIG=2'}]})

TASK [DEBUG: vlan_basics_results from TASK 2] ***** ok: [meraki_prod] => { "msg": { "changed": false, "msg": "All items completed", "results": [ { "ansible_loop_var": "vlan", "changed": false, "data": { "appliance_ip": "192.168.1.1", "dhcp_boot_options_enabled": false, "dhcp_handling": "Run a DHCP server", "dhcp_lease_time": "1 day", "dhcp_options": [ { "code": "242", "type": "text", "value": "HTTPSRVR=10.10.10.70,TLSSRVR=10.10.10.70,DOT1X=1,SIG=2" } ], "dns_nameservers": "upstream_dns", "fixed_ip_assignments": {}, "group_policy_id": "100", "id": 11, "interface_id": "622622648487113032", "ipv6": { "enabled": false }, "mandatory_dhcp": { "enabled": false }, "name": "Local_Office_Wired_Wireless", "network_id": "L_622622648484030932", "reserved_ip_ranges": [ { "comment": "Network-Reserved", "end": "192.168.1.20", "start": "192.168.1.1" } ], "subnet": "192.168.1.0/24" }, "failed": false, "invocation": { "module_args": { "appliance_ip": "192.168.1.1", "auth_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "dhcp_boot_filename": null, "dhcp_boot_next_server": null, "dhcp_boot_options_enabled": null, "dhcp_handling": null, "dhcp_lease_time": null, "dhcp_options": null, "dhcp_relay_server_ips": null, "dns_nameservers": null, "fixed_ip_assignments": null, "follow_redirects": "all", "host": "api.meraki.com", "internal_error_retry_time": 60, "name": "Local_Office_Wired_Wireless", "net_id": null, "net_name": "01-Test-Ansible", "org_id": "211212", "org_name": null, "output_format": "snakecase", "output_level": "normal", "protocol": "https", "rate_limit_retry_time": 165, "reserved_ip_range": null, "state": "present", "subnet": "192.168.1.0/24", "timeout": 30, "use_https": true, "use_proxy": false, "validate_certs": true, "vlan_id": 11, "vpn_nat_subnet": null } }, "response": "OK (unknown bytes)", "status": 200, "vlan": { "net_name": "01-Test-Ansible", "new_dhcp_options": [ { "code": "242", "type": "text", "value": "HTTPSRVR=10.10.10.100,TLSSRVR=10.10.10.100,DOT1X=1,SIG=2" } ], "vlan_appliance_ip": "192.168.1.1", "vlan_id": 11, "vlan_name": "Local_Office_Wired_Wireless", "vlan_subnet": "192.168.1.0/24" } } ], "skipped": false } }

TASK [TASK 3: Update VLAN and DHCP Options Usng Input Data] *** changed: [meraki_prod] => (item={'net_name': '01-Test-Ansible', 'vlan_id': 11, 'vlan_name': 'Local_Office_Wired_Wireless', 'vlan_subnet': '192.168.1.0/24', 'vlan_appliance_ip': '192.168.1.1', 'new_dhcp_options': [{'code': '242', 'type': 'text', 'value': 'HTTPSRVR=10.10.10.100,TLSSRVR=10.10.10.100,DOT1X=1,SIG=2'}]})

TASK [DEBUG: vlan_dhcp_results from TASK 3] *** ok: [meraki_prod] => { "msg": [ { "changed": true, "msg": "All items completed", "results": [ { "ansible_loop_var": "vlan", "changed": true, "data": { "appliance_ip": "192.168.1.1", "dhcp_boot_options_enabled": false, "dhcp_handling": "Run a DHCP server", "dhcp_lease_time": "1 day", "dhcp_options": [ { "code": "242", "type": "text", "value": "HTTPSRVR=10.10.10.100,TLSSRVR=10.10.10.100,DOT1X=1,SIG=2" } ], "dns_nameservers": "upstream_dns", "fixed_ip_assignments": {}, "group_policy_id": "100", "id": 11, "interface_id": "622622648487113032", "ipv6": { "enabled": false }, "mandatory_dhcp": { "enabled": false }, "name": "Local_Office_Wired_Wireless", "network_id": "L_622622648484030932", "reserved_ip_ranges": [ { "comment": "Network-Reserved", "end": "192.168.1.20", "start": "192.168.1.1" } ], "subnet": "192.168.1.0/24" }, "diff": { "after": { "dhcp_options": [ { "code": "242", "type": "text", "value": "HTTPSRVR=10.10.10.100,TLSSRVR=10.10.10.100,DOT1X=1,SIG=2" } ] }, "before": { "dhcp_options": [ { "code": "242", "type": "text", "value": "HTTPSRVR=10.10.10.70,TLSSRVR=10.10.10.70,DOT1X=1,SIG=2" } ] } }, "failed": false, "invocation": { "module_args": { "appliance_ip": "192.168.1.1", "auth_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "dhcp_boot_filename": null, "dhcp_boot_next_server": null, "dhcp_boot_options_enabled": null, "dhcp_handling": null, "dhcp_lease_time": null, "dhcp_options": [ { "code": 242, "type": "text", "value": "HTTPSRVR=10.10.10.100,TLSSRVR=10.10.10.100,DOT1X=1,SIG=2" } ], "dhcp_relay_server_ips": null, "dns_nameservers": null, "fixed_ip_assignments": null, "follow_redirects": "all", "host": "api.meraki.com", "internal_error_retry_time": 60, "name": "Local_Office_Wired_Wireless", "net_id": null, "net_name": "01-Test-Ansible", "org_id": "211212", "org_name": null, "output_format": "snakecase", "output_level": "normal", "protocol": "https", "rate_limit_retry_time": 165, "reserved_ip_range": null, "state": "present", "subnet": "192.168.1.0/24", "timeout": 30, "use_https": true, "use_proxy": false, "validate_certs": true, "vlan_id": 11, "vpn_nat_subnet": null } }, "response": "OK (unknown bytes)", "status": 200, "vlan": { "net_name": "01-Test-Ansible", "new_dhcp_options": [ { "code": "242", "type": "text", "value": "HTTPSRVR=10.10.10.100,TLSSRVR=10.10.10.100,DOT1X=1,SIG=2" } ], "vlan_appliance_ip": "192.168.1.1", "vlan_id": 11, "vlan_name": "Local_Office_Wired_Wireless", "vlan_subnet": "192.168.1.0/24" } } ], "skipped": false } ] }

PLAY RECAP **** meraki_prod : ok=6 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0

obrigg commented 1 year ago

Hello, With the introduction of the new official Ansible collection, can you try running the playbook with the networks_appliance_vlans module and the networks_appliance_vlans_settings module?

fmunozmiranda commented 1 year ago

Hi @dwebr do you try with new modules?

dwebr commented 1 year ago

I did use the new networks_appliance_vlans module and it now works. Really appreciate the new modules aligning with the SDK as well.