Hi everyone!
I’m facing an issue that I’m unable to solve.
I’ve a AWX installation, and I’m trying to populate an inventory with a script. It’s just an api call to Linode to list my instances, and then I present the output like ansible inventory wants it (or at least, what I think it wants).
In my local pc, if I run
ansible-inventory --inventory ./linode_inventory.py --list
I can see my list no problem. The output looks like this:
{
"_meta": {
"hostvars": {
"instance.example1": {
"id": 1111111,
"private_ip": null,
"public_ip": "11.11.11.11",
"region": "us-east",
"status": "running",
"tags": [
]
},
"instance.example2": {
"id": 2222222,
"private_ip": null,
"public_ip": "22.22.22.22",
"region": "us-east",
"status": "running",
"tags": [
]
}
}
},
"all": {
"children": [
"ungrouped"
]
},
"ungrouped": {
"hosts": [
"instance.example1",
"instance.example2"
]
}
}
So, what I did, is adding a Project on AWX with this github repo, created an inventory, and as “source inventory” I selected “source from project”. When I try to sync the inventory though, I get:
ansible-inventory [core 2.15.10]
config file = /runner/project/ansible.cfg
configured module search path = ['/runner/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.9/site-packages/ansible
ansible collection location = /runner/requirements_collections:/runner/.ansible/collections:/usr/share/ansible/collections:/usr/share/automation-controller/collections
executable location = /usr/local/bin/ansible-inventory
python version = 3.9.19 (main, Aug 23 2024, 00:00:00) [GCC 11.5.0 20240719 (Red Hat 11.5.0-2)] (/usr/bin/python3)
jinja version = 3.1.4
libyaml = True
Using /runner/project/ansible.cfg as config file
setting up inventory plugins
Loading collection ansible.builtin from
host_list declined parsing /runner/project/inventory/linode_inventory.py as it did not pass its verify_file() method
auto declined parsing /runner/project/inventory/linode_inventory.py as it did not pass its verify_file() method
yaml declined parsing /runner/project/inventory/linode_inventory.py as it did not pass its verify_file() method
toml declined parsing /runner/project/inventory/linode_inventory.py as it did not pass its verify_file() method
[WARNING]: * Failed to parse /runner/project/inventory/linode_inventory.py
with script plugin: failed to parse executable inventory script results from
/runner/project/inventory/linode_inventory.py: Expecting value: line 1 column 1
(char 0). Expecting value: line 1 column 1 (char 0)
File "/usr/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 "/usr/local/lib/python3.9/site-packages/ansible/plugins/inventory/script.py", line 150, in parse
raise AnsibleParserError(to_native(e))
[WARNING]: * Failed to parse /runner/project/inventory/linode_inventory.py
with ini plugin: /runner/project/inventory/linode_inventory.py:2: Expected
key=value host variable assignment, got: requests
File "/usr/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 "/usr/local/lib/python3.9/site-packages/ansible/plugins/inventory/ini.py", line 137, in parse
raise AnsibleParserError(e)
[WARNING]: Unable to parse /runner/project/inventory/linode_inventory.py as an
inventory source
ERROR! No inventory was parsed, please check your configuration and options.
I get a similar error on my local pc if I try to run ansible-inventory against a yml inventory with this content:
plugin: ansible.builtin.script
script: ../inventory/linode_inventory.py
This is the python script. the value for the linode token it’s provided via awx credential attached to the inventory source:
#!/usr/bin/env python
import requests
import json
import os
# Set Linode API token (from environment variables for security)
LINODE_API_TOKEN = os.getenv('a_variable_coming_from_my_awx_credentials')
# Linode API base URL
API_URL = "https://api.linode.com/v4/linode/instances"
# Headers for authentication
headers = {
"Authorization": f"Bearer {LINODE_API_TOKEN}",
"Content-Type": "application/json"
}
def get_linode_instances():
try:
response = requests.get(API_URL, headers=headers)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"Error fetching Linode instances: {e}")
return {}
def parse_linode_instances(data):
hosts = []
linode_instances = data.get('data', [])
for linode in linode_instances:
host = {
'name': linode['label'], # Instance name
'public_ip': linode['ipv4'][0], # Public IP address
'private_ip': linode['ipv4'][1] if len(linode['ipv4']) > 1 else None,
'region': linode['region'],
'tags': linode['tags'],
'status': linode['status'],
'id': linode['id']
}
hosts.append(host)
return hosts
def generate_inventory():
linode_data = get_linode_instances()
if not linode_data:
print("No data received from Linode API")
return # Exit if no data is received
instances = parse_linode_instances(linode_data)
if not instances:
print("No instances found.")
# Prepare the inventory structure
inventory = {
'_meta': {
'hostvars': {host['name']: {'public_ip': host['public_ip'], 'private_ip': host['private_ip'], 'region': host['region'], 'tags': host['tags'], 'status': host['status'], 'id': host['id']} for host in instances}
},
'all': {
'hosts': [host['name'] for host in instances],
'children': {}
}
}
# Print the inventory for debugging
print(json.dumps(inventory, indent=2))
if __name__ == '__main__':
generate_inventory()
Any idea why?
Tks!
Cheers
Andrea