Dynamic Inventory script output vs JSON file - ansible

I'm writing a dynamic inventory script which queries Docker containers. It outputs JSON, which I can save to a file and use, but I get parse errors from Ansible when I try to use the script directly.
[root#297b1ca0cfa4 /]# docker-dynamic-inventory > inv.json
[root#297b1ca0cfa4 /]# cat inv.json
{"all": {"hosts": {"inv_clyde_1": null, "inv_blinky_1": null, "inv_inky_1": null, "inv_pinky_1": null, "admiring_chandrasekhar": null}, "_meta": {"hostvars": {}}, "vars": {"ansible_connection": "docker"}}}
[root#297b1ca0cfa4 /]# ansible all -i inv.json -m ping
inv_clyde_1 | FAILED! => {
"failed": true,
"msg": "docker command not found in PATH"
}
Note that I don't care if the ping fails, getting that far means that my inventory works. Ansible is successfully interpreting the JSON and the inventory it represents. Now compare this with using the script directly:
[root#297b1ca0cfa4 /]# ansible all -i /usr/bin/docker-dynamic-inventory -m ping
[WARNING]: * Failed to parse /usr/bin/docker-dynamic-inventory with script plugin:
You defined a group 'all' with bad data for the host list:
{u'hosts': {u'inv_clyde_1': None, u'inv_inky_1': None,
u'admiring_chandrasekhar': None, u'inv_pinky_1': None, u'inv_blinky_1': None},
u'_meta': {u'hostvars': {}}, u'vars': {u'ansible_connection': u'docker'}}
Ansible's docs on Inventory show it using a dictionary and null values to represent hosts, which is why I do that here.
Apart from the fact that Ansible prints the dict it read in from JSON, I'm not seeing what's different/wrong here. Why does the stored JSON output work where the script won't?

So it turns out all is a special group, but only when interpreted with the script parser. In a static inventory, all can be a dictionary of keys with null values, but when coming from a script, the host value for all must be a list of strings.
{"all":
{"hosts": ["admiring_chandrasekhar", "inv_inky_1", "inv_pinky_1",
"inv_blinky_1", "inv_clyde_1"],
"_meta": {"hostvars": {}},
"vars": {"ansible_connection": "docker"}}}

Related

how to execute multiline command in ansible 2.9.10 in fedora 32

I want to execute a command using ansible 2.9.10 in remote machine, first I tried like this:
ansible kubernetes-root -m command -a "cat > /etc/docker/daemon.json <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"registry-mirrors":[
"https://kfwkfulq.mirror.aliyuncs.com",
"https://2lqq34jg.mirror.aliyuncs.com",
"https://pee6w651.mirror.aliyuncs.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com"
]
}"
obviously it is not working.so I read this guide and tried like this:
- hosts: kubernetes-root
remote_user: root
tasks:
- name: add docker config
shell: >
cat > /etc/docker/daemon.json <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"registry-mirrors":[
"https://kfwkfulq.mirror.aliyuncs.com",
"https://2lqq34jg.mirror.aliyuncs.com",
"https://pee6w651.mirror.aliyuncs.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com"
]
}
and execute it like this:
[dolphin#MiWiFi-R4CM-srv playboook]$ ansible-playbook add-docker-config.yaml
[WARNING]: Invalid characters were found in group names but not replaced, use
-vvvv to see details
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: Expecting value: line 1 column 1 (char 0)
Syntax Error while loading YAML.
could not find expected ':'
The error appears to be in '/home/dolphin/source-share/source/dolphin/dolphin-scripts/ansible/playboook/add-docker-config.yaml': line 7, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
cat > /etc/docker/daemon.json <<EOF
{
^ here
is there anyway to achive this?how to fix it?
your playbook should work fine, you just have to add some indentation after the shell clause line, and change the > to |:
here is the updated PB:
---
- name: play name
hosts: dell420
gather_facts: false
vars:
tasks:
- name: run shell task
shell: |
cat > /tmp/temp.file << EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"registry-mirrors":[
"https://kfwkfulq.mirror.aliyuncs.com",
"https://2lqq34jg.mirror.aliyuncs.com",
"https://pee6w651.mirror.aliyuncs.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com"
]
}
EOF
Not sure what is wrong with the ad-hoc command, i tried a few things but didnt manage to make it work.
hope these help
EDIT:
as pointed out by Zeitounator, the ad-hoc command will work if you use shell module instead of command. example:
ansible -i hosts dell420 -m shell -a 'cat > /tmp/temp.file <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"registry-mirrors":[
"https://kfwkfulq.mirror.aliyuncs.com",
"https://2lqq34jg.mirror.aliyuncs.com",
"https://pee6w651.mirror.aliyuncs.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com"
]
}
EOF
'

How to pass environment variable to sh script?

I want to create an environment variable in Jenkinsfile which will consist of current workspace (using envirnoment variable WORKSPACE). Then I want to pass this new variable to sh script in next stage.
I've tried declaring variable in the following way:
environment {
artefact_path = "${env.WORKSPACE}/temp/unzipped/${artefact_name}/dev"
}
But after passing it to sh script:
sh "ansible-playbook -i inventory playbook.yml -e \"artefact_path=${env.artefact_path}\""
I get the following output:
+ ansible-playbook -i inventory playbook.yml -e 'artefact_path=C:\nowy_dir\workspace\something/temp/unzipped/something/dev'
PLAY [play] **************************************************************
TASK [task] *********************************************************
fatal: [host]: FAILED! => {"changed": false, "dest": "D:/inetpub/something", "msg": "Get-AnsibleParam: Parameter 'src' has an invalid path 'C:\nowy_dir\\workspace\\something/temp/unzipped/something/dev' specified.", "src": "C:\nowy_dir\\workspace\\something/temp/unzipped/something/dev"}
to retry, use: --limit #/etc/ansible/ansible-scripts/playbook.retry
As you can see the variable is passed with two \ instead of one. How can I prevent this from happening?
EDIT 1
I decided to change the way in which I declare the variable to:
def get_deploy_path() {
def site = get_site()
def wspace = "${env.WORKSPACE}"
def deploy_path = wspace.toString() + "\\temp\\snapshot\\" + site + "\\"
return deploy_path
}
environment {
deploy_path = get_deploy_path()
}
Now I have a problem with workspace. I'm operating on two agents (first is Windows and the second is Linux based). I need the path to be the same both in stages on Windows and on Linux. Any idea how I can make it happen?

How do I pass a dictionary to an ansible ad-hoc command?

If I have an ansible ad-hoc command that wants a dictionary or list valued argument, like the queries argument to postgresql_query, how do I invoke that in ansible ad-hoc commands?
Do I have to write a one-command playbook? I'm looking for a way to minimise the numbers of layers of confusing quoting (shell, yaml/json, etc) involved.
The ansible docs mention accepting structured forms for variables. So I tried the yaml and json syntax for the arguments:
ansible -m postgresql_query -sU postgres -a '{"queries":["SELECT 1", "SELECT 2"]}'
... but got ERROR! this task 'postgresql_query' has extra params, which is only allowed in the following modules: ....
the same is true if I #include a file with yaml or json contents like
cat > 'query.yml' <<'__END__'
queries:
- "SELECT 1"
- "SELECT 2"
__END__
ansible -m postgresql_query -sU postgres -a #queries.yml
You can define a dictionary in a JSON variable to pass it as parameter next:
ansible -m module_name -e '{"dict": {"key": "value"}}' -a "param={{ dict }}"
(parameters positions are arbitrary)
I have most of a solution - a way to express something like a shell script or query payload without extra quoting. But it's ugly:
ansible hostname -m postgresql_query -sU postgres -a 'query="{{query}}"' -e #/dev/stdin <<'__END__'
query: |
SELECT 'no special quotes needed' AS "multiline
identifier works fine" FROM
generate_series(1,2)
__END__
Not only is that shamefully awful, but it doesn't seem to work for lists (arrays):
ansible hostname -m postgresql_query -sU postgres -vvv -a 'query="{{query}}"' -e #/dev/stdin <<'__END__'
queries:
- |
SELECT 1
- |
SELECT 2
__END__
fails with
hostname | FAILED! => {
"changed": false,
"err": "syntax error at or near \"<\"\nLINE 1: <bound method Templar._query_lookup of <ansible.template.Tem...\n ^\n",
"invocation": {
"module_args": {
"autocommit": false,
"conninfo": "",
"queries": null,
"query": "<bound method Templar._query_lookup of <ansible.template.Templar object at 0x7f72531c61d0>>"
}
},
"msg": "Database query failed"
}
so it looks like some kind of lazy evaluation is breaking things.

Ansible pass multiple lines in input variable file

I am running ansible playbook via taking variables from a file outside
ansible-playbook -v /path/export.yml --extra-vars '#input.json'
Now the file has only one line like below
{ out_file: exp_app_12.xml, control_file: export_control.xml}
Now I want to push multiple lines in the input.json file like below
{ out_file: exp_app_12.xml, control_file: export_control1.xml}
{ out_file: exp_app_13.xml, control_file: export_control2.xml}
{ out_file: exp_app_14.xml, control_file: export_control3.xml}
But it's not working , how to achieve this ?
You should pass the JSON file in proper format like this;
ansible-playbook arcade.yml --extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}'
I think your JSON file is not in correct format, it must be like this;
[
{"out_file": "exp_app_12.xml","control_file": "export_control1.xml"},
{"out_file": "exp_app_12.xml","control_file": "export_control1.xml"},
{"out_file": "exp_app_12.xml","control_file": "export_control1.xml"}
]
Furthermore see the ansible docs here for more understanding.

How to pass multiple executable in ansible within a shell module?

I am trying to pass a prompt y in ansible when it executes command.
When i do manually on server it asks for a prompt.
The issue is for command to run i need to pass the executable /bin/bash
command: source /etc/profile.d/tableau_server.sh && tsm pending-changes apply for expect command to run i need to pass /usr/bin/expect .
My question, how can i pass 2 executable in ansible such that for command it uses /bin/bash and for expect prompt it should use /usr/bin/expect and the error is because i am using source, what is an alternative i can use?
Update: I dont know why but i am not able to pass --ignore-prompt , It gives an error
ubuntu#ip-xx-xxx-xx-xx:~$ tsm pending-changes apply --ignore-prompt
Unrecognized option: --ignore-prompt
Please help me with a solution!
ubuntu#ip-xx-xxx-xx-xx:~$ tsm pending-changes apply
This operation will perform a server restart. Are you sure you wish to continue?
(y/n):
My ansible script:
shell: |
source /etc/profile.d/tableau_server.sh && tsm pending-changes apply
expect "This operation will perform a server restart. Are you sure you wish to continue?\n(y/n):"
send "y\n"
exit 0
args:
executable: /usr/bin/expect
args:
executable: /bin/bash/expect
when: inventory_hostname == "xx.xxx.xx.xx"
ERROR:
changed: [xx.xxx.xxx.xx] => {
"changed": true,
"cmd": "source /etc/profile.d/tableau_server.sh && tsm pending-changes apply\n expect \"This operation will perform a server restart. Are you sure you wish to continue?\\n(y/n):\"\n send \"y\\n\"\n exit 0",
"delta": "0:00:00.034824",
"end": "2018-08-20 17:29:41.457700",
"invocation": {
"module_args": {
"_raw_params": "source /etc/profile.d/tableau_server.sh && tsm pending-changes apply\n expect \"This operation will perform a server restart. Are you sure you wish to continue?\\n(y/n):\"\n send \"y\\n\"\n exit 0",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": "/usr/bin/expect",
"removes": null,
"stdin": null,
"warn": true
}
},
"rc": 0,
"start": "2018-08-20 17:29:41.422876",
"stderr": "wrong # args: should be \"source ?-encoding name? fileName\"\n while executing\n\"source /etc/profile.d/tableau_server.sh && tsm pending-changes apply\"",
"stderr_lines": [
"wrong # args: should be \"source ?-encoding name? fileName\"",
" while executing",
"\"source /etc/profile.d/tableau_server.sh && tsm pending-changes apply\""
],
"stdout": "",
"stdout_lines": []
I would say you are doing far too much with bash commands and '&&' inside command, none of this feels idempotent.
Can I recommend going back to the drawing board with this. I would recommend creating the command using the 'creates' parameter so it can tell if it needs to run.
https://docs.ansible.com/ansible/2.6/modules/command_module.html
Or alternatively check before hand which will then see if the command needs running using register.
In this instance of your issue with the:
tsm pending-changes apply
should support as per https://onlinehelp.tableau.com/current/server-linux/en-us/cli_pending-changes.htm
tsm pending-changes apply --ignore-prompt
which will then not prompt for a yes and will not need the expect module.
I solved my issue by passing an -r option.
- name: Initialize and Start Tableau Server
shell: source /etc/profile.d/tableau_server.sh && tsm pending-changes apply -r -u ubuntu -p '{{ tableau_server_admin_password }}'
args:
executable: /bin/bash
when: inventory_hostname == "xx.xxx.xx.xx"

Resources