Ansible object list from merging a list and a value - ansible

Given a string list
list:
- a
- b
and a string
str: c
How can be obtained an object list with the same number of elements as the first list with the following structure?:
new_list:
- list_key: a
str_key: c
- list_key: b
str_key: c
Also, I'm using Ansible 2.8

For example
- set_fact:
new_list: "{{ list|
map('regex_replace', '^(.*)$', '{list_key: \\1}')|
map('from_yaml')|
map('combine', {'str_key': str})|
list }}"

Related

Ansible template from jinja2

Who can tell you how to implement the output of all regions in the name1 group when entering a template named region1a, and when entering a template named region2b, output all regions from the name2 group
I implement it like this:
there is a task that starts template generation:
vars:
AllCountry:
- name1
- name2
name1:
- region1a
- region1b
name2:
- region2a
- region2b
tasks:
- name:
template:
src: "regions.j2"
dest: "{{ item }}.conf"
loop:
- region1a
- region2b
---regions.j2---
regions [{%for count in name1%} "my country = {{count}}", {%end for %}]
this gives the desired output, but only because it is explicitly specified for which name (1 or 2) to output
regions "my country = region1a", "my country = region1b"
For each value specified in the loop, a template configuration file must be generated.
When you specify values in loop region1a and region1b template should generate only one row in the configuration file for region1a.conf
regions "my country = region1a", "my country = region1b"
for region1b generate only one row in the configuration file for region1b.conf
regions "my country = region1a", "my country = region1b"
User β.εηοιτ.βε a more optimal structure was proposed. If convenient, you can use it.
vars:
countries:
country1:
regions:
- region1
- region2
- region3
capital: region1
country2:
regions:
- region4
- region5
capital: region5
Thank you all for your help. Still, I managed to figure it out myself.
Here is the final solution:
{% for country in AllCountry %}
{% if item in lookup('vars', country) %}{% for count in lookup('vars', country) %} "My country = {{ count }}"{% if not loop.last %},{% endif %}{% endfor %}{% endif %}{% endfor %}

Ansible if else while iterating over a dictionary

I have a dictionary of dictionaries collecting data from openshift using prometheus. Now I intend to add values in all the dictionaries. But some projects don't have quota and hence some pods don't have request/limit set for cpu and memory. I am trying the following and it fails in case the key:value is not there.
If possible I want to use if else such that, if the variable exists then add the variable else use the value as 0.
- name: Total section for Projects
set_fact:
pod_count_total: "{{ (pod_count_total|int) + (item.value.pod_count|int)}}"
total_cpu_request: "{{ (total_cpu_request|float |round(2,'ceil')) + (item.value.cpu_request|float |round(2,'ceil'))}}"
total_cpu_limit: "{{ (total_cpu_limit|float |round(2,'ceil')) + (item.value.cpu_limit|float |round(2,'ceil'))}}"
total_memory_request: "{{ (total_memory_request|float |round(2,'ceil')) + (item.value.memory_request|float |round(2,'ceil'))}}"
total_memory_limit: "{{ (total_memory_limit|float |round(2,'ceil')) + (item.value.memory_limit|float |round(2,'ceil'))}}"
with_dict: "{{all_project}}"
Dictionary of dictionaries is like
ok: [127.0.0.1] => {
"msg": {
"openshift-web-console": {
"cpu_usage": 0.015,
"memory_used": 0.04,
"cpu_request": 0.301,
"memory_request": 0.293,
"pod_count": 3
},
"srv-test": {
"cpu_usage": 0.013,
"memory_used": 0.02,
"pod_count": 5
},
"test": {
"cpu_usage": 0.001,
"memory_used": 0.0,
"pod_count": 1
},
"openshift-monitoring": {
"cpu_limit": 1.026,
"cpu_request": 0.556,
"cpu_usage": 0.786,
"memory_limit": 1.866,
"memory_request": 1.641,
"memory_used": 0.14,
"pod_count": 98
}
}
}
If possible I want to use if else such that, if the variable exists then add the variable else use the value as 0.
The thing you are looking for is the default filter
total_memory_request: "{{ (
total_memory_request | default(0)
| float | round(2,'ceil')
) + (
item.value.memory_request | default(0)
| float | round(2,'ceil')
) }}"
There's a subtlety in that if the variable exists but is the empty string, you'll need to pass in the 2nd parameter to default to have it act in a python "truthiness" way: {{ "" | default(0, true) | float }} -- that might not apply to you, but if it does, you'll be glad to know what that 2nd param does

Combine 2 dictionary in ansible / jinja2 (simple)

I want to use ansible to merge below 2 dictionary to become 1.
I have been stucking for few days already. Headache.
I have 2 variables now.
1. variable "my_directories":
{
"directoryA": [
"/bar/foo/file1.txt",
"/bar/foo/file2.txt"
],
"directoryB": [
"/bar/baz/file3.txt",
"/bar/baz/file4.txt",
"/bar/baz/file5.txt"
]
}
2. variable "my_filecontents":
{
"/bar/foo/file1.txt": "file1Content",
"/bar/foo/file2.txt": "file2Content",
"/bar/baz/file3.txt": "file3Content",
"/bar/baz/file4.txt": "file4Content",
"/bar/baz/file5.txt": "file5Content"
}
i want to merge it to become:
Result:
variable my_result
{
"directoryA": {
"/bar/foo/file1.txt": "file1Content",
"/bar/foo/file2.txt": "file2Content"
},
"directoryB": {
"/bar/baz/file3.txt": "file3Content",
"/bar/baz/file4.txt": "file4Content",
"/bar/baz/file5.txt": "file5Content"
}
}
The directories and files could be dynamic.
I tried so many codes but still did not work.
Thank you!
The task below does the job
- set_fact:
my_result: "{{ my_result|default({})|
combine({item.0.key: {item.1: my_filecontents[item.1]}},
recursive=True) }}"
with_subelements:
- "{{ my_directories|dict2items }}"
- value
- debug:
var: my_result
gives
"my_result": {
"directoryA": {
"/bar/foo/file1.txt": "file1Content",
"/bar/foo/file2.txt": "file2Content"
},
"directoryB": {
"/bar/baz/file3.txt": "file3Content",
"/bar/baz/file4.txt": "file4Content",
"/bar/baz/file5.txt": "file5Content"
}
}

How to dynamically select json object to be used in an Ansible task?

Is it possible to select which json object to use based on some dynamic criteria?
I have a yml file:
- name: Get data
uri:
url: "foo/get_data/{{item.name}}"
return_content: yes
with_items: "{{stuff_names}}"
register: app_out
- name: Use data
uri:
url: "foo/use_data/item.json[0].id"
method: POST
with_items: "{{ app_out.results }}"
Where the call to foo/get_data/<name> returns a json array with 1 or 2 items:
Sample from foo/get_data/bar1:
[{"id": 1, "type": "x"},{"id": 2, "type": "y"}]
Sample from foo/get_data/bar2:
[{"id": 1, "type": "x"}]
In the "Use data" task is there a way to specify which json object in the array to use based on if an object exists in item or the size of item etc? Right now its hard coded to the first object item.json[0].
For example,
if item contains an object of type "y" then use that object, otherwise use item of type "x".
or
if size of item is > 1, use item of type "x".
EDIT:
Or perhaps even a separate ansible task to prune the registered app_out?
Q: "if item contains an object of type "y" then use that object, otherwise use item of type "x"
A: The play below implements this logic, I think.
- hosts: localhost
vars:
results:
- {"id": 1, "type": "x"}
- {"id": 2, "type": "y"}
- {"id": 3, "type": "z"}
tasks:
- set_fact:
my_list: "{{ results|
selectattr('type', 'defined')|
selectattr('type', 'equalto','y')|list }}"
- set_fact:
my_list: "{{ results|
selectattr('type', 'defined')|
selectattr('type', 'equalto','x')|list }}"
when: my_list|length == 0
- name: Use type y, otherwise use type x
debug:
var: my_list
gives
"my_list": [
{
"id": 2,
"type": "y"
}
]

is this valid yaml?

items:
house:
- bathroom:
- toothbrush
- soap
- bedroom:
- bed:
- pillow
- sheet
- closet:
- clothes:
- underwear
- socks
garden:
- treehouse:
- toys:
- nerfgun
- car
- window
- garage:
- car
- toolbox:
- hammer
- scewdriver
- pliers
- lawnmower
Here is another try at this document, it has no compound list (I guess that's how it's called).
items2:
house:
- bathroom:
- toothbrush
- soap
- bedroom:
- bed:
- pillow
- sheet
- closet:
- clothes:
- underwear
- socks
Which of those two yaml documents are valid ? I'm still wondering if I can use a list of keyed lists like that (nested list ?):
items:
- list1:
-itemA
-itemB
- list2:
-itemC
-itemD
You can use this to check if your yaml is ok: yamlint
It's seems ok.
Yes, it's valid YAML (well, the first two are; in the third, make sure that you have a space after your - in the sequences); but it may not do exactly what you think. In your toy example
items:
- list1:
- itemA
- itemB
- list2:
- itemC
- itemD
the value associated with items is a sequence; and each entry of that sequence is a map with a single key/value pair (for the first entry, the key is list1, and in the second, list2).
What may have confused you in your first real example was how to access each element. Since you tagged this yaml-cpp, here's how you would get, say, the list of the toys in the greenhouse of your first example:
doc["items"]["garden"][0]["treehouse"][0]["toys"];
(Note the [0] before accessing the "treehouse" and "toys" keys.)

Resources