ill have a role playbook which get json from gitlab
---
- name: Make an API call
uri:
method: GET
url: "https://gitlab.example.com/api/v4/projects/***/repository/files/Dev/raw?ref=master"
headers:
PRIVATE-TOKEN: **************
register: json_var
- name: show json
debug:
msg: "{{json_var}}"
- name: test
debug:
var: json_var.json.plannedrelease
register: release
- name: debug
debug:
msg: "{{ release }}"
but cant get json value to variable, i need only version "1.0" in variable release (from "plannedrelease" : "1.0"), how can i filter it?
Playbook output is:
PLAY [127.0.0.1] ***************************************************************
TASK [Gathering Facts] *********************************************************
ok: [127.0.0.1]
TASK [get_contour_version : Make an API call] **********************************
ok: [127.0.0.1]
TASK [get_contour_version : show json] *****************************************
ok: [127.0.0.1] => {
"msg": {
"cache_control": "max-age=0, private, must-revalidate, no-store, no-cache",
"changed": false,
"connection": "close",
"content_disposition": "inline; filename=\"Dev\"; filename*=UTF-8''Dev",
"content_length": "402",
"content_type": "text/plain; charset=utf-8",
"cookies": {},
"cookies_string": "",
"date": "Tue, 19 Jan 2021 19:42:33 GMT",
"elapsed": 0,
"expires": "Fri, 01 Jan 1990 00:00:00 GMT",
"failed": false,
"json": {
"Created": "12/06/2020 10:11",
"Key": "123",
"Updated": "01/12/2020 11:51",
"contour": "Dev",
"plannedrelease": "1.0",
"…
TASK [get_contour_version : test] **********************************************
ok: [127.0.0.1] => {
"json_var.json.plannedrelease": "1.0"
}
TASK [get_contour_version : debug] *********************************************
ok: [127.0.0.1] => {
"msg": {
"changed": false,
"failed": false,
"json_var.json.plannedrelease": "1.0"
}
}
PLAY RECAP *********************************************************************
127.0.0.1 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
probably i used wrong method for filtering
Have you tried https://docs.ansible.com/ansible/latest/collections/ansible/builtin/set_fact_module.html ? Reading the documentation the following might work:
- name: Save release var
set_fact:
release: "{{ json_var.json.plannedrelease }}"
Related
I am new to writing Ansible playbooks and have hit a roadblock. I am trying to use the Site24x7 API to schedule maintenance and need to get a specific ID from a list of Monitor Groups. I created the following:
- name: Download Monitor Groups from Site24x7
uri:
url: https://www.site24x7.com/api/monitor_groups
method: GET
return_content: yes
headers:
Accept: "application/json; version=2.1"
Authorization: "Zoho-oauthtoken {{authtoken.json.access_token}}"
body:
return_content: yes
register: monitor_groups
- name: Get Monitor Group ID
set_fact:
monitorGroupID_query: "[?display_name=='{{hostname.stdout}}'].group_id"
- name: Assign Monitor Group ID
set_fact:
monitorGroupID: "{{ monitor_groups.json | json_query(monitor_group_id_query) }}"
- debug:
var: monitorGroupID
My API call returns data that looks like the following
"monitor_groups.json": {
"code": 0,
"data": [
{
"description": "System Generated",
"display_name": "server1",
"group_id": "319283000000505864",
"group_type": 1,
"health_threshold_count": 1,
"monitors": [
"319283000000483017"
]
},
{
"display_name": "server2",
"group_id": "319283000004701003",
"group_type": 3,
"health_threshold_count": 1,
"monitors": [
"319283000003989345",
"319283000004061005"
]
}
],
"message": "success"
}
My query constantly returns an empty string.
TASK [Assign Monitor Group ID] *****************************************************************************************
ok: [server1.fdu.edu] => {"ansible_facts": {"monitorGroupID": ""}, "changed": false}
TASK [debug] *****************************************************************************************
ok: [server1.fdu.edu] => {
"monitorGroupID": ""
}
Thank you in advance for your help
Single quotes don't work in JMESPath the way it does in almost any other "query language" -- you'll want ` characters wrapped around the JSON literal values. Also, the data:[] in your response is not implied, so if you meant to use it you'll need to either .json.data | json_query or alter your JMESPath to add the data[?display... part
- name: Assign Monitor Group ID
set_fact:
monitorGroupID: "{{ monitor_groups.json | json_query( monitorGroupID_query ) }}"
vars:
monitorGroupID_query: 'data[?display_name==`"{{hostname.stdout}}"`].group_id'
given hostname.stdout=server2 yields
ok: [localhost] => {"ansible_facts": {"monitorGroupID": ["319283000004701003"]}, "changed": false}
For example, given the data below
monitor_groups:
json:
code: 0
data:
- description: System Generated
display_name: server1
group_id: '319283000000505864'
group_type: 1
health_threshold_count: 1
monitors:
- '319283000000483017'
- display_name: server2
group_id: '319283000004701003'
group_type: 3
health_threshold_count: 1
monitors:
- '319283000003989345'
- '319283000004061005'
message: success
Create a dictionary of the hostnames and their group_id. Use it to evaluate the variable monitorGroupID
name_id: "{{ monitor_groups.json.data|
items2dict(key_name='display_name', value_name='group_id') }}"
monitorGroupID: "{{ name_id[hostname.stdout] }}"
gives
name_id:
server1: '319283000000505864'
server2: '319283000004701003'
Then, the task below
- debug:
var: monitorGroupID
gives
TASK [debug] **********************************************************
ok: [server1] =>
monitorGroupID: '319283000000505864'
ok: [server2] =>
monitorGroupID: '319283000004701003'
We have to set and unset a VLAN configuration to a switch. Fortunately, Unify Controller provide an API, which works with Ansible in general.
However, it is not possible with the API to change a single element, Ansible has to get the current configuration, find and change the desired element and send back the configuration. (I pushed the desired config change. This yielded to the situation that the switch had nothing but my config change, I felt like the Facebook guys...)
---
- hosts: adm01.local
gather_facts: false
tasks:
- name: Use vars from Vault
include_vars: unifi_info.yaml
- name: Get Cookie from Unifi
uri:
url: https://{{ url }}/api/login
method: POST
body_format: json
body: {"username":"{{ username }}","password":"{{ password }}"}
validate_certs: false
register: login
- name: Print returned data to ensure it worked
debug:
msg: "{{ login }}"
- name: Get current config
uri:
url: https://{{ url }}/api/s/default/stat/device/
method: GET
body_format: json
headers:
Cookie: "{{ login.cookies_string }}"
validate_certs: false
register: switchConf
- name: Print SwitchConf
debug:
msg: "{{ switchConf }}"
- name: Set port 2 to CLIENTVLAN
uri:
url: https://{{ url }}/api/s/default/rest/device/60acc79964542d80774123b5/
method: PUT
body_format: json
# DISABLE VLAN: "60aca5ee64542d807741239d"
# CLIENTVLAN: ""60accb9b64542d80774123d1"
#body: {"port_overrides": [{"port_idx": 2, "portconf_id":"60accb9b64542d80774123d1"}]}
headers:
Cookie: "{{ login.cookies_string }}"
validate_certs: false
register: portConf
- name: Print portConf
debug:
msg: "{{ portConf }}"
SwitchConf prints (the relevant part, only):
"msg": {
"access_control_allow_credentials": "true",
"access_control_expose_headers": "Access-Control-Allow-Origin,Access-Control-Allow-Credentials",
"changed": false,
"connection": "close",
"content_type": "application/json;charset=UTF-8",
"cookies": {},
"cookies_string": "",
"date": "Wed, 20 Oct 2021 18:22:24 GMT",
"elapsed": 0,
"failed": false,
"json": {
"data": [
{
(...)
"port_overrides": [
{
"port_idx": 1,
"portconf_id": "A"
},
{
"port_idx": 2,
"portconf_id": "B"
},
{
"port_idx": 3,
"portconf_id": "B"
},
{
"port_idx": 5,
"portconf_id": "B"
},
{
"port_idx": 6,
"portconf_id": "B"
},
{
"port_idx": 7,
"portconf_id": "B"
},
(...)
],
(...)
}
]
},
"msg": "OK (unknown bytes)",
"redirected": false,
"status": 200,
"transfer_encoding": "chunked",
"url": "https://adm01.local:8443/api/s/default/stat/device/",
"vary": "accept-encoding,origin,accept-encoding",
"x_frame_options": "DENY"
}
}
So, the question is: how can Ansible be used to fetch the JSON datagram portConf->data->port_overrides and change the "portconf_id" to "X" with "port_idx"==2, so that post_overrides can be used to POST it back?
The dictionaries in YAML are immutable. You have to create a new one to change anything. Given the simplified data
portConf:
status: 200
json:
data:
- port_overrides:
- port_idx: 1
portconf_id: A
- port_idx: 2
portconf_id: B
- port_idx: 3
portconf_id: C
and the required changes
diff:
port_idx: 2
portconf_id: X
the task below makes the changes in json.data
- set_fact:
_json: "{{ {'data': [{'port_overrides': _port_change}]} }}"
vars:
_port_remove: "{{ portConf.json.data.0.port_overrides|
rejectattr('port_idx', 'eq', diff.port_idx)|
list }}"
_port_change: "{{ (_port_remove + [diff])|sort(attribute='port_idx') }}"
gives
_json:
data:
- port_overrides:
- port_idx: 1
portconf_id: A
- port_idx: 2
portconf_id: X
- port_idx: 3
portconf_id: C
and the next task combines the changes
- set_fact:
portConf: "{{ portConf|combine({'json': _json}) }}"
gives
portConf:
json:
data:
- port_overrides:
- port_idx: 1
portconf_id: A
- port_idx: 2
portconf_id: X
- port_idx: 3
portconf_id: C
status: 200
Environment:
Ansible: 2.10.12
Python: 3.6.8
Jinja2: 3.0.1
Given an API response, I'm trying to grab the address, but getting "variable is not defined". Here's a playbook that has the API response as a var:
---
- name: test
gather_facts: no
hosts: localhost
vars:
pool_details: {
"allow": "",
"cache_control": "no-store, no-cache, must-revalidate",
"changed": false,
"connection": "close",
"content_length": "668",
"content_security_policy": "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:; img-src 'self' data: http://127.4.1.1 http://127.4.2.1",
"content_type": "application/json; charset=UTF-8",
"cookies": {},
"cookies_string": "",
"date": "Tue, 24 Aug 2021 16:34:47 GMT",
"elapsed": 0,
"expires": "-1",
"failed": false,
"json": {
"items": [
{
"address": "10.200.136.22",
"connectionLimit": 0,
"dynamicRatio": 1,
"ephemeral": "false",
"fqdn": {
"autopopulate": "disabled"
},
"fullPath": "/Common/sensor01:443",
"generation": 103,
"inheritProfile": "enabled",
"kind": "tm:ltm:pool:members:membersstate",
"logging": "disabled",
"monitor": "default",
"name": "sensor01:443",
"partition": "Common",
"priorityGroup": 0,
"rateLimit": "disabled",
"ratio": 1,
"selfLink": "https://localhost/mgmt/tm/ltm/pool/~Common~sensor01/members/~Common~sensor01:443?ver=16.1.0",
"session": "monitor-enabled",
"state": "up"
}
],
"kind": "tm:ltm:pool:members:memberscollectionstate",
"selfLink": "https://localhost/mgmt/tm/ltm/pool/~Common~sensor01/members?ver=16.1.0"
},
"msg": "OK (668 bytes)",
"pragma": "no-cache",
"redirected": false,
"server": "Jetty(9.2.22.v20170606)",
"status": 200,
"strict_transport_security": "max-age=16070400; includeSubDomains",
"x_content_type_options": "nosniff",
"x_frame_options": "SAMEORIGIN",
"x_xss_protection": "1; mode=block"
}
tasks:
- debug:
var: pool_details
- debug:
var: pool_details.json.items[0].address
The full error, with verbosity enabled is:
pool_details.json.items[0].address: 'VARIABLE IS NOT DEFINED!: builtin_function_or_method object has no element 0'
My second debug to grab the address isn't working ("not defined"), & I can't figure out why. If I take the same json, & pipe it to jq, it works just fine:
pbpaste | jq '.json.items[0].address'
"10.200.136.22"
items is a Python dictionary method.
To return the value to the key "items":
- debug:
var: pool_details.json['items'][0].address
If there are more items in the list you might want to use json_query, e.g.
- set_fact:
list_address: "{{ pool_details.json['items']|
json_query('[].address') }}"
gives
list_address:
- 10.200.136.22
If you don't want to, or can't, install JmesPath, the next option would be map attribute, e.g. the code below gives the same result
- set_fact:
list_address: "{{ pool_details.json['items']|
map(attribute='address')|
list }}"
I have a problem with a role to create user in jenkins with credentials plugin using private key in body.
I can get the crumb however when I make the request to create the credential I get the invalid crumb error.
My role:
- name: Get Jenkins Crumb
uri:
url: "{{ master_url }}/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,\":\",//crumb)"
user: '{{ master_username }}'
password: "my_token"
method: GET
return_content: yes
force_basic_auth: yes
body_format: form-urlencoded
register: crumb
- debug:
msg: "{{ crumb.content.split(':')[1]}}"
- name: Add credential to Node
uri:
url: "{{ master_url }}/credentials/store/system/domain/_/createCredentials"
user: "{{ master_username }}"
password: "my_token"
method: POST
status_code: 302
body_format: form-urlencoded
headers:
Jenkins-Crumb: "{{ crumb.content.split(':')[1]}}"
Cookie: "{{ crumb.set_cookie }}"
body: |
json={
"": "0",
"credentials": {
"scope": "GLOBAL",
"id": "jenkins_linux_slave1_auth",
"username": "jenkins",
"password": "jenkins",
"privateKeySource": {
"stapler-class": "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource",
"privateKey": "{{ private_key_file['content'] | b64decode | to_json}}"
},
"description": "Jenkins Linux Slave1 Authentication",
"stapler-class": "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey"
}
}
Out \ Error:
TASK [jenkins : Get Jenkins Crumb] *********************************************
ok: [protheus]
TASK [jenkins : debug] *********************************************************
ok: [protheus] => {
"msg": "f87df3f83a7ecfb5f5b2e7af0c0beb218dd239ec460fba832ff36f0a90e42287"
}
TASK [jenkins : Add credential to Node] ****************************************
fatal: [protheus]: FAILED! => {"cache_control": "must-revalidate,no-cache,no-store", "changed": false, "connection": "close", "content": "<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/>\n<title>Error 403 No valid crumb was included in the request</title>\n</head>\n<body><h2>HTTP ERROR 403 No valid crumb was included in the request</h2>\n<table>\n<tr><th>URI:</th><td>/credentials/store/system/domain/_/createCredentials</td></tr>\n<tr><th>STATUS:</th><td>403</td></tr>\n<tr><th>MESSAGE:</th><td>No valid crumb was included in the request</td></tr>\n<tr><th>SERVLET:</th><td>Stapler</td></tr>\n</table>\n<hr>Powered by Jetty:// 9.4.27.v20200227<hr/>\n\n</body>\n</html>\n", "content_length": "593", "content_type": "text/html;charset=iso-8859-1", "elapsed": 0, "msg": "Status code was 403 and not [302]: HTTP Error 403: Forbidden", "redirected": false, "server": "Jetty(9.4.27.v20200227)", "status": 403, "url": "http://192.168.0.120:9080/credentials/store/system/domain/_/createCredentials", "x_content_type_options": "nosniff"}
PLAY RECAP *********************************************************************
protheus : ok=9 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Ansible failed to complete successfully. Any error output should be
visible above. Please fix these errors and try again.
Does anyone know what's wrong?
Thks!
Since you're not calling the API, you can remove the Jenkins-Crumb header and use the username and the username Token.
Also add the option force_basic_auth: true
So your task will be like this:
- name: Add credential to Node
uri:
url: "{{ master_url }}/credentials/store/system/domain/_/createCredentials"
user: "{{ master_username }}"
password: "my_token"
method: POST
status_code: 302
body_format: form-urlencoded
force_basic_auth: true
body: |
json={
"": "0",
"credentials": {
"scope": "GLOBAL",
"id": "jenkins_linux_slave1_auth",
"username": "jenkins",
"password": "jenkins",
"privateKeySource": {
"stapler-class": "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource",
"privateKey": "{{ private_key_file['content'] | b64decode | to_json}}"
},
"description": "Jenkins Linux Slave1 Authentication",
"stapler-class": "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey"
}
}
I'm looking on the web but I'm unable to find a solution, the scenario in quite complex.
I have one json array with values like these:
[
{
"IP1": "1.2.3.4"
"IP1_VLAN": "900 - CLOUD-DEV"
"IP1_Role": "Management"
"IP2": "4.5.6.7"
"IP2_VLAN": "901 - CLOUD-DEV"
"IP2_Role": "Production"
"IP2": "8.9.10.11"
"IP2_VLAN": "902 - CLOUD-DEV"
"IP2_Role": "Backup"
}
]
My goal is: "Select the IP with the role of Management and tell me the address", it should select IP*_Role == Management and tell me in this case 1.2.3.4
Do you have any idea how to deal with this?
Following my comment, my 2 cent answer: searching through your actual data structure is going to be hard and ugly. If you have the option to change it a bit, things can get much easier.
Here is an example using a list declared in yaml in the following demo playbook. Data is extracted using json_query with the relevent jmespath expression (i.e. the query variable in the debug task)
---
- hosts: localhost
gather_facts: False
vars:
my_ips:
- ip: "1.2.3.4"
vlan: "900 - CLOUD-DEV"
role: "Management"
- ip: "4.5.6.7"
vlan: "901 - CLOUD-DEV"
role: "Production"
- ip: "8.9.10.11"
vlan: "902 - CLOUD-DEV"
role: "Backup"
tasks:
- name: Get ips per role
vars:
query: >-
[?role=='{{ item }}']
debug:
msg: "List of ips for role {{ item }}: {{ my_ips | json_query(query) }}"
loop:
- Management
- Production
- Backup
Which gives
PLAY [localhost] ********************************************************************************************************************************************************************************************************************************************************
TASK [Get ips per role] *************************************************************************************************************************************************************************************************************************************************
Wednesday 23 October 2019 16:14:23 +0200 (0:00:00.043) 0:00:00.043 *****
ok: [localhost] => (item=Management) => {
"msg": "List of ips for role Management: [{'ip': '1.2.3.4', 'vlan': '900 - CLOUD-DEV', 'role': 'Management'}]"
}
ok: [localhost] => (item=Production) => {
"msg": "List of ips for role Production: [{'ip': '4.5.6.7', 'vlan': '901 - CLOUD-DEV', 'role': 'Production'}]"
}
ok: [localhost] => (item=Backup) => {
"msg": "List of ips for role Backup: [{'ip': '8.9.10.11', 'vlan': '902 - CLOUD-DEV', 'role': 'Backup'}]"
}
PLAY RECAP **************************************************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The equivalent variable declaration in plain json would be:
[
{
"ip": "1.2.3.4",
"vlan": "900 - CLOUD-DEV",
"role": "Management"
},
{
"ip": "4.5.6.7",
"vlan": "901 - CLOUD-DEV",
"role": "Production"
},
{
"ip": "8.9.10.11",
"vlan": "902 - CLOUD-DEV",
"role": "Backup"
}
]
If you have to load this from an external string (e.g. loading a file...), you can use the from_json filter in your playbook