diff --git a/extensions/Servers/Proxmox/Livewire/Component.php b/extensions/Servers/Proxmox/Livewire/Component.php index 3707cf448..32997a9c1 100644 --- a/extensions/Servers/Proxmox/Livewire/Component.php +++ b/extensions/Servers/Proxmox/Livewire/Component.php @@ -127,6 +127,7 @@ class Component extends BaseComponent /** * Calculate total storage from VM config (including unused/detached drives). + * Uses the same calculation method as the Storage page. */ protected function calculateTotalStorage(array $config): float { @@ -159,66 +160,93 @@ class Component extends BaseComponent /** * Check if a disk should be excluded (cloud-init, EFI, TPM). + * Uses regex patterns matching the Storage page implementation. */ protected function isExcludedDisk(string $value): bool { - $excludePatterns = ['cloudinit', 'efidisk', 'tpmstate']; + $excludePatterns = [ + 'cloudinit', + 'vm-\d+-cloudinit', + 'efidisk', + 'tpmstate', + ]; + foreach ($excludePatterns as $pattern) { - if (str_contains(strtolower($value), $pattern)) { + if (preg_match('/' . $pattern . '/i', $value)) { return true; } } + return false; } /** * Extract disk size in GB from a disk configuration string. + * Matches the Storage page parseDiskString logic. */ protected function extractDiskSize(string $value): float { - // Example: cephpool:vm-100-disk-0,cache=writeback,size=50G - if (preg_match('/size=(\d+)([GMTK])?/i', $value, $matches)) { - $size = (float) $matches[1]; - $unit = strtoupper($matches[2] ?? 'G'); - - return match ($unit) { - 'T' => $size * 1024, - 'G' => $size, - 'M' => $size / 1024, - 'K' => $size / 1024 / 1024, - default => $size, - }; + // Example: cephpool:vm-100-disk-0,size=32G,cache=none,ssd=1 + $parts = explode(',', $value); + + foreach ($parts as $part) { + if (preg_match('/^size=(\d+)([KMGT]?)$/i', $part, $sizeMatch)) { + $size = (float) $sizeMatch[1]; + $unit = strtoupper($sizeMatch[2] ?? 'G'); + + switch ($unit) { + case 'K': + return $size / (1024 * 1024); + case 'M': + return $size / 1024; + case 'G': + case '': + return $size; + case 'T': + return $size * 1024; + } + } } + return 0; } /** - * Extract size from unused disk by querying Proxmox. + * Extract size from unused disk by querying Proxmox storage content. + * Matches the Storage page parseUnusedDiskString logic. */ protected function extractUnusedDiskSize(string $value): float { - // For unused disks, we need to query the volume info - // Format: storage:volume (e.g., cephpool:vm-100-disk-1) + // Unused disks typically just have the volume reference + // Example: cephpool:vm-100-disk-1 + $volume = trim($value); + + // Extract storage name from volume (format: storage:volume-name) + $storageName = 'cephpool'; + if (str_contains($volume, ':')) { + [$storageName, ] = explode(':', $volume, 2); + } + try { - if (!preg_match('/^([^:]+):(.+)$/', $value, $matches)) { - return 0; - } - - $storage = $matches[1]; - $volume = $matches[2]; - $proxmox = new Proxmox; - $response = $proxmox->request( - '/nodes/' . $this->server->node->name . '/storage/' . $storage . '/content/' . $storage . ':' . $volume + $storageContent = $proxmox->request( + '/nodes/' . $this->server->node->name . '/storage/' . $storageName . '/content', + data: ['vmid' => $this->server->vm_id] ); - - if ($response->successful()) { - $data = $response->json()['data'] ?? []; - $sizeBytes = $data['size'] ?? 0; - return $sizeBytes / 1024 / 1024 / 1024; // Convert to GB + + if (isset($storageContent['data']) && is_array($storageContent['data'])) { + foreach ($storageContent['data'] as $item) { + if (isset($item['volid']) && $item['volid'] === $volume) { + // Size is in bytes + if (isset($item['size'])) { + return round($item['size'] / (1024 * 1024 * 1024), 2); + } + break; + } + } } } catch (\Throwable $e) { - // Ignore errors, return 0 + // If we can't fetch size, return 0 } return 0;