Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The cisco.ios.ios_config module doesn't push valid duplicate lines (identical config lines in different subcatecories) to the switch #740

Open
RobKoerts opened this issue Feb 3, 2023 · 0 comments · May be fixed by #1050 or #778
Assignees
Labels
config ios_config module

Comments

@RobKoerts
Copy link

RobKoerts commented Feb 3, 2023

SUMMARY

We're experiencing an issue with the cisco.ios.ios_config module. It appears that it corrects/removes duplicate lines before pushing config to the switch. Our task configures these duplicate lines underneath different subcategories however, so they're actually valid code instead of real duplicates. We tried several module options (lines, block, etc.) to see if that would solve our issue, but unfortunately it didn't.

Simple example

Class-map A
Line A
Class-map B
Line A
Class-map C
Line A

turns into

Class-map A
Line A
Class-map B
Class-map C

Could you please assist with this issue? Is is a functionality that can be disabled? Is it an issue that requires a fix? Can we code an easy work-around?

ISSUE TYPE
  • Bug Report
COMPONENT NAME

cisco.ios.ios_config

ANSIBLE VERSION
ansible 2.9.18
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/installation/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.6/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.6.8 (default, Jan 14 2022, 11:04:20) [GCC 8.5.0 20210514 (Red Hat 8.5.0-7)]
COLLECTION VERSION
usage: ansible-galaxy collection [-h] COLLECTION_ACTION ...
ansible-galaxy collection: error: argument COLLECTION_ACTION: invalid choice: 'list' (choose from 'init', 'build', 'publish', 'install')

we're using: 
tnl3p/redhat/ansible-collections/ansible_collection-cisco.ios/3.3.0/ansible_collection-cisco.ios-3.3.0-noarch.tar.gz
CONFIGURATION
No output
OS / ENVIRONMENT

Server that runs Ansible

NAME="Red Hat Enterprise Linux"
VERSION="8.6 (Ootpa)"
ID="rhel"
ID_LIKE="fedora"
VERSION_ID="8.6"
PLATFORM_ID="platform:el8"
PRETTY_NAME="Red Hat Enterprise Linux 8.6 (Ootpa)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:redhat:enterprise_linux:8::baseos"
HOME_URL="https://www.redhat.com/"
DOCUMENTATION_URL="https://access.redhat.com/documentation/red_hat_enterprise_linux/8/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"

REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 8"
REDHAT_BUGZILLA_PRODUCT_VERSION=8.6
REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux"
REDHAT_SUPPORT_PRODUCT_VERSION="8.6"

.
Target switch

Cisco IOS XE Software, Version 17.03.05
Cisco IOS Software [Amsterdam], Catalyst L3 Switch Software (CAT9K_IOSXE), Version 17.3.5, RELEASE SOFTWARE (fc2)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2022 by Cisco Systems, Inc.
Compiled Wed 09-Feb-22 10:41 by mcpre
STEPS TO REPRODUCE

Here's an example regarding QoS policy-maps from A to Z, including a debug and the configured and expected switch config:

Policy-map task

- name: Configure policy-map
  cisco.ios.ios_config:
    lines: "{{ lookup('ansible.builtin.template', 'policy_map.j2') | from_yaml }}"
    parents:
      - "policy-map {{ policy_map.name }}"
  loop: "{{ switch_qos.policy_map }}"
  loop_control:
    label: "{{ policy_map.name }}"
    loop_var: "policy_map"
  when: switch_qos.policy_map is defined
  notify: Save startup-config
  tags:
    - switch_iosxe
    - switch_iosxe-qos

.
Policy-map JINJA2 template

{% if policy_map.description is defined %}
- "description {{ policy_map.description }}" 
{% endif %}

{% for class in policy_map.class %}
- "class {{ class.name }}"

{% if class.bandwidth.throughput is defined %}
{% if (class.bandwidth.throughput|string)[-1] == '%' %}
- "bandwidth percent {{ (class.bandwidth.throughput|string)[:-1] }}"
{% else %}
- "bandwidth {{ class.bandwidth.throughput }}"
{% endif %}
{% endif %}

{% if class.bandwidth.remaining is defined %}
{% if (class.bandwidth.remaining|string)[-1] == '%' %}
- "bandwidth remaining percent {{ (class.bandwidth.remaining|string)[:-1] }}"
{% else %}
- "bandwidth remaining ratio {{ class.bandwidth.remaining }}"
{% endif %}
{% endif %}

{% if class.policing.rate is defined %}
{% if class.policing.rate_burst is defined %}
- "police rate percent {{ (class.policing.rate|string)[:-1] }} burst {{ class.policing.rate_burst }} ms"
{% else %}
- "police rate percent {{ (class.policing.rate)[:-1] }}"
{% endif %}
{% for rule in class.policing.rules | default([]) %}
- "{{ rule.type }}-action {{ rule.action }}"
{% endfor %}
- "exit" 
{% endif %}

{% if class.policing.tbr is defined %}
{% if class.policing.tbr_burst is defined %}
- "police {{ class.policing.tbr }} {{ class.policing.tbr_burst }}"
{% else %}
- "police {{ class.policing.tbr }}"
{% endif %}
{% for rule in class.policing.rules | default([]) %}
- "{{ rule.type }}-action {{ rule.action }}"
{% endfor %}
- "exit" 
{% endif %}

{% if class.priority.level is defined %}
{% if class.priority.level_throughput is defined %}
{% if (class.priority.level_throughput|string)[-1] == '%' %}
- "priority level {{ class.priority.level }} percent {{ (class.priority.level_throughput|string)[:-1] }}"
{% else %}
- "priority level {{ class.priority.level }} {{ class.priority.level_throughput }}"
{% endif %}
{% else %}
- "priority level {{ class.priority.level }}"
{% endif %}
{% endif %}

{% if class.queue_buffers_ratio is defined %}
- "queue-buffers ratio {{ class.queue_buffers_ratio }}"
{% endif %}

{% if class.set.cos is defined %}
- "set cos {{ class.set.cos }}"
{% endif %}

{% if class.set.dscp is defined %}
- "set dscp {{ class.set.dscp }}"
{% endif %}

{% if class.set.precedence is defined %}
- "set precedence {{ class.set.precedence }}"
{% endif %}

{% if class.shape is defined %}
{% if (class.shape|string)[-1] == '%' %}
- "shape average percent {{ (class.shape|string)[:-1] }}"
{% else %}
- "shape average {{ class.shape }}"
{% endif %}
{% endif %}

- "exit"
{% endfor %}

.
Variables

### Define 1Gb egress policy
    - name: 'PM_E_1gb'
      description: 'policy map that processes all outgoing traffic for 1Gb interfaces'
      class:
        - name: 'class-default'
          bandwidth:
            remaining: 3
          policing:
            rate: '10%'
            rules:
              - type: 'conform'
                action: 'transmit'
              - type: 'exceed'
                action: 'drop'
        - name: 'CM_Q1'
          bandwidth:
            remaining: 3
        - name: 'CM_Q2'
          bandwidth:
            remaining: 3
        - name: 'CM_Q3'
          bandwidth:
            remaining: 3
        - name: 'CM_Q4'
          bandwidth:
            remaining: 3
        - name: 'CM_Q5'
          bandwidth:
            remaining: 3
        - name: 'CM_Q6'
          priority:
            level: 2
        - name: 'CM_Q7'
          priority:
            level: 1
EXPECTED RESULTS

This is the switch config we expected:

!
policy-map PM_E_1gb
 description policy map that processes all outgoing traffic for 1Gb interfaces
 class CM_Q1
  bandwidth remaining ratio 3
 class CM_Q2
  bandwidth remaining ratio 3
 class CM_Q3
  bandwidth remaining ratio 3
 class CM_Q4
  bandwidth remaining ratio 3
 class CM_Q5
  bandwidth remaining ratio 3
 class CM_Q6
  priority level 2
 class CM_Q7
  priority level 1
 class class-default
  bandwidth remaining ratio 3
  police rate percent 10
   conform-action transmit 
   exceed-action drop 
ACTUAL RESULTS

After running the task the switch config shows that 'bandwidth remaining ratio 3' has only been applied to class-default.
We ran some tests, and its always the first 'bandwidth remaining ratio 3' command to the switch that gets applied. In this case class-default.

!
policy-map PM_E_1gb
 description policy map that processes all outgoing traffic for 1Gb interfaces
 class CM_Q1
 class CM_Q2
 class CM_Q3
 class CM_Q4
 class CM_Q5
 class CM_Q6
  priority level 2
 class CM_Q7
  priority level 1
 class class-default
  bandwidth remaining ratio 3
  police rate percent 10
   conform-action transmit 
   exceed-action drop 

.
Task output
This contains the debug and -vvv task output:

ok: [cisco-2] => (item=PM_E_1gb) => {
    "msg": [
        "description policy map that processes all outgoing traffic for 1Gb interfaces",
        "class class-default",
        "bandwidth remaining ratio 3",
        "police rate percent 10",
        "conform-action transmit",
        "exceed-action drop",
        "exit",
        "exit",
        "class CM_Q1",
        "bandwidth remaining ratio 3",
        "exit",
        "class CM_Q2",
        "bandwidth remaining ratio 3",
        "exit",
        "class CM_Q3",
        "bandwidth remaining ratio 3",
        "exit",
        "class CM_Q4",
        "bandwidth remaining ratio 3",
        "exit",
        "class CM_Q5",
        "bandwidth remaining ratio 3",
        "exit",
        "class CM_Q6",
        "priority level 2",
        "exit",
        "class CM_Q7",
        "priority level 1",
        "exit"
    ]
}

<cisco-2> Using network group action cisco.ios.ios for cisco.ios.ios_config
<cisco-2> ANSIBLE_NETWORK_IMPORT_MODULES: enabled
<cisco-2> ANSIBLE_NETWORK_IMPORT_MODULES: found cisco.ios.ios_config  at /usr/share/ansible/collections/ansible_collections/cisco/ios/plugins/modules/ios_config.py
<cisco-2> ANSIBLE_NETWORK_IMPORT_MODULES: running cisco.ios.ios_config
<cisco-2> ANSIBLE_NETWORK_IMPORT_MODULES: complete
changed: [cisco-2] => (item=PM_E_1gb) => {
    "ansible_loop_var": "policy_map",
    "banners": {},
    "changed": true,
    "commands": [
        "policy-map PM_E_1gb",
        "description policy map that processes all outgoing traffic for 1Gb interfaces",
        "class class-default",
        "bandwidth remaining ratio 3",
        "police rate percent 10",
        "conform-action transmit",
        "exceed-action drop",
        "exit",
        "class CM_Q1",
        "class CM_Q2",
        "class CM_Q3",
        "class CM_Q4",
        "class CM_Q5",
        "class CM_Q6",
        "priority level 2",
        "class CM_Q7",
        "priority level 1"
    ],
    "invocation": {
        "module_args": {
            "after": null,
            "backup": false,
            "backup_options": null,
            "before": null,
            "defaults": false,
            "diff_against": null,
            "diff_ignore_lines": null,
            "intended_config": null,
            "lines": [
                "description policy map that processes all outgoing traffic for 1Gb interfaces",
                "class class-default",
                "bandwidth remaining ratio 3",
                "police rate percent 10",
                "conform-action transmit",
                "exceed-action drop",
                "exit",
                "exit",
                "class CM_Q1",
                "bandwidth remaining ratio 3",
                "exit",
                "class CM_Q2",
                "bandwidth remaining ratio 3",
                "exit",
                "class CM_Q3",
                "bandwidth remaining ratio 3",
                "exit",
                "class CM_Q4",
                "bandwidth remaining ratio 3",
                "exit",
                "class CM_Q5",
                "bandwidth remaining ratio 3",
                "exit",
                "class CM_Q6",
                "priority level 2",
                "exit",
                "class CM_Q7",
                "priority level 1",
                "exit"
            ],
            "match": "none",
            "multiline_delimiter": "@",
            "parents": [
                "policy-map PM_E_1gb"
            ],
            "provider": null,
            "replace": "line",
            "running_config": null,
            "save_when": "never",
            "src": null
        }
    },
    "policy_map": {
        "class": [
            {
                "bandwidth": {
                    "remaining": 3
                },
                "name": "class-default",
                "policing": {
                    "rate": "10%",
                    "rules": [
                        {
                            "action": "transmit",
                            "type": "conform"
                        },
                        {
                            "action": "drop",
                            "type": "exceed"
                        }
                    ]
                }
            },
            {
                "bandwidth": {
                    "remaining": 3
                },
                "name": "CM_Q1"
            },
            {
                "bandwidth": {
                    "remaining": 3
                },
                "name": "CM_Q2"
            },
            {
                "bandwidth": {
                    "remaining": 3
                },
                "name": "CM_Q3"
            },
            {
                "bandwidth": {
                    "remaining": 3
                },
                "name": "CM_Q4"
            },
            {
                "bandwidth": {
                    "remaining": 3
                },
                "name": "CM_Q5"
            },
            {
                "name": "CM_Q6",
                "priority": {
                    "level": 2
                }
            },
            {
                "name": "CM_Q7",
                "priority": {
                    "level": 1
                }
            }
        ],
        "description": "policy map that processes all outgoing traffic for 1Gb interfaces",
        "name": "PM_E_1gb"
    },
    "updates": [
        "policy-map PM_E_1gb",
        "description policy map that processes all outgoing traffic for 1Gb interfaces",
        "class class-default",
        "bandwidth remaining ratio 3",
        "police rate percent 10",
        "conform-action transmit",
        "exceed-action drop",
        "exit",
        "class CM_Q1",
        "class CM_Q2",
        "class CM_Q3",
        "class CM_Q4",
        "class CM_Q5",
        "class CM_Q6",
        "priority level 2",
        "class CM_Q7",
        "priority level 1"
    ],
    "warnings": [
        "To ensure idempotency and correct diff the input configuration lines should be similar to how they appear if present in the running configuration on device"
    ]
}
@RobKoerts RobKoerts changed the title The cisco.ios.ios_config module doesn't push valid duplicate lines (same config lines in subcatecories) to the switch The cisco.ios.ios_config module doesn't push valid duplicate lines (identical config lines in subcatecories) to the switch Feb 3, 2023
@RobKoerts RobKoerts changed the title The cisco.ios.ios_config module doesn't push valid duplicate lines (identical config lines in subcatecories) to the switch The cisco.ios.ios_config module doesn't push valid duplicate lines (identical config lines in different subcatecories) to the switch Feb 3, 2023
@KB-perByte KB-perByte self-assigned this Feb 8, 2023
@KB-perByte KB-perByte added the config ios_config module label Feb 20, 2023
@bentole bentole linked a pull request Mar 14, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
config ios_config module
Projects
None yet
2 participants