Site icon techbeatly

Python inside Ansible Playbook

I want to share some tips on how to directly use Python inside an Ansible playbook.

Python String Method

Ooops, error: “template error while templating string: no filter named ‘split’…”. This error is not abnormal when you execute the below task.

Normally it is caused by the plugin filter setup.

  vars:
    s1: "Abc,Efg"

  tasks:
  - name: convert string to list
    set_fact:
      list_01: "{{ s1 | split(',') }}"

Is there any quick workaround?

The answer is to directly use the Python string method “split()”:

  tasks:
  - name: convert string to list
    set_fact:
      list_02: "{{ s1.split(',') }}"

The below simple playbook demonstrates more of the Python string methods. “upper()”, “replace()”, “count()” and “isdigit()”.

Directly using Python string methods can improve the playbook string handling capability.

- hosts: localhost
  connection: local
  gather_facts: no
  vars:
    s1: "red hat, red hat"
    d1: '123'
  tasks:
  - name: Python String Methods
    debug: 
      msg: 
      - "Original String s1 - {{ s1 }}; d1 - {{ d1 }}"
      - "Converts a string into upper case - s1.upper(): {{ s1.upper() }}"
      - "Converts a string into upper case - s1.replace('r','R').replace('h','H'): {{ s1.replace('r','R').replace('h','H') }}"
      - "Returns the number of times a specified value occurs in a string - s1.count('red'): {{ s1.count('red') }}"
      - "Returns True if all characters in the string are digits - d1.isdigit(): {{ d1.isdigit() }}"

The output of the playbook:

PLAY [localhost] ****************************************************************************************************************************************************

TASK [Python String Methods] ****************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        "Original String s1 - red hat, red hat; d1 - 123",
        "Converts a string into upper case - s1.upper(): RED HAT, RED HAT",
        "Converts a string into upper case - s1.replace('r','R').replace('h','H'): Red Hat, Red Hat",
        "Returns the number of times a specified value occurs in a string - s1.count('red'): 2",
        "Returns True if all characters in the string are digits - d1.isdigit(): True"
    ]
}

PLAY RECAP **********************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Python print() Method

Let’s view the below use case. It is part of the requirements to auto-generate the hostname during VM provision.

Convert ‘numeric’ index to 3 character string:

Standard Ansible code looks like this. It includes three tasks.

- hosts: localhost
  connection: local
  gather_facts: no

  vars:
    vm_index: 10

  tasks:
  - name: index less 10
    debug:
      msg: "{{ '00' + vm_index | string }}"
    when: vm_index < 10

  - name: index less 100
    debug:
      msg: "{{ '0' + vm_index | string }}"
    when: 
    - vm_index < 100
    - vm_index >= 10

  - name: index less 1000
    debug:
      msg: "{{ vm_index | string }}"
    when: 
    - vm_index < 1000
    - vm_index >= 100

It can be simplified using Python if statement. It includes one task.

  - name: index string
    debug:
      msg: "{{ '00' + vm_index | string
               if vm_index < 10 
               else '0' + vm_index | string 
               if vm_index < 100 
               else vm_index 
               if vm_index < 999 
               else '---' }}"

Can we make it better? Yes, using Python print() method.

Simpler, nicer.

  - name: index string
    debug:
      msg: "{{ '%03d' % vm_index }}"

Python Code

Sometimes, the Ansible module is not available for certain tasks. This task can be done within Python with less than 10 lines of code but to write a customized Python module would incur too much “overhead” work. In such a scenario, I prefer to extract and use the Python code inside the playbook.

One use case is to convert the SGT time to UTC time. I was not able to find the available Ansible module for such a simple task. But I know it can be done easily with a few Python codes. I used the Python code inside the playbook to solve this problem.

The server’s time zone is “Asia/Singapore”. Below is the playbook. It demonstrates how to directly use Python code and how to pass the input and output value between Python code and playbook.

  - debug:
          msg: "Singapore Time: {{ year }}-{{ month }}-{{ date }}T{{ hour }}:{{ min }}:{{ sec }}"

  # it is easy to do time conversion in the python
  # Below is to directly use python code for time conversion
  # It requires tzdata packge and environment TZ is SGT
  - name: python code converts SGT to UTC
    command: /usr/bin/python3
    args:
      stdin: |
        from datetime import datetime
        import pytz

        dt_str  = "{{ year }}{{ month }}{{ date }}T{{ hour }}{{ min }}{{ sec }}"
        format = "%Y%m%dT%H%M%S"
        local_dt = datetime.strptime(dt_str, format)

        dt_utc = local_dt.astimezone(pytz.UTC)
        format = "%Y-%m-%dT%H:%M:%SZ"
        dt_utc_str = dt_utc.strftime(format)

        print(dt_utc_str)
    register: results

  - set_fact:
      utc_date: "{{ results.stdout }}"
Exit mobile version