ansible/yaml/json : Concat dict and string - json

I have a dict in json (ip_address) which consists of the following structure:
"0": {
"json": {
"code": 200,
"success": true,
"data": "10.100.146.3",
"time": 0.004
}
"1": {
"json": {
"code": 200,
"success": true,
"data": "10.100.146.4",
"time": 0.005
}
I'm trying to call the json twice, in seperate tasks like this:
"{{ ip_address + '.0.json.data' }}"
"{{ ip_address + '.1.json.data' }}"
This gives me the following error:
"Unexpected templating type error occurred on ({{ ip_address + '.0.json.data' }}): unsupported operand type(s) for +: 'dict' and 'str'"
I've tried various suggestions, here and on other guides/documentation but almost everything seems to give the same error. Anyone has any idea on how to solve this?

Related

How to change JSON file to be readable?

I've created a JSON.
This JSON file will be a source of query in ElasticSearch
This query works in ElasticSearch and returns results.
When I store the query to JSON, I see that JSON is valid. But when I read it from JSON I have an error.
It's my JSON:
{
"function_score": {
"query": {
"bool": {
"should": [
{
"query_string": {
"default_field": "headline",
"query": "engineer"
}
},
{
"match_all": {}
}
]
}
},
"functions": [
{
"script_score": {
"script": {
"params": {
"queryVector": [1,2,5],
"max_score": "max_score"
},
"source": "if (_score>0 && params.max_score>0){return doc['embedding_headline'].size() == 0 ? 0 : Math.min(Math.max(_score/params.max_score,cosineSimilarity(params.queryVector, 'embedding_headline')),1) + 1.0} else { return doc['embedding_headline'].size() == 0 ? 0 : cosineSimilarity(params.queryVector, 'embedding_headline') + 1.0}",
"lang": "painless"
}
}
}
],
"score_mode": "max",
"boost_mode": "replace",
}
}
I use python for reading JSON.
It's code for reading:
import json
file_path = 'queries.json'
with open(file_path) as f:
data = json.load(f)
print(data)
It's error:
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 34 column 5 (char 1012)
What should I change in this JSON to make it readable?
Thanks
That json is invalid.
I just pasted it into a json validator and it has errors in it.
CHeck this link:
json lint
Paste your json there, it throws the following errors:
Error: Parse error on line 30:
..._mode": "replace", }}
----------------------^
Expecting 'STRING', got '}'
If you are using python it often throws errors due to string quotation as well.
Try to enclose the script source field in single quotes instead of doubles. That could be another problem.
Last but not the least, function score queries are extremely slow, and I would recommend not using them!

ansible.builtin.uri module - Read and format JSON file content as a string to use in payload

I am trying to use the ansible.builtin.lookup plugin in order to read a JSON file from the local directory and then pass it as the payload to the ansible.builtin.uri module to send a POST message to a URI endpoint.
Following are the contents of my JSON file (config.json):
{
"Configuration": {
"Components": [
{
"Name": "A",
"Attributes": [
{
"Name": "A1",
"Value": "1",
"Set On Import": "True",
"Comment": "Read and Write"
},
{
"Name": "A2",
"Value": "2",
"Set On Import": "True",
"Comment": "Read and Write"
}
]
}
]
}
}
I need to send the above JSON content as the below string in the payload to ansible.builtin.uri module:
"{\"Configuration\": {\"Components\": [{\"Name\": \"A\", \"Attributes\": [{\"Name\": \"A1\", \"Value\": \"1\", \"Set On Import\": \"True\", \"Comment\": \"Read and Write\"}, {\"Name\": \"A2\", \"Value\": \"2\", \"Set On Import\": \"True\", \"Comment\": \"Read and Write\"}]}]}}"
I am trying to use the lookup plugin with the to_json filter to read and format the JSON content. Following is my playbook:
- name: import scp
ansible.builtin.uri:
url: "https://{{ inventory_hostname }}/api/config/actions/import"
user: "{{ user }}"
password: "{{ password }}"
method: POST
headers:
Accept: "application/json"
Content-Type: "application/json"
body:
Parameters:
Type: "LOCAL_FILE"
Target: "ALL"
IgnoreCertificateWarning: "Enabled"
Buffer: "{{ lookup('file', 'config.json') | to_json }}"
body_format: json
status_code: 202
validate_certs: no
force_basic_auth: yes
However, the uri module double escapes all the new-line and tab characters. Following is the how the payload is sent when I run the playbook:
"invocation": {
"module_args": {
"attributes": null,
"body": {
"Buffer": "\"{\\n\\t\\\"Configuration\\\": {\\n\\t\\t\\\"Components\\\": [\\n\\t\\t\\t{\\n\\t\\t\\t\\t\\\"Name\\\": \\\"A\\\",\\n\\t\\t\\t\\t\\\"Attributes\\\": [\\n\\t\\t\\t\\t\\t{\\n\\t\\t\\t\\t\\t\\t\\\"Name\\\": \\\"A1\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Value\\\": \\\"1\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Set On Import\\\": \\\"True\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Comment\\\": \\\"Read and Write\\\"\\n\\t\\t\\t\\t\\t},\\n\\t\\t\\t\\t\\t{\\n\\t\\t\\t\\t\\t\\t\\\"Name\\\": \\\"A2\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Value\\\": \\\"2\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Set On Import\\\": \\\"True\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Comment\\\": \\\"Read and Write\\\"\\n\\t\\t\\t\\t\\t}\\n\\t\\t\\t\\t]\\n\\t\\t\\t}\\n\\t\\t]\\n\\t}\\n}\"",
"Parameters": {
"IgnoreCertificateWarning": "Enabled",
"Type": "LOCAL_FILE",
"Target": "ALL"
},
},
"body_format": "json",
...
},
Could you please let me know how I can format the payload with uri module? Appreciate any help.
Edited (5/11/2021):
I made the changes as suggested by #mdaniel in his response and used string filter instead of to_json. With the suggested change, I can see the JSON being formatted properly into a string with newline ('\n') and tab ('\t') characters. I tried to use the replace filter to remove the \n and \t characters. However, now the whole string is converted back into the JSON.
Following is the playbook and the output when using the string filter alone:
...
body:
Parameters:
Type: "LOCAL_FILE"
Target: "ALL"
IgnoreCertificateWarning: "Enabled"
Buffer: "{{ lookup('file', 'config.json') | string }}"
$ ansible-playbook import_file.yml -i hosts --tags
...
"body": {
"HostPowerState": "On",
"Buffer": "{\n\t\"Configuration\": {\n\t\t\"Components\": [\n\t\t\t{\n\t\t\t\t\"Name\": \"A\",\n\t\t\t\t\"Attributes\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Name\": \"A1\",\n\t\t\t\t\t\t\"Value\": \"1\",\n\t\t\t\t\t\t\"Set On Import\": \"True\",\n\t\t\t\t\t\t\"Comment\": \"Read and Write\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Name\": \"A2\",\n\t\t\t\t\t\t\"Value\": \"2\",\n\t\t\t\t\t\t\"Set On Import\": \"True\",\n\t\t\t\t\t\t\"Comment\": \"Read and Write\"\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t}\n}",
"Parameters": {
"IgnoreCertificateWarning": "Enabled",
"Type": "LOCAL_FILE",
"Target": "ALL"
},
},
Following is the playbook and the output when using replace filter in conjunction with string filter:
...
body:
Parameters:
Type: "LOCAL_FILE"
Target: "ALL"
IgnoreCertificateWarning: "Enabled"
Buffer: "{{ lookup('file', 'config.json') | string | replace('\n', '') | replace('\t', '') }}"
...
$ ansible-playbook import_file.yml -i hosts --tags
...
"body": {
"Buffer": {
"Configuration": {
"Components": [
{
"Attributes": [
{
"Comment": "Read and Write",
"Name": "A1",
"Set On Import": "True",
"Value": "1"
},
{
"Comment": "Read and Write",
"Name": "A2",
"Set On Import": "True",
"Value": "2"
}
],
"Name": "A"
}
]
}
},
"Parameters": {
"IgnoreCertificateWarning": "Enabled",
"Type": "LOCAL_FILE",
"Target": "ALL"
},
},
...
Any pointers on how I remove the \n and \t characters from the string?
You have used to_json on a dict value that is, itself, going to be to_json-ed; ansible cannot transmit a python dict over HTTP, so any yaml structure that is not already a string needs to be converted into one first
What you'll want is just that lookup result (which will return a str, not a dict) and then ansible will apply to_json to the whole body: value for the aforementioned reason
However, because ansible is trying to be "helpful", it will auto-coerce a yaml value that it finds starting with { back into a dict -- that's why you just need to send the result of lookup through the | string filter to reinforce to ansible that yes, you really do want it to remain a str in that context
...
body:
Parameters:
Type: "LOCAL_FILE"
Target: "ALL"
IgnoreCertificateWarning: "Enabled"
Buffer: "{{ lookup('file', 'config.json') | string }}"
updated answer approach
In light of the comment discussion that the dict coercion was continuing to be a problem, and the leading space concerned the OP, the alternative approach is to build up the actual payload structure completely, and only "JSON-ify" it before transmission, to keep ansible and jinja on the same page about the data types:
- name: import scp
vars:
body_dict:
Parameters:
Type: "LOCAL_FILE"
Target: "ALL"
IgnoreCertificateWarning: "Enabled"
# this will be filled in before submission
# Buffer:
whitespace_free_config_json: >-
{{ lookup('file', 'config.json')
| regex_replace('[\t\n]', '')
| string
}}
ansible.builtin.uri:
...
body: >-
{{ body_dict
| combine({"Buffer": whitespace_free_config_json})
| to_json }}
body_format: json
status_code: 202

Json parse through ansible

I have below json
"edge_router_uuid.stdout": {
"buildInfo": {
"buildNumber": "20004",
"buildTimestamp": "1539995399724",
},
"isUp": true,
"pod": "gateway",
"reachable": true,
"region": "dc-1",
"tags": {
........
Actually it is a big json and I am showing above is just a part of it. I need to use the "debug" in ansible to get the variable values for region and reachable values. When I am trying the below
- debug:
var: edge_router_uuid.stdout.region
getting below error.
ok: [10.10.10.10] => {
"edge_router_uuid.stdout.region": "VARIABLE IS NOT DEFINED!"
The JSON syntax is wrong. The problem is "," behind the last value
"buildInfo": {
"buildNumber": "20004",
"buildTimestamp": "1539995399724",
}
Correct syntax is
"buildInfo": {
"buildNumber": "20004",
"buildTimestamp": "1539995399724"
}
Finally I am able to get it as below
- set_fact:
response_dict: "{{ edge_router_uuid.stdout }}"
I ended up with a dictionary named response_dict. Then I am able to get the region value like this:
- debug:
var: response_dict.region

Parsing string containing complex struct

I'm receiving a third-party API payload response like:
{
"message": "Validation failed because [{reason=CONDITIONAL_INVALID_VALUE, field=/targetingCriteria, batchIndex=0, type=INVALID_VALUE, message=/locale cannot be set to en if urn:li:adTargetingFacet:interfaceLocales is set to urn:li:locale:it_IT, parameters={field1=/locale, value2=urn:li:locale:it_IT, value1=en, field2=urn:li:adTargetingFacet:interfaceLocales, key=}}, {reason=FIELD_VALUE_TOO_LOW, field=dailyBudget, batchIndex=0, type=INVALID_VALUE, message=/dailyBudget/amount value 1 cannot be lower than 10.00, parameters={min=10.00, field=/dailyBudget/amount, costType=CPM, type=SPONSORED_UPDATES, value=1, key=}}]",
"status": 400
}
and I'd like to transform in something like:
{
"errors": [{
"reason": "CONDITIONAL_INVALID_VALUE",
"field": "/targetingCriteria",
"batchIndex": "0",
"type": "INVALID_VALUE",
"message": "/locale cannot be set to en if urn:li:adTargetingFacet:interfaceLocales is set to urn:li:locale:it_IT",
"parameters": "{field1=/locale, value2=urn:li:locale:it_IT, value1=en, field2=urn:li:adTargetingFacet:interfaceLocales, key=}"
},
{
"reason": "FIELD_VALUE_TOO_LOW",
"field": "dailyBudget",
"batchIndex": "0",
"type": "INVALID_VALUE",
"message": "/dailyBudget/amount value 1 cannot be lower than 10.00",
"parameters": "{min=10.00, field=/dailyBudget/amount, costType=CPM, type=SPONSORED_UPDATES, value=1, key=}"
}
]
}
But I'm struggling to find a clear golang approach to this problem, the main problems are:
no valid json is available: word are not correctly quoted with "
= symbol instead of :
nested graphs bracket
I'm currently try to transform in a valid json string and then parse as json but I have various problem with nested elements
Any idea?
EDIT: This is what I've done right now: https://play.golang.org/p/B7bdPCJoHc2

Extract field from JSON response with Ansible

I have a task which performs a GET request to a page. The response's body is a JSON like the following.
{
"ips": [
{
"organization": "1233124121",
"reverse": null,
"id": "1321411312",
"server": {
"id": "1321411",
"name": "name1"
},
"address": "x.x.x.x"
},
{
"organization": "2398479823",
"reverse": null,
"id": "2418209841",
"server": {
"id": "234979823",
"name": "name2"
},
"address": "x.x.x.x"
}
]
}
I want to extract the fields id and address, and tried (for id field):
tasks:
- name: get request
uri:
url: "https://myurl.com/ips"
method: GET
return_content: yes
status_code: 200
headers:
Content-Type: "application/json"
X-Auth-Token: "0010101010"
body_format: json
register: json_response
- name: copy ip_json content into a file
copy: content={{json_response.json.ips.id}} dest="/dest_path/json_response.txt"
but I get this error:
the field 'args' has an invalid value, which appears to include a variable
that is undefined. The error was: 'list object' has no attribute 'id'..
Where is the problem?
The error was: 'list object' has no attribute 'id'
json_response.json.ips is a list.
You either need to choose one element (first?): json_response.json.ips[0].id.
Or process this list for example with map or json_query filters if you need all ids.
Ansible command to copy to file:
copy:
content: "{{ output.stdout[0] }}"
dest: "~/ansible/local/facts/{{ inventory_hostname }}.txt"