脚本如下:
1#!/bin/bash
2
3# ==========================================
4# PVE VM 月度流量计算脚本 (自动安装依赖版)
5# 用法: ./vm_traffic_auto.sh <VMID>
6# ==========================================
7
8# 1. 检查参数
9if [ -z "$1" ]; then
10 echo "错误: 请提供 VMID。"
11 echo "用法: $0 <vmid>"
12 exit 1
13fi
14VMID=$1
15
16# 2. 自动检测并安装 jq
17if ! command -v jq &> /dev/null; then
18 echo "⚠️ 未检测到 'jq' 工具,正在自动安装..."
19
20 # 更新软件源缓存并安装 jq
21 # PVE 经常没有订阅企业源,apt update 可能会有警告,但通常不影响主源安装软件
22 if apt-get update > /dev/null 2>&1 && apt-get install -y jq > /dev/null 2>&1; then
23 echo "✅ 'jq' 安装成功。"
24 else
25 # 如果静默安装失败,尝试显示输出以便调试,或者退出
26 echo "❌ 自动安装失败。正在尝试非静默模式重试..."
27 apt-get install -y jq
28
29 # 再次检查
30 if ! command -v jq &> /dev/null; then
31 echo "❌ 无法安装 jq。请检查网络或手动执行: apt-get install -y jq"
32 exit 1
33 fi
34 fi
35fi
36
37# 3. 查找 VM 所在的节点 (Node)
38# PVE 集群可能由多个节点组成,必须知道 VM 在哪个节点才能查 RRD
39NODE=$(pvesh get /cluster/resources --type vm --output-format json | jq -r ".[] | select(.vmid == $VMID) | .node")
40
41if [ -z "$NODE" ] || [ "$NODE" == "null" ]; then
42 echo "❌ 错误: 未找到 VMID 为 $VMID 的虚拟机 (仅支持 QEMU/KVM)。"
43 exit 1
44fi
45
46echo "🔍 VM $VMID 位于节点: $NODE"
47
48# 4. 获取本月1号零点的时间戳
49CURRENT_MONTH_START=$(date -d "$(date +%Y-%m-01) 00:00:00" +%s)
50START_DATE_STR=$(date -d @$CURRENT_MONTH_START '+%Y-%m-%d %H:%M:%S')
51
52echo "📅 统计周期: $START_DATE_STR 至今"
53echo "⏳ 正在拉取并计算 RRD 数据 (可能需要几秒钟)..."
54
55# 5. 调用 PVE API 获取 RRD 数据
56JSON_DATA=$(pvesh get /nodes/"$NODE"/qemu/"$VMID"/rrddata --timeframe month --output-format json)
57
58# 6. 计算流量 (积分算法)
59echo "$JSON_DATA" | jq -r --argjson start_ts "$CURRENT_MONTH_START" '
60 .[] | select(.time >= $start_ts) | [.time, .netin, .netout] | @tsv
61' | awk '
62BEGIN {
63 total_bytes = 0;
64 last_time = 0;
65}
66{
67 time = $1;
68 # 处理 null 值,视为 0
69 netin = ($2 == "null" || $2 == "") ? 0 : $2;
70 netout = ($3 == "null" || $3 == "") ? 0 : $3;
71
72 # 当前速率 (B/s) = 入网 + 出网
73 current_rate = netin + netout;
74
75 if (last_time != 0) {
76 # 计算时间间隔 (秒)
77 interval = time - last_time;
78
79 # 如果间隔正常,累加流量: 速率 * 时间
80 if (interval > 0) {
81 bytes_in_slot = current_rate * interval;
82 total_bytes += bytes_in_slot;
83 }
84 }
85
86 last_time = time;
87}
88END {
89 gb = total_bytes / 1024 / 1024 / 1024;
90 mb = total_bytes / 1024 / 1024;
91
92 printf "\n📊 --- 统计结果 ---\n";
93 printf "VMID: %s\n", "'"$VMID"'";
94 printf "节点: %s\n", "'"$NODE"'";
95 printf "总流量: %.2f GB (%.2f MB)\n", gb, mb;
96 printf "-------------------\n";
97}
98'