181 lines
5.3 KiB
Python
181 lines
5.3 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
汇总测试平台中由 agent-manager 管理的 agents 数量及资源使用(请求/限制/实际使用)
|
||
|
||
用法示例:
|
||
python3 scripts/aggregate_agents_resources.py --namespace ai-agents
|
||
|
||
注意:
|
||
- 需要在运行环境中能访问 Kubernetes 集群(in-cluster 或提供 KUBECONFIG)
|
||
- 若想使用实际资源使用(usage),需要集群安装 metrics-server
|
||
"""
|
||
import argparse
|
||
import sys
|
||
import os
|
||
|
||
# ensure repo root is on sys.path so we can import k8s_manager when running from scripts/
|
||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||
if ROOT not in sys.path:
|
||
sys.path.insert(0, ROOT)
|
||
|
||
from k8s_manager import K8sManager
|
||
from typing import Optional
|
||
|
||
|
||
def parse_cpu(s: Optional[str]) -> float:
|
||
if not s:
|
||
return 0.0
|
||
s = str(s).strip()
|
||
try:
|
||
if s.endswith('m'):
|
||
return float(s[:-1]) / 1000.0
|
||
return float(s)
|
||
except ValueError:
|
||
return 0.0
|
||
|
||
|
||
def parse_memory(s: Optional[str]) -> float:
|
||
"""解析内存字符串并返回Mi单位的浮点数值"""
|
||
if not s:
|
||
return 0.0
|
||
s = str(s).strip()
|
||
units = {
|
||
'Ki': 1024.0,
|
||
'Mi': 1024.0 ** 2,
|
||
'Gi': 1024.0 ** 3,
|
||
'Ti': 1024.0 ** 4,
|
||
'K': 1000.0,
|
||
'M': 1000.0 ** 2,
|
||
'G': 1000.0 ** 3,
|
||
}
|
||
# 直接 numeric
|
||
try:
|
||
return float(s) / (1024.0 ** 2)
|
||
except Exception:
|
||
pass
|
||
|
||
for u, factor in units.items():
|
||
if s.endswith(u):
|
||
try:
|
||
num = float(s[:-len(u)])
|
||
# 返回 Mi 为单位
|
||
return (num * factor) / (1024.0 ** 2)
|
||
except Exception:
|
||
return 0.0
|
||
# 未知单位,尝试移除非数字字符
|
||
num = ''.join(ch for ch in s if (ch.isdigit() or ch == '.' ))
|
||
try:
|
||
return float(num) / (1024.0 ** 2)
|
||
except Exception:
|
||
return 0.0
|
||
|
||
|
||
def human_mem(mib: float) -> str:
|
||
if mib >= 1024:
|
||
return f"{mib/1024:.2f} GiB"
|
||
return f"{mib:.1f} MiB"
|
||
|
||
|
||
def human_cpu(cores: float) -> str:
|
||
if cores < 1:
|
||
return f"{int(cores*1000)} m"
|
||
return f"{cores:.3f} cores"
|
||
|
||
|
||
def aggregate(namespace: str, kubeconfig: Optional[str], detailed: bool = False):
|
||
mgr = K8sManager(namespace=namespace, kubeconfig_path=kubeconfig)
|
||
|
||
pods = mgr.list_pods()
|
||
total = len(pods)
|
||
|
||
sum_req_cpu = 0.0
|
||
sum_lim_cpu = 0.0
|
||
sum_usage_cpu = 0.0
|
||
have_usage_cpu = False
|
||
|
||
sum_req_mem = 0.0
|
||
sum_lim_mem = 0.0
|
||
sum_usage_mem = 0.0
|
||
have_usage_mem = False
|
||
|
||
details = []
|
||
for p in pods:
|
||
name = p.get('name')
|
||
status = mgr.get_pod_status(name)
|
||
|
||
# requests/limits
|
||
resources = status.get('resources', {})
|
||
requests = resources.get('requests', {}) or {}
|
||
limits = resources.get('limits', {}) or {}
|
||
|
||
r_cpu = parse_cpu(requests.get('cpu'))
|
||
l_cpu = parse_cpu(limits.get('cpu'))
|
||
sum_req_cpu += r_cpu
|
||
sum_lim_cpu += l_cpu
|
||
|
||
r_mem = parse_memory(requests.get('memory'))
|
||
l_mem = parse_memory(limits.get('memory'))
|
||
sum_req_mem += r_mem
|
||
sum_lim_mem += l_mem
|
||
|
||
usage = resources.get('usage') or {}
|
||
u_cpu = parse_cpu(usage.get('cpu'))
|
||
u_mem = parse_memory(usage.get('memory'))
|
||
if u_cpu:
|
||
have_usage_cpu = True
|
||
sum_usage_cpu += u_cpu
|
||
if u_mem:
|
||
have_usage_mem = True
|
||
sum_usage_mem += u_mem
|
||
|
||
details.append({
|
||
'name': name,
|
||
'status': status.get('status'),
|
||
'template': status.get('template'),
|
||
'req_cpu': r_cpu,
|
||
'lim_cpu': l_cpu,
|
||
'use_cpu': u_cpu,
|
||
'req_mem_mi': r_mem,
|
||
'lim_mem_mi': l_mem,
|
||
'use_mem_mi': u_mem,
|
||
})
|
||
|
||
# 输出
|
||
print(f"Agents 总数: {total}")
|
||
print("")
|
||
print("CPU 总计:")
|
||
print(f" 请求 (requests): {human_cpu(sum_req_cpu)}")
|
||
print(f" 限制 (limits): {human_cpu(sum_lim_cpu)}")
|
||
if have_usage_cpu:
|
||
print(f" 实际使用 (usage): {human_cpu(sum_usage_cpu)}")
|
||
else:
|
||
print(" 实际使用 (usage): 未获取(需安装 metrics-server 或 无法访问 metrics API)")
|
||
|
||
print("")
|
||
print("内存 总计:")
|
||
print(f" 请求 (requests): {human_mem(sum_req_mem)}")
|
||
print(f" 限制 (limits): {human_mem(sum_lim_mem)}")
|
||
if have_usage_mem:
|
||
print(f" 实际使用 (usage): {human_mem(sum_usage_mem)}")
|
||
else:
|
||
print(" 实际使用 (usage): 未获取(需安装 metrics-server 或 无法访问 metrics API)")
|
||
|
||
if detailed:
|
||
print('\n每个 Pod 详情:')
|
||
for d in details:
|
||
print(f"- {d['name']}: status={d['status']}, template={d['template']}, req={human_cpu(d['req_cpu'])}/{human_mem(d['req_mem_mi'])}, lim={human_cpu(d['lim_cpu'])}/{human_mem(d['lim_mem_mi'])}, use={human_cpu(d['use_cpu'])}/{human_mem(d['use_mem_mi'])}")
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser()
|
||
parser.add_argument('--namespace', '-n', default='ai-agents', help='Kubernetes namespace')
|
||
parser.add_argument('--kubeconfig', '-k', default=None, help='可选 kubeconfig 文件路径')
|
||
parser.add_argument('--detailed', '-d', action='store_true', help='输出每个 pod 的详细资源信息')
|
||
args = parser.parse_args()
|
||
|
||
aggregate(args.namespace, args.kubeconfig, args.detailed)
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|