I have two dictionaries as below
"fictional_characters": {
"male": [
"Donkey",
"Humpty",
"Piranha"
],
"female": [
"Fiona",
"Kitty_Softpaws",
"Diane_Foxington"
]
}
And
"movie_names": {
"Donkey": "Shrek",
"Humpty": "Puss_in_Boots",
"Piranha": "The_Bad_Guys",
"Fiona": "Shrek",
"Kitty_Softpaws": "Puss_in_Boots",
"Diane_Foxingtin": "The_Bad_Guys"
}
I would like to change the dictionary values in "fictional_characters" to the values in "movie_names", e.g.
"fictional_characters": {
"male": [
"Shrek",
"Puss_in_Boots",
"The_Bad_Guys"
],
"female": [
"Shrek",
"Puss_in_Boots",
"The_Bad_Guys"
]
}
I started by converting the "fictional_characters" dictionary into a list
- name: Convert fictional_characters to a list
set_fact:
fictional_characters_list: "{{fictional_characters | dict2items }}"
That gave me
"fictional_characters_list": [
{
"key": "male",
"value": [
"Donkey",
"Humpty",
"Piranha"
],
},
"key": "female",
"value": [
"Fiona",
"Kitty_Softpaws",
"Diane_Foxington"
]
}
]
Next, some Jinja
- name: Using Jinja to swap dict values
set_fact:
fict_char_movies: |
{% for e in fictional_characters_list %}
{{ e.key }}:
{% for char in e.value %}
{% if char in movie_names %}
- {{ movie_names[char]|split(',') %}
{% endif %}
{% endfor %}
{% endfor %}
- name: Print the result
debug:
msg: "{{ fict_char_movies | from_yaml }}"
the above returns the following result
"male": [
[
"Shrek"
],
[
"Puss_in_Boots"
],
[
"The_Bad_Guys"
],
"female": [
[
"Shrek"
],
[
"Puss_in_Boots"
],
[
"The_Bad_Guys"
]
]
How do I get rid of this nested list so that I get the below structure ?
"fictional_characters": {
"male": [
"Shrek",
"Puss_in_Boots",
"The_Bad_Guys"
],
"female": [
"Shrek",
"Puss_in_Boots",
"The_Bad_Guys"
]
}
The declarations below
fict_char_movies_str: |
{% for k,v in fictional_characters.items() %}
{{ k }}:
{% for char in v %}
{% if char in movie_names %}
- {{ movie_names[char] }}
{% endif %}
{% endfor %}
{% endfor %}
fict_char_movies: "{{ fict_char_movies_str|from_yaml }}"
The template can be simplified
fict_char_movies_str: |
{% for k,v in fictional_characters.items() %}
{{ k }}:
{{ v|map('extract', movie_names) }}
{% endfor %}
give what you want
fict_char_movies:
female:
- Shrek
- Puss_in_Boots
- The_Bad_Guys
male:
- Shrek
- Puss_in_Boots
- The_Bad_Guys
If you want to iterate set_fact the task below gives the same result
- set_fact:
fict_char_movies: "{{ fict_char_movies|d({})|combine(_dict|from_yaml) }}"
loop: "{{ fictional_characters|dict2items }}"
vars:
_dict: "{ {{ item.key }}: {{ item.value|map('extract', movie_names) }} }"
Example of a complete playbook for testing
- hosts: localhost
vars:
fictional_characters:
female:
- Fiona
- Kitty_Softpaws
- Diane_Foxington
male:
- Donkey
- Humpty
- Piranha
movie_names:
Diane_Foxington: The_Bad_Guys
Donkey: Shrek
Fiona: Shrek
Humpty: Puss_in_Boots
Kitty_Softpaws: Puss_in_Boots
Piranha: The_Bad_Guys
fict_char_movies_str: |
{% for k,v in fictional_characters.items() %}
{{ k }}:
{{ v|map('extract', movie_names) }}
{% endfor %}
fict_char_movies: "{{ fict_char_movies_str|from_yaml }}"
tasks:
- debug:
var: fict_char_movies
Vladimir's solution is probably cleaner, but it's also possible to get to the same place without using a Jinja template. For example, we can write:
- hosts: localhost
gather_facts: false
vars:
fictional_characters:
male:
- Donkey
- Humpty
- Piranha
female:
- Fiona
- Kitty_Softpaws
- Diane_Foxington
- Meilin_Lee
movie_names:
Donkey: Shrek
Humpty: Puss_in_Boots
Piranha: The_Bad_Guys
Fiona: Shrek
Kitty_Softpaws: Puss_in_Boots
Diane_Foxington: The_Bad_Guys
Meilin_Lee: Turning_Red
tasks:
- set_fact:
new_fictional_characters: >-
{{
new_fictional_characters|combine(
{
'male': item.0|ternary(new_fictional_characters.male + [movie_names[item.0]], new_fictional_characters.male),
'female': item.1|ternary(new_fictional_characters.female + [movie_names[item.1]], new_fictional_characters.female),
}
)
}}
loop: >-
{{ fictional_characters.male|zip_longest(fictional_characters.female) }}
vars:
new_fictional_characters:
male: []
female: []
- set_fact:
fictional_characters: "{{ new_fictional_characters }}"
- debug:
var: fictional_characters
Note that I've added an additional character here to demonstrate that this works when the lists are of different lengths. Running this produces:
PLAY [localhost] ***************************************************************
TASK [set_fact] ****************************************************************
ok: [localhost] => (item=['Donkey', 'Fiona'])
ok: [localhost] => (item=['Humpty', 'Kitty_Softpaws'])
ok: [localhost] => (item=['Piranha', 'Diane_Foxington'])
ok: [localhost] => (item=[None, 'Meilin_Lee'])
TASK [set_fact] ****************************************************************
ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {
"fictional_characters": {
"female": [
"Shrek",
"Puss_in_Boots",
"The_Bad_Guys",
"Turning_Red"
],
"male": [
"Shrek",
"Puss_in_Boots",
"The_Bad_Guys"
]
}
}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
An even simpler solution is to drop the following into filter_plugins/swap_names.py:
def filter_swap_names(val, name_map):
return {k: [name_map[name] for name in v] for k, v in val.items()}
class FilterModule:
def filters(self):
return dict(swap_names=filter_swap_names)
And then write the playbook like this:
- hosts: localhost
gather_facts: false
vars:
fictional_characters:
male:
- Donkey
- Humpty
- Piranha
female:
- Fiona
- Kitty_Softpaws
- Diane_Foxington
- Meilin_Lee
movie_names:
Donkey: Shrek
Humpty: Puss_in_Boots
Piranha: The_Bad_Guys
Fiona: Shrek
Kitty_Softpaws: Puss_in_Boots
Diane_Foxington: The_Bad_Guys
Meilin_Lee: Turning_Red
tasks:
- set_fact:
fictional_characters: "{{ fictional_characters|swap_names(movie_names) }}"
- debug:
var: fictional_characters
I am trying to access elements from both lists: lista1 and lista2.
First join them into one dictionary and then to be able to do something like this:
Vegetable {{ lista1.fruits.name }} has taste {{ lista1.fruits.taste }}
This is what I got so far, but it's really bad,and I would like to have dictionary like access instead of regex_search.
---
- name: demo how register works
hosts: localhost
vars:
lista1:
fruits:
- name: tomato
taste: tomato-like
- name: lemon
taste: sour
lista2:
vegetables:
- name: carrot
taste: sweet
tasks:
- name: debug
debug:
var: item
loop:
- lista1.fruits
- lista2.vegetables
register: echo
- name: show register results
debug:
msg: "Food named: {{ item| map(attribute='name')|list|join(', ') |regex_search('tomato') }} tastes: {{ item| map(attribute='taste')|list|join(', ')|regex_search('tomato-like') }}"
loop: "{{ lista1.fruits|zip(lista2.vegetables)|list }} "
A debug task to show the "food" with its "taste" can be displayed by accessing item.name and item.taste by looping through lista1.fruits and lista2.vegetables.
Example:
- name: show register results
debug:
msg: "Food named: {{ item.name }} tastes: {{ item.taste }}"
with_items:
- "{{ lista1.fruits }}"
- "{{ lista2.vegetables }}"
Update
Instead of with_items, loop can be used as:
- name: show register results
debug:
msg: "Food named: {{ item.name }} tastes: {{ item.taste }}"
loop: "{{ lista1.fruits + lista2.vegetables }}"
I have an ingress definition like this
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ $fullName }}-stateful
labels:
app: oxauth
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.org/ssl-services: "oxtrust"
nginx.ingress.kubernetes.io/app-root: "/identity" <------------------- CONFUSING
nginx.ingress.kubernetes.io/affinity: "cookie"
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ . | quote }}
http:
paths:
- path: /identity
backend:
serviceName: oxtrust
servicePort: 8080
- path: /idp
backend:
serviceName: oxshibboleth
servicePort: 8080
- path: /passport
backend:
serviceName: oxpassport
servicePort: 8090
I know nginx.ingress.kubernetes.io/app-root is supposed to redirect requests coming from / to the one define there.
My question is since now we have multiple services defined under that ingress object what will happen?? I am very confused here. Putting in mind there is another ingress defined as shown.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ $fullName }}-base
labels:
app: oxtrust
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/app-root: "/identity"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "route"
nginx.ingress.kubernetes.io/session-cookie-hash: "sha1"
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ . | quote }}
http:
paths:
- path: /
backend:
serviceName: oxtrust
servicePort: 8080
{{- end }}
Much appreciated.
Any request coming through the specified routes in that ingress definition should be rewritten or directed to /identity
I have snippet like this in the init.sls:
{% for server, args in pillar.get('servers', {}).items() %}
software-server#{{ server }}
service.running:
- enable: true
- require:
- pkg: software_pkgs
- watch:
- file: software_config
/etc/software/{{server}}.json:
file.managed:
- source: salt://software/files/config.json.j2
- template: jinja
{% endfor %}
config.json.j2:
{
listen: {{server}}:{{listen_addr}}
}
and in the pillar:
software.servers:
server1:
listen_addr:10.0.0.1
server2:
listen_addr:127.0.01
in each of the {{server}}.json the listen_addr is different. I don't know if saltstack has something like a scope for current loop, or is there a workaround for this.
You probably need to use context or defaults options in file.managed:
file.managed
In your example it would like like :
/etc/software/{{server}}.json:
file.managed:
- source: salt://software/files/config.json.j2
- template: jinja
- context:
server: {{ server }}
listen_addr: {{ server['listen_addr'] }}
I would like to build a macro, which automatically reads the image's width and height of the corresponding filter.
Let's say, this is a part of my images.yml
liip_imagine:
filter_sets:
original:
path: original
quality: 80
small:
path: small
quality: 75
filters:
thumbnail: { size: [400, 400], mode: inset }
How can I re-use the size in my Twig macro?
{% macro img(media, filter) %}
{% spaceless %}
<img
src="{{ media.filename|imagine_filter(filter) }}"
alt=""
{# is there something like this? #}
width="{{ imagine_get_filter(filter).filters[0].size[0] }}"
height="{{ imagine_get_filter(filter).filters[0].size[1] }}"
/>
{% endspaceless %}
{% endmacro %}