How to get a substring of a path name with Ansible? - ansible

I want to install tomcat via ansible on a windows server, I check if there is alreay a java installation and get the path to java.exe. Which is:
"path": "D:\\Products\\abcd\\java\\jdk1.8.0_51\\bin\\java.exe"
I need to set JAVA_HOME in a config var for tomcat, how can I get the needed part -> D:\\Products\\abcd\\java\\jdk1.8.0_51 from the path? I can not get JAVA_HOME from the remote server since there are various installations.
TASK [tomcat : debug]
ok: [v-sax-769-e-a.develop.ebiz.grp] => {
"file_info_java.files[0]": {
"attributes": "Archive",
"checksum": "8c2c2f3d687fe99d9a724514f09f53bcb989a1e0",
"creationtime": 1511365795.4331064,
"extension": ".exe",
"filename": "java.exe",
"isarchive": true,
"isdir": false,
"ishidden": false,
"islnk": false,
"isreadonly": false,
"isshared": false,
"lastaccesstime": 1511365795.4331064,
"lastwritetime": 1438168336,
"owner": "BUILTIN\\Administrators",
"path": "D:\\Products\\CPeRef\\java\\jdk1.8.0_51\\bin\\java.exe",
"size": 206400
}
}

To extract a path given in Windows format, use win_dirname filter:
vars:
path: "D:\\Products\\abcd\\java\\jdk1.8.0_51\\bin\\java.exe"
tasks:
- debug:
msg: "{{ path | win_dirname }}"
Result:
ok: [localhost] => {
"msg": "D:\\Products\\abcd\\java\\jdk1.8.0_51\\bin"
}
But, as you seemingly want to get rid of bin-part, you need to decide on a method. Either way, you can do it with a regex filter:
remove the last element from the path:
{{ path | win_dirname | regex_replace('\\\\(?:(?!\\\\).)*$', '') }}
remove the last \\bin from the path:
{{ path | win_dirname | regex_replace('\\\\bin$', '') }}
remove the fixed part without using win_dirname at all:
{{ path | regex_replace('\\\\bin\\\\java.exe$', '') }}

Related

Ansible, how to query deeply nested json keys

I'm using Ansible to make an API call that returns a huge data set, and I need to be able to get a nested value to print to the screen. I tried using json_query but not sure what I'm doing wrong.
My task:
- name: Get certificate by CN name.
uri:
method: GET
url: "https://mayapi/api/1/certificates?filter=cn;{{ inventory_hostname }}"
headers:
Authorization: Bearer {{ login.json.token }}
Content-Type: application/json
validate_certs: no
register: certs
- name: Print certs for application
debug:
msg: "{{ certs.json | json_query(items) }}"
This is a small snippet of the output. I want to be able to print ID, and email.
{
"msg": {
"changed": false,
"connection": "close",
"content_length": "65833",
"content_type": "application/json",
"cookies": {},
"cookies_string": "",
"date": "Mon, 10 May 2021 21:33:29 GMT",
"elapsed": 0,
"failed": false,
"json": {
"items": [
{
"active": true,
"application": [
{
"director": {
"active": true,
"email": "user#domain.com",
"fullname": "John Doe",
"id": 1611,
"manager": "John Doe",
"managerEmail": "johndoe#email.com",
"username": "jdoe"
},
...
...
...
}
I get the following error indicating "certs.items" doesn't exist:
FAILED! => {"msg": "Error in jmespath.search in json_query filter plugin:\n'items' is undefined"}
I was expecting all of the items to get printed to the screen and then if I wanted something below items I would do items.active, items.application, etc... But this is not correct since I keep erroring out.
I also tried looping through cert.json and cert.json.items:
- name: Print certs for application
debug:
msg: "{{ item.application.name }}"
loop: "{{ certs.json}}"
But get this error message:
{"msg": "Invalid data passed to 'loop', it requires a list, got this instead: {u'items': [{u'status': u'Active-Pending Install'...shows all the data of the nested json
Then I tried this:
- name: Print certs for application
debug:
msg: "{{ item.application.name }}"
loop: "{{ certs.json.items}}"
But got this error message:
{"msg": "Invalid data passed to 'loop', it requires a list, got this instead: <built-in method items of dict object at 0x7f0c9ec43050>. Hint: If you passed a list/dict of just one element, try adding wantlist=True to your lookup invocation or use q/query instead of lookup."}
Made some progress with this:
- name: Print certs for application
debug:
msg: "KEY:::: {{ item.key }}, VALUE:::: {{ item.value.0 }}"
loop: "{{ lookup('dict', certs.json) }}"
when: "'items' in item.key"
ignore_errors: yes
But this only prints items in index 0 of the list:
"msg": "KEY:::: items, VALUE:::: {u'status': u'Active-Pending Install', u'serialHex': u'1111', u'validityStart': u'2021-05-10T21:01:36+00:00', u'cn': u'node2.test.corp.net', u'validityEnd': u'2023-05-10T21:11:36+00:00', u'application': [{u'uuid': u'2222', u'name': u'abc'}], u'certType': u'CertType.INTERNAL', u'id': 2582, u'issuer': u'server1'}"
I'm trying to print the 'cn', 'id', and 'serialHex' values from each list element for the key 'items'.
This is the data set that I'm trying to query with Ansible:
{
"total": 2,
"items": [
{
"application": [
{
"uuid": "111",
"name": "CDE"
}
],
"validityEnd": "2023-05-10T21:11:36+00:00",
"certType": "CertType.INTERNAL",
"issuer": "server1",
"id": 2582,
"validityStart": "2021-05-10T21:01:36+00:00",
"status": "Active-Pending Install",
"serialHex": "aaa",
"cn": "node2.corp.net"
},
{
"application": [
{
"uuid": "222",
"name": "CDE"
}
],
"validityEnd": "2023-05-10T21:05:26+00:00",
"certType": "CertType.INTERNAL",
"issuer": "server1",
"id": 2581,
"validityStart": "2021-05-10T20:55:26+00:00",
"status": "Active-Pending Install",
"serialHex": "bbbb",
"cn": "node1.corp.net"
}
]
}
You are regrettably stepping on a quirk of "objects in ansible are python dicts" in that .items() and .keys() and quite a few other attributes-which-are-methods cannot be referenced using the . notation since jinja2 believes you intend to call that method. Rather, one must use the __getitem__ syntax of ["items"] in order to make it abundantly clear that you mean the dict key, and not the method of the same name
tasks:
- name: use json_query as you were originally asking
debug:
msg: >-
{{ certs.json | json_query('items[*].{c: cn,i: id,s: serialHex}') }}
- name: or a jinja2 for loop as you separately attempted
debug:
msg: >-
[
{%- for i in certs.json["items"] -%}
{{ "" if loop.first else "," }}
{{ [i.cn, i.id, i.serialHex ]}}
{%- endfor -%}
]
produces the output from their respective steps:
TASK [debug] ******************************************************************************************************************************
ok: [localhost] => {
"msg": [
{
"c": "node2.corp.net",
"i": 2582,
"s": "aaa"
},
{
"c": "node1.corp.net",
"i": 2581,
"s": "bbbb"
}
]
}
TASK [debug] ******************************************************************************************************************************
ok: [localhost] => {
"msg": [
[
"node2.corp.net",
2582,
"aaa"
],
[
"node1.corp.net",
2581,
"bbbb"
]
]
}

ansible execute task when variable contain specific string

i have the following playbook which check all the roles in elasticsearch, and if the specific role doesn't exist, it creates it
- name: Get all security roles
uri:
url: 'http://192.168.2.14:9200/_security/role'
method: GET
url_username: elastic
url_password: strong
register: security_roles
- debug:
msg: {{ security_roles }}
- name: make cURL call if anthill_role exists
shell: curl -u elastic:strong 192.168.2.14:9200
when: '"sobaka" not in security_roles'
and let's see the output from playbook execution
ansible-playbook elasticsearch-3dc.yml -i hosts.yml
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
[WARNING]: Found variable using reserved name: remote_user
PLAY [Deploy & Configure Elasticsearch on 3DC] *************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************************************ok: [elasticsearch-db-02]
TASK [elasticsearch-3dc : Get all security roles] **********************************************************************************************************************************ok: [elasticsearch-db-02]
TASK [elasticsearch-3dc : debug] ***************************************************************************************************************************************************ok: [elasticsearch-db-02] => {
"msg": {
"changed": false,
"content_length": "8422",
"content_type": "application/json; charset=UTF-8",
"cookies": {},
"cookies_string": "",
"elapsed": 0,
"failed": false,
"json": {
"sobaka": {
"applications": [],
"cluster": [],
"indices": [
{
"allow_restricted_indices": false,
"names": [
"*"
],
"privileges": [
"create",
"index",
"read",
"read_cross_cluster",
"view_index_metadata",
"write",
"create_index"
]
}
],
"metadata": {},
"run_as": [],
"transient_metadata": {
"enabled": true
}
},
},
"msg": "OK (8422 bytes)",
"redirected": false,
"status": 200,
"url": "http://192.168.2.14:9200/_security/role"
}
}
TASK [elasticsearch-3dc : make cURL call if anthill_role exists] *******************************************************************************************************************[WARNING]: Consider using the get_url or uri module rather than running 'curl'. If you need to use command because get_url or uri is insufficient you can add 'warn: false' to
this command task or set 'command_warnings=False' in ansible.cfg to get rid of this message.
changed: [elasticsearch-db-02]
but as you can see from the output, the role sobaka is existed, why the task make cURL car if sobaka role exists is runned?
Can you try like when: '"sobaka" not in security_roles.json'. Seems like key sobaka is under value dict of key json
The return of you debug show a dict.
You have to check if that dict dont have the specific key
- name: make cURL call if anthill_role exists
shell: curl -u elastic:strong 192.168.2.14:9200
when: "'sobaka' not in {{ security_roles.json.keys()|list }}"

How to retrieve a value from stdout of ansible registered variable?

One of my ansible play is using a shell module to create a vault token.The command returns some value that I want to use in next play.
I registered the command output in vault_output parameter.Here I am getting stdout from that variable.
"vault_output.stdout": {
"auth": {
"accessor": "XXXXXXXXXXXXXXXXXXX",
"client_token": "abcdefghijkl",
"entity_id": "XXXXXXXXXXXXXXXXXXX",
"lease_duration": 600,
"metadata": {
"username": "vault"
},
"policies": [
"default",
],
"renewable": true,
"token_policies": [
"default",
]
},
"data": {},
"lease_duration": 0,
"lease_id": "",
"renewable": false,
"request_id": "3470a160-3ed5-ceaa-f57b-4f3d74f6a269",
"warnings": null,
"wrap_info": null
}
}
I am looking to get value of client_token which should be abcdefghijkl. Can anyone help me out to get that value which can be used in next play.
I have tried using vault_output.stdout[num], vault_output.stdout_lines, vault_output.stdout.auth , vault_output.stdout.['auth'] but no luck.
Expecting Result:
"client_token": "abcdefghijkl"
Finally found an answer to this.
- set_fact:
result: "{{ (vault_output.stdout | from_json).auth.client_token }}"
- debug:
var: result
result: 9fa7fdd6-c8da-ac8c-b5d8-df18b17eb3f0
The output from debug: var=vault_output.stdout is actually a bit misleading here. The variable does not contain a dict object Ansible can index. You will need to first parse it with the from_json filter:
- set_fact:
result: "{{ vault_output.stdout | from_json }}"
- debug:
var: result.auth.client_token

json_query for a value of json output in Ansible

The following outputs to the shell when I run my Ansible play:
TASK [java : debug] ************************************************************************************************************************************************************
ok: [10.3.16.114] => {
"msg": {
"changed": false,
"examined": 1,
"failed": false,
"files": [
{
"attributes": "Directory, NotContentIndexed",
"creationtime": 1551375173.18858,
"filename": "jre1.8.0_201",
"isarchive": false,
"isdir": true,
"ishidden": false,
"islnk": false,
"isreadonly": false,
"isshared": false,
"lastaccesstime": 1551375199.2058175,
"lastwritetime": 1551375199.2058175,
"owner": "NT AUTHORITY\\SYSTEM",
"path": "C:\\Program Files\\Java\\jre1.8.0_201",
"size": 205713652
}
],
"matched": 1
}
}
I need just the value of path so that I can use it as a varuable to set a dynamic java_home inside of windows.
I have been trying to use json_query inside of my yaml but I'm having knowledge limitations here.
- name: Obtain information about a folder
win_find:
paths: C:\Program Files\Java
recurse: no
file_type: directory
register: java_folder
- set_fact:
java_home_dir: "{{java_folder.file | json_query()}}"
It's a simple path. Addressing of the first element is needed, because "files" is a list.
- set_fact:
java_home_dir: "{{ java_folder.files[0].path }}"
If there is more files in the list json_query will be needed.

Ansible Dynamic Variables in Playbook

I'm provisioning a bunch of systems (VMs) on a physical host. I'm up to the step where the VM's are running. Now I need to ssh into the VM's via their DHCP addresses. I can pull the IP addresses from the server but I need a way to set these to host_vars. Here are my groups:
ok: [kvm01] => {
"msg": {
"all": [
"kvm01",
"dcos-master-1",
"dcos-agent-1",
"dcos-agent-2",
"dcos_bootstrap"
],
"dcos": [
"dcos-master-1",
"dcos-agent-1",
"dcos-agent-2",
"dcos_bootstrap"
],
"dcos-agents": [
"dcos-agent-1",
"dcos-agent-2"
],
"dcos-bootstraps": [
"dcos_bootstrap"
],
"dcos-masters": [
"dcos-master-1"
],
"kvm": [
"kvm01"
],
"ungrouped": []
}
}
Here's my command:
- name: Get the IP of the VM (DHCP)
command: "/getip.sh {{ item }}"
register: "result"
with_items: "{{ groups['dcos'] }}"
- name: List the output of the variables
debug: msg="{{item.stdout}}"
with_items: "{{result.results}}"
When I run the playbook I get results but they are the FULL JSON result of the command rather than the stdout. There's probably a way to pull out the stdout and assign it to a fact but it's a complex hash. Here's the last result:
TASK [vm_install : Get the IP of the VM (DHCP)] ***************************************************************************
changed: [kvm01] => (item=dcos-master-1)
changed: [kvm01] => (item=dcos-agent-1)
changed: [kvm01] => (item=dcos-agent-2)
changed: [kvm01] => (item=dcos_bootstrap)
TASK [vm_install : List the output of the variables] **********************************************************************
......
ok: [kvm01] => (item={'_ansible_parsed': True, 'stderr_lines': [u'] => {
"item": {
"changed": false,
"cmd": [
"/getip.sh",
"dcos_bootstrap"
],
"delta": "0:00:00.056193",
"end": "2017-09-18 15:45:45.409693",
"invocation": {
"module_args": {
"_raw_params": "/getip.sh dcos_bootstrap",
"_uses_shell": false,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"warn": true
}
},
"item": "dcos_bootstrap",
"rc": 0,
"start": "2017-09-18 15:45:45.353500",
"stderr": " ",
"stdout": "192.168.1.130",
"stdout_lines": [
"192.168.1.130"
]
},
"msg": "192.168.1.130"
}
How can I put the output of the command into an array so that I can use it later in my playbook?
So, like I said in my comment, you've already managed to extract the information you want into an array. You can iterate over those items using with_items as in the follow task that will create an ip_address for each host:
- set_fact:
ip_address: "{{ item.stdout }}"
with_items: "{{ results.results }}"
delegate_to: "{{ item.item }}"
delegate_facts: true
Or you can create a single array containing all of the addresses using Jinja filters:
- set_fact:
all_ip_addresses: "{{ results.results|map(attribute='stdout')|list }}"
Or you could create a dictionary with the same information:
- set_fact:
all_ip_addresses: >
{{ all_ip_addresses
|default({})
|combine({ item.item: item.stdout })}}
with_items: "{{ results.results }}"

Resources