如何让Python编写的Ansible动态清单脚本被Ansible识别?
问题解决:Ansible动态清单脚本解析失败(unhashable type: 'dict')
问题背景
编写了用于获取AWS实例公网DNS的Python动态清单脚本,单独运行能输出合法JSON,但执行ansible all -i get_dns.py -m ping时出现解析失败警告,核心错误为unhashable type: 'dict',无法识别清单源。
原脚本代码
#!/usr/bin/python3 import subprocess import json def run_terraform(): result = subprocess.run(["terraform", "output", "-json"], capture_output=True, text=True, cwd="../terraform") result = json.loads(result.stdout) return result def generate_ansible_inventory(): terraform_outputs = run_terraform() instance_ips = terraform_outputs.get("instance_public_dns", {}).get("value", []) instance_names = terraform_outputs.get("instance_name", {}).get("value", []) master = [] worker = [] inventory = {} items = zip(instance_ips, instance_names) for item in items: if "master" in item[1]: master.append({"ansible_ssh_host":item[0], "ansible_ssh_user": "ubuntu", "ansible_ssh_private_key_file": "kanban.pem"}) else: worker.append({"ansible_ssh_host": item[0], "ansible_ssh_user": "ubuntu", "ansible_ssh_private_key_file": "kanban.pem"}) inventory["master"] = master inventory["workers"] = worker return print(json.dumps(inventory, indent=2)) if __name__== "__main__": generate_ansible_inventory()
脚本单独运行输出
{ "master": [ { "ansible_ssh_host": "ec2-54-165-95-159.compute-1.amazonaws.com", "ansible_ssh_user": "ubuntu", "ansible_ssh_private_key_file": "kanban.pem" } ], "workers": [ { "ansible_ssh_host": "ec2-3-238-58-66.compute-1.amazonaws.com", "ansible_ssh_user": "ubuntu", "ansible_ssh_private_key_file": "kanban.pem" } ] }
执行命令后的报错信息
[WARNING]: * Failed to parse /home/vagrant/fullstack-kanban-app/ansible/get_dns.py with script plugin: unhashable type: 'dict' [WARNING]: * Failed to parse /home/vagrant/fullstack-kanban-app/ansible/get_dns.py with ini plugin: /home/vagrant/fullstack-kanban- app/ansible/get_dns.py:3: Expected key=value host variable assignment, got: subprocess [WARNING]: Unable to parse /home/vagrant/fullstack-kanban-app/ansible/get_dns.py as an inventory source [WARNING]: No inventory was parsed, only implicit localhost is available [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
错误原因
Ansible动态清单的格式要求:组内的主机必须是字符串形式的主机地址/别名,不能直接放包含变量的字典。原脚本把带SSH变量的字典放到组列表中,Ansible无法识别该结构,导致解析报错。
正确的格式需要将主机地址作为键,变量作为对应值,或通过_meta字段统一管理主机变量,组内仅存储主机地址。
修改后的脚本
调整清单结构,组内存储公网DNS地址,SSH变量通过_meta的hostvars统一绑定:
#!/usr/bin/python3 import subprocess import json def run_terraform(): result = subprocess.run(["terraform", "output", "-json"], capture_output=True, text=True, cwd="../terraform") return json.loads(result.stdout) def generate_ansible_inventory(): terraform_outputs = run_terraform() instance_dns_list = terraform_outputs.get("instance_public_dns", {}).get("value", []) instance_names_list = terraform_outputs.get("instance_name", {}).get("value", []) inventory = { "master": [], "workers": [], "_meta": { "hostvars": {} } } # 通用SSH变量 ssh_vars = { "ansible_ssh_user": "ubuntu", "ansible_ssh_private_key_file": "kanban.pem" } for dns, name in zip(instance_dns_list, instance_names_list): # 将主机地址分配到对应组 if "master" in name: inventory["master"].append(dns) else: inventory["workers"].append(dns) # 给主机绑定变量 inventory["_meta"]["hostvars"][dns] = ssh_vars # 若主机地址与连接地址一致,ansible_ssh_host可省略,此处保留是为了兼容格式 inventory["_meta"]["hostvars"][dns]["ansible_ssh_host"] = dns print(json.dumps(inventory, indent=2)) if __name__== "__main__": generate_ansible_inventory()
验证步骤
- 单独运行脚本,确认输出格式符合要求:
{ "master": [ "ec2-54-165-95-159.compute-1.amazonaws.com" ], "workers": [ "ec2-3-238-58-66.compute-1.amazonaws.com" ], "_meta": { "hostvars": { "ec2-54-165-95-159.compute-1.amazonaws.com": { "ansible_ssh_user": "ubuntu", "ansible_ssh_private_key_file": "kanban.pem", "ansible_ssh_host": "ec2-54-165-95-159.compute-1.amazonaws.com" }, "ec2-3-238-58-66.compute-1.amazonaws.com": { "ansible_ssh_user": "ubuntu", "ansible_ssh_private_key_file": "kanban.pem", "ansible_ssh_host": "ec2-3-238-58-66.compute-1.amazonaws.com" } } } }
- 重新执行Ansible命令:
ansible all -i get_dns.py -m ping
此时应能正常识别清单主机并执行ping模块。
补充优化
也可以在组级别定义通用变量,避免重复设置:
inventory = { "master": { "hosts": [], "vars": ssh_vars }, "workers": { "hosts": [], "vars": ssh_vars } }
随后将主机地址添加到对应组的hosts列表中,结构会更简洁。
内容的提问来源于stack exchange,提问作者user11631346
相关产品推荐
相关产品推荐

