diff --git a/AppImage/components/virtual-machines.tsx b/AppImage/components/virtual-machines.tsx index 75abecd..ac4f415 100644 --- a/AppImage/components/virtual-machines.tsx +++ b/AppImage/components/virtual-machines.tsx @@ -10,7 +10,6 @@ import { Server, Play, Square, - Monitor, Cpu, MemoryStick, HardDrive, @@ -94,6 +93,38 @@ const formatBytes = (bytes: number | undefined): string => { return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}` } +const formatUptime = (seconds: number) => { + const days = Math.floor(seconds / 86400) + const hours = Math.floor((seconds % 86400) / 3600) + const minutes = Math.floor((seconds % 3600) / 60) + return `${days}d ${hours}h ${minutes}m` +} + +const extractIPFromConfig = (config?: VMConfig): string => { + if (!config) return "DHCP" + + // Check net0, net1, net2, etc. + for (let i = 0; i < 10; i++) { + const netKey = `net${i}` + const netConfig = config[netKey] + + if (netConfig && typeof netConfig === "string") { + // Look for ip=x.x.x.x/xx pattern + const ipMatch = netConfig.match(/ip=([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})(\/\d+)?/) + if (ipMatch) { + return ipMatch[1] // Return just the IP without CIDR + } + + // Check if it's explicitly DHCP + if (netConfig.includes("ip=dhcp")) { + return "DHCP" + } + } + } + + return "DHCP" +} + export function VirtualMachines() { const { data: vmData, @@ -218,13 +249,6 @@ export function VirtualMachines() { return { color: "bg-purple-500/10 text-purple-500 border-purple-500/20", label: "VM" } } - const formatUptime = (seconds: number) => { - const days = Math.floor(seconds / 86400) - const hours = Math.floor((seconds % 86400) / 3600) - const minutes = Math.floor((seconds % 3600) / 60) - return `${days}d ${hours}h ${minutes}m` - } - // Safe data handling with default empty array const safeVMData = vmData || [] @@ -327,448 +351,429 @@ export function VirtualMachines() { - - Average Load - + + + + Virtual Machines & Containers + -
- {safeVMData.filter((vm) => vm.status === "running").length > 0 - ? ( - (safeVMData.reduce((sum, vm) => sum + (vm.cpu || 0), 0) / - safeVMData.filter((vm) => vm.status === "running").length) * - 100 - ).toFixed(0) - : 0} - % -
-

Average resource utilization

+ {safeVMData.length === 0 ? ( +
No virtual machines found
+ ) : ( +
+ {safeVMData.map((vm) => { + const cpuPercent = (vm.cpu * 100).toFixed(1) + const memPercent = vm.maxmem > 0 ? ((vm.mem / vm.maxmem) * 100).toFixed(1) : "0" + const memGB = (vm.mem / 1024 ** 3).toFixed(1) + const maxMemGB = (vm.maxmem / 1024 ** 3).toFixed(1) + const typeBadge = getTypeBadge(vm.type) + const vmIP = extractIPFromConfig(vm.config) + + return ( +
handleVMClick(vm)} + > +
+
+ +
+
+ {vm.name} + + {typeBadge.label} + +
+
ID: {vm.vmid}
+
IP: {vmIP}
+
+
+ + + {getStatusIcon(vm.status)} + {vm.status.toUpperCase()} + +
+ +
+
+
CPU Usage
+
{cpuPercent}%
+ +
+ +
+
Memory Usage
+
+ {memGB} / {maxMemGB} GB +
+ +
+ +
+
Disk I/O
+
+
+ + ↓ {formatBytes(vm.diskread)} +
+
+ + ↑ {formatBytes(vm.diskwrite)} +
+
+
+ +
+
Network I/O
+
+
+ + ↓ {formatBytes(vm.netin)} +
+
+ + ↑ {formatBytes(vm.netout)} +
+
+
+
+ +
+
Uptime
+
{formatUptime(vm.uptime)}
+
+
+ ) + })} +
+ )}
- - {/* Virtual Machines List */} - - - - - Virtual Machines & Containers - - - - {safeVMData.length === 0 ? ( -
No virtual machines found
- ) : ( -
- {safeVMData.map((vm) => { - const cpuPercent = (vm.cpu * 100).toFixed(1) - const memPercent = vm.maxmem > 0 ? ((vm.mem / vm.maxmem) * 100).toFixed(1) : "0" - const memGB = (vm.mem / 1024 ** 3).toFixed(1) - const maxMemGB = (vm.maxmem / 1024 ** 3).toFixed(1) - const typeBadge = getTypeBadge(vm.type) - - return ( -
handleVMClick(vm)} - > -
-
- -
-
- {vm.name} - - {typeBadge.label} - -
-
ID: {vm.vmid}
-
-
- - - {getStatusIcon(vm.status)} - {vm.status.toUpperCase()} - -
- -
-
-
CPU Usage
-
{cpuPercent}%
- -
- -
-
Memory Usage
-
- {memGB} / {maxMemGB} GB -
- -
- -
-
Disk I/O
-
-
- - ↓ {formatBytes(vm.diskread)} -
-
- - ↑ {formatBytes(vm.diskwrite)} -
-
-
- -
-
Network I/O
-
-
- - ↓ {formatBytes(vm.netin)} -
-
- - ↑ {formatBytes(vm.netout)} -
-
-
-
- -
-
Uptime
-
{formatUptime(vm.uptime)}
-
+ {/* VM Details Modal */} + { + setSelectedVM(null) + setVMDetails(null) + }} + > + + + +
+ + {selectedVM?.name} +
+ {selectedVM && ( +
+ + {getTypeBadge(selectedVM.type).label} + + + {selectedVM.status.toUpperCase()} +
- ) - })} -
- )} - - + )} + + - {/* VM Details Modal */} - { - setSelectedVM(null) - setVMDetails(null) - }} - > - - - -
- - {selectedVM?.name} -
+
{selectedVM && ( -
- - {getTypeBadge(selectedVM.type).label} - - - {selectedVM.status.toUpperCase()} - -
- )} - - - -
- {selectedVM && ( - <> -
-

- Basic Information -

-
-
-
Name
-
{selectedVM.name}
-
-
-
VMID
-
{selectedVM.vmid}
-
-
-
CPU Usage
-
80 - ? "text-red-500" - : selectedVM.cpu * 100 > 60 - ? "text-yellow-500" - : "text-green-500" - }`} - > - {(selectedVM.cpu * 100).toFixed(1)}% + <> +
+

+ Basic Information +

+
+
+
Name
+
{selectedVM.name}
-
-
-
Memory
-
80 - ? "text-red-500" - : (selectedVM.mem / selectedVM.maxmem) * 100 > 60 - ? "text-yellow-500" - : "text-blue-500" - }`} - > - {(selectedVM.mem / 1024 ** 3).toFixed(1)} / {(selectedVM.maxmem / 1024 ** 3).toFixed(1)} GB +
+
VMID
+
{selectedVM.vmid}
-
-
-
Disk
-
- {(selectedVM.disk / 1024 ** 3).toFixed(1)} / {(selectedVM.maxdisk / 1024 ** 3).toFixed(1)} GB -
-
-
-
Uptime
-
{formatUptime(selectedVM.uptime)}
-
-
-
Disk I/O
-
-
- ↓ {formatBytes(selectedVM.diskread)} -
-
- ↑ {formatBytes(selectedVM.diskwrite)} +
+
CPU Usage
+
80 + ? "text-red-500" + : selectedVM.cpu * 100 > 60 + ? "text-yellow-500" + : "text-green-500" + }`} + > + {(selectedVM.cpu * 100).toFixed(1)}%
-
-
-
Network I/O
-
-
- ↓ {formatBytes(selectedVM.netin)} +
+
Memory
+
80 + ? "text-red-500" + : (selectedVM.mem / selectedVM.maxmem) * 100 > 60 + ? "text-yellow-500" + : "text-blue-500" + }`} + > + {(selectedVM.mem / 1024 ** 3).toFixed(1)} / {(selectedVM.maxmem / 1024 ** 3).toFixed(1)} GB
-
- ↑ {formatBytes(selectedVM.netout)} +
+
+
Disk
+
+ {(selectedVM.disk / 1024 ** 3).toFixed(1)} / {(selectedVM.maxdisk / 1024 ** 3).toFixed(1)} GB +
+
+
+
Uptime
+
{formatUptime(selectedVM.uptime)}
+
+
+
Disk I/O
+
+
+ ↓ {formatBytes(selectedVM.diskread)} +
+
+ ↑ {formatBytes(selectedVM.diskwrite)} +
+
+
+
+
Network I/O
+
+
+ ↓ {formatBytes(selectedVM.netin)} +
+
+ ↑ {formatBytes(selectedVM.netout)} +
-
- {/* Resources Configuration */} - {detailsLoading ? ( -
Loading configuration...
- ) : vmDetails?.config ? ( - <> -
-

- Resources -

-
- {vmDetails.config.cores && ( -
-
CPU Cores
-
{vmDetails.config.cores}
-
- )} - {vmDetails.config.sockets && ( -
-
CPU Sockets
-
{vmDetails.config.sockets}
-
- )} - {vmDetails.config.memory && ( -
-
Memory
-
{vmDetails.config.memory} MB
-
- )} - {vmDetails.config.swap && ( -
-
Swap
-
{vmDetails.config.swap} MB
-
- )} - {vmDetails.config.rootfs && ( -
-
Root Filesystem
-
- {vmDetails.config.rootfs} + {/* Resources Configuration */} + {detailsLoading ? ( +
Loading configuration...
+ ) : vmDetails?.config ? ( + <> +
+

+ Resources +

+
+ {vmDetails.config.cores && ( +
+
CPU Cores
+
{vmDetails.config.cores}
-
- )} - {Object.keys(vmDetails.config) - .filter((key) => key.match(/^(scsi|sata|ide|virtio)\d+$/)) - .map((diskKey) => ( -
-
- {diskKey.toUpperCase().replace(/(\d+)/, " $1")} -
+ )} + {vmDetails.config.sockets && ( +
+
CPU Sockets
+
{vmDetails.config.sockets}
+
+ )} + {vmDetails.config.memory && ( +
+
Memory
+
{vmDetails.config.memory} MB
+
+ )} + {vmDetails.config.swap && ( +
+
Swap
+
{vmDetails.config.swap} MB
+
+ )} + {vmDetails.config.rootfs && ( +
+
Root Filesystem
- {vmDetails.config[diskKey]} + {vmDetails.config.rootfs}
- ))} + )} + {Object.keys(vmDetails.config) + .filter((key) => key.match(/^(scsi|sata|ide|virtio)\d+$/)) + .map((diskKey) => ( +
+
+ {diskKey.toUpperCase().replace(/(\d+)/, " $1")} +
+
+ {vmDetails.config[diskKey]} +
+
+ ))} +
-
- {/* Network Configuration */} -
-

- Network -

-
- {Object.keys(vmDetails.config) - .filter((key) => key.match(/^net\d+$/)) - .map((netKey) => ( -
-
- Network Interface {netKey.replace("net", "")} -
-
- {vmDetails.config[netKey]} + {/* Network Configuration */} +
+

+ Network +

+
+ {Object.keys(vmDetails.config) + .filter((key) => key.match(/^net\d+$/)) + .map((netKey) => ( +
+
+ Network Interface {netKey.replace("net", "")} +
+
+ {vmDetails.config[netKey]} +
+ ))} + {vmDetails.config.nameserver && ( +
+
DNS Nameserver
+
{vmDetails.config.nameserver}
- ))} - {vmDetails.config.nameserver && ( -
-
DNS Nameserver
-
{vmDetails.config.nameserver}
-
- )} - {vmDetails.config.searchdomain && ( -
-
Search Domain
-
{vmDetails.config.searchdomain}
-
- )} - {vmDetails.config.hostname && ( -
-
Hostname
-
{vmDetails.config.hostname}
-
- )} + )} + {vmDetails.config.searchdomain && ( +
+
Search Domain
+
{vmDetails.config.searchdomain}
+
+ )} + {vmDetails.config.hostname && ( +
+
Hostname
+
{vmDetails.config.hostname}
+
+ )} +
-
- {/* Options */} -
-

- Options -

-
- {vmDetails.config.onboot !== undefined && ( -
-
Start on Boot
- - {vmDetails.config.onboot ? "Yes" : "No"} - -
- )} - {vmDetails.config.unprivileged !== undefined && ( -
-
Unprivileged
- - {vmDetails.config.unprivileged ? "Yes" : "No"} - -
- )} - {vmDetails.config.ostype && ( -
-
OS Type
-
{vmDetails.config.ostype}
-
- )} - {vmDetails.config.arch && ( -
-
Architecture
-
{vmDetails.config.arch}
-
- )} - {vmDetails.config.boot && ( -
-
Boot Order
-
{vmDetails.config.boot}
-
- )} - {vmDetails.config.features && ( -
-
Features
-
{vmDetails.config.features}
-
- )} + {/* Options */} +
+

+ Options +

+
+ {vmDetails.config.onboot !== undefined && ( +
+
Start on Boot
+ + {vmDetails.config.onboot ? "Yes" : "No"} + +
+ )} + {vmDetails.config.unprivileged !== undefined && ( +
+
Unprivileged
+ + {vmDetails.config.unprivileged ? "Yes" : "No"} + +
+ )} + {vmDetails.config.ostype && ( +
+
OS Type
+
{vmDetails.config.ostype}
+
+ )} + {vmDetails.config.arch && ( +
+
Architecture
+
{vmDetails.config.arch}
+
+ )} + {vmDetails.config.boot && ( +
+
Boot Order
+
{vmDetails.config.boot}
+
+ )} + {vmDetails.config.features && ( +
+
Features
+
{vmDetails.config.features}
+
+ )} +
-
- - ) : null} + + ) : null} - {/* Control Actions */} -
-

- Control Actions -

-
- - - - + {/* Control Actions */} +
+

+ Control Actions +

+
+ + + + +
-
- - )} -
- -
+ + )} +
+ + + ) }