item = $item; $this->isRetry = $isRetry; } /** * Execute the job. */ public function handle(vCenter $vCenter, Telegram $telegram) { try { $product = $this->item->getProductAttribute(); if (!$product) { $errorMessage = "Không tìm thấy sản phẩm từ OrderItem #{$this->item->id}"; Log::warning($errorMessage); $telegram->SendErrorMessage($errorMessage, 'CreateVirtualMachineJob - Missing Product'); if (!$this->isRetry) { $this->sendFailureNotification($errorMessage); $this->writeCustomerFailureLog($errorMessage); } return false; } $osModel = Os::with('osGroup.host.datacenter.vcenter')->find($this->item->os_id); if (!$osModel) { $errorMessage = "Không tìm thấy OS id={$this->item->os_id} trong OrderItem #{$this->item->id}. os_id không hợp lệ."; Log::error($errorMessage); $telegram->SendErrorMessage([ 'error' => $errorMessage, 'os_id' => $this->item->os_id, 'order_item_id' => $this->item->id, 'product_id' => $product->id, ], 'CreateVirtualMachineJob - Invalid OS ID'); if (!$this->isRetry) { $this->sendFailureNotification($errorMessage); $this->writeCustomerFailureLog($errorMessage); } return; } $template = $osModel->template_id; $host = $osModel->osGroup?->host; // từ os_group.module_host_id $datacenter = $host?->datacenter; $vcenter = $datacenter?->vcenter; if (!$vcenter || !$host || !$datacenter) { $errorMessage = "Thiếu thông tin host/datacenter/vcenter từ sản phẩm ID #{$product->id}"; Log::error($errorMessage); $telegram->SendErrorMessage([ 'error' => $errorMessage, 'product_id' => $product->id, 'order_item_id' => $this->item->id, 'host_id' => $host?->id, 'datacenter_id' => $datacenter?->id, 'vcenter_id' => $vcenter?->id ], 'CreateVirtualMachineJob - Missing Infrastructure'); if (!$this->isRetry) { $this->sendFailureNotification($errorMessage); $this->writeCustomerFailureLog($errorMessage); } return; } // Login to vCenter if (!$vCenter->loginServer($vcenter)) { $errorMessage = "Login vào vCenter thất bại với host: {$vcenter->host}"; Log::error($errorMessage); $telegram->SendErrorMessage([ 'error' => $errorMessage, 'vcenter_host' => $vcenter->host, 'vcenter_user' => $vcenter->user, 'product_id' => $product->id, 'order_item_id' => $this->item->id ], 'CreateVirtualMachineJob - vCenter Login Failed'); if (!$this->isRetry) { $this->sendFailureNotification($errorMessage); $this->writeCustomerFailureLog($errorMessage); } return false; } // Lấy thông tin addon (CPU, RAM) $addonData = [ 'cpu' => 0, 'ram' => 0, 'disk' => 0 ]; // Lấy addon từ order item if ($this->item->addons) { foreach ($this->item->addons as $orderAddon) { $addon = $orderAddon->addonItems; if (!$addon) continue; $addonName = strtolower(trim($addon->name)); $quantity = (int) $orderAddon->qty; switch ($addonName) { case 'cpu': $addonData['cpu'] += $quantity; break; case 'ram': $addonData['ram'] += $quantity; break; case 'disk': case 'ssd': $addonData['disk'] += $quantity; break; } } } $result = $vCenter->CreateVirtualMachine([ 'item' => $this->item, 'host' => $host, 'datacenter' => $datacenter, 'vcenter' => $vcenter, 'template' => $template, 'addon' => $addonData, ]); if (is_array($result) && ($result['error'] ?? false)) { $errorMessage = 'Tạo máy ảo thất bại: ' . json_encode($result); Log::warning($errorMessage); $telegram->SendErrorMessage([ 'error' => $result['error'] ?? 'Unknown error', 'result' => $result, 'product_id' => $product->id, 'order_item_id' => $this->item->id, 'host_name' => $host?->name, 'datacenter_name' => $datacenter?->name, 'vcenter_host' => $vcenter?->host ], 'CreateVirtualMachineJob - VM Creation Failed'); if (!$this->isRetry) { $this->sendFailureNotification($errorMessage); $this->writeCustomerFailureLog($errorMessage); } return false; } elseif ($result === 'Login_Again') { $errorMessage = 'Tạo máy ảo thất bại: cần đăng nhập lại.'; Log::error($errorMessage); $telegram->SendErrorMessage([ 'error' => $errorMessage, 'product_id' => $product->id, 'order_item_id' => $this->item->id, 'vcenter_host' => $vcenter?->host ], 'CreateVirtualMachineJob - vCenter Re-login Required'); if (!$this->isRetry) { $this->sendFailureNotification($errorMessage); $this->writeCustomerFailureLog($errorMessage); } return false; } else { Log::info('Tạo máy ảo thành công: ' . json_encode($result)); // Lấy vm_id từ kết quả $vmId = is_array($result) ? ($result['data'] ?? 'unknown') : $result; if ($vmId !== 'unknown' && $vmId !== 'UNKNOWN') { try { // Lưu vm_id vào bảng invoice $invoice = \App\Models\Invoice::where('order_id', $this->item->order->id)->first(); if ($invoice) { $invoice->update(['vm_id' => $vmId]); } // Start VM ngay sau khi tạo thành công (sử dụng API trực tiếp) $startResult = $this->startVirtualMachineDirectly($vCenter, $vmId); if (is_array($startResult) && $startResult['error'] === false) { // Dispatch ConfigCLICommand để cấu hình VM và lấy password $configJob = new ConfigCLICommand($vmId, $this->item, $vCenter); dispatch($configJob)->delay(now()->addMinutes(2)); // Chờ 2 phút để VM boot hoàn toàn } // Lưu dữ liệu VM vào database try { $customer = $this->item->order->customer; $serverName = $this->item->server_name ?? 'VM-' . $vmId; $networkId = $product->ipAddress?->module_network_id ?? 1; $availableIP = $this->getAvailableIPFromPool($networkId); $moduleIpAddress = ModuleIpAddress::create([ 'vm_name' => $serverName, 'module_host_id' => $host->id, 'module_network_id' => $networkId, 'ip' => $availableIP, 'gateway' => $product->ipAddress?->gateway ?? '0.0.0.0', 'netmask' => $product->ipAddress?->netmask ?? '255.255.255.0', 'dns' => $product->ipAddress?->dns ?? '8.8.8.8', 'status' => 1 // 1 = Assigned ]); $moduleVmware = ModuleVmware::create([ 'name' => $serverName, 'module_ip_address_id' => $moduleIpAddress->id, 'port' => 22, 'order_id' => $this->item->order->id, 'first_payment' => $this->item->order->first_payment ?? 0, 'next_payment' => $this->item->order->next_payment ?? 0, 'next_due_date' => $this->item->order->next_due_date ?? null, 'next_invoice' => $this->item->order->next_invoice ?? 0, 'user' => 'Administrator', 'password' => '', 'hostname' => $serverName, 'vmware_id' => $vmId, 'os_id' => $this->item->os_id ?? 1, 'customer_id' => $customer->id, 'status' => 1 ]); AssignIPJob::dispatch($vmId, $moduleVmware->id, $moduleIpAddress->ip) ->delay(now()->addMinutes(5)); } catch (\Exception $e) { Log::error('❌ Lỗi khi lưu dữ liệu VM và IP address: ' . $e->getMessage(), [ 'vmware_id' => $vmId, 'order_item_id' => $this->item->id, 'error' => $e->getMessage() ]); } } catch (\Exception $e) { Log::error('❌ Lỗi khi lưu dữ liệu VM hoặc start VM: ' . $e->getMessage(), [ 'vm_id' => $vmId, 'order_id' => $this->item->order->id, 'trace' => $e->getTraceAsString() ]); } } $telegram->SendJobRunInfo( 'CreateVirtualMachineJob', 'success', [ 'product_id' => $product->id, 'order_item_id' => $this->item->id, 'result' => $result, 'addon_applied' => !empty($addonData) && array_sum($addonData) > 0, 'vm_id_saved' => $vmId !== 'unknown' && $vmId !== 'UNKNOWN', 'vm_started' => isset($startResult) && is_array($startResult) && $startResult['error'] === false ] ); $this->writeCustomerSuccessLog(); return true; } } catch (\Throwable $e) { $errorMessage = 'Lỗi khi tạo máy ảo (Job): ' . $e->getMessage(); Log::error($errorMessage, [ 'exception' => $e, 'order_item_id' => $this->item->id, 'trace' => $e->getTraceAsString() ]); $telegram->SendErrorMessage([ 'error' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'order_item_id' => $this->item->id, 'trace' => substr($e->getTraceAsString(), 0, 500) // Giới hạn độ dài trace ], 'CreateVirtualMachineJob - Exception'); if (!$this->isRetry) { $this->sendFailureNotification($errorMessage); $this->writeCustomerFailureLog($errorMessage); } return false; } } private function writeCustomerSuccessLog(): void { try { $order = $this->item->order; $customer = $order?->customer; if (!$order || !$customer) { return; } CustomerLog::create([ 'agent' => 'system', 'client_ip' => null, 'customer_id' => $customer->id, 'description' => 'Khởi tạo dịch vụ thành công cho đơn hàng #' . $order->order_num . ' (product_id: ' . $this->item->product_id . ', os_id: ' . $this->item->os_id . ').', ]); } catch (\Throwable $e) { Log::warning('Không thể ghi log thành công khởi tạo VM: ' . $e->getMessage()); } } private function writeCustomerFailureLog(string $errorMessage): void { try { $order = $this->item->order; $customer = $order?->customer; if (!$order || !$customer) { return; } CustomerLog::create([ 'agent' => 'system', 'client_ip' => null, 'customer_id' => $customer->id, 'description' => 'Khởi tạo dịch vụ thất bại cho đơn hàng #' . $order->order_num . ' (product_id: ' . $this->item->product_id . ', os_id: ' . $this->item->os_id . ').' . ' Lỗi: ' . $errorMessage . '. ' . 'Nếu có lỗi xảy ra khi khởi tạo dịch vụ quý khách nhấn khởi tạo tại đây, hoặc quý khách có thể liên hệ với kỹ thuật support@tgs.com.vn', ]); } catch (\Throwable $e) { Log::warning('Không thể ghi log thất bại khởi tạo VM: ' . $e->getMessage()); } } private function getAvailableIPFromPool(int $networkId): string { // Lấy tất cả IP từ IPPool theo network_id, order by ip_address $ipPoolList = IpPool::where('module_network_id', $networkId) ->orderBy('ip_address') ->get(); if ($ipPoolList->isEmpty()) { // Nếu không có IP trong pool, trả về IP default return '0.0.0.0'; } $assignedIPs = ModuleIpAddress::where('module_network_id', $networkId) ->pluck('ip') ->toArray(); foreach ($ipPoolList as $ipPool) { if (!in_array($ipPool->ip_address, $assignedIPs)) { return $ipPool->ip_address; } } return '0.0.0.0'; } /** * Start VM directly using vCenter API */ private function startVirtualMachineDirectly(vCenter $vCenter, string $vmId): array|string { try { // Kiểm tra xem đã có session chưa $check = $vCenter->checkApiExits(); if (!$check) { return 'Login_Again'; } // Gọi API start VM trực tiếp $response = Http::withoutVerifying() ->withHeaders([ 'vmware-api-session-id' => $vCenter->token ]) ->post("https://{$vCenter->apiUri}/rest/vcenter/vm/{$vmId}/power/start"); if ($response->status() === 200) { return [ 'error' => false, 'data' => 'success' ]; } return [ 'error' => true, 'data' => $response->body() ]; } catch (\Exception $e) { Log::error('Lỗi khi start VM: ' . $e->getMessage()); return [ 'error' => true, 'data' => $e->getMessage() ]; } } /** * Send failure notification to customer */ private function sendFailureNotification(string $errorMessage): void { try { $customer = $this->item->order->customer; $product = $this->item->getProductAttribute(); VMCreationFailedJob::dispatch([ 'customer_email' => $customer->email, 'customer_name' => trim($customer->firstname . ' ' . $customer->lastname), 'product_name' => $product?->name ?? 'Unknown Product', 'order_id' => $this->item->order->id, 'order_num' => $this->item->order->order_num, 'error_message' => $errorMessage, 'order_item_id' => $this->item->id ]); } catch (\Throwable $e) { Log::error('Không thể gửi email thông báo lỗi VM: ' . $e->getMessage()); } } }