playbooks/inventory/terraform_cmdb.py
Haitao Pan df48cb4f5a feat(inventory): add Terraform CMDB dynamic inventory for ai-workspace
Reads cmdb.json produced by iac_modules vultr-vps/envs/ai-workspace
generate.py and exposes hosts/groups/hostvars to Ansible, linking IaC
provisioning to playbook deploys (terraform_cmdb.py).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 20:57:58 +08:00

107 lines
3.1 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Ansible 动态 inventory —— 数据源为 Terraform 导出的 CMDB。
与 IAC 联动方式:
iac_modules/terraform-hcl-standard/vultr-vps/envs/ai-workspace/ 的 generate.py
在 `terraform apply` 后,把 YAML 静态字段与 terraform 运行时输出合并写出
cmdb.json结构化主机事实。本脚本把它翻译成 Ansible 动态 inventory
于是 IaC 一变更、重跑 `generate.py inventory`inventory 就跟着变。
取数优先级:
1. 环境变量 AI_WORKSPACE_CMDB_JSON 指向的文件
2. 环境变量 AI_WORKSPACE_TF_DIR或默认 env 目录)下的 cmdb.json
用法:
ansible-inventory -i inventory/terraform_cmdb.py --list
ansible all -i inventory/terraform_cmdb.py -m ping
"""
import json
import os
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
# playbooks/inventory -> 仓库根 -> terraform env
REPO_ROOT = os.path.abspath(os.path.join(HERE, "..", ".."))
DEFAULT_TF_DIR = os.path.join(
REPO_ROOT,
"iac_modules",
"terraform-hcl-standard",
"vultr-vps",
"envs",
"ai-workspace",
)
def _from_explicit_file():
path = os.environ.get("AI_WORKSPACE_CMDB_JSON")
if path and os.path.isfile(path):
with open(path, encoding="utf-8") as fh:
return json.load(fh)
return None
def _from_default_file(tf_dir):
path = os.path.join(tf_dir, "cmdb.json")
if os.path.isfile(path):
with open(path, encoding="utf-8") as fh:
return json.load(fh)
return None
def load_cmdb():
tf_dir = os.environ.get("AI_WORKSPACE_TF_DIR", DEFAULT_TF_DIR)
for loader in (
_from_explicit_file,
lambda: _from_default_file(tf_dir),
):
data = loader()
if data:
return data
return {}
def build_inventory(cmdb):
inv = {"_meta": {"hostvars": {}}}
groups = {}
for name, host in cmdb.items():
hostvars = {
"ansible_host": host.get("ip"),
"ansible_user": host.get("ansible_user", "root"),
# 云主机 IP 常被回收,放宽 host key 校验避免撞到旧 known_hosts
"ansible_ssh_common_args": (
"-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
),
}
# CMDB 其余字段一并暴露给 playbook 使用
hostvars.update(host.get("host_vars", {}))
hostvars["cmdb_instance_id"] = host.get("instance_id")
hostvars["cmdb_os_id"] = host.get("os_id")
hostvars["cmdb_tags"] = host.get("tags", [])
inv["_meta"]["hostvars"][name] = hostvars
for group in host.get("groups", []) or ["ungrouped"]:
groups.setdefault(group, {"hosts": []})["hosts"].append(name)
inv.update(groups)
inv["all"] = {"children": sorted(list(groups.keys()) + ["ungrouped"])}
return inv
def main():
args = sys.argv[1:]
cmdb = load_cmdb()
if "--host" in args:
# hostvars 已在 _meta 里,单主机查询返回空对象即可
print(json.dumps({}))
return
# 默认与 --list 行为一致
print(json.dumps(build_inventory(cmdb), indent=2))
if __name__ == "__main__":
main()