8
n8n 中文网amn8n.com

Proxmox监控报告 - VM状态、主机资源使用情况、温度控制 - 通过Telegram定时发送

高级

这是一个自动化工作流,包含 18 个节点。主要使用 Set, Ssh, Code, Telegram, HttpRequest 等节点。 Proxmox系统监控 - 通过Telegram发送虚拟机状态、主机资源和温度警报

前置要求
  • Telegram Bot Token
  • 可能需要目标 API 的认证凭证

分类

-
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "EyPTvUFPOkBXeelq",
  "meta": {
    "instanceId": "5c7ce220523e8664f49208a8be668a8dc6fab5f747ce4de865fa1309727919f1"
  },
  "name": "Proxmox 监控报告 - VM 状态、主机资源使用情况、温度控制 - 通过 Telegram 导出定时发送",
  "tags": [],
  "nodes": [
    {
      "id": "62234df5-c87e-40f7-8fca-0b2cb4346d31",
      "name": "每15分钟定时执行",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -896,
        224
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "698e55da-cdf3-475f-b009-34e075274dca",
      "name": "设置变量",
      "type": "n8n-nodes-base.set",
      "position": [
        -672,
        224
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "var1",
              "name": "PROXMOX_IP",
              "type": "string",
              "value": "192.168.1.100"
            },
            {
              "id": "var2",
              "name": "PROXMOX_PORT",
              "type": "string",
              "value": "8006"
            },
            {
              "id": "var3",
              "name": "PROXMOX_NODE",
              "type": "string",
              "value": "pve"
            },
            {
              "id": "var4",
              "name": "TELEGRAM_CHAT_ID",
              "type": "string",
              "value": "YOUR_CHAT_ID"
            },
            {
              "id": "var5",
              "name": "PROXMOX_USER",
              "type": "string",
              "value": "root@pam"
            },
            {
              "id": "var6",
              "name": "PROXMOX_PASSWORD",
              "type": "string",
              "value": "your_password"
            }
          ]
        }
      },
      "typeVersion": 3.3
    },
    {
      "id": "b25669c5-a50b-4d0d-9b1b-fb763661bd4a",
      "name": "Proxmox 登录",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -448,
        224
      ],
      "parameters": {
        "url": "=https://{{ $json.PROXMOX_IP }}:{{ $json.PROXMOX_PORT }}/api2/json/access/ticket",
        "method": "POST",
        "options": {
          "allowUnauthorizedCerts": true
        },
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "username",
              "value": "={{ $json.PROXMOX_USER }}"
            },
            {
              "name": "password",
              "value": "={{ $json.PROXMOX_PASSWORD }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "25af5d5f-3a23-45cf-8387-c00d756cfcb6",
      "name": "准备认证",
      "type": "n8n-nodes-base.set",
      "position": [
        -224,
        224
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "ticket",
              "name": "ticket",
              "type": "string",
              "value": "={{ $json.data.ticket }}"
            },
            {
              "id": "csrf",
              "name": "csrf",
              "type": "string",
              "value": "={{ $json.data.CSRFPreventionToken }}"
            },
            {
              "id": "ip",
              "name": "PROXMOX_IP",
              "type": "string",
              "value": "={{ $('Set Variables').item.json.PROXMOX_IP }}"
            },
            {
              "id": "port",
              "name": "PROXMOX_PORT",
              "type": "string",
              "value": "={{ $('Set Variables').item.json.PROXMOX_PORT }}"
            },
            {
              "id": "node",
              "name": "PROXMOX_NODE",
              "type": "string",
              "value": "={{ $('Set Variables').item.json.PROXMOX_NODE }}"
            }
          ]
        }
      },
      "typeVersion": 3.3
    },
    {
      "id": "43ffeb7f-396a-4e69-a8cc-dc17b506321c",
      "name": "API - VM 列表",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        0,
        0
      ],
      "parameters": {
        "url": "=https://{{ $json.PROXMOX_IP }}:{{ $json.PROXMOX_PORT }}/api2/json/nodes/{{ $json.PROXMOX_NODE }}/qemu",
        "options": {
          "allowUnauthorizedCerts": true
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Cookie",
              "value": "=PVEAuthCookie={{ $json.ticket }}"
            },
            {
              "name": "CSRFPreventionToken",
              "value": "={{ $json.csrf }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "66371dcf-84e5-4467-a104-a466517ee645",
      "name": "API - 节点任务",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        0,
        224
      ],
      "parameters": {
        "url": "=https://{{ $('Set Variables').item.json.PROXMOX_IP }}:{{ $('Set Variables').item.json.PROXMOX_PORT }}/api2/json/nodes/{{ $('Set Variables').item.json.PROXMOX_NODE }}/tasks?limit=100",
        "options": {
          "allowUnauthorizedCerts": true
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Cookie",
              "value": "=PVEAuthCookie={{ $('Prepare Auth').item.json.ticket }}"
            },
            {
              "name": "CSRFPreventionToken",
              "value": "={{ $json.csrf }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "fe91fc19-09e6-44c9-a0d2-403494e5273e",
      "name": "API - 节点状态",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        0,
        448
      ],
      "parameters": {
        "url": "=https://{{ $('Set Variables').item.json.PROXMOX_IP }}:{{ $('Set Variables').item.json.PROXMOX_PORT }}/api2/json/nodes/{{ $('Set Variables').item.json.PROXMOX_NODE }}/status",
        "options": {
          "allowUnauthorizedCerts": true
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Cookie",
              "value": "=PVEAuthCookie={{ $('Prepare Auth').item.json.ticket }}"
            },
            {
              "name": "CSRFPreventionToken",
              "value": "={{ $json.csrf }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "958b184a-aade-42c1-aa6b-26f4bc3d0aa1",
      "name": "SSH - 获取传感器数据",
      "type": "n8n-nodes-base.ssh",
      "position": [
        304,
        224
      ],
      "parameters": {
        "command": "=# Temperature sensors\nsensors 2>/dev/null | grep -E 'Package|Core' || echo 'No temperature data available'\n\n# Detailed uptime\nuptime"
      },
      "credentials": {
        "sshPassword": {
          "id": "AR4GTfnrziYoW31z",
          "name": "SSH Password account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "5d2150c2-34b8-4cbc-8b08-263dc9a2b583",
      "name": "处理数据",
      "type": "n8n-nodes-base.code",
      "position": [
        528,
        112
      ],
      "parameters": {
        "jsCode": "// Get data from previous nodes by name\nconst sshData = $input.first().json.stdout;\nconst vmData = $('API - VM List').first().json.data;\nconst nodeData = $('API - Node Status').first().json.data;\nconst tasksData = $('API - Node Tasks').first().json.data;\n\nif (!vmData) {\n  throw new Error('VM data not received from API - VM List node');\n}\nif (!nodeData) {\n  throw new Error('Node status data not received from API - Node Status node');\n}\nif (!tasksData) {\n  throw new Error('Tasks data not received from API - Node Tasks node');\n}\n\n// Parse SSH output for temperature\nconst sshLines = (sshData || '').split('\\n');\nconst tempLines = sshLines.filter(l => l.includes('Package') || l.includes('Core'));\n\nlet temperatureDisplay = 'No temperature data available';\nlet hasTempWarning = false;\n\nif (tempLines.length > 0) {\n  const tempData = [];\n  let hasHighTemp = false;\n  \n  for (const line of tempLines) {\n    const currentMatch = line.match(/([+\\-]?[0-9.]+)°C/);\n    const highMatch = line.match(/high\\s*=\\s*([+\\-]?[0-9.]+)°C/);\n    \n    if (currentMatch && highMatch) {\n      const current = parseFloat(currentMatch[1]);\n      const high = parseFloat(highMatch[1]);\n      const label = line.split(':')[0].trim();\n      \n      tempData.push({ label, current, high, line: line.trim() });\n      \n      if (current >= high) {\n        hasHighTemp = true;\n        hasTempWarning = true;\n      }\n    }\n  }\n  \n  if (hasHighTemp) {\n    temperatureDisplay = tempData.map(t => t.line).join('\\n');\n  } else {\n    const avgTemp = (tempData.reduce((sum, t) => sum + t.current, 0) / tempData.length).toFixed(1);\n    const maxTemp = Math.max(...tempData.map(t => t.current)).toFixed(1);\n    const minTemp = Math.min(...tempData.map(t => t.current)).toFixed(1);\n    temperatureDisplay = `Average: ${avgTemp}°C (Min: ${minTemp}°C, Max: ${maxTemp}°C)`;\n  }\n}\n\n// Parse uptime from SSH\nlet uptimeString = 'N/A';\nconst uptimeLine = sshLines.find(l => l.includes('up'));\nif (uptimeLine) {\n  uptimeString = uptimeLine.trim();\n}\n\n// VM statistics\nconst totalVMs = vmData.length;\nconst runningVMs = vmData.filter(vm => vm.status === 'running').length;\nconst stoppedVMs = totalVMs - runningVMs;\n\n// Find recently stopped VMs from task log\nconst now = Math.floor(Date.now() / 1000);\nconst fifteenMinutesAgo = now - 900;\n\nconst recentStopTasks = tasksData.filter(task => {\n  const isStopTask = task.type === 'qmstop' || task.type === 'qmshutdown';\n  const taskTime = task.starttime || task.endtime || 0;\n  return isStopTask && taskTime >= fifteenMinutesAgo;\n});\n\nconst recentlyStoppedVMs = recentStopTasks.map(task => {\n  let vmid = 'N/A';\n  vmid = task.id;\n  \n  const vm = vmData.find(v => String(v.vmid) === String(vmid));\n  const vmName = vm ? vm.name : 'Unknown';\n  \n  const taskTime = task.starttime || task.endtime || now;\n  const minutesAgo = Math.floor((now - taskTime) / 60);\n  \n  return {\n    id: vmid,\n    name: vmName,\n    minutesAgo: minutesAgo,\n    status: task.status || 'stopped'\n  };\n});\n\nconst recentlyStopped = recentlyStoppedVMs.length;\n\n// Node statistics from API\nconst cpuUsage = nodeData.cpu ? (nodeData.cpu * 100).toFixed(2) + '%' : 'N/A';\nconst memTotal = nodeData.memory ? (nodeData.memory.total / (1024**3)).toFixed(2) + ' GB' : 'N/A';\nconst memUsed = nodeData.memory ? (nodeData.memory.used / (1024**3)).toFixed(2) + ' GB' : 'N/A';\nconst memPercent = nodeData.memory ? ((nodeData.memory.used / nodeData.memory.total) * 100).toFixed(2) + '%' : 'N/A';\nconst uptimeSeconds = nodeData.uptime || 0;\nconst uptimeDays = Math.floor(uptimeSeconds / 86400);\nconst uptimeHours = Math.floor((uptimeSeconds % 86400) / 3600);\nconst uptimeFormatted = `${uptimeDays}d ${uptimeHours}h`;\n\nreturn {\n  timestamp: new Date().toISOString(),\n  vms: {\n    total: totalVMs,\n    running: runningVMs,\n    stopped: stoppedVMs,\n    recentlyStopped: recentlyStopped,\n    recentlyStoppedDetails: recentlyStoppedVMs\n  },\n  host: {\n    cpu: cpuUsage,\n    memoryUsed: memUsed,\n    memoryTotal: memTotal,\n    memoryPercent: memPercent,\n    uptime: uptimeFormatted,\n    uptimeDetailed: uptimeString,\n    temperature: temperatureDisplay,\n    tempWarning: hasTempWarning\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "3396072a-4d74-4b52-a8a4-55ba02f75bc2",
      "name": "生成格式化消息",
      "type": "n8n-nodes-base.code",
      "position": [
        528,
        352
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\n\nconst tempWarningEmoji = data.host.tempWarning ? '🔥 ' : '';\nconst tempTitle = data.host.tempWarning ? 'Temperature Warning' : 'Temperature Data';\n\nlet recentlyStoppedSection = '';\nif (data.vms.recentlyStopped > 0 && data.vms.recentlyStoppedDetails && data.vms.recentlyStoppedDetails.length > 0) {\n  const vmList = data.vms.recentlyStoppedDetails\n    .map(vm => `  • VM ${vm.id} - ${vm.name} (${vm.minutesAgo} min ago)`)\n    .join('\\n');\n  recentlyStoppedSection = `\\n\\n<b>Recently Stopped VMs Details:</b>\\n${vmList}`;\n}\n\nconst message = `<b>Proxmox Monitoring Report</b>\n<i>Generated: ${new Date(data.timestamp).toLocaleString('en-US')}</i>\n\n<b>━━━━━━━━━━━━━━━━━━━━</b>\n<b>Virtual Machines</b>\n<b>━━━━━━━━━━━━━━━━━━━━</b>\n\n<b>Total VMs:</b> ${data.vms.total}\n<b>Running:</b> ${data.vms.running} ✅\n<b>Stopped:</b> ${data.vms.stopped} ⛔\n<b>Recently Stopped:</b> ${data.vms.recentlyStopped} ⚠️${recentlyStoppedSection}\n\n<b>━━━━━━━━━━━━━━━━━━━━</b>\n<b>Host Resources</b>\n<b>━━━━━━━━━━━━━━━━━━━━</b>\n\n<b>CPU Usage:</b> ${data.host.cpu}\n<b>Memory:</b> ${data.host.memoryUsed} / ${data.host.memoryTotal}\n           (${data.host.memoryPercent})\n<b>Uptime:</b> ${data.host.uptime}\n\n<b>━━━━━━━━━━━━━━━━━━━━</b>\n<b>${tempWarningEmoji}${tempTitle}</b>\n<b>━━━━━━━━━━━━━━━━━━━━</b>\n\n<code>${data.host.temperature}</code>`;\n\nreturn { message };"
      },
      "typeVersion": 2
    },
    {
      "id": "81a4878f-35a2-44d1-81a2-ca209d6e70e3",
      "name": "发送 Telegram 报告",
      "type": "n8n-nodes-base.telegram",
      "position": [
        816,
        224
      ],
      "webhookId": "0a44bae4-6570-4b87-8080-4328fe78a753",
      "parameters": {
        "text": "={{ $json.message }}",
        "chatId": "={{ $('Set Variables').first().json.TELEGRAM_CHAT_ID }}",
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "FUXl519hpM0FsK8j",
          "name": "Telegram account"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "31ae9494-5e80-40be-8aea-4ca982960dbc",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1184,
        16
      ],
      "parameters": {
        "width": 380,
        "height": 180,
        "content": "## Proxmox 监控工作流"
      },
      "typeVersion": 1
    },
    {
      "id": "467412ef-997f-4666-81e2-01c3fb23202f",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -784,
        400
      ],
      "parameters": {
        "width": 320,
        "height": 292,
        "content": "## 需要配置"
      },
      "typeVersion": 1
    },
    {
      "id": "9f4196b9-741d-4536-b487-89be23dfa9e3",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -432,
        -16
      ],
      "parameters": {
        "width": 300,
        "height": 204,
        "content": "## 认证流程"
      },
      "typeVersion": 1
    },
    {
      "id": "23393f4f-3ba3-4900-aec5-7952f9b64586",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        608
      ],
      "parameters": {
        "width": 300,
        "height": 272,
        "content": "## 数据收集"
      },
      "typeVersion": 1
    },
    {
      "id": "7be5a2e6-d39a-4b4f-9cfd-888eab5261ce",
      "name": "便签说明4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        176,
        -16
      ],
      "parameters": {
        "width": 300,
        "height": 220,
        "content": "## SSH 温度检查"
      },
      "typeVersion": 1
    },
    {
      "id": "af79ac1a-e92a-47aa-b541-4d038175f28e",
      "name": "便签说明5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        640,
        -128
      ],
      "parameters": {
        "width": 320,
        "height": 264,
        "content": "## 数据处理"
      },
      "typeVersion": 1
    },
    {
      "id": "e4bc84d3-85ba-4cbe-bb21-8832642ddf82",
      "name": "便签 6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        816,
        384
      ],
      "parameters": {
        "width": 300,
        "height": 256,
        "content": "## TELEGRAM 配置"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "2c9715a6-bb36-4d9d-8476-cd3c18a1fac0",
  "connections": {
    "Prepare Auth": {
      "main": [
        [
          {
            "node": "API - VM List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Data": {
      "main": [
        [
          {
            "node": "Generate Formatted Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API - VM List": {
      "main": [
        [
          {
            "node": "API - Node Tasks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Proxmox Login": {
      "main": [
        [
          {
            "node": "Prepare Auth",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Variables": {
      "main": [
        [
          {
            "node": "Proxmox Login",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API - Node Tasks": {
      "main": [
        [
          {
            "node": "API - Node Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API - Node Status": {
      "main": [
        [
          {
            "node": "SSH - Get Sensors",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SSH - Get Sensors": {
      "main": [
        [
          {
            "node": "Process Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Every 15min": {
      "main": [
        [
          {
            "node": "Set Variables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Formatted Message": {
      "main": [
        [
          {
            "node": "Send Telegram Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

高级

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量18
分类-
节点类型7
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

外部链接
在 n8n.io 查看

分享此工作流