ansible-collections / cisco.ios

Ansible Network Collection for Cisco IOS
GNU General Public License v3.0
261 stars 162 forks source link

cisco.ios.ios_l2_interfaces: fail on configuring trunk interface with a bunch of vlans #1061

Open alezkv opened 3 weeks ago

alezkv commented 3 weeks ago
SUMMARY

The ios_l2_interfaces task is failing with an error when attempting to apply configurations with multiple VLANs.

failed: [csw] (item={'key': 'gw1', 'value': 'Gi2/0/21'}) => {"ansible_loop_var": "interface", "changed": false, "interface": {"key": "gw1", "value": "Gi2/0/21"}, "module_stderr": "\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\r\nCommand rejected: Bad VLAN list - character #223 is a comma at end of list.\r\nkrs-csw-1(config-if)#", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error"}

I resolved this issue in my fork some time ago, but it was for a much older version. https://github.com/ansible-collections/cisco.ios/compare/main...serverkursk:cisco.ios:sk

I can adapt that fix to the current codebase, but it would be great to hear some hints from the maintainers about proper integration.

ISSUE TYPE
COMPONENT NAME

cisco.ios.ios_l2_interfaces

ANSIBLE VERSION
# ansible --version
ansible [core 2.16.6]
  config file = /drone/src/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.12/site-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.12.3 (main, Apr 10 2024, 14:35:18) [GCC 12.2.0] (/usr/local/bin/python)
  jinja version = 3.1.3
  libyaml = True
COLLECTION VERSION
# ansible-galaxy collection list cisco.ios

# /root/.ansible/collections/ansible_collections
Collection Version
---------- -------
cisco.ios  8.0.0

# /usr/local/lib/python3.12/site-packages/ansible_collections
Collection Version
---------- -------
cisco.ios  5.3.0
CONFIGURATION
# ansible-config dump --only-changed
/bin/sh: 1: less: not found
# ansible-config dump --only-changed
CONFIG_FILE() = /drone/src/ansible.cfg
HOST_KEY_CHECKING(/drone/src/ansible.cfg) = False
PERSISTENT_COMMAND_TIMEOUT(/drone/src/ansible.cfg) = 600
OS / ENVIRONMENT
Cisco IOS Software, C3750E Software (C3750E-UNIVERSALK9-M), Version 15.2(4)E6, RELEASE SOFTWARE (fc4)
STEPS TO REPRODUCE

Try configuring a trunk interface with a number of VLANs exceeding the "CLI limit for an IOS device for a single command." In my case, the module generated the following single line of configuration and attempted to apply it to a switch:

switchport trunk allowed vlan 2,100-101,234,903,1412,1453-1454,2001,2003-2004,2006-2008,2011-2012,2015-2019,2021-2026,2029-2032,2035-2038,2040-2042,2044-2046,2048,2052,2054,2056-2061,2063-2066,2068,2070-2076,2078-2079,2081-2082,2084,2086-2091,2093,2095,2098,2100,2103,2105,2107-2109,2113-2115,2117-2120,2122-2123,2125,2127-2128,2130,2132-2133,2135,2137,2139-2141,2145-2146,2148,2150,2156,2158-2159,2164,2166-2168,2171,2173,2176,2179-2181,2185,2187-2188,2191-2195,2197-2199,2203-2204,2206-2207,2210-2215,2217,2225,2227-2228,2230-2231,2233-2234,2238-2240,2242-2243,2246,2249,2251,2258,2260,2264,2266-2271,2273-2274,2276,2278-2280,2282,2284,2286,2288,2290-2291,2293,2295-2296,2302-2303,2308-2312,2316-2317,2323,2326,2329,2331-2333,2339-2342,2344-2345,2349,2351,2353-2359,2361-2362,2364,2366,2369,2371,2373-2374,2376,2378,2384-2390,2392-2393,2395,2398,2401,2403-2404,2407-2409,2411-2412,2414-2415,2417,2421-2422,2425-2427,2430,2433-2436,2438,2440-2442,2444-2445,2447,2450-2451,2455,2457,2462-2463,2466-2469,2475-2477,2479-2480,2482-2487,2492,2495,2501-2503,2505,2510,2513-2516,2518-2519,2522-2524,2530-2533,2535-2536,2540,2542-2543,2545-2547,2549-2552,2554-2557,2559-2561,2565,2567-2569,2571-2573,2575-2583,2585,2587-2588,2591,2595,2601-2602,2604-2612,2617,2619-2620,2622,2624-2625,2628-2667,2669-2800

When this command is executed on the device, it produces the following results:

csw(config)#interface GigabitEthernet2/0/21
csw(config-if)#$3-2004,2006-2008,2011-2012,2015-2019,2021-2026,2029-2032,2035-2038,2040-2042,2044-2046,2048,2052,2054,2056-2061,2063-2066,2068,2070-2076,2078-2079,2081-2082,2084,2086-2091,2093,2095,
Command rejected: Bad VLAN list - character #223 is a comma at end of list.
csw(config-if)#

In my setup, I generate a list of VLANs from JSON, but that's not relevant for this case. Therefore, I have created a minimal reproducible playbook.

# cat repro.yaml
---
- hosts: all
  gather_facts: false

  tasks:
    - name: configure vlan on interfaces
      cisco.ios.ios_l2_interfaces:
        config:
        - name: "Gi3/0/33"
          mode: "trunk"
          trunk:
            allowed_vlans: "2,100,101,234,903,1412,1453,1454,2001,2003,2004,2006,2007,2008,2011,2012,2015,2016,2017,2018,2019,2021,2022,2023,2024,2025,2026,2029,2030,2031,2032,2035,2036,2037,2038,2040,2040,2041,2042,2044,2045,2046,2048,2052,2054,2056,2057,2058,2059,2060,2061,2063,2064,2065,2066,2068,2070,2071,2072,2073,2074,2075,2076,2078,2079,2081,2082,2084,2086,2087,2088,2089,2090,2091,2093,2095,2098"
            encapsulation: dot1q
        state: replaced

Play results:

fatal: [csw]: FAILED! => {"changed": false, "module_stderr": "5-2019,2021-2026,2029-2032,2035-2038,2040-2042,2044-2046,2048,2052,2054,2056-2061,2063-2066,2068,2070-2076,2078-2079,2081-2082,2084,2086-2091,2093,2095,\u0007\u0007\u0007\u0007\r\nCommand rejected: Bad VLAN list - character #223 is a comma at end of list.\r\nkrs-csw-1(config-if)#", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error"}
EXPECTED RESULTS

The module should generate commands that can be fed to the device without errors. When Cisco 'show run' displays an interface with many VLANs, it breaks down the configuration into multiple lines as follows:

interface GigabitEthernet2/0/21
 switchport trunk allowed vlan 2,100,101,234,903,1412,1453,1454,2052,2054
 switchport trunk allowed vlan add 2056-2061,2063-2066,2068,2070-2076,2078,2079
 switchport trunk allowed vlan add 2081,2082,2084,2086-2091,2093,2095,2098,2100
 switchport trunk allowed vlan add 2103,2105,2107-2109,2113-2115,2117-2120,2122
 switchport trunk allowed vlan add 2123,2125,2127,2128,2130,2132,2133,2135,2137
 switchport trunk allowed vlan add 2139-2141,2145,2146,2148,2150,2156,2158,2159
 ...

The module can read such representations, but it fails to produce something similar and instead tries to apply all changes in one line. When applying configurations to a device, the module should split a large single command into several parts.

ACTUAL RESULTS

The module fails to apply a large single command on the device:

# ansible-playbook -i hosts repro.yaml -vvvv
ansible-playbook [core 2.16.6]
  config file = /drone/src/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.12/site-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible-playbook
  python version = 3.12.3 (main, Apr 10 2024, 14:35:18) [GCC 12.2.0] (/usr/local/bin/python)
  jinja version = 3.1.3
  libyaml = True
Using /drone/src/ansible.cfg as config file
setting up inventory plugins
Loading collection ansible.builtin from
host_list declined parsing /drone/src/hosts as it did not pass its verify_file() method
script declined parsing /drone/src/hosts as it did not pass its verify_file() method
auto declined parsing /drone/src/hosts as it did not pass its verify_file() method
Parsed /drone/src/hosts inventory source with ini plugin
Loading collection cisco.ios from /root/.ansible/collections/ansible_collections/cisco/ios
Loading callback plugin default of type stdout, v2.0 from /usr/local/lib/python3.12/site-packages/ansible/plugins/callback/default.py
Skipping callback 'default', as we already have a stdout callback.
Skipping callback 'minimal', as we already have a stdout callback.
Skipping callback 'oneline', as we already have a stdout callback.

PLAYBOOK: repro.yaml *************************************************************************************************************************************************************************************************
Positional arguments: repro.yaml
verbosity: 4
connection: ssh
become_method: sudo
tags: ('all',)
inventory: ('/drone/src/hosts',)
forks: 5
1 plays in repro.yaml

PLAY [all] ***********************************************************************************************************************************************************************************************************

TASK [configure vlan on interfaces] **********************************************************************************************************************************************************************************
task path: /drone/src/repro.yaml:6
Loading collection ansible.netcommon from /root/.ansible/collections/ansible_collections/ansible/netcommon
Loading collection ansible.utils from /root/.ansible/collections/ansible_collections/ansible/utils
redirecting (type: become) ansible.builtin.enable to ansible.netcommon.enable
<> Using network group action cisco.ios.ios for cisco.ios.ios_l2_interfaces
<> attempting to start connection
<> using connection plugin ansible.netcommon.network_cli
Found ansible-connection at path /usr/local/bin/ansible-connection
<> local domain socket does not exist, starting it
<> control socket path is /root/.ansible/pc/713152bf27
<> Loading collection ansible.builtin from
<> Loading collection ansible.netcommon from /root/.ansible/collections/ansible_collections/ansible/netcommon
<> Loading collection ansible.utils from /root/.ansible/collections/ansible_collections/ansible/utils
<> Loading collection cisco.ios from /root/.ansible/collections/ansible_collections/cisco/ios
<> local domain socket listeners started successfully
<> loaded cliconf plugin ansible_collections.cisco.ios.plugins.cliconf.ios from path /root/.ansible/collections/ansible_collections/cisco/ios/plugins/cliconf/ios.py for network_os cisco.ios.ios
<> ssh type is set to auto
<> autodetecting ssh_type
[WARNING]: ansible-pylibssh not installed, falling back to paramiko
<> ssh type is now set to paramiko
<> Loading collection ansible.builtin from
<> local domain socket path is /root/.ansible/pc/713152bf27
<> ANSIBLE_NETWORK_IMPORT_MODULES: enabled
<> ANSIBLE_NETWORK_IMPORT_MODULES: found cisco.ios.ios_l2_interfaces  at /root/.ansible/collections/ansible_collections/cisco/ios/plugins/modules/ios_l2_interfaces.py
<> ANSIBLE_NETWORK_IMPORT_MODULES: running cisco.ios.ios_l2_interfaces
<> ANSIBLE_NETWORK_IMPORT_MODULES: complete
fatal: [csw]: FAILED! => {
    "changed": false,
    "module_stderr": "5-2019,2021-2026,2029-2032,2035-2038,2040-2042,2044-2046,2048,2052,2054,2056-2061,2063-2066,2068,2070-2076,2078-2079,2081-2082,2084,2086-2091,2093,2095,\u0007\u0007\u0007\u0007\r\nCommand rejected: Bad VLAN list - character #223 is a comma at end of list.\r\nkrs-csw-1(config-if)#",
    "module_stdout": "",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error"
}

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