first commit

This commit is contained in:
2025-09-11 16:31:32 +08:00
commit aebada2894

150
sync.py Normal file
View File

@@ -0,0 +1,150 @@
#!/usr/bin/env python3
from pyVmomi import vim
from pyVim.connect import SmartConnect, Disconnect
import ssl
import argparse
import getpass
import atexit
import requests
import os
import sys
import ipaddress
def get_vm_info(content):
vm_view = content.viewManager.CreateContainerView(
content.rootFolder, [vim.VirtualMachine], True)
return [
{
'name': vm.name,
'ip': vm.guest.ipAddress if vm.guest and vm.guest.ipAddress else "N/A"
}
for vm in vm_view.view
]
def get_session(acl_url, username, password):
login_url = f"{acl_url}/acl/login"
session = requests.Session()
resp = session.post(login_url, json={
"username": username,
"password": password
})
if resp.status_code != 200:
raise Exception(f"登录失败,状态码: {resp.status_code},响应: {resp.text}")
print("🔐 登录成功")
return session
def main():
parser = argparse.ArgumentParser(description='同步 vCenter 虚拟机资产信息到 OneTerm')
parser.add_argument('--vc-host', required=True, help='vCenter服务器地址')
parser.add_argument('--vc-user', required=True, help='vCenter用户名')
parser.add_argument('--vc-pass', help='vCenter密码可选优先级高于环境变量')
parser.add_argument('--api-url', required=True, help='OneTerm API 地址(如 http://host:port/api')
parser.add_argument('--acl-url', required=True, help='OneTerm ACL 地址(如 http://host:port/api')
parser.add_argument('--api-user', default=os.environ.get("API_USERNAME", "admin"), help='API 登录用户名')
parser.add_argument('--api-pass', default=os.environ.get("API_PASSWORD", "Duguang.io123"), help='API 登录密码')
args = parser.parse_args()
# 获取 vCenter 密码
password = args.vc_pass or os.environ.get("VCENTER_PASSWORD")
if not password:
password = getpass.getpass(f"请输入 {args.vc_user} 的密码: ")
try:
# 创建不验证SSL的上下文
context = ssl._create_unverified_context()
# 连接 vCenter
si = SmartConnect(
host=args.vc_host,
user=args.vc_user,
pwd=password,
sslContext=context
)
atexit.register(Disconnect, si)
# 获取虚拟机信息
vms = get_vm_info(si.RetrieveContent())
vm_dict = {}
for vm in vms:
name = vm["name"]
ip = vm["ip"]
try:
ipaddress.ip_address(ip)
vm_dict[name] = ip
except ValueError:
print(f"⚠️ 忽略资产IP 非法): {name} -> {ip}")
# 登录获取 token
session = get_session(args.acl_url, args.api_user, args.api_pass)
# 获取已有资产
asset_url = f"{args.api_url}/oneterm/v1/asset?page_index=1&page_size=100"
resp = session.get(asset_url)
if resp.status_code != 200:
raise Exception(f"获取资产列表失败,状态码: {resp.status_code},响应: {resp.text}")
# 根据实际接口返回格式提取资产
data = resp.json()
items = data.get("data", {}).get("list", []) or data.get("data", {}).get("items", [])
exist_names = {item["name"]: item for item in items}
# 创建新资产
for name, ip in vm_dict.items():
if name not in exist_names:
try:
ipaddress.ip_address(ip)
except ValueError:
print(f"⚠️ 忽略资产IP 非法): {name} -> {ip}")
continue
print(f"🆕 创建资产: {name} -> {ip}")
create_resp = session.post(f"{args.api_url}/oneterm/v1/asset", json={
"name": name,
"ip": ip,
"parent_id": 1,
"allow": True,
"cmd_ids": "",
"connectable": True,
"protocols": ["ssh:22"],
"authorization": {"1": [1]},
"comment": ""
})
if create_resp.status_code >= 300:
print(f"⚠️ 创建失败: {create_resp.status_code} - {create_resp.text}")
if create_resp.status_code >= 300:
print(f"⚠️ 创建失败: {create_resp.status_code} - {create_resp.text}")
# 删除旧资产
# 重新拉取最新资产(包含新添加的),确保删除所有离线资产
asset_url = f"{args.api_url}/oneterm/v1/asset?page_index=1&page_size=100"
latest_resp = session.get(asset_url)
if latest_resp.status_code != 200:
raise Exception(f"重新获取资产失败: {latest_resp.status_code} - {latest_resp.text}")
latest_data = latest_resp.json()
latest_items = latest_data.get("data", {}).get("list", []) or latest_data.get("data", {}).get("items", [])
for item in latest_items:
ip_in_oneterm = str(item.get("ip", "")).strip().lower()
asset_id = item["id"]
# 判断并删除离线状态的资产
if item.get("connectable") is False:
delete_url = f"{args.api_url}/oneterm/v1/asset/{asset_id}"
print(f"❌ 删除资产: {ip_in_oneterm} -> ID: {asset_id}")
del_resp = session.delete(delete_url)
if del_resp.status_code >= 300:
print(f"⚠️ 删除失败: {del_resp.status_code} - {del_resp.text}")
else:
print(f"✅ 删除成功: {ip_in_oneterm}")
print("✅ 同步完成")
except Exception as e:
print(f"❌ 错误: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
main()