From 5d6e2060a874fbfcaa2262f95bd79c345aa2f86c Mon Sep 17 00:00:00 2001 From: Pham Bao Date: Tue, 29 Jul 2025 23:49:33 +0700 Subject: [PATCH] update assign ip windows --- app/Http/Services/vCenter.php | 67 ++++++++++++++----- .../VirtualMachine/Create/CheckIPAddress.php | 44 +++++++++++- .../Create/CreateVirtualMachineJob.php | 6 +- .../VirtualMachine/ProcessOrderPaymentJob.php | 43 ++++++++++-- 4 files changed, 132 insertions(+), 28 deletions(-) diff --git a/app/Http/Services/vCenter.php b/app/Http/Services/vCenter.php index bed3268..b8a3323 100644 --- a/app/Http/Services/vCenter.php +++ b/app/Http/Services/vCenter.php @@ -456,9 +456,11 @@ class vCenter 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(120) - ->connectTimeout(120) + ->timeout(300) // Tăng timeout lên 5 phút cho quá trình clone + ->connectTimeout(30) // Giữ connect timeout ngắn để phát hiện lỗi kết nối sớm ->withHeaders([ 'vmware-api-session-id' => $this->token, 'Content-Type' => 'application/json', @@ -2130,29 +2132,60 @@ class vCenter public function assignIPWindows($product, $vmware_id = ''): array|string { try { - // Kiểm tra null safety - if (!$product || !$product->ipAddress || !$product->ipAddress->host || !$product->ipAddress->host->datacenter || !$product->ipAddress->host->datacenter->vCenter) { + // Support both old Product structure and new ModuleVmware structure + $ipAddress = null; + $gateway = null; + $netmask = '255.255.255.0'; + $dns = '8.8.8.8'; + $vmware_name = ''; + $vCenter = null; + + // Check if it's ModuleVmware (new structure) + if ($product && isset($product->ipAddress) && $product->ipAddress) { + Log::info('✅ Using ModuleVmware structure for assignIPWindows', [ + 'module_vmware_id' => $product->id, + 'vmware_id' => $vmware_id + ]); + + // Extract from ModuleVmware->ipAddress relationship + $ipAddress = $product->ipAddress->ip ?? null; + $gateway = $product->ipAddress->gateway ?? null; + $netmask = $product->ipAddress->netmask ?? '255.255.255.0'; + $dns = $product->ipAddress->dns ?? '8.8.8.8'; + $vmware_name = $product->name ?? $vmware_id; + $vCenter = $product->ipAddress->host->datacenter->vCenter ?? null; + + } elseif ($product && isset($product->IpAddress) && $product->IpAddress) { + Log::info('✅ Using old Product structure for assignIPWindows', [ + 'product_id' => $product->id, + 'vmware_id' => $vmware_id + ]); + + // Extract from old Product->IpAddress relationship + $ipAddress = $product->IpAddress->ip ?? null; + $gateway = $product->IpAddress->gateway ?? null; + $netmask = $product->IpAddress->netmask ?? '255.255.255.0'; + $dns = $product->IpAddress->dns ?? '8.8.8.8'; + $vmware_name = $product->name ?? $vmware_id; + $vCenter = $product->IpAddress->Host->Datacenter->vCenter ?? null; + } + + // Validate required data + if (!$ipAddress || !$gateway || !$vCenter) { Log::error('❌ assignIPWindows: Missing required data', [ 'vmware_id' => $vmware_id, - 'product_id' => $product?->id ?? 'unknown', - 'ip_address' => $product?->ipAddress?->ip ?? 'unknown', - 'host' => $product?->ipAddress?->host?->name ?? 'unknown', - 'datacenter' => $product?->ipAddress?->host?->datacenter?->name ?? 'unknown', - 'vcenter' => $product?->ipAddress?->host?->datacenter?->vCenter?->host ?? 'unknown' + 'ip_address' => $ipAddress, + 'gateway' => $gateway, + 'has_vcenter' => $vCenter ? 'yes' : 'no', + 'product_class' => get_class($product) ]); return [ 'error' => true, - 'data' => 'Missing required data for IP assignment' + 'data' => 'Missing required data for IP assignment (IP, Gateway, or vCenter)' ]; } - $this->SetConfig($product->ipAddress->host->datacenter->vCenter); - - $vmware_name = (string) $product->name; - $ipAddress = $product->ipAddress->ip; - $gateway = $product->ipAddress->gateway; - $netmask = $product->ipAddress->netmask ?? '255.255.255.0'; - $dns = $product->ipAddress->dns ?? '8.8.8.8'; + $this->SetConfig($vCenter); // Convert netmask to prefix length (e.g., 255.255.255.0 = 24) $maskBits = $this->convertNetmaskToPrefixLength($netmask); diff --git a/app/Jobs/VirtualMachine/Create/CheckIPAddress.php b/app/Jobs/VirtualMachine/Create/CheckIPAddress.php index e77fa66..ce7624d 100644 --- a/app/Jobs/VirtualMachine/Create/CheckIPAddress.php +++ b/app/Jobs/VirtualMachine/Create/CheckIPAddress.php @@ -44,8 +44,48 @@ class CheckIPAddress implements ShouldQueue throw new \Exception(json_encode($DetailVirtualMachine['message'])); } - if (!$DetailVirtualMachine['data']['ip_address'] === $this->data->IpAddress->ip) { - throw new \Exception(json_encode($DetailVirtualMachine['message'])); + // Get expected IP address with null safety + $expectedIP = null; + + // Try to get IP from old relationship first + if ($this->data && isset($this->data->IpAddress) && $this->data->IpAddress) { + $expectedIP = $this->data->IpAddress->ip; + } + + // If no IP from old relationship, try to get from ModuleVmware + if (!$expectedIP && $this->data) { + // Try to find ModuleVmware by vmware_id + $moduleVmware = \App\Models\ModuleVmware::with('ipAddress') + ->where('vmware_id', $this->vmware_id) + ->first(); + + if ($moduleVmware && $moduleVmware->ipAddress) { + $expectedIP = $moduleVmware->ipAddress->ip; + } + } + + // If still no expected IP, log warning but don't fail + if (!$expectedIP) { + \Illuminate\Support\Facades\Log::warning('⚠️ Không tìm thấy expected IP cho VM', [ + 'vmware_id' => $this->vmware_id, + 'data_class' => $this->data ? get_class($this->data) : 'null', + 'has_ip_address_relation' => isset($this->data->IpAddress) && $this->data->IpAddress ? 'yes' : 'no' + ]); + + // Skip IP check and continue to next job + $jobs_configDB = new ConfigDatabase($this->data, $this->info); + dispatch($jobs_configDB)->delay(now()->addSeconds(5)); + return; + } + + // Compare actual vs expected IP + if ($DetailVirtualMachine['data']['ip_address'] !== $expectedIP) { + \Illuminate\Support\Facades\Log::warning('⚠️ IP address mismatch', [ + 'vmware_id' => $this->vmware_id, + 'actual_ip' => $DetailVirtualMachine['data']['ip_address'], + 'expected_ip' => $expectedIP + ]); + throw new \Exception('IP address mismatch: actual=' . $DetailVirtualMachine['data']['ip_address'] . ', expected=' . $expectedIP); } $jobs_configDB = new ConfigDatabase($this->data,$this->info); dispatch($jobs_configDB)->delay(now()->addSeconds(5)); diff --git a/app/Jobs/VirtualMachine/Create/CreateVirtualMachineJob.php b/app/Jobs/VirtualMachine/Create/CreateVirtualMachineJob.php index 5929b57..992a4b1 100644 --- a/app/Jobs/VirtualMachine/Create/CreateVirtualMachineJob.php +++ b/app/Jobs/VirtualMachine/Create/CreateVirtualMachineJob.php @@ -247,11 +247,7 @@ class CreateVirtualMachineJob implements ShouldQueue 'customer_id' => $customer->id, 'status' => 1 ]); - - // 4. Cập nhật product với module_vmware_id - $product->module_vmware_id = $moduleVmware->id; - $product->save(); - + Log::info('✅ Lưu dữ liệu VM và IP address thành công', [ 'vmware_id' => $vmId, 'module_vmware_id' => $moduleVmware->id, diff --git a/app/Jobs/VirtualMachine/ProcessOrderPaymentJob.php b/app/Jobs/VirtualMachine/ProcessOrderPaymentJob.php index c9d9e47..dd868f0 100644 --- a/app/Jobs/VirtualMachine/ProcessOrderPaymentJob.php +++ b/app/Jobs/VirtualMachine/ProcessOrderPaymentJob.php @@ -163,11 +163,18 @@ class ProcessOrderPaymentJob implements ShouldQueue // Thử lại nếu chưa vượt quá số lần thử if ($this->retryCount < self::MAX_RETRIES) { - Log::warning("Có lỗi khi tạo VM, thử lại lần " . ($this->retryCount + 1), $errorDetails); + // Kiểm tra nếu lỗi là timeout, tăng delay time + $isTimeoutError = $this->isTimeoutError($failedVMs); + $delayMinutes = $isTimeoutError ? 10 : 5; // Timeout thì chờ 10 phút, lỗi khác chờ 5 phút + + Log::warning("Có lỗi khi tạo VM, thử lại lần " . ($this->retryCount + 1), array_merge($errorDetails, [ + 'is_timeout_error' => $isTimeoutError, + 'delay_minutes' => $delayMinutes + ])); - // Thử lại sau 5 phút + // Thử lại với delay tùy theo loại lỗi self::dispatch($this->order, $this->retryCount + 1) - ->delay(now()->addMinutes(5)); + ->delay(now()->addMinutes($delayMinutes)); } else { // Đã thử hết số lần, gửi thông báo lỗi Log::error("Đã thử tạo VM {$this->retryCount} lần nhưng vẫn thất bại", $errorDetails); @@ -183,9 +190,14 @@ class ProcessOrderPaymentJob implements ShouldQueue } } catch (\Throwable $e) { + $isTimeoutException = strpos(strtolower($e->getMessage()), 'timeout') !== false || + strpos(strtolower($e->getMessage()), 'curl error 28') !== false; + Log::error("Lỗi trong ProcessOrderPaymentJob: " . $e->getMessage(), [ 'order_id' => $this->order->id, 'order_num' => $this->order->order_num, + 'is_timeout' => $isTimeoutException, + 'retry_count' => $this->retryCount, 'trace' => $e->getTraceAsString() ]); @@ -194,7 +206,9 @@ class ProcessOrderPaymentJob implements ShouldQueue 'order_id' => $this->order->id, 'order_num' => $this->order->order_num, 'file' => $e->getFile(), - 'line' => $e->getLine() + 'line' => $e->getLine(), + 'is_timeout' => $isTimeoutException, + 'retry_count' => $this->retryCount ], 'ProcessOrderPaymentJob - Exception'); } } @@ -243,6 +257,27 @@ class ProcessOrderPaymentJob implements ShouldQueue } } + /** + * Check if the error is a timeout error + */ + private function isTimeoutError(array $failedVMs): bool + { + foreach ($failedVMs as $vmResult) { + if (isset($vmResult['error'])) { + $errorMessage = strtolower($vmResult['error']); + + // Kiểm tra các dấu hiệu của timeout error + if (strpos($errorMessage, 'timeout') !== false || + strpos($errorMessage, 'curl error 28') !== false || + strpos($errorMessage, 'operation timed out') !== false || + strpos($errorMessage, 'connection timeout') !== false) { + return true; + } + } + } + return false; + } + /** * Send failure notification to customer */