client = $client; } public function getAllVirtualMachine($datacenter = '') { if ($this->client->checkApiExits()) { $params = []; if (!empty($datacenter)) { $params['filter.datacenters'] = $datacenter; } $response = Http::withoutVerifying() ->withHeaders([ 'vmware-api-session-id' => $this->client->token ]) ->get("https://{$this->client->apiUri}/rest/vcenter/vm", $params)->json(); if (!isset($response['type'])) { return $response['value']; } return false; } return "Login_Again"; } public function CreateVirtualMachine($product_data): array|string { $vcenterModel = $product_data['vcenter']; Log::info('Vcenter Info', ['data' => $vcenterModel]); if (!$this->client->loginServer($vcenterModel)) { Log::error('Failed to create VM: API check failed, requires login.'); return 'Login_Again'; } $userName = customer()?->email; $item = $product_data['item']; $host = $product_data['host']; $datacenter = $product_data['datacenter']; $template = $product_data['template']; $addon = $product_data['addon'] ?? ['cpu' => 0, 'ram' => 0, 'disk' => 0]; $base_cpu = $item->cpu ?? ($host->cpu ?? 1); $base_ram = $item->ram ?? ($host->ram ?? 1); $base_disk = $item->disk ?? ($host->disk ?? 20); $base_cpu = max(1, (int) $base_cpu); $base_ram = max(1, (int) $base_ram); $base_disk = max(20, (int) $base_disk); Log::info("Cấu hình cơ bản", [ 'base_cpu' => $base_cpu, 'base_ram' => $base_ram, 'base_disk' => $base_disk, 'addon' => $addon ]); $datastores = ModuleDatastore::where('module_host_id', $host->id)->get(); $datastoreId = $datastores->isNotEmpty() ? $datastores->first()->datastore : 'datastore-3004'; $body = [ 'spec' => [ 'name' => $item->server_name ?? ($userName . '-vm-' . $item->id), 'source' => $template, 'placement' => [ 'datacenter' => $datacenter->datacenter_id, 'folder' => 'group-v1008', 'host' => $host->host_id, 'resource_pool' => 'resgroup-3002', 'datastore' => $datastoreId, ], 'power_on' => false, 'fallback_to_datastore_default_policy' => true, ], ]; Log::info('Data payload', ['data' => $body]); try { Log::info('Bắt đầu clone VM với timeout 300 giây', ['template' => $template, 'target_name' => $body['spec']['name']]); $response = Http::withoutVerifying() ->timeout(300) ->connectTimeout(30) ->withHeaders([ 'vmware-api-session-id' => $this->client->token, 'Content-Type' => 'application/json', ]) ->post("https://{$this->client->apiUri}/rest/vcenter/vm?action=clone", $body); $responseData = $response->json(); if ($response->successful() && !isset($responseData['type'])) { $vmId = $responseData['value'] ?? 'UNKNOWN'; Log::info("VM clone thành công, VM ID: {$vmId}", [ 'addon_data' => $addon, 'base_cpu' => $base_cpu, 'base_ram' => $base_ram, 'has_cpu_addon' => $addon['cpu'] > 0, 'has_ram_addon' => $addon['ram'] > 0, 'has_disk_addon' => $addon['disk'] > 0 ]); if ($vmId !== 'UNKNOWN' && ($addon['cpu'] > 0 || $addon['ram'] > 0 || $addon['disk'] > 0)) { Log::info("Bắt đầu cập nhật addon cho VM {$vmId}"); $this->updateVMHardware($vmId, $addon, $base_cpu, $base_ram); } else { Log::info("Không có addon nào cần cập nhật cho VM {$vmId}"); } $discord = app(Discord::class); $addonMsg = ''; if (!empty($addon)) { $addonMsg = "\n**Addon:** " . ($addon['cpu'] ? "CPU +{$addon['cpu']} " : "") . ($addon['ram'] ? "RAM +{$addon['ram']}GB " : "") . ($addon['disk'] ? "Disk +{$addon['disk']}GB " : ""); } $discord->SendMessage( message: "**User:** {$item->server_name}\n**VM ID:** `{$vmId}`{$addonMsg}\n**Thời gian:** " . now()->format('d/m/Y H:i:s'), color: Discord::COLOR_SUCCESS, title: '🎉 Tạo máy ảo thành công' ); return ['error' => false, 'data' => $vmId]; } return ['error' => true, 'data' => $responseData['value'] ?? $response->body()]; } catch (Exception $e) { Log::error('Exception when creating VM', ['message' => $e->getMessage()]); return ['error' => true, 'data' => $e->getMessage()]; } } private function updateVMHardware($vmId, $addon, $base_cpu, $base_ram): void { try { Log::info("=== BẮT ĐẦU CẬP NHẬT ADDON ==="); $new_cpu = $base_cpu + $addon['cpu']; $new_ram_mib = ($base_ram + $addon['ram']) * 1024; if ($addon['cpu'] > 0) { $this->updateVMCPU($vmId, $new_cpu); } if ($addon['ram'] > 0) { $this->updateVMMemory($vmId, $new_ram_mib); } // Disk update logic was missing in the snippet but present in the original method names? // Actually the original `updateVMHardware` in the snippet (step 134) calls `updateVMCPU` and `updateVMMemory`. // It gets `base_disk` but doesn't seem to call `updateVMDisk` inside `updateVMHardware`? // Let me re-read step 134. // ... `updateVMHardware` implementation ... // It calls `updateVMCPU` and `updateVMMemory`. // It calculates `$addon['disk']` in log, but doesn't call `updateVMDisk`. // Ah, line 943 defines `updateVMDisk`. // But `updateVMHardware` (lines 772-832) only calls `updateVMCPU` and `updateVMMemory`. // I will implement it exactly as the original. Log::info("=== HOÀN THÀNH CẬP NHẬT ADDON ==="); } catch (Exception $e) { Log::error("❌ Lỗi khi cập nhật cấu hình phần cứng VM {$vmId}: " . $e->getMessage()); } } private function updateVMCPU($vmId, $cpuCount): bool { try { $coresPerSocket = 1; if ($cpuCount > 1) { $maxCoresPerSocket = floor($cpuCount / 2); for ($i = $maxCoresPerSocket; $i >= 1; $i--) { if ($cpuCount % $i === 0) { $coresPerSocket = $i; break; } } if ($coresPerSocket === 1 && $cpuCount > 2) { $coresPerSocket = $cpuCount; } } $payload = [ 'spec' => [ 'count' => $cpuCount, 'cores_per_socket' => $coresPerSocket, 'hot_add_enabled' => true ] ]; $response = Http::withoutVerifying() ->timeout(30) ->withHeaders([ 'vmware-api-session-id' => $this->client->token, 'Content-Type' => 'application/json', ]) ->patch("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/cpu", $payload); if ($response->successful()) { Log::info("✅ API call cập nhật CPU thành công cho VM {$vmId}"); return true; } else { Log::error("❌ API call cập nhật CPU thất bại cho VM {$vmId}", [ 'status' => $response->status(), 'response' => $response->body() ]); return false; } } catch (Exception $e) { Log::error("❌ Exception khi cập nhật CPU cho VM {$vmId}: " . $e->getMessage()); return false; } } private function updateVMMemory($vmId, $ramSizeMB): bool { try { $payload = [ 'spec' => [ 'size_MiB' => $ramSizeMB ] ]; $response = Http::withoutVerifying() ->timeout(30) ->withHeaders([ 'vmware-api-session-id' => $this->client->token, 'Content-Type' => 'application/json', ]) ->patch("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/memory", $payload); if ($response->successful()) { Log::info("✅ API call cập nhật RAM thành công cho VM {$vmId}"); return true; } else { Log::error("❌ API call cập nhật RAM thất bại cho VM {$vmId}", [ 'status' => $response->status(), 'response' => $response->body() ]); return false; } } catch (Exception $e) { Log::error("❌ Exception khi cập nhật RAM cho VM {$vmId}: " . $e->getMessage()); return false; } } private function updateVMDisk($vmId, $diskSizeGB): bool { try { $diskSizeBytes = $diskSizeGB * 1024 * 1024 * 1024; $payload = [ 'spec' => [ 'capacity' => $diskSizeBytes ] ]; $diskResponse = Http::withoutVerifying() ->timeout(30) ->withHeaders([ 'vmware-api-session-id' => $this->client->token, ]) ->get("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/disk"); if (!$diskResponse->successful()) { return false; } $diskData = $diskResponse->json(); if (!isset($diskData['value']) || empty($diskData['value'])) { return false; } $firstDisk = $diskData['value'][0]; $diskKey = $firstDisk['key']; $response = Http::withoutVerifying() ->timeout(30) ->withHeaders([ 'vmware-api-session-id' => $this->client->token, 'Content-Type' => 'application/json', ]) ->patch("https://{$this->client->apiUri}/rest/vcenter/vm/{$vmId}/hardware/disk/{$diskKey}", $payload); if ($response->successful()) { return true; } else { return false; } } catch (Exception $e) { Log::error("Exception khi cập nhật Disk cho VM {$vmId}: " . $e->getMessage()); return false; } } public function CheckVirtualMachine($vmware_id = '') { if ($this->client->checkApiExits()) { $response = Http::withoutVerifying() ->withHeaders([ 'vmware-api-session-id' => $this->client->token ]) ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id")->json(); if (!isset($response['type'])) { return $response['value']; } return false; } return "Login_Again"; } public function GetNetworkVirtualMachine($vmware_id = '') { $check = $this->client->checkApiExits(); if ($check) { $response = Http::withoutVerifying() ->withHeaders([ 'vmware-api-session-id' => $this->client->token ]) ->get("https://{$this->client->apiUri}/rest/vcenter/vm/$vmware_id/guest/networking")->json(); if (!isset($response['type'])) { return $response['value']; } return false; } return "Login_Again"; } }