Search code examples
ansible

How to validate list of host names from extra variables via `assert`?


I have an Ansible playbook which requires --extra-vars to be passed and would like to know if there is any way that validate the hostnames from the --extra-vars values is within the variables.

Sample playbook test.yml

- hosts: localhost
  gather_facts: false
  vars:
    prg:
      prod_mgmt_network: "{{ ['prodsrv00']|product(range(prod_start,prod_end))|map('join')|list }}"
      test_mgmt_network: "{{ ['testsrv00']|product(range(test_start,test_end))|map('join')|list }}"
    prod_start: 4281
    prod_end: 4283
    test_start: 8281
    test_end: 8283


  tasks:
    - debug:
        var: prg.prod_mgmt_network, prg.test_mgmt_network

Sample command

ansible-playbook --extra-vars hostname=['prodsrv004281', 'prodsrv004282', 'testsrv008281', 'testsrv008282', 'testsrv008283', 'testsrv008284'] test.yml

I would like to fail the task if a hostname is not within the variable. for example, testsrv008283 and testsrv008284 should fail the task.


Solution

  • The first problem is the format of the extra variables. See Defining variables at runtime. The key=value (INI) syntax are always interpreted as strings. You might want to test it. For example, the below play

    shell> cat pb.yml
    - hosts: localhost
    
      tasks:
    
        - debug:
            var: hostname
        - debug:
            var: hostname|type_debug
    

    gives (abridged)

    shell> ansible-playbook pb.yml -e hostname=['host1','host2']
    
      hostname: '[host1,host2]'
    
      hostname|type_debug: str
    

    There are more options for how to get a list:

    • Convert the string to a list
      hostname_list: "{{ hostname|from_yaml }}"
    

    Then, you'll get

      hostname_list:
      - host1
      - host2
    
      hostname_list|type_debug: list
    
    • Use JSON format
    shell> ansible-playbook pb.yml -e '{"hostname": ["host1", "host2"]}'
    
      hostname:
      - host1
      - host2
    
      hostname_list|type_debug: list
    
    • Put the extra variable(s) into a file. For example,
    shell> cat hostname.yml 
    hostname:
      - host1
      - host2
    

    Then, the below play gives the same result

    shell> ansible-playbook pb.yml -e @hostname.yml
    

    For testing your case, the simplest option is to put the list into a file

    shell> cat hostname.yml
    hostname:
      - prodsrv004281
      - prodsrv004282
      - testsrv008281
      - testsrv008282
      - testsrv008283
      - testsrv008284
    

    Q: "Fail if hostname is not within the variable. For example, testsrv008283 and testsrv008284 should fail."

    A: Select the lists of hosts and compare the differences. For example,

      prod_hosts: "{{ hostname|select('match', 'prodsrv') }}"
      test_hosts: "{{ hostname|select('match', 'testsrv') }}"
    

    gives

      prod_hosts:
      - prodsrv004281
      - prodsrv004282
    
      test_hosts:
      - testsrv008281
      - testsrv008282
      - testsrv008283
      - testsrv008284
    

    Evaluation of the networks gives

      prg:
        prod_mgmt_network:
        - prodsrv004281
        - prodsrv004282
        test_mgmt_network:
        - testsrv008281
        - testsrv008282
    

    The below assert will pass

        - assert:
            that: prod_out|length == 0
            fail_msg: "Hosts: {{ prod_out }} out of range."
          vars:
            prod_out: "{{ prod_hosts|difference(prg.prod_mgmt_network) }}"
    
    ok: [localhost] => changed=false 
      msg: All assertions passed
    

    but, this one will fail

        - assert:
            that: test_out|length == 0
            fail_msg: "Hosts: {{ test_out }} out of range."
          vars:
            test_out: "{{ test_hosts|difference(prg.test_mgmt_network) }}"
    
    fatal: [localhost]: FAILED! => changed=false 
      assertion: test_out|length == 0
      evaluated_to: false
      msg: 'Hosts: [''testsrv008283'', ''testsrv008284''] out of range.'
    

    Example of a complete playbook for testing

    - hosts: localhost
    
      vars:
    
        prg:
          prod_mgmt_network: "{{ ['prodsrv00']|
                                 product(range(prod_start,prod_end))|map('join') }}"
          test_mgmt_network: "{{ ['testsrv00']|
                                 product(range(test_start,test_end))|map('join') }}"
        prod_start: 4281
        prod_end: 4283
        test_start: 8281
        test_end: 8283
    
        prod_hosts: "{{ hostname|select('match', 'prodsrv') }}"
        test_hosts: "{{ hostname|select('match', 'testsrv') }}"
    
      tasks:
    
        - debug:
            var: prg.prod_mgmt_network
        - debug:
            var: prg.test_mgmt_network
        - debug:
            var: hostname
    
        - debug:
            var: prod_hosts
        - debug:
            var: test_hosts
    
        - assert:
            that: prod_out|length == 0
            fail_msg: "Hosts: {{ prod_out }} out of range."
          vars:
            prod_out: "{{ prod_hosts|difference(prg.prod_mgmt_network) }}"
    
        - assert:
            that: test_out|length == 0
            fail_msg: "Hosts: {{ test_out }} out of range."
          vars:
            test_out: "{{ test_hosts|difference(prg.test_mgmt_network) }}"