You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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() 

验证步骤

  1. 单独运行脚本,确认输出格式符合要求:
{
  "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"
      }
    }
  }
}
  1. 重新执行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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 21:30:25