diff --git a/plugins/ProxmoxVE/v1/configValidation.json b/plugins/ProxmoxVE/v1/configValidation.json new file mode 100644 index 0000000..9852aa4 --- /dev/null +++ b/plugins/ProxmoxVE/v1/configValidation.json @@ -0,0 +1,11 @@ +{ + "steps": [ + { + "displayName": "Authenticate", + "dataStream": { "name": "apiVersion" }, + "required": true, + "error": "Could not connect to Proxmox VE. Check your Host URL is reachable, and that your Token ID and Token Secret are correct.", + "success": "Connected to Proxmox VE successfully." + } + ] +} diff --git a/plugins/ProxmoxVE/v1/custom_types.json b/plugins/ProxmoxVE/v1/custom_types.json new file mode 100644 index 0000000..c4b2ff7 --- /dev/null +++ b/plugins/ProxmoxVE/v1/custom_types.json @@ -0,0 +1,23 @@ +[ + { + "name": "Proxmox Node", + "sourceType": "Proxmox Node", + "icon": "server", + "singular": "Node", + "plural": "Nodes" + }, + { + "name": "Proxmox VM", + "sourceType": "Proxmox VM", + "icon": "display", + "singular": "VM", + "plural": "VMs" + }, + { + "name": "Proxmox Container", + "sourceType": "Proxmox Container", + "icon": "box", + "singular": "Container", + "plural": "Containers" + } +] diff --git a/plugins/ProxmoxVE/v1/dataStreams/apiVersion.json b/plugins/ProxmoxVE/v1/dataStreams/apiVersion.json new file mode 100644 index 0000000..9eac76e --- /dev/null +++ b/plugins/ProxmoxVE/v1/dataStreams/apiVersion.json @@ -0,0 +1,18 @@ +{ + "name": "apiVersion", + "displayName": "API Version", + "description": "Proxmox VE API version and release information", + "tags": ["Info"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/version", + "pathToData": "data" + }, + "matches": "none", + "visibility": { "type": "hidden" }, + "metadata": [ + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/ProxmoxVE/v1/dataStreams/clusterResources.json b/plugins/ProxmoxVE/v1/dataStreams/clusterResources.json new file mode 100644 index 0000000..5b4bb3e --- /dev/null +++ b/plugins/ProxmoxVE/v1/dataStreams/clusterResources.json @@ -0,0 +1,30 @@ +{ + "name": "clusterResources", + "displayName": "Cluster Resources", + "description": "All nodes, VMs, and containers across the cluster with current resource utilisation", + "tags": ["Cluster", "Resources"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/cluster/resources", + "postRequestScript": "clusterResources.js" + }, + "matches": "none", + "metadata": [ + { "name": "sourceId", "displayName": "Source ID", "shape": "string", "visible": false }, + { "name": "sourceType", "displayName": "Type", "shape": "string" }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "node", "displayName": "Node", "shape": "string" }, + { "name": "vmid", "displayName": "VM ID", "shape": "number" }, + { "name": "status", "displayName": "Status", "shape": ["state", { "map": { "success": ["running", "online"], "error": ["stopped", "offline"], "warning": ["paused", "unknown"] } }] }, + { "name": "cpu", "displayName": "CPU Usage", "shape": ["percent", { "asZeroToOne": true }] }, + { "name": "maxcpu", "displayName": "CPU Cores", "shape": "number" }, + { "name": "mem", "displayName": "Memory Used", "shape": "bytes" }, + { "name": "maxmem", "displayName": "Memory Total", "shape": "bytes" }, + { "name": "disk", "displayName": "Disk Used", "shape": "bytes" }, + { "name": "maxdisk", "displayName": "Disk Total", "shape": "bytes" }, + { "name": "uptime", "displayName": "Uptime", "shape": "seconds" }, + { "name": "type", "displayName": "API Type", "shape": "string", "visible": false } + ], + "timeframes": false +} diff --git a/plugins/ProxmoxVE/v1/dataStreams/containerMetrics.json b/plugins/ProxmoxVE/v1/dataStreams/containerMetrics.json new file mode 100644 index 0000000..9855bd5 --- /dev/null +++ b/plugins/ProxmoxVE/v1/dataStreams/containerMetrics.json @@ -0,0 +1,34 @@ +{ + "name": "containerMetrics", + "displayName": "Container Metrics", + "description": "Historical CPU, memory, and network metrics for an LXC container", + "tags": ["Container", "Metrics"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/nodes/{{object.node}}/lxc/{{object.rawId}}/rrddata", + "getArgs": [ + { + "key": "timeframe", + "value": "{{timeframe.enum === 'last1hour' ? 'hour' : timeframe.enum === 'last7days' ? 'week' : timeframe.enum === 'last30days' ? 'month' : 'day'}}" + }, + { + "key": "cf", + "value": "AVERAGE" + } + ], + "pathToData": "data" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Proxmox Container"] } }, + "metadata": [ + { "name": "time", "displayName": "Time", "shape": "timestamp", "role": "timestamp" }, + { "name": "cpu", "displayName": "CPU Usage", "shape": ["percent", { "asZeroToOne": true }], "role": "value" }, + { "name": "mem", "displayName": "Memory Used", "shape": "bytes" }, + { "name": "maxmem", "displayName": "Memory Total", "shape": "bytes" }, + { "name": "netin", "displayName": "Network In", "shape": "bytespersecondbinary" }, + { "name": "netout", "displayName": "Network Out", "shape": "bytespersecondbinary" }, + { "name": "diskread", "displayName": "Disk Read", "shape": "bytespersecondbinary" }, + { "name": "diskwrite", "displayName": "Disk Write", "shape": "bytespersecondbinary" } + ], + "timeframes": ["last1hour", "last24hours", "last7days", "last30days"] +} diff --git a/plugins/ProxmoxVE/v1/dataStreams/containerStatus.json b/plugins/ProxmoxVE/v1/dataStreams/containerStatus.json new file mode 100644 index 0000000..8063fe8 --- /dev/null +++ b/plugins/ProxmoxVE/v1/dataStreams/containerStatus.json @@ -0,0 +1,28 @@ +{ + "name": "containerStatus", + "displayName": "Container Status", + "description": "Current CPU, memory, disk, and uptime for an LXC container", + "tags": ["Container", "Status"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/nodes/{{object.node}}/lxc/{{object.rawId}}/status/current", + "pathToData": "data" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Proxmox Container"] } }, + "metadata": [ + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "status", "displayName": "Status", "shape": ["state", { "map": { "success": ["running"], "error": ["stopped"], "warning": ["paused", "unknown"] } }], "role": "value" }, + { "name": "cpu", "displayName": "CPU Usage", "shape": ["percent", { "asZeroToOne": true }] }, + { "name": "cpus", "displayName": "CPU Cores", "shape": "number" }, + { "name": "mem", "displayName": "Memory Used", "shape": "bytes" }, + { "name": "maxmem", "displayName": "Memory Total", "shape": "bytes" }, + { "name": "swap", "displayName": "Swap Used", "shape": "bytes" }, + { "name": "maxswap", "displayName": "Swap Total", "shape": "bytes" }, + { "name": "disk", "displayName": "Disk Used", "shape": "bytes" }, + { "name": "maxdisk", "displayName": "Disk Total", "shape": "bytes" }, + { "name": "uptime", "displayName": "Uptime", "shape": "seconds" }, + { "name": "containerid", "displayName": "Container ID", "shape": "number" } + ], + "timeframes": false +} diff --git a/plugins/ProxmoxVE/v1/dataStreams/nodeMetrics.json b/plugins/ProxmoxVE/v1/dataStreams/nodeMetrics.json new file mode 100644 index 0000000..43c0375 --- /dev/null +++ b/plugins/ProxmoxVE/v1/dataStreams/nodeMetrics.json @@ -0,0 +1,34 @@ +{ + "name": "nodeMetrics", + "displayName": "Node Metrics", + "description": "Historical CPU, memory, and network metrics for a node", + "tags": ["Node", "Metrics"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/nodes/{{object.rawId}}/rrddata", + "getArgs": [ + { + "key": "timeframe", + "value": "{{timeframe.enum === 'last1hour' ? 'hour' : timeframe.enum === 'last7days' ? 'week' : timeframe.enum === 'last30days' ? 'month' : 'day'}}" + }, + { + "key": "cf", + "value": "AVERAGE" + } + ], + "pathToData": "data" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Proxmox Node"] } }, + "metadata": [ + { "name": "time", "displayName": "Time", "shape": "timestamp", "role": "timestamp" }, + { "name": "cpu", "displayName": "CPU Usage", "shape": ["percent", { "asZeroToOne": true }], "role": "value" }, + { "name": "maxcpu", "displayName": "CPU Cores", "shape": "number" }, + { "name": "memused", "displayName": "Memory Used", "shape": "bytes" }, + { "name": "memtotal", "displayName": "Memory Total", "shape": "bytes" }, + { "name": "netin", "displayName": "Network In", "shape": "bytespersecondbinary" }, + { "name": "netout", "displayName": "Network Out", "shape": "bytespersecondbinary" }, + { "name": "iowait", "displayName": "I/O Wait", "shape": ["percent", { "asZeroToOne": true }] } + ], + "timeframes": ["last1hour", "last24hours", "last7days", "last30days"] +} diff --git a/plugins/ProxmoxVE/v1/dataStreams/nodeStatus.json b/plugins/ProxmoxVE/v1/dataStreams/nodeStatus.json new file mode 100644 index 0000000..380651f --- /dev/null +++ b/plugins/ProxmoxVE/v1/dataStreams/nodeStatus.json @@ -0,0 +1,25 @@ +{ + "name": "nodeStatus", + "displayName": "Node Status", + "description": "Current CPU, memory, disk, and uptime for a node", + "tags": ["Node", "Status"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/nodes/{{object.rawId}}/status", + "pathToData": "data", + "expandInnerObjects": true + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Proxmox Node"] } }, + "metadata": [ + { "name": "cpu", "displayName": "CPU Usage", "shape": ["percent", { "asZeroToOne": true }], "role": "value" }, + { "name": "memory.used", "displayName": "Memory Used", "shape": "bytes" }, + { "name": "memory.total", "displayName": "Memory Total", "shape": "bytes" }, + { "name": "rootfs.used", "displayName": "Disk Used", "shape": "bytes" }, + { "name": "rootfs.total", "displayName": "Disk Total", "shape": "bytes" }, + { "name": "uptime", "displayName": "Uptime", "shape": "seconds" }, + { "name": "kversion", "displayName": "Kernel Version", "shape": "string" }, + { "name": "pveversion", "displayName": "PVE Version", "shape": "string" } + ], + "timeframes": false +} diff --git a/plugins/ProxmoxVE/v1/dataStreams/nodeUpdates.json b/plugins/ProxmoxVE/v1/dataStreams/nodeUpdates.json new file mode 100644 index 0000000..ec4fcf0 --- /dev/null +++ b/plugins/ProxmoxVE/v1/dataStreams/nodeUpdates.json @@ -0,0 +1,22 @@ +{ + "name": "nodeUpdates", + "displayName": "Node Updates", + "description": "Pending OS package updates available for a node", + "tags": ["Node", "Updates"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/nodes/{{object.rawId}}/apt/update", + "pathToData": "data" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Proxmox Node"] } }, + "metadata": [ + { "name": "Package", "displayName": "Package Name", "shape": "string", "role": "label" }, + { "name": "Version", "displayName": "New Version", "shape": "string" }, + { "name": "OldVersion", "displayName": "Current Version", "shape": "string" }, + { "name": "Priority", "displayName": "Priority", "shape": "string" }, + { "name": "Origin", "displayName": "Origin", "shape": "string" }, + { "name": "Description", "displayName": "Description", "shape": "string" } + ], + "timeframes": false +} diff --git a/plugins/ProxmoxVE/v1/dataStreams/scripts/clusterResources.js b/plugins/ProxmoxVE/v1/dataStreams/scripts/clusterResources.js new file mode 100644 index 0000000..3e22205 --- /dev/null +++ b/plugins/ProxmoxVE/v1/dataStreams/scripts/clusterResources.js @@ -0,0 +1,28 @@ +const resources = (data && data.data) || []; + +const typeMap = { + node: 'Proxmox Node', + qemu: 'Proxmox VM', + lxc: 'Proxmox Container' +}; + +result = resources + .filter(function(r) { return typeMap[r.type]; }) + .map(function(r) { + return { + sourceId: r.type === 'node' ? r.node : String(r.vmid), + sourceType: typeMap[r.type], + name: r.name || r.node, + node: r.node, + vmid: r.vmid != null ? r.vmid : null, + type: r.type, + status: r.status, + cpu: r.cpu || 0, + maxcpu: r.maxcpu || 0, + mem: r.mem || 0, + maxmem: r.maxmem || 0, + disk: r.disk || 0, + maxdisk: r.maxdisk || 0, + uptime: r.uptime || 0 + }; + }); diff --git a/plugins/ProxmoxVE/v1/dataStreams/vmMetrics.json b/plugins/ProxmoxVE/v1/dataStreams/vmMetrics.json new file mode 100644 index 0000000..a449f9a --- /dev/null +++ b/plugins/ProxmoxVE/v1/dataStreams/vmMetrics.json @@ -0,0 +1,34 @@ +{ + "name": "vmMetrics", + "displayName": "VM Metrics", + "description": "Historical CPU, memory, and network metrics for a QEMU/KVM virtual machine", + "tags": ["VM", "Metrics"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/nodes/{{object.node}}/qemu/{{object.rawId}}/rrddata", + "getArgs": [ + { + "key": "timeframe", + "value": "{{timeframe.enum === 'last1hour' ? 'hour' : timeframe.enum === 'last7days' ? 'week' : timeframe.enum === 'last30days' ? 'month' : 'day'}}" + }, + { + "key": "cf", + "value": "AVERAGE" + } + ], + "pathToData": "data" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Proxmox VM"] } }, + "metadata": [ + { "name": "time", "displayName": "Time", "shape": "timestamp", "role": "timestamp" }, + { "name": "cpu", "displayName": "CPU Usage", "shape": ["percent", { "asZeroToOne": true }], "role": "value" }, + { "name": "mem", "displayName": "Memory Used", "shape": "bytes" }, + { "name": "maxmem", "displayName": "Memory Total", "shape": "bytes" }, + { "name": "netin", "displayName": "Network In", "shape": "bytespersecondbinary" }, + { "name": "netout", "displayName": "Network Out", "shape": "bytespersecondbinary" }, + { "name": "diskread", "displayName": "Disk Read", "shape": "bytespersecondbinary" }, + { "name": "diskwrite", "displayName": "Disk Write", "shape": "bytespersecondbinary" } + ], + "timeframes": ["last1hour", "last24hours", "last7days", "last30days"] +} diff --git a/plugins/ProxmoxVE/v1/dataStreams/vmStatus.json b/plugins/ProxmoxVE/v1/dataStreams/vmStatus.json new file mode 100644 index 0000000..b6b2b23 --- /dev/null +++ b/plugins/ProxmoxVE/v1/dataStreams/vmStatus.json @@ -0,0 +1,28 @@ +{ + "name": "vmStatus", + "displayName": "VM Status", + "description": "Current CPU, memory, disk, and uptime for a QEMU/KVM virtual machine", + "tags": ["VM", "Status"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/nodes/{{object.node}}/qemu/{{object.rawId}}/status/current", + "pathToData": "data" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Proxmox VM"] } }, + "metadata": [ + { "name": "status", "displayName": "Status", "shape": ["state", { "map": { "success": ["running"], "error": ["stopped"], "warning": ["paused", "unknown"] } }], "role": "value" }, + { "name": "cpu", "displayName": "CPU Usage", "shape": ["percent", { "asZeroToOne": true }] }, + { "name": "cpus", "displayName": "CPU Cores", "shape": "number" }, + { "name": "mem", "displayName": "Memory Used", "shape": "bytes" }, + { "name": "maxmem", "displayName": "Memory Total", "shape": "bytes" }, + { "name": "disk", "displayName": "Disk Used", "shape": "bytes" }, + { "name": "maxdisk", "displayName": "Disk Total", "shape": "bytes" }, + { "name": "uptime", "displayName": "Uptime", "shape": "seconds" }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "vmid", "displayName": "VM ID", "shape": "number" }, + { "name": "netin", "displayName": "Network In", "shape": "bytes" }, + { "name": "netout", "displayName": "Network Out", "shape": "bytes" } + ], + "timeframes": false +} diff --git a/plugins/ProxmoxVE/v1/defaultContent/containerDashboard.dash.json b/plugins/ProxmoxVE/v1/defaultContent/containerDashboard.dash.json new file mode 100644 index 0000000..cf90884 --- /dev/null +++ b/plugins/ProxmoxVE/v1/defaultContent/containerDashboard.dash.json @@ -0,0 +1,166 @@ +{ + "name": "Container", + "schemaVersion": "1.5", + "timeframe": "last24hours", + "variables": ["{{variables.[Container]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "91935d67-63ec-4106-91cc-507adb5829e1", + "x": 0, + "y": 0, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Container Details", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[containerStatus]}}", + "name": "containerStatus", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Containers]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Container]}}" + }, + "variables": ["{{variables.[Container]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true + } + } + } + } + }, + { + "i": "1e1eb720-c871-4e5f-8b57-4974cf7f1523", + "x": 2, + "y": 0, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "CPU Usage", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[containerMetrics]}}", + "name": "containerMetrics", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Containers]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Container]}}" + }, + "variables": ["{{variables.[Container]}}"], + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "time", + "yAxisColumn": ["cpu"], + "seriesColumn": "none", + "showLegend": true, + "legendPosition": "bottom" + } + } + } + } + }, + { + "i": "9942ecc2-0c69-4051-b1e2-7d067f0b603f", + "x": 0, + "y": 2, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Memory", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[containerMetrics]}}", + "name": "containerMetrics", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Containers]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Container]}}" + }, + "variables": ["{{variables.[Container]}}"], + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "time", + "yAxisColumn": ["mem", "maxmem"], + "seriesColumn": "none", + "showLegend": true, + "legendPosition": "bottom" + } + } + } + } + }, + { + "i": "8786771a-3bd3-4ed4-af0a-afceb0f16164", + "x": 2, + "y": 2, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Network & Disk", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[containerMetrics]}}", + "name": "containerMetrics", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Containers]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Container]}}" + }, + "variables": ["{{variables.[Container]}}"], + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "time", + "yAxisColumn": ["netin", "netout", "diskread", "diskwrite"], + "seriesColumn": "none", + "showLegend": true, + "legendPosition": "bottom" + } + } + } + } + } + ] + } +} diff --git a/plugins/ProxmoxVE/v1/defaultContent/manifest.json b/plugins/ProxmoxVE/v1/defaultContent/manifest.json new file mode 100644 index 0000000..3989746 --- /dev/null +++ b/plugins/ProxmoxVE/v1/defaultContent/manifest.json @@ -0,0 +1,8 @@ +{ + "items": [ + { "name": "overviewDashboard", "type": "dashboard" }, + { "name": "nodeDashboard", "type": "dashboard" }, + { "name": "containerDashboard", "type": "dashboard" }, + { "name": "vmDashboard", "type": "dashboard" } + ] +} diff --git a/plugins/ProxmoxVE/v1/defaultContent/nodeDashboard.dash.json b/plugins/ProxmoxVE/v1/defaultContent/nodeDashboard.dash.json new file mode 100644 index 0000000..be9aacd --- /dev/null +++ b/plugins/ProxmoxVE/v1/defaultContent/nodeDashboard.dash.json @@ -0,0 +1,248 @@ +{ + "name": "Node", + "schemaVersion": "1.5", + "timeframe": "last24hours", + "variables": ["{{variables.[Node]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "17833f5b-6ce3-4cce-8e9d-06add1e81e97", + "x": 0, + "y": 0, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Node Details", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[nodeStatus]}}", + "name": "nodeStatus", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Nodes]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Node]}}" + }, + "variables": ["{{variables.[Node]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true + } + } + } + } + }, + { + "i": "d75441be-4fce-4755-877e-0555eaac35b0", + "x": 2, + "y": 0, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "CPU Usage", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[nodeMetrics]}}", + "name": "nodeMetrics", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Nodes]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Node]}}" + }, + "variables": ["{{variables.[Node]}}"], + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "time", + "yAxisColumn": ["cpu"], + "seriesColumn": "none", + "showLegend": true, + "legendPosition": "bottom" + } + } + } + } + }, + { + "i": "e7415941-2a36-4198-a5a5-672403484e4f", + "x": 0, + "y": 2, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Memory", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[nodeMetrics]}}", + "name": "nodeMetrics", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Nodes]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Node]}}" + }, + "variables": ["{{variables.[Node]}}"], + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "time", + "yAxisColumn": ["memused", "memtotal"], + "seriesColumn": "none", + "showLegend": true, + "legendPosition": "bottom" + } + } + } + } + }, + { + "i": "0cfde06a-4da9-43c2-a6a0-b20e2b63e18a", + "x": 2, + "y": 2, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Network", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[nodeMetrics]}}", + "name": "nodeMetrics", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Nodes]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Node]}}" + }, + "variables": ["{{variables.[Node]}}"], + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "time", + "yAxisColumn": ["netin", "netout"], + "seriesColumn": "none", + "showLegend": true, + "legendPosition": "bottom" + } + } + } + } + }, + { + "i": "1ef61f7f-9d88-4cfe-b2d9-a547669cd536", + "x": 0, + "y": 4, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Updates by Priority", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[nodeUpdates]}}", + "name": "nodeUpdates", + "pluginConfigId": "{{configId}}", + "group": { + "by": [["Priority", "uniqueValues"]], + "aggregate": [{ "type": "count" }] + } + }, + "scope": { + "scope": "{{scopes.[Nodes]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Node]}}" + }, + "variables": ["{{variables.[Node]}}"], + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "labelColumn": "Priority_uniqueValues", + "valueColumn": "count", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + } + } + }, + { + "i": "87ec6ada-ca2b-4425-9788-401d969af525", + "x": 2, + "y": 4, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Pending Updates", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[nodeUpdates]}}", + "name": "nodeUpdates", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Nodes]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Node]}}" + }, + "variables": ["{{variables.[Node]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["Package", "OldVersion", "Version", "Priority", "Origin", "Description"] + } + } + } + } + } + ] + } +} diff --git a/plugins/ProxmoxVE/v1/defaultContent/overviewDashboard.dash.json b/plugins/ProxmoxVE/v1/defaultContent/overviewDashboard.dash.json new file mode 100644 index 0000000..ffe2042 --- /dev/null +++ b/plugins/ProxmoxVE/v1/defaultContent/overviewDashboard.dash.json @@ -0,0 +1,134 @@ +{ + "name": "Overview", + "schemaVersion": "1.5", + "timeframe": "last24hours", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "5f800755-0fcb-41f6-8f73-f5e2729588c7", + "x": 0, + "y": 0, + "w": 4, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Cluster Resources", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[clusterResources]}}", + "name": "clusterResources", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["name", "sourceType", "node", "status", "cpu", "mem", "maxmem", "uptime"] + } + } + } + } + }, + { + "i": "26c4f206-a9e0-42fd-bc98-b1e3243e13f6", + "x": 0, + "y": 3, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Node Status", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[clusterResources]}}", + "name": "clusterResources", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { "column": "sourceType", "operation": "equals", "value": "Proxmox Node" } + ] + }, + "group": { + "by": [["status", "uniqueValues"]], + "aggregate": [{ "type": "count" }] + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "status_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + } + } + }, + { + "i": "3ea21d34-8a92-40bc-87c4-a944c22bf095", + "x": 2, + "y": 3, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Container Status", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[clusterResources]}}", + "name": "clusterResources", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { "column": "sourceType", "operation": "equals", "value": "Proxmox Container" } + ] + }, + "group": { + "by": [["status", "uniqueValues"]], + "aggregate": [{ "type": "count" }] + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "status_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + } + } + } + ] + } +} diff --git a/plugins/ProxmoxVE/v1/defaultContent/scopes.json b/plugins/ProxmoxVE/v1/defaultContent/scopes.json new file mode 100644 index 0000000..96f8e6c --- /dev/null +++ b/plugins/ProxmoxVE/v1/defaultContent/scopes.json @@ -0,0 +1,38 @@ +[ + { + "name": "Nodes", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Proxmox Node"] } + }, + "variable": { + "name": "Node", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Containers", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Proxmox Container"] } + }, + "variable": { + "name": "Container", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "VMs", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Proxmox VM"] } + }, + "variable": { + "name": "VM", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + } +] diff --git a/plugins/ProxmoxVE/v1/defaultContent/vmDashboard.dash.json b/plugins/ProxmoxVE/v1/defaultContent/vmDashboard.dash.json new file mode 100644 index 0000000..c1d3ee9 --- /dev/null +++ b/plugins/ProxmoxVE/v1/defaultContent/vmDashboard.dash.json @@ -0,0 +1,166 @@ +{ + "name": "VM", + "schemaVersion": "1.5", + "timeframe": "last24hours", + "variables": ["{{variables.[VM]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "f34bc061-249a-4082-a09a-ae4bfae5247c", + "x": 0, + "y": 0, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "VM Details", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[vmStatus]}}", + "name": "vmStatus", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[VMs]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[VM]}}" + }, + "variables": ["{{variables.[VM]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true + } + } + } + } + }, + { + "i": "e2f04978-f3c8-44cc-a1fe-197d17dd3dea", + "x": 2, + "y": 0, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "CPU Usage", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[vmMetrics]}}", + "name": "vmMetrics", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[VMs]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[VM]}}" + }, + "variables": ["{{variables.[VM]}}"], + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "time", + "yAxisColumn": ["cpu"], + "seriesColumn": "none", + "showLegend": true, + "legendPosition": "bottom" + } + } + } + } + }, + { + "i": "cb0c3d59-3001-43a2-9778-2674a7f13393", + "x": 0, + "y": 2, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Memory", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[vmMetrics]}}", + "name": "vmMetrics", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[VMs]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[VM]}}" + }, + "variables": ["{{variables.[VM]}}"], + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "time", + "yAxisColumn": ["mem", "maxmem"], + "seriesColumn": "none", + "showLegend": true, + "legendPosition": "bottom" + } + } + } + } + }, + { + "i": "673f5bd1-ec7d-47d6-934e-f2b37e5d1475", + "x": 2, + "y": 2, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Network & Disk", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[vmMetrics]}}", + "name": "vmMetrics", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[VMs]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[VM]}}" + }, + "variables": ["{{variables.[VM]}}"], + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "time", + "yAxisColumn": ["netin", "netout", "diskread", "diskwrite"], + "seriesColumn": "none", + "showLegend": true, + "legendPosition": "bottom" + } + } + } + } + } + ] + } +} diff --git a/plugins/ProxmoxVE/v1/docs/README.md b/plugins/ProxmoxVE/v1/docs/README.md new file mode 100644 index 0000000..0953c35 --- /dev/null +++ b/plugins/ProxmoxVE/v1/docs/README.md @@ -0,0 +1,63 @@ +# Proxmox VE Plugin + +Monitor your Proxmox VE cluster from SquaredUp. This plugin imports nodes, virtual machines (QEMU/KVM), and LXC containers into the SquaredUp graph and provides dashboards for cluster health, resource utilisation, and per-object metrics. + +## What this plugin monitors + +**Imported objects:** +- **Proxmox Node** — physical or virtual cluster member nodes +- **Proxmox VM** — QEMU/KVM virtual machines +- **Proxmox Container** — LXC containers + +**Out-of-the-box dashboards:** +- **Overview** — cluster-wide resource summary, node health table, VM and container inventory +- **Node perspective** — per-node CPU, memory, and network metrics +- **VM perspective** — per-VM CPU, memory, network, and disk I/O metrics +- **Container perspective** — per-container CPU, memory, network, and disk I/O metrics + +## Prerequisites + +You need a Proxmox VE API token. Tokens are created per-user and can be scoped to read-only access. + +**Step 1 — Create an API token:** + +1. Log in to your Proxmox VE web UI. +2. Go to **Datacenter → Permissions → API Tokens**. +3. Click **Add**. +4. Select the user (e.g. `monitoring@pam`), enter a Token ID (e.g. `squaredup`), and uncheck **Privilege Separation** if you want the token to inherit the user's permissions. +5. Click **Add** — the token secret (UUID) is shown **once**. Copy it immediately. + +**Step 2 — Grant read permissions:** + +Assign the `PVEAuditor` role to the token at the datacenter level: + +1. Go to **Datacenter → Permissions → Add → API Token Permission**. +2. Set Path to `/`, Token to `monitoring@pam!squaredup`, Role to `PVEAuditor`. +3. Check **Propagate**. + +**Note on self-signed certificates:** Proxmox uses a self-signed certificate by default. If your Proxmox host uses a self-signed or internal CA certificate, you may need to configure your SquaredUp agent to trust it, replace the certificate with one from a trusted CA, or enable the **Ignore certificate errors** option below. + +## Configuration fields + +| Field | Description | Required | +|---|---|---| +| **Host URL** | Full URL to your Proxmox VE host, including port. Example: `https://192.168.1.100:8006` | Yes | +| **Token ID** | The full API token identifier in the format `USER@REALM!TOKENID`. Example: `monitoring@pam!squaredup` | Yes | +| **Token Secret** | The UUID secret displayed when the token was created. Example: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` | Yes | +| **Ignore certificate errors** | Skip TLS certificate validation. Enable if your Proxmox host uses a self-signed certificate you haven't configured your agent to trust. | No | + +## What gets indexed + +| Object type | Source endpoint | Identifier | +|---|---|---| +| Proxmox Node | `/cluster/resources?type=node` | Node name (e.g. `pve1`) | +| Proxmox VM | `/cluster/resources?type=vm` (filtered to `qemu`) | VM ID (e.g. `100`) | +| Proxmox Container | `/cluster/resources?type=vm` (filtered to `lxc`) | Container ID (e.g. `101`) | + +## Known limitations + +- **Write operations are not supported** — this plugin is read-only. +- **Storage metrics are not included** in v1. +- **RRD time-series data** uses Proxmox's fixed timeframe windows (`hour`, `day`, `week`, `month`) which are mapped to SquaredUp's timeframe picker. Data granularity is pre-aggregated by Proxmox (not raw per-second data). +- **Self-signed TLS** — Proxmox ships with a self-signed certificate. If requests fail with TLS errors, replace the certificate or configure your agent to trust the Proxmox CA. +- **Rate limits** — Proxmox VE does not enforce strict API rate limits, but excessive polling may impact cluster performance. diff --git a/plugins/ProxmoxVE/v1/icon.svg b/plugins/ProxmoxVE/v1/icon.svg new file mode 100644 index 0000000..a4e660a --- /dev/null +++ b/plugins/ProxmoxVE/v1/icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/ProxmoxVE/v1/indexDefinitions/default.json b/plugins/ProxmoxVE/v1/indexDefinitions/default.json new file mode 100644 index 0000000..0473080 --- /dev/null +++ b/plugins/ProxmoxVE/v1/indexDefinitions/default.json @@ -0,0 +1,18 @@ +{ + "steps": [ + { + "name": "clusterResources", + "dataStream": { "name": "clusterResources" }, + "timeframe": "none", + "objectMapping": { + "id": "sourceId", + "name": "name", + "type": "sourceType", + "properties": [ + "node", + "vmid" + ] + } + } + ] +} diff --git a/plugins/ProxmoxVE/v1/metadata.json b/plugins/ProxmoxVE/v1/metadata.json new file mode 100644 index 0000000..7ba9b55 --- /dev/null +++ b/plugins/ProxmoxVE/v1/metadata.json @@ -0,0 +1,41 @@ +{ + "name": "proxmox-ve", + "displayName": "Proxmox VE", + "version": "1.2.3", + "author": { "name": "@iBePixel", "type": "community" }, + "description": "Monitor your Proxmox VE cluster — import nodes, VMs, and LXC containers and track resource utilisation metrics.", + "category": "Infrastructure", + "type": "hybrid", + "schemaVersion": "2.1", + "importNotSupported": false, + "restrictedToPlatforms": [], + "keywords": ["proxmox", "virtualization", "kvm", "lxc", "hypervisor", "infrastructure"], + "objectTypes": ["Proxmox Node", "Proxmox VM", "Proxmox Container"], + "links": [ + { + "category": "documentation", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/ProxmoxVE/v1/docs/README.md", + "label": "Help adding this plugin" + }, + { + "category": "source", + "url": "https://github.com/squaredup/plugins", + "label": "Repository" + } + ], + "base": { + "plugin": "WebAPI", + "majorVersion": "1", + "config": { + "baseUrl": "{{host}}/api2/json", + "authMode": "none", + "headers": [ + { + "key": "Authorization", + "value": "PVEAPIToken={{tokenId}}={{tokenSecret}}" + } + ], + "queryArgs": [] + } + } +} diff --git a/plugins/ProxmoxVE/v1/ui.json b/plugins/ProxmoxVE/v1/ui.json new file mode 100644 index 0000000..3b14249 --- /dev/null +++ b/plugins/ProxmoxVE/v1/ui.json @@ -0,0 +1,32 @@ +[ + { + "name": "host", + "label": "Host URL", + "type": "url", + "help": "Full URL to your Proxmox VE host including port. Example: https://192.168.1.100:8006", + "placeholder": "https://192.168.1.100:8006", + "validation": { "required": true } + }, + { + "name": "tokenId", + "label": "Token ID", + "type": "text", + "help": "API token identifier in the format USER@REALM!TOKENID. Example: monitoring@pam!squaredup", + "placeholder": "monitoring@pam!squaredup", + "validation": { "required": true } + }, + { + "name": "tokenSecret", + "label": "Token Secret", + "type": "password", + "help": "The UUID secret displayed when the API token was created.", + "placeholder": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "validation": { "required": true } + }, + { + "type": "checkbox", + "name": "ignoreCertificateErrors", + "label": "Ignore certificate errors", + "help": "Enable when connecting to a Proxmox VE instance with a self-signed certificate." + } +]