ansible how to get last 2 digit of number - ansible

I have question about how to convert a digit to string and then get last 2 number using filter
For string, it's easy to use like:
vars:
string_1: 'abcd'
number_1: 1234
For string, it's easy:
"{{ string_1[-2:] }}"
But for number then i have to convert to string first but it failed while templating.
"{{ number_1 | string | [-2:] }}
How can I achieve this in single line code?

Close the conversion in parenthesis. The index has higher precedence compared to the filter. (And index can't be used as a filter, of course).
msg: "{{ (number_1|string)[-2:] }}"
The only difference is that modulus % returns an integer. The tasks
- debug:
msg: "{{ (number_1 % 100)|type_debug }}"
- debug:
msg: "{{ (number_1|string)[-2:]|type_debug }}"
give
"msg": "int"
"msg": "str"

You may use a modulus trick here:
{{ number_1 % 100 }}
The modulus dividing by 100 should yield the final two digits of any number input.

Related

Ansible string split

I have a split function in ansible based on a delimiter. But only want to get the first occurance of the delimiter string and the rest as the second string.
string: "hello=abcd=def=asd"
string1= string.split("=")[0]
string2= string.split("=)[1..n] (This is what i missing)
How can i achieve this in ansible with string.split?
Q: "Get the first occurrence of the delimiter string and the rest as the second string."
A: Join the rest of the string again
arr: "{{ string.split('=') }}"
string1: "{{ arr[0] }}"
string2: "{{ arr[1:]|join('=') }}"
Optionally, set the maxsplit parameter to 1
arr: "{{ string.split('=', 1) }}"
string1: "{{ arr.0 }}"
string2: "{{ arr.1 }}"
Both options give the same result
string1: hello
string2: abcd=def=asd

Json_query filter in Ansible

I cannot manage to make json_query work, even with the simplest examples.
I have a fact old_new_nodes like:
ok: [localhost] => {
"msg": [
{
"id_new": 2430,
"id_old": 2263
},
{
"id_new": 2431,
"id_old": 2283
}
]
}
I want to save the id_new parameter whenever the id_old is equal to a certain value.
I do:
- name: Get id of the new node
set_fact:
new_node_id: "{{ (old_new_nodes | first) |json_query('[?id_old==original_node.id].id_new') }}"
I have checked that original_node.id is 2263 so the expected result is 2430.
Moreover, I have tried without the first and I still get [] as a result
A literal in JMESPath expression must be quoted. See Grammar
list-filter-expr = expression comparator expression
comparator = "<" / "<=" / "==" / ">=" / ">" / "!="
expression =/ "*" / multi-select-list / multi-select-hash / literal
literal = "`" json-value "`"
Unquoted literal causes the error bellow
- debug:
msg: "{{ old_new_nodes|json_query('[?id_old == 2263].id_new') }}"
msg: |-
JMESPathError in json_query filter plugin:
invalid token: Parse error at column 12, token "2263" (NUMBER), for expression:
"[?id_old == 2263].id_new"
^
The query works as expected if you quote the literal
- debug:
msg: "{{ old_new_nodes|json_query('[?id_old == `2263`].id_new') }}"
gives
msg:
- 2430
If you want to put the searched value into a variable simplify the quotation of the query and put it into a variable too. The task below gives the same result
- debug:
msg: "{{ old_new_nodes|json_query(query) }}"
vars:
id: 2263
query: '[?id_old == `{{ id }}`].id_new'
It's possible to iterate the list, e.g.
- debug:
msg: "{{ old_new_nodes|json_query(query) }}"
loop: "{{ old_new_nodes|map(attribute='id_old')|list }}"
vars:
query: '[?id_old == `{{ item }}`].id_new'
gives
msg:
- 2430
msg:
- 2431
It'd be better to convert the list to a dictionary if you have to search it repeatedly, e.g.
- set_fact:
old_new: "{{ old_new_nodes|
items2dict(key_name='id_old', value_name='id_new') }}"
gives
old_new:
2263: 2430
2283: 2431
This would make the searching both trivial and fast.

Checking the first digit in a number in ansible playbook

I have an ansible playbook that reads in a vars_file containing usernames and uids
users:
- name: josh
uid: 1201
- name: peter
uid: 1202
- name: paul
uid: 2101
- name: ryan
uid: 2102
I have two host groups in my inventory file, db and web. I want users to be created in db if their uid starts with a 1, and web if it starts with 2.
My playbook so far looks like this
---
- name: users playbook
hosts: all
become: yes
vars_files:
- vars/user_list.yml
tasks:
- name: test debug
debug:
msg: "{{ item.username }}, {{ item.uid }}"
loop: "{{ users }}"
when: '{{ item.uid[0] }} == 1'
But my when conditional throws the error
The error was: error while evaluating conditional ({{ item.uid[0] }} == 1)
Is there a better way of doing this for both conditionals?
Several problems.
First, you are not comparing anything. In the expression '{{ item.uid[0] }} == 1' the last part (i.e. == 1) will be literally treated as a string and written as output. If used in a full jinja2 expression, the comparison must be inside the markers: {{ item.uid[0] == 1 }}
Second, when clauses should not contain any jinja2 markers to expand variables. This is also the case for failed_when and changed_when. See the conditionals doc
Lastly, getting the character with an index will only work if the input is a string and not an int. So you first need to make sure or that by casting it correctly with the string filter. The char you will then get will be itself a string. Comparing it to an integer will always return false. So you either have to write the comparison value as a string (i.e. '1') or cast the extracted car to an integer with the int filter.
This is how I would fix your task:
- name: test debug
debug:
msg: "{{ item.username }}, {{ item.uid }}"
loop: "{{ users }}"
when: (item.uid | string)[0] | int == 1

generating a numeric hash for a string in Ansible

I need to generate a unique TCP port number for a given string (I need this to mock a response from a server, unique for each string).
I want to use a string parameter as a source for this port number. In other words, I need to generate a numeric hash in a given range (1000-32767) from an arbitrary string.
I can do this in Python with no issues, but I don't know how to do it in Ansible.
Is there any way to generate a stable numeric in range hash from a string in Ansible?
An example of a play:
- hosts: localhost
gather_facts: no
tasks:
- debug: msg="Hash for {{ item }} is {{ item |HELP_ME_HERE }}"
with_items:
- string1
- string_two
Is there any way to generate a stable numeric in range hash from a string in Ansible?
- set_fact:
r: "{{ range(1000, 37272) | random(seed=item) }}"
run_once: yes
loop:
- string
- debug:
msg: "{{ r }}"

Ansible random UUID generation

In my Ansible script, I want to generate UUIDs on the fly and use them later on.
Here is my approach:
- shell: echo uuidgen
with_sequence: count=5
register: uuid_list
- uri:
url: http://www.myapi.com
method: POST
body: "{{ item.item.stdout }}"
with_items: uuid_list.result
However I get the following error:
fatal: [localhost] => One or more undefined variables: 'str object' has no attribute 'stdout'
How can I solve this issue?
In ansible 1.9 there is a new filter : to_uuid , which given a string it will return an ansible domain specific UUID,you can find the usage in here https://docs.ansible.com/playbooks_filters.html#other-useful-filters
As metioned by Xingxing Gao, there is to_uuid which can be used with a large enough number and the random filter to produce a random UUID. The larger the number the greater the randomness. eg:
{{ 99999999 | random | to_uuid }}
or
{{ 9999999999999999999999 | random | to_uuid }}
Generate a random UUID from a 20 char string with upper/lower case letters and digits:
{{ lookup('password', '/dev/null chars=ascii_letters,digits') | to_uuid }}
This is very close. I only had to change a few things. I figured this out by using the task "debug: var=uuid_list" and iterating.
- shell: uuidgen # note 1
with_sequence: count=5
register: uuid_list
- uri:
url: http://www.myapi.com
method: GET
body: "{{ item.stdout }}" # note 2
timeout: 1 # note 3
with_items: uuid_list.results # note 4
Notes:
echo caused the string uuidgen to be printed. removed echo, kept uuidgen.
item.item.stdout needed to be item.stdout
I used a short timeout so I could test this without having a rest endpoint available. This gives failure error messages but it's clear that it is correct.
uuid_list.stdout needed to be uuid_list.results
Be aware if you use Willem's solution, Jinja and Ansible will cache the result for multiple executions of the same filter, so you must change the source number each time
api_key_1: "{{ 999999999999999999995 | random | to_uuid }}"
api_key_2: "{{ 999999999999999999994 | random | to_uuid }}"
and for situations where you need a normal md5 instead of to_uuid, hash('md5') does not take an integer. The most convenient way to convert the random to a string I've found is to use to_uuid:
api_key_3: "{{ 999999999999999999999 | random | to_uuid | hash('md5') }}"
api_key_4: "{{ 999999999999999999998 | random | to_uuid | hash('md5') }}"
One solution which should be immune to caching/stale fact gathering and give you a reasonably random UUID each time you use it is:
{{ (999999999999999999999 | random | string + (lookup('pipe', 'date +%s%N'))) | to_uuid() }}
It concatenates a random number between 0 and 999999999999999999999 with current nanoseconds since Unix epoch and feeds that through Ansible's to_uuid() filter (available since version 1.9). Fact caching should not cause a problem as lookups are always evaluated each and every time they are invoked.
If you want a UUID that remains constant throughout the plays in a playbook (but doesn't persist between multiple invocations of the playbook - even with fact caching turned on) then use:
set_fact: uuid={{ (999999999999999999999 | random | string + (lookup('pipe', 'date +%s%N'))) | to_uuid() }}
I tried to avoid using the filter to_uuid, because it does not only take numbers as input but also characters. So, if I would only give it numbers as input, I would not be able to create every uuid possible.
I came up with this solution:
- name: Generate UUID
vars:
# This creates a random 128-bit integer and translates it to hexadecimal string.
# Zeros are put infront of the hex string, to make it always 32 hex-digits long.
__uuid: "{{ '%032x' % ((2**128) | random) }}"
# Format the hexadecimal string to a UUID format: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
uuid: "{{ __uuid[0:8] }}-{{ __uuid[8:12] }}-{{ __uuid[12:16] }}-{{ __uuid[16:20] }}-{{ __uuid[20:32] }}"
debug:
msg: "{{ uuid }}"

Resources