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:
- 1 ⇒ 001
- 10 ⇒ 010
- 100 ⇒ 100
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 }}"