13

Apparently, according to several hours of searching nobody has encountered this use-case:

Its simple - I would like to execute some ansible logic depending on variable type. Basically equivalent of e.g. instanceof(dict, var_name) but in Ansible:

- name: test
  debug:
    msg: "{{ instanceof(var_name, dict) | ternary('is a dictionary', 'is something else') }}"

Is there any way this can be done?

Megakoresh
  • 746
  • 1
  • 11
  • 30
  • I know about the `mapping` filter that would resolve the use-case above, but it only works for what jinja2 considers a "mapping", which can be whatever (e.g. I have no idea what it is). I would like to know if there is a proper solution to this issue. – Megakoresh Apr 16 '20 at 16:51
  • If you really think "mapping" is not "a proper solution" why don't you post an example to prove it? – Vladimir Botka Apr 16 '20 at 17:40
  • The example is in the question title. I did not ask for a way to test whether something is whatever jinja2 developers think "mapping" is. I asked for a way to check variable type. This means ability check whether something is a list, a set, a dict and so on and differentiating between all of those. I used a dict here only as an example. In absence of other answers I have marked yours as one, since there's no other way to do it without a custom filter, but this is definitely not a real solution to the problem I described. – Megakoresh Jul 28 '20 at 14:15

3 Answers3

12

Q: "Execute some ansible logic depending on the variable type."

A: The tests including mapping work as expected. For example, the tasks below

    - set_fact:
        myvar:
          key1: value1
    - debug:
        msg: "{{ (myvar is mapping)|
                 ternary('is a dictionary', 'is something else') }}"

give

    msg: is a dictionary

Q: "Ansible - check variable type"

A: An option would be to discover the data type and dynamically include_tasks. For example, the tasks below

shell> cat tasks-int
- debug:
    msg: Processing integer {{ item }}

shell> cat tasks-str
- debug:
    msg: Processing string {{ item }}

shell> cat tasks-list
- debug:
    msg: Processing list {{ item }}

shell> cat tasks-dict
- debug:
    msg: Processing dictionary {{ item }}

with this playbook

- hosts: localhost
  vars:
    test:
    - 123
    - '123'
    - [a,b,c]
    - {key1: value1}
  tasks:
    - include_tasks: "tasks-{{ item|type_debug }}"
      loop: "{{ test }}"

give (abridged)

  msg: Processing integer 123

  msg: Processing string 123

  msg: Processing list ['a', 'b', 'c']

  msg: 'Processing dictionary {''key1'': ''value1''}'

If you want to simulate the switch statement create a dictionary

  case:
    int: tasks-int
    str: tasks-str
    list: tasks-list
    dict: tasks-dict
    default: tasks-default

and use it in the include

    - include_tasks: "{{ case[item|type_debug]|d(case.default) }}"
      loop: "{{ test }}"
Vladimir Botka
  • 58,131
  • 4
  • 32
  • 63
5

Since Ansible version 2.3 there is type_debug:

- name: test
  debug:
        msg: "{{ (( var_name | type_debug) == 'dict') | ternary('is a dictionary', 'is something else') }}"

Note that the docs state a preference for 'type tests'.

musicformellons
  • 12,283
  • 4
  • 51
  • 86
0

Older question, but you can do this easily with a Python custom filter plugin. Granted it would give you Python specific types, but that may be fine for your use case.

This could work. It just needs to be placed in a folder named filter_plugins alongside your playbook or role.

import sys

if sys.version_info[0] < 3:
    raise Exception("Must be using Python 3")

def get_type(var, **kwargs):
    return type(var)

class FilterModule(object):
    def filters(self):
        return {
            "get_type": get_type
        }

Then in your playbook:

- name: test
  debug:
    msg: "{{ var_name | get_type }}"
crock
  • 423
  • 3
  • 11