I want to generate valid dictionary in Ansible with condition depends for sending it by uli module (as json) and try to use Jinja2:
- name: set_fact
set_fact:
request_body:
matchers: |
{% if "myserver" in server__fqdn %}
- name: "host"
value: "{{ server__fqdn }}"
type: "EQUAL"
- name: "node"
value: "myserver"
type: "EQUAL"
{% else %}
- name: "host"
value: "{{ server__fqdn }}"
type: "EQUAL"
{% endif %}
teamName: "{{ server__team }}"
- name: send data to endpoint
vars:
endpoint_url: "http://myendpoint.org"
uri:
url: "{{ endpoint_url }}/api/v1/data"
body: "{{ request_body }}"
body_format: json
headers:
Content-Type: "application/json"
Authorization: "endpointKey {{ provision__endpoint_data_token }}"
method: POST
register: data_status
ignore_errors: true
I checking type of generated var request_body with debug:
- name: debug
debug:
msg:
- "{{ request_body }}"
- "{{ request_body | type_debug }}"
and it show valid dict:
TASK [provision : debug] ************************************************************************************************************************************************************************
четверг 19 октября 2023 16:39:06 +0300 (0:00:00.078) 0:00:05.771 *******
ok: [56040] =>
msg:
matchers: |-
- name: "host"
value: "myserver.local"
type: "EQUAL"
- name: "node"
value: "myserver"
type: "EQUAL"
teamName: superduper_team
- dict
But task with sending var to endpoint failed:
TASK [provision : send data to endpoint] ********************************
четверг 19 октября 2023 16:39:06 +0300 (0:00:00.039) 0:00:05.810 *******
fatal: [myserver.local]: FAILED! => changed=false
connection: close
content_length: '262'
content_type: application/json
date: Thu, 19 Oct 2023 13:39:07 GMT
elapsed: 0
json:
code: 3
details: []
message: 'proto: syntax error (line 1:14): unexpected token "- name: \"host\"\n value: \"myserver.local\"\n type: \"EQUAL\"\n- name: \"node\"\n value: \"myserver\"\n type: \"EQUAL\"\n"'
msg: 'Status code was 400 and not [200]: HTTP Error 400: Bad Request'
redirected: false
server: nginx
status: 400
url: http://myendpoint.org
vary: Origin
...ignoring
But, when I do not use var generation it works:
- name: send data to endpoint
vars:
endpoint_url: "http://myendpoint.org"
request_body:
matchers:
- name: "host"
value: "myserver.local"
type: "EQUAL"
- name: "node"
value: "myserver"
type: "EQUAL"
uri:
url: "{{ endpoint_url }}/api/v1/data"
body: "{{ request_body | to_json }}"
body_format: json
headers:
Content-Type: "application/json"
Authorization: "endpointKey {{ provision__endpoint_data_token }}"
method: POST
register: data_status
Where is my problem?
Convert the YAML block to the list. Use filter
request_body:
matchers: |
{% filter from_yaml %}
{% if "myserver" in server__fqdn %}
- name: "host"
value: "{{ server__fqdn }}"
type: "EQUAL"
- name: "node"
value: "myserver"
type: "EQUAL"
{% else %}
- name: "host"
value: "{{ server__fqdn }}"
type: "EQUAL"
{% endif %}
{% endfilter %}
teamName: "{{ server__team }}"
Example of a complete playbook for testing
- hosts: all
vars:
server__fqdn: myserver.local
server__team: superduper_team
request_body:
matchers: |
{% filter from_yaml %}
{% if "myserver" in server__fqdn %}
- name: "host"
value: "{{ server__fqdn }}"
type: "EQUAL"
- name: "node"
value: "myserver"
type: "EQUAL"
{% else %}
- name: "host"
value: "{{ server__fqdn }}"
type: "EQUAL"
{% endif %}
{% endfilter %}
teamName: "{{ server__team }}"
tasks:
- debug:
var: request_body|type_debug
- debug:
var: request_body
- debug:
var: request_body|to_json
gives
shell> ansible-playbook pb2.yml -l test_11
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [test_11] =>
request_body|type_debug: dict
TASK [debug] *********************************************************************************
ok: [test_11] =>
request_body:
matchers:
- name: host
type: EQUAL
value: myserver.local
- name: node
type: EQUAL
value: myserver
teamName: superduper_team
TASK [debug] *********************************************************************************
ok: [test_11] =>
request_body|to_json: '{"matchers": [{"name": "host", "value": "myserver.local", "type": "EQUAL"}, {"name": "node", "value": "myserver", "type": "EQUAL"}], "teamName": "superduper_team"}'
PLAY RECAP ***********************************************************************************
test_11: ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The next option is to create a dictionary
matchers_dict:
true:
- name: host
value: "{{ server__fqdn }}"
type: EQUAL
- name: node
value: myserver
type: EQUAL
false:
- name: host
value: "{{ server__fqdn }}"
type: EQUAL
and select the attribute depending on the condition
matchers: "{{ matchers_dict['myserver' in server__fqdn] }}"
request_body:
teamName: "{{ server__team }}"
matchers: "{{ matchers }}"
Example of a complete playbook for testing
- hosts: all
vars:
server__fqdn: myserver.local
server__team: superduper_team
matchers_dict:
true:
- name: host
value: "{{ server__fqdn }}"
type: EQUAL
- name: node
value: myserver
type: EQUAL
false:
- name: host
value: "{{ server__fqdn }}"
type: EQUAL
matchers: "{{ matchers_dict['myserver' in server__fqdn] }}"
request_body:
teamName: "{{ server__team }}"
matchers: "{{ matchers }}"
tasks:
- debug:
var: request_body|type_debug
- debug:
var: request_body
- debug:
var: request_body|to_json
Optionally, use the filter ternary
matchers_dict:
A:
- name: host
value: "{{ server__fqdn }}"
type: EQUAL
- name: node
value: myserver
type: EQUAL
B:
- name: host
value: "{{ server__fqdn }}"
type: EQUAL
matchers: "{{ ('myserver' in server__fqdn)|
ternary(matchers_dict.A, matchers_dict.B) }}"