How to concatenate string in YAML? - yaml

I am writing a playbook in which I need to execute mysql query and pass
output in json format. playbook code working fine just I want facing error in string concatenate part. If I am passing sample json string it is working fine.
- name: Execute php script with json parameter
command: php -f /path/to/php/test.php "{'colors':{'red','blue','yellow'}}"
register: php_output
output.stdout_lines is variable already set in my playbook which contains output in {'red','blue','yellow'} format.
- name: Execute php script with json parameter
command: php -f /path/to/php/test.php '{"stdout_lines": {{output.stdout_lines}} }'
register: php_output
So how can I concatenate output.stdout_lines variable in '{"stdout_lines": {{output.stdout_lines}} }' ? any suggestions

stdout_lines was created for convenience. Back in the day we only had stdout. Which is what you want, I think:
command: php -f /path/to/php/test.php '{"stdout_lines": {{output.stdout}} }'
and if you want to really concat yourself, say because you have your own list of strings then you can use the Jinja2 built-in filter join:
- hosts: localhost
gather_facts: False
tags: so9
vars:
- str_list: ['Hello', 'world', '!', ]
tasks:
- debug: msg="orig list={{ str_list }}"
- debug: msg="concated = {{ str_list | join(' ') }}"
- set_fact: concated_str="{{ str_list | join(' ') }}"
- debug: msg="assigned to a variable concated_str = {{ concated_str }}"

Try like this.
vars:
- img_path: "/var/lib/libvirt/images/{{ instance_name }}-disk2.img"
Where instance name is a another variable

This will do
- name: Generate JSON output based on template
template: src=colors.json.j2 dest=/tmp/colors.json
with_items: colors
It will generate a file like
{'colors':
{
'blue',
'red',
'green',
}
}

The to_json filter is what you want.
Ansible doesn't store variables as JSON strings, it stores them as Python objects. When you're debugging e.g. with -vvv, Ansible displays these Python objects as JSON strings, but that's for your convenience.
To produce the desired command:
- name: Execute php script with json parameter
vars:
# define my desired object
my_object:
colors: "{{ output.stdout_lines }}"
# my_object contains a dict with a single key
# "colors" who's value is the list contained
# in output.stdoutlines
command: "php -f /path/to/php/test.php {{ my_object | to_json | quote }}"
register: php_output
to_json takes the object stored in my_object and outputs a JSON string.
quote automatically handles any shell quoting that might be required for the JSON string. e.g. If my JSON string contains double-quotes, quote will wrap the whole JSON string in single-quotes.

Related

extract data with json_query from jinja2 variable in ansible

I have a variable in the inventory that contains a JSON formatted data.
I want to extract a specific part of the data with json_query.
The variable contains a list of domains with related IP addresses (the JSON is valid):
DOMAIN={"domain1.net": {"addr": ["10.10.10.10", "10.10.10.20"]}, "domain2.net": {"addr": ["172.16.10.1", "172.16.20.1", "172.16.30.1"]}}
With an ansible-playbook, using json_query, I want to extact only the domain2.net IP addresses.
I've used https://api.gopipeline.io/jmespath-tester to validate the JMESPath query.
With this filter: "domain2.net".addr in the jmespath-tester, I got the following (expected) output:
[
"172.16.10.1",
"172.16.20.1",
"172.16.30.1"
]
When I apply the same json_query with this ansible-playbook, I have no output:
Task
---
- name: Extract addr for domain2.net
tags: test
debug: msg="{{ DOMAIN | to_json | from_json | json_query("domain2.net".addr) }}"
Output:
ok: [domain-lab-1] => {
"msg": ""
}
I've tested also another query, by filtering only domain2.net in JMESPath online tester:
https://api.gopipeline.io/jmespath-tester and I get this expected output:
{
"addr": [
"172.16.10.1",
"172.16.20.1",
"172.16.30.1"
]
}
But, when I try to do the same within an Ansible playbook, still no output:
Task
---
- name: Extract addr for domain2.net
tags: test
debug: msg="{{ DOMAIN | to_json | from_json | json_query("domain2.net") }}"
Output:
ok: [domain-lab-1] => {
"msg": ""
}
If I try to print only the DOMAIN var, I can see the whole JSON output.
So, the variable is correctly read.
I'm using ansible 2.9.14.
I've read that the to json|from json from here:
Ansible : filter elements containing string with JMESPath
I'm not sure if is needed in my case, anyway adding or removing them does not make any difference.
You don't need json_query. Simply reference the attribute. You can't use the dot notation because the attribute domain2.net is not a valid name of the variable in Ansible. Put it into the brackets instead. For example
- name: Extract addr for domain2.net
debug:
msg: "{{ DOMAIN['domain2.net'].addr }}"
gives
msg:
- 172.16.10.1
- 172.16.20.1
- 172.16.30.1
Notes
See Referencing key:value dictionary variables.
Any string is a valid key in the YAML dictionary(mapping).
Ansible variable name can only include letters, numbers, and underscores.

How to read an Ansible variable from a string containing YAML?

I have a task which runs an uri call to get a JSON file containing a YAML fragment (it is obtained from Rancher API). I'm able to extract the YAML fragment using the following task
- name: generate_stack_call_body.yml read docker compose from catalog
set_fact:
docker_compose: '{{ template_detail|json_query(dc_query) }}'
When I run the ansible debug strategy, it indicates my docker_compose variable contains
(debug) p vars['docker_compose']
u"consul:\n labels:\n io.rancher.container.hostname_override: container_name\n io.rancher.container.pull_image: always\n io.rancher.container.hostname_override: container_name\n io.rancher.scheduler.global: 'true'\n stdin_open: true\n image: registry.mycompany.com/my-project/consul-rancher:0.9.0\n volumes:\n - /data/consul:/consul/data\nload-balancer:\n ports:\n - 8500:8500\n labels:\n io.rancher.container.hostname_override: container_name\n io.rancher.scheduler.global: 'true'\n stdin_open: true\n image: rancher/load-balancer-service\n links:\n
- consul:consul\n"
Which looks like valid YAML to me.
As a consequence, I guess it should be possible to have that value interpreted as a valid Ansible variable.
But how can I have it interpreted as a variable?
And how can I later put back that variable in a string?
But how can I have it interpreted as a variable?
set_fact:
docker_compose: '{{ template_detail | json_query(dc_query) | from_yaml }}'
And how can I later put back that variable in a string?
{{ docker_compose | to_yaml }}
Reference

Ansible truncate concatentated string

I am generating an yaml template in Ansible and I am trying to truncate two concatenated strings:
Here the following code doesn't work because of the concatenation does not pipe into the regex_replace correctly.
I am only wanting the first n characters (first 10 characters in this example)
Normally I could just combine these two into one variable and then do
{{variabel [:10] }}
But I am no able to do that in this case because the file I am working in is getting combined with variables and then saved as a yaml file...
Basically I want to truncate the string without first combining or creating a new variable.
- hosts: localhost
gather_facts: False
vars:
foo: "somelongstring"
tasks:
- name: Display debug output
debug:
msg: "{{ foo + '-moretext' | regex_replace('^.{0,10}', '\\1') }} "
To apply a filter or an operator on a complex expression (other than a sequence of filters), you have to surround it with parenthesis.
So to truncate the result of a concatenation in 1 action:
msg: "{{ (foo + '-moretext')[:10] }} "
BTW, there is also the truncate filter:
msg: "{{ (foo + '-moretext') | truncate(10, True, '') }} "

Ansible manipulate variable with gsub

I have an argument that is being passed in the form of foobar-a
ansible-playbook test.yml -e "argument=foobar-a"
- name: whatever
hosts: whatever
tasks:
- name: echo
shell: echo {{ argument }}
#should return foobar-b
I need to manipulate that information to be in the form of foobar-b What is the Ansible way of doing this?
In Chef I would just use ruby gsub to do this
argument.gsub!('-a', '-b')
You should use regex_replace filter, like in the playbook below.
- name: whatever
hosts: whatever
tasks:
- name: echo
shell: echo {{ argument | regex_replace('-a','-b') }}
See filters documentation

How can I use jinja2 to join with quotes in Ansible?

I have an ansible list value:
hosts = ["site1", "site2", "site3"]
if I try this:
hosts | join(", ")
I get:
site1, site2, site3
But I want to get:
"site1", "site2", "site3"
Why not simply join it with the quotes?
"{{ hosts | join('", "') }}"
Ansible has a to_json, to_nice_json, or a to_yaml in it's filters:
{{ some_variable | to_json }}
{{ some_variable | to_yaml }}
Useful if you are outputting a JSON/YAML or even (it's a bit cheeky, but JSON mostly works) a python config file (ie Django settings).
For reference: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#filters-for-formatting-data
The previous answer leaves "" if the list is empty. There is another approach that may be more robust, e.g. for assigning the joined list as a string to a different variable (example with single quotes):
{{ hosts | map("regex_replace","(.+)","\'\\1\'") | join(',')}}
From a json file file_name.json array like this
"hosts": [
"site1",
"site2",
"site3"
]
Set_fact from a json file
- name: Set variables from parameters file
set_fact:
vars_from_json: "{{ lookup('file', 'file_name.json') | from_json }}"
Use join with double quote and comma, and wrap all around in double quote
- name: Create host list
set_fact:
host_list: "{{ '\"' + vars_from_json.hosts | join('\"'', ''\"') + '\"' }}"

Resources