Mapping strings into numbers in Ansible
A rather simple solution for a rather obscure problem: We have a set of strings and we want to map each string into number in a given range. Our range is a very special — it’s a list of non-privileged non-dynamic TCP ports [1024–49152].
So, our goal is to create a hash function from arbitrary string into fixed range of integers. Moreover, we need to make it stable, that means, that each time we pass the same string, we are getting the same number on every following run of Ansible.
Examples of the matching:
- string1 — 6870
- string_two — 46399
- bar-bar — 33750
- some string — 43305
The solution
(((item|md5)[-4:]| int(0, 16))/1.4 + 1024)|int
Explanation:
item
— our variable with a string to mapmd5
— a filter which produces md5 hash[-4:]
— takes last for character of the string|int(0,16)
— converts those 4 characters from hex to int (range [0–65535])./1.4
— a special coefficient. I calculated it as roundup from 65535.0/(49152–1024).+1024
— shift values into desired range.- final
|int
strips away any fractional remains after division.
The example:
- hosts: localhost
gather_facts: no
tasks:
- debug: msg="Hash for {{ item }} is {{ (((item |md5)[-4:]| int(0, 16))/1.4 + 1024)|int }}"
with_items:
- string1
- string_two
- bar-bar
- some string
Update: there is a better solution
I just can’t compete with SO hivemind. Here is a really good solution, from this answer:
range(1000, 37272) | random(seed=item)