添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Hello Team.
I have an Ansible playbook where am trying to automate VLAN mapping on my network.
Cureently, script is able to create and map VLAN on respective interfaces as set in the inventory. If one switch in the inventory has VLAN already, it aborts the process and goes to the next switch and if it does nit have then VLAN is created and mapped.
I need it to work in a manner that if VLAN exists in at least one switch, process should be aborted and playbook stopped.

name: Gather VLAN facts from Cisco devices
hosts: cisco
gather_facts: false

vars_files:

  • /var/MIKROTIK/cisco/inventory
  • vars_prompt:

    - name: “username”

    prompt: “Enter User Name”

    private: no

    - name: “password”

    prompt: “Enter your password”

    private: no

    name: “VLAN”
    prompt: “Enter VLAN ID to add”
    private: no

    name: “NAME”
    prompt: “Enter VLAN ID Name”
    private: no

    vars:

    - ansible_user:

    - ansible_password:

    tasks:

  • name: Check if VLAN exists
    nxos_command:
    commands:
    - show vlan id {{ VLAN }}
    register: vlan_output
    ignore_errors: true
  • - name: Display message if VLAN exists on any switch

    fail:

    msg: “VLAN {{ VLAN }} already exists on {{ item }}. Aborting VLAN addition.”

    loop: “{{ ansible_play_batch }}”

    when: vlan_output.stdout is search(‘VLAN {{ VLAN }}’)

    name: Fail if VLAN already exists
    block:

    name: Set fact whether VLAN exists
    set_fact:
    vlan_exists: “{{ vlan_output.stdout | search(‘VLAN\s+’ + VLAN + ‘\s+’) is not none }}”
    delegate_to: localhost

    name: Fail if VLAN exists
    fail:
    msg: “VLAN {{ VLAN }} already exists on {{ inventory_hostname }}. Aborting VLAN addition.”
    when: vlan_exists

    when: vlan_output is succeeded

    name: Adding VLAN ID to Database
    nxos_vlans:
    config:
    - vlan_id: “{{ VLAN }}”
    name: “{{ NAME }}”
    state: active
    register: vlan_added
    when: vlan_output is failed

    when: vlan_output.stdout is not search(‘VLAN {{ VLAN }}’)

    when: “‘VLAN {{ VLAN }} not found in current VLAN database’ in vlan_output.stdout”

    name: Send notification if VLAN already exists
    community.general.mattermost:
    text: “VLAN {{ VLAN }} already exists on {{ ansible_host }}. Aborting VLAN addition.”
    when: vlan_output is succeeded

    name: Debug vlan_output variable
    debug:
    var: vlan_output

    name: Debug etherports variable
    debug:
    var: etherports

    name: Merge provided configuration with device configuration
    cisco.ios.ios_l2_interfaces:
    config:
    - name: “{{ item }}”
    mode: trunk
    trunk:
    allowed_vlans: “{{ VLAN }}”
    state: merged
    loop: “{{ etherports }}”

    name: Send notification message via Mattermost if VLAN is added
    community.general.mattermost:

    text: |
    {% if vlan_added.changed %}
    VLAN {{ VLAN }} added successfully!
    Has been tagged to {{ ansible_host }} by User {{ ansible_user }} on the following interfaces:
    {{ etherports }}
    {% else %}
    VLAN {{ VLAN }} was not added as it already exist on host {{ ansible_host }}. Please use a new VLAN_ID, thank you!!
    {% endif %}

    Could you use a code block for you post to make it more readable? For example as follows (terminating back ticks omitted as they generate an additional code block in this case):

    ```yaml
    foo: bar
        # - name: Display message if VLAN exists on any switch
        #   fail:
        #     msg: "VLAN {{ VLAN }} already exists on {{ item }}. Aborting VLAN addition."
        #   loop: "{{ ansible_play_batch }}"
        #   when: vlan_output.stdout is search('VLAN {{ VLAN }}')
        - name: Fail if VLAN already exists
          block:
            - name: Set fact whether VLAN exists
              set_fact:
                vlan_exists: "{{ vlan_output.stdout | search('VLAN\\s+' + VLAN + '\\s+') is not none }}"
              delegate_to: localhost
            - name: Fail if VLAN exists
              fail:
                msg: "VLAN {{ VLAN }} already exists on {{ inventory_hostname }}. Aborting VLAN addition."
              when: vlan_exists
          when: vlan_output is succeeded
        - name: Adding VLAN ID to Database
          nxos_vlans:
            config:
              - vlan_id: "{{ VLAN }}"
                name: "{{ NAME }}"
                state: active
          register: vlan_added
          when: vlan_output is failed
          # when: vlan_output.stdout is not search('VLAN {{ VLAN }}')
          # when: "'VLAN {{ VLAN }} not found in current VLAN database' in vlan_output.stdout"
        - name: Send notification if VLAN already exists
          community.general.mattermost:
            text: "VLAN {{ VLAN }} already exists on {{ ansible_host }}. Aborting VLAN addition."
          when: vlan_output is succeeded
        - name: Debug vlan_output variable
          debug:
            var: vlan_output
        - name: Debug etherports variable
          debug:
            var: etherports
        - name: Merge provided configuration with device configuration
          cisco.ios.ios_l2_interfaces:
            config:
              - name: "{{ item }}"
                mode: trunk
                trunk:
                  allowed_vlans: "{{ VLAN }}"
            state: merged
          loop: "{{ etherports }}"
        - name: Send notification message via Mattermost if VLAN is added
          community.general.mattermost:
            text: |
              {% if vlan_added.changed %}
              VLAN {{ VLAN }} added successfully!
              Has been tagged to {{ ansible_host }} by User {{ ansible_user }} on the following interfaces:
              {{ etherports }}
              {% else %}
              VLAN {{ VLAN }} was not added as it already exist on host {{ ansible_host }}. Please use a new VLAN_ID, thank you!!
              {% endif %}
                  

    You could also use the ansible.builtin.meta module, which allows you to end_play. This affects all inventory hosts in the play, so ansible will stop here.

    I also think you might benefit from using block: rescue: and always: for handling what to do when your first command fails or succeeds. If the first command to check for VLAN succeeds on any inventory host, we want to abort the entire play correct? Then add meta: end_play in the block. If the play doesn’t end here, it continues to the rescue step.

    - name: Gather VLAN facts from Cisco devices hosts: cisco gather_facts: false vars_files: - /var/MIKROTIK/cisco/inventory vars_prompt: # - name: "username" # prompt: "Enter User Name" # private: no # - name: "password" # prompt: "Enter your password" # private: no - name: "VLAN" prompt: "Enter VLAN ID to add" private: no - name: "NAME" prompt: "Enter VLAN ID Name" private: no # vars: # - ansible_user: # - ansible_password: [] tasks: - name: Fail if VLAN already exists block: - name: Check if VLAN exists nxos_command: commands: - show vlan id {{ VLAN }} register: vlan_output - name: Debug vlan_output variable debug: var: vlan_output - name: Debug etherports variable debug: var: etherports - name: Send notification if VLAN already exists community.general.mattermost: text: "VLAN {{ VLAN }} already exists on {{ ansible_host }}. Aborting VLAN addition." - name: End Play if VLAN exists meta: end_play rescue: - name: Debug vlan_output variable debug: var: vlan_output - name: Debug etherports variable debug: var: etherports - name: Adding VLAN ID to Database nxos_vlans: config: - vlan_id: "{{ VLAN }}" name: "{{ NAME }}" state: active register: vlan_added - name: Merge provided configuration with device configuration cisco.ios.ios_l2_interfaces: config: - name: "{{ item }}" mode: trunk trunk: allowed_vlans: "{{ VLAN }}" state: merged loop: "{{ etherports }}" - name: Send notification message via Mattermost if VLAN is added community.general.mattermost: text: | {% if vlan_added.changed %} VLAN {{ VLAN }} added successfully! Has been tagged to {{ ansible_host }} by User {{ ansible_user }} on the following interfaces: {{ etherports }} {% else %} VLAN {{ VLAN }} was not added as it already exist on host {{ ansible_host }}. Please use a new VLAN_ID, thank you!! {% endif %}

    Getting bellow error
    TASK [Set fact whether VLAN exists] ************************************************************************************************************************************************** fatal: [switch-01]: FAILED! => {"msg": "template error while templating string: Could not load \"search\": 'search'. String: {{ vlan_output.stdout | search('VLAN\\\\s+' + VLAN + '\\\\s+') is not none }}. Could not load \"search\": 'search'"}

    @Denney-tech

    TASK [Set fact whether VLAN exists] ************************************************************************************************************************************************** fatal: [switch-01]: FAILED! => {"msg": "template error while templating string: Could not load \"search\": 'search'. String: {{ vlan_output.stdout | search('VLAN\\\\s+' + VLAN + '\\\\s+') is not none }}. Could not load \"search\": 'search'"}
                  

    Thank you for the correction. But script only skipping a host where the VLAN is and proceeds to one that do not have. I need it to abort executing on another device if one of the host already has the VLAN`ansible-playbook add-vlan.yml -i inventory
    Enter VLAN ID to add: 54
    Enter VLAN ID Name: VID-54-TEST

    PLAY [Gather VLAN facts from Cisco devices] ******************************************************************************************************************************************

    TASK [Check if VLAN exists] **********************************************************************************************************************************************************
    fatal: [switch-02]: FAILED! => {“changed”: false, “msg”: "show vlan id 54\r\r\nVLAN 54 not found in current VLAN database\r\n\r\n\r\n\rswitch# "}
    ok: [switch-01]

    TASK [Debug vlan_output variable] ****************************************************************************************************************************************************
    ok: [switch-01] => {
    “vlan_output”: {
    “changed”: false,
    “failed”: false,
    “stdout”: [
    “VLAN Name Status Ports\n---- -------------------------------- --------- -------------------------------\n54 VLAN0054 active \n\nVLAN Type Vlan-mode\n---- ----- ----------\n54 enet CE \n\nRemote SPAN VLAN\n----------------\nDisabled \n\nPrimary Secondary Type Ports\n------- --------- --------------- -------------------------------------------”
    “stdout_lines”: [
    “VLAN Name Status Ports”,
    “---- -------------------------------- --------- -------------------------------”,
    "54 VLAN0054 active ",
    “VLAN Type Vlan-mode”,
    “---- ----- ----------”,
    "54 enet CE ",
    “Remote SPAN VLAN”,
    “----------------”,
    "Disabled ",
    “Primary Secondary Type Ports”,
    “------- --------- --------------- -------------------------------------------”

    TASK [Debug etherports variable] *****************************************************************************************************************************************************
    ok: [switch-01] => {
    “etherports”: [
    “Ethernet1/20”,
    “Ethernet1/15”

    TASK [Set fact whether VLAN exists] **************************************************************************************************************************************************
    ok: [switch-01]

    TASK [Send notification if VLAN already exists] **************************************************************************************************************************************
    ok: [switch-01]

    TASK [End Play if VLAN exists] *******************************************************************************************************************************************************
    skipping: [switch-01]

    TASK [Debug vlan_output variable] ****************************************************************************************************************************************************
    ok: [switch-02] => {
    “vlan_output”: {
    “changed”: false,
    “failed”: true,
    “msg”: "show vlan id 54\r\r\nVLAN 54 not found in current VLAN database\r\n\r\n\r\n\rswitch# "

    TASK [Debug etherports variable] *****************************************************************************************************************************************************
    ok: [switch-02] => {
    “etherports”: [
    “Ethernet1/24”,
    “Ethernet1/25”

    TASK [Adding VLAN ID to Database] ****************************************************************************************************************************************************
    changed: [switch-02]

    TASK [Merge provided configuration with device configuration] ************************************************************************************************************************
    changed: [switch-02] => (item=Ethernet1/24)
    changed: [switch-02] => (item=Ethernet1/25)

    TASK [Send notification message via Mattermost if VLAN is added] *********************************************************************************************************************
    ok: [switch-02]

    PLAY RECAP ***************************************************************************************************************************************************************************
    switch-01 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
    switch-02 : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=1 ignored=0 `

    Edited the snippet above. Not sure why the regex_search fails to find your vlan in the output, but we should already know it’s there when the first command succeeds right? I just removed the set_fact step and conditional on the end_play.

    I think the issue with the regex_search was that successful output had ‘%04d’ padded the VLAN id: 54 VLAN0054 active which is "{{ VLAN }} VLAN{{ '%04d' | format(VLAN) }} active" in ansible.

    So maybe try this if you want to continue double-checking the output.

            - name: Set fact whether VLAN exists
              vars:
                search: ".*{{ VLAN }} VLAN{{ '%04d' | format(VLAN) }} active.*"
              set_fact:
                vlan_exists: "{{ vlan_output.stdout | regex_search(search)}}"
                  

    Getting below error. Tried to figure out but no success

    TASK [Set fact whether VLAN exists] ***************************************************************************************************************************************************************** fatal: [switch-01]: FAILED! => {"msg": "An unhandled exception occurred while templating '.*{{ VLAN }} VLAN{{ '%04d' | format(VLAN) }} active.*'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Unexpected templating type error occurred on (.*{{ VLAN }} VLAN{{ '%04d' | format(VLAN) }} active.*): %d format: a real number is required, not str. %d format: a real number is required, not str"} fatal: [switch-02]: FAILED! => {"msg": "An unhandled exception occurred while templating '.*{{ VLAN }} VLAN{{ '%04d' | format(VLAN) }} active.*'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Unexpected templating type error occurred on (.*{{ VLAN }} VLAN{{ '%04d' | format(VLAN) }} active.*): %d format: a real number is required, not str. %d format: a real number is required, not str"} Cysco_Colloh:

    VLAN{{ '%04d' | format(VLAN) }} active.*): %d format: a real number is required, not str. %d format: a real number is required, not str

    And this is the documentation for the format filter and that links to the printf-style String Formatting documentation.

    @chris I think it’s treating his VLAN as a string since he’s entering it through prompt, not as a var.

    So: search: ".*{{ VLAN }} VLAN{{ '%04d' | format(VLAN|int) }} active.*" should fix the typing.

    That said, @Cysco_Colloh, it looks like VLAN0054 is the name field from the output? If that’s the case, it may be coincidence that it is “VLAN” + <0-padded-54>. Unless you can say with confidence that all of the names of your vlans are consistent (or even better, programmatic), then I can’t say my search string will be consistent.

    On another note, while we have spent a lot of effort trying to help you achieve the process you’re going for, I can’t help but wonder if there’s an easier way to do all of this if you can change your approach.

    Ansible is meant to be idempotent, where you can run the same playbook over and over again and never do the same thing twice. Obviously you want to do that with your vlans by making sure they’re unique to your switches. You could define the vlans in your host_vars for each switch, and then have the playbook only configure the vlans defined per switch. Depending on how many switches and vlans you have, that might be tedius, but at least then you would know the switches would get the vlans you want them to have. You could create the list of vlans you want first, gather facts about what vlans are there after they’re configured, and remove any extra vlans that weren’t in the original list (or at least notify you/network admins that there are ‘rogue’ vlans on a given switch).