gonuladami
gonuladami

Reputation: 97

Ansible nested loops and conditional together in the same task

---

    - hosts: leaf-1, leaf-2
      vars_files:
        - /vars/all.yml

      tasks:
      - name: collect the vlan databse
        raw: "show vlan brief"
        register: vlan_db

      - name: compare vlan_id(.*)Ports against the vlan_db 
        set_fact:
          fact1: "{{ item.1.vlan_id }}"
          fact2: "{{ item.1.sap.inventory_hostname | join(', ') }}" 
          fact3: "{{ fact1 }}(.*){{ fact2 }}"
        failed_when: not vlan_db.stdout is regex(fact3)
        when: inventory_hostname in item.1.sap
        with_subelements:
          -  "{{ customers }}"
          -  services

  1. I collect an output from a switch

  2. I want to loop through the "customer" list and check if the "inventory_hostname" is listed in the "services.sap"

I thought I can do this with following: when: inventory_hostname in item.1.sap

  1. If the above check is true, I want to make a regex compare between fact3 and the stdout of the first task and raise a failure if the regex is not present in the stdout of the first task:

    set_fact: fact1: "{{ item.1.vlan_id }}" fact2: "{{ item.1.sap.inventory_hostname | join(', ') }}" fact3: "{{ fact1 }}(.*){{ fact2 }}" failed_when: not vlan_db.stdout is regex(fact3)

This is like nested loops and conditional together. Is there a way to do this with Ansible? What I am doing wrong?

Just to make sure that my intention is clear:

/vars/all.yml looks like this:


    customers:
        - name: "cust-1"
          l3_vni: "101"
          services:
            - vlan_id: "10"
              vni: "1010"
              gw: "10.0.0.254/24"
              sap: 
                leaf-1: ["Eth3"]
                leaf-2: ["Eth3"]

            - vlan_id: "11"
              vni: "1011"
              gw: "10.0.1.254/24"
              sap: 
                leaf-1: ["Eth3"]
                leaf-2: ["Eth3"]

        - name: "cust-2"
          l3_vni: "102"
          services:
            - vlan_id: "20"
              vni: "1020"
              gw: "20.0.0.254/24"
              sap: 
                leaf-3: ["Eth3", "Eth4"]
                leaf-4: ["Eth3"]

            - vlan_id: "21"
              vni: "1021"
              gw: "20.0.1.254/24"
              sap: 
                leaf-3: ["Eth3"]
                leaf-4: ["Eth3"]
    ```

    a switch vlan database usually looks like this:

    ```
    leaf-1#show vlan brief

    VLAN Name                             Status    Ports
    ---- -------------------------------- --------- ----------------------
    1    default                          active    Eth5
    10   cust-1                           active    Eth3, Eth4
    20   cust-2                           active    Eth1, Eth2
    ```

error log:

fatal: [leaf-1]: FAILED! => { "msg": "The task includes an option with an undefined variable. The error was: 'fact1' is undefined\n\nThe error appears to be in '/vagrant/Ansible Folder Setup/NetAutHardWay/Step4-CICD/vlan_test.yml': line 23, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: compare vlan_id(.*)Ports against the vlan_db\n ^ here\n" }


Upvotes: 1

Views: 3573

Answers (2)

gonuladami
gonuladami

Reputation: 97

Thanks larsks. After some struggle I was able to achieve my goal with the below play. I had to change the failed_when syntax as well.

- hosts: leaf-1
  gather_facts: no 
  tags: [ verify ]
  vars_files: 
    - ../Step2-config/roles/services_config/vars/main.yml

  tasks:
    - name: collect the vlan databse
      raw: "show vlan brief"
      register: vlan_db

    - debug: 
        var: vlan_db.stdout
        var: inventory_hostname, vlan_db        

    - name: compare vlan_id(.*)Ports against the vlan_db 
      set_fact:
        fact1: "{{ item.1.vlan_id }}"
        fact2: "{{ item.0.name }}"
        fact3: "{{ item.1.sap[inventory_hostname] | join('.*')}}" 
      when: "inventory_hostname in item.1.sap"
      failed_when: not vlan_db.stdout_lines is regex(fact1 + '.*' + fact2 + '.*' + fact3)
      with_subelements:
        -  "{{ customers }}"
        -  services

Upvotes: 0

larsks
larsks

Reputation: 311406

There are several problems here.

First, your playbook is syntactically invalid: it simply won't run. It will fail with the error:

ERROR! unexpected parameter type in action: <class 'ansible.parsing.yaml.objects.AnsibleSequence'>

This is because the content of set_fact should be a dictionary, not a list:

- name: "compare vlan_id(.*)Ports against the vlan_db"
  set_fact:
    fact1: "{{ item.1.vlan_id }}"
    fact2: "{{ item.1.sap.inventory_hostname | join(', ') }}"
    fact3: "{{ fact1 }}(.*){{ fact2 }}"
  failed_when: not vlan_db.stdout is regex(fact3)
  when: inventory_hostname in item.1.sap
  with_subelements:
    - "{{ customers }}"
    - services

However, you've got another problem here: your facts aren't available until after the set_fact task runs, so your conditional which is checking is regex(fact3) is never going to match correctly. If you want to use the value of that expression, you will need to expose the value as a variable.

But before we look at that, there's nother issue: when you write:

fact2: "{{ item.1.sap.inventory_hostname | join(', ') }}"

You are looking for a literal key named "inventory_hostname" in the sap dictionary. Remember, when you write foo.bar.baz, that's effectively shorthand for foo["bar"]["baz"]. You need instead:

fact2: "{{ item.1.sap[inventory_hostname] | join(', ') }}"

So, back to variables. If you want to use the value of fact3 in your conditional, there are a couple of ways of doing that. One option is to use a vars block on your task, which creates new variables that exist for the duration of the task. I've used a debug task here to demonstrate what's happening; you could obviously replace that with a set_fact task if you need to persist some or all of those variables after the task completes:

    - name: "compare vlan_id(.*)Ports against the vlan_db"
      debug:
        msg:
          - "{{ fact1 }}"
          - "{{ fact2 }}"
          - "{{ fact3 }}"
      vars:
        fact1: "{{ item.1.vlan_id }}"
        fact2: "{{ item.1.sap[inventory_hostname] | join(', ') }}"
        fact3: "{{ fact1 }}(.*){{ fact2 }}"
      failed_when: not vlan_db.stdout is regex(fact3)
      when: inventory_hostname in item.1.sap
      with_subelements:
        - "{{ customers }}"
        - services

That is hopefully enough to point you in the right direction. Let me know if I can clarify anything.

Upvotes: 1

Related Questions