I am having issues with Ansible not executing a PHP dynamic inventory file that I have written. To troubleshoot the issue I have created a test.json
inventory file to verify my syntax is correct.
cat test.json
{
"aruba_cx": {
"children": {
"hardware_6000": {
"hosts": {
"sw-01.domain.uk": null
}
},
"hardware_6200": {
"hosts": {
"sw-02.net.domain.uk": null
}
}
}
},
"aruba_aos": {
"children": {
"hardware_2930": {
"hosts": {
"sw-03.domain.uk": null
}
}
}
}
}
This is accepted by Ansible.
ansible-inventory -i 'test.json' --list
{
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"ungrouped",
"aruba_cx",
"aruba_aos"
]
},
"aruba_aos": {
"children": [
"hardware_2930"
]
},
"aruba_cx": {
"children": [
"hardware_6000",
"hardware_6200"
]
},
"hardware_2930": {
"hosts": [
"sw-03.domain.uk"
]
},
"hardware_6000": {
"hosts": [
"sw-01.domain.uk"
]
},
"hardware_6200": {
"hosts": [
"sw-02.domain.uk"
]
}
}
However, when I was the same JSON via a PHP executable script Ansible doesn't seem to execute the file before parsing it.
cat test.php
#!/usr/bin/env php
<?php
// Require iMC Device List
echo require_once "test.json";
ansible-inventory -i './test.php' --list -vvvv
ansible-inventory [core 2.15.12]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/william/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/william/.local/lib/python3.9/site-packages/ansible
ansible collection location = /home/william/.ansible/collections:/usr/share/ansible/collections
executable location = /home/william/.local/bin/ansible-inventory
python version = 3.9.2 (default, Feb 28 2021, 17:03:44) [GCC 10.2.1 20210110] (/usr/bin/python3)
jinja version = 3.1.4
libyaml = True
Using /etc/ansible/ansible.cfg as config file
setting up inventory plugins
Loading collection ansible.builtin from
[WARNING]: * Failed to parse /home/william/ansible-networks/t.php with script plugin: failed to parse executable inventory script results from /home/william/ansible-networks/t.php: Extra data: line 25 column 2 (char
449). Extra data: line 25 column 2 (char 449)
File "/home/william/.local/lib/python3.9/site-packages/ansible/inventory/manager.py", line 293, in parse_source
plugin.parse(self._inventory, self._loader, source, cache=cache)
File "/home/william/.local/lib/python3.9/site-packages/ansible/plugins/inventory/script.py", line 150, in parse
raise AnsibleParserError(to_native(e))
[WARNING]: Unable to parse /home/william/ansible-networks/t.php as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
{
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"ungrouped"
]
}
}
Ansible version: 2.15.12
PHP version: 7.4.33
The shebang on this system is #!/usr/bin/env php
and has been tested using ./test.php
which executes successfully.
The following permissions are set -rwxrwx--- 1 william it-dept 82 Aug 13 12:03 test.php
I have tested adding the enable_plugins = script
into my ansible.cfg
cat /etc/ansible/ansible.cfg
[defaults]
forks=50
deprecation_warnings=False
transport = ssh
host_key_checking = False
pipelining = True
[ssh_connection]
ssh_args = -F /etc/ssh_config -o ControlMaster=auto -o ControlPersist=60s
[inventory]
#enable_plugins = script
I'm running out of ideas, any pointers on this one would be smashing.
Ansible has absolutely no troubles to execute your script and get its output. The issue pops up when the inventory script plugin tries to parse the result. The next errors are simple try with other plugins which don't work either.
You actually have two problems:
echo
in you php file to write out the result of a function/expression. The result of require_once "test.json"
is 1
. So what you actually get is the content of your json file (as gotten by the require_once
instruction) followed by 1
at the end. That extra character confuses the script plugin parser.Fixing the first problem is trivial. Just change your php file to:
#!/usr/bin/env php
<?php
// Require iMC Device List
require_once "test.json";
To fix the second, here is a rearranged output as expected by the script plugin as explained in the developper's guide. Note the presence of a _meta
section with an empty hostvars
entry to make sure ansible only calls your current script with the --list
parameter (which will work by default as is) and never with the --host <hostname>
parameter that would need to be implemented in such case.
{
"aruba_cx": {
"children": ["hardware_6000", "hardware_6200"]
},
"hardware_6000": {
"hosts": ["sw-01.domain.uk"]
},
"hardware_6200": {
"hosts": ["sw-02.net.domain.uk"]
},
"aruba_aos": {
"children": ["hardware_2930"]
},
"hardware_2930": {
"hosts": ["sw-03.domain.uk"]
},
"_meta": {
"hostvars": {}
}
}
After those fix, it works as expected:
$ ansible-inventory -i test.php --list
{
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"ungrouped",
"aruba_cx",
"aruba_aos"
]
},
"aruba_aos": {
"children": [
"hardware_2930"
]
},
"aruba_cx": {
"children": [
"hardware_6000",
"hardware_6200"
]
},
"hardware_2930": {
"hosts": [
"sw-03.domain.uk"
]
},
"hardware_6000": {
"hosts": [
"sw-01.domain.uk"
]
},
"hardware_6200": {
"hosts": [
"sw-02.net.domain.uk"
]
}
}