脚本如下:

 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'