for('UpdateVmConfigJob'); $logger->step('UpdateVmConfigJob started', [ 'module_vmware_id' => $this->moduleVmwareId, 'vm_id' => $this->vmId, ]); $moduleVmware = ModuleVmware::with(['order.orderItems.product', 'vcenter'])->find($this->moduleVmwareId); if (!$moduleVmware) { Log::error('UpdateVmConfigJob: ModuleVmware not found', [ 'module_vmware_id' => $this->moduleVmwareId, 'vm_id' => $this->vmId, ]); $this->nextStep(); return; } $orderItem = $moduleVmware->order?->orderItems?->first(); $product = $orderItem?->product; $addonsMeta = is_array($orderItem?->addons_meta) ? $orderItem->addons_meta : []; [$addonCpu, $addonRamGb] = $this->resolveAddonCpuRam($addonsMeta); if ($addonCpu === 0 && $addonRamGb === 0) { $logger->step('no cpu/ram addon — skipping UpdateVmConfigJob', [ 'module_vmware_id' => $this->moduleVmwareId, 'vm_id' => $this->vmId, ]); $this->nextStep(); return; } // Load vCenter credentials — same pattern as ChangePasswordViaVsphereJob $vcenter = $moduleVmware->vcenter ?? vCenter::first(); if (!$vcenter) { $msg = "UpdateVmConfigJob: no vCenter record found for server [{$moduleVmware->name}]"; Log::error($msg, ['module_vmware_id' => $this->moduleVmwareId, 'server_name' => $moduleVmware->name]); $telegram->SendMessage("🚨 UpdateVmConfigJob: Lỗi\n⚠️ Không tìm thấy vCenter\n🖥️ Server: {$moduleVmware->name}\n🕒 " . Carbon::now()->format('d/m/Y H:i:s')); throw new \RuntimeException($msg); } $vcenterHost = $vcenter->host; $vcenterUser = $vcenter->user; $vcenterPass = SignRSA::DecryptFunc($vcenter->password); $serverName = (string) $moduleVmware->name; $totalCpu = ((int) ($product?->cpu ?? 0)) + $addonCpu; $totalRam = ((int) ($product?->ram ?? 0)) + $addonRamGb; $logger->step('calling updateVMConfig.ps1', [ 'module_vmware_id' => $this->moduleVmwareId, 'server_name' => $serverName, 'vcenter_host' => $vcenterHost, 'addon_cpu' => $addonCpu, 'addon_ram_gb' => $addonRamGb, 'total_cpu' => $totalCpu, 'total_ram_gb' => $totalRam, ]); $output = $ssh->updateVmConfig( $serverName, $totalCpu, $totalRam, $vcenterHost, $vcenterUser, $vcenterPass, ); Log::info('UpdateVmConfigJob: PS1 output', [ 'module_vmware_id' => $this->moduleVmwareId, 'server_name' => $serverName, 'output' => trim($output), ]); if (!str_contains(strtoupper(trim($output)), 'SUCCESS')) { $msg = "updateVMConfig.ps1 failed for server [{$serverName}]. Output: " . trim($output); Log::error('UpdateVmConfigJob: script returned FAIL', [ 'module_vmware_id' => $this->moduleVmwareId, 'server_name' => $serverName, 'total_cpu' => $totalCpu, 'total_ram_gb' => $totalRam, 'output' => trim($output), 'attempt' => $this->attempts(), 'max_tries' => $this->tries, ]); $telegram->SendMessage( "⚠️ UpdateVmConfigJob thất bại\n" . "🖥️ Server: {$serverName}\n" . "🔁 Lần thử: {$this->attempts()}/{$this->tries}\n" . "💻 CPU: {$totalCpu} | RAM: {$totalRam} GB\n" . "📋 Output: " . htmlspecialchars(trim($output) ?: '(trống)') . "\n" . "🕒 " . Carbon::now()->format('d/m/Y H:i:s') ); throw new \RuntimeException($msg); } $logger->success('VM config updated via addon script', [ 'module_vmware_id' => $this->moduleVmwareId, 'vm_id' => $this->vmId, 'total_cpu' => $totalCpu, 'total_ram_gb' => $totalRam, 'output' => trim($output), ]); $this->nextStep(); } public function failed(\Throwable $e): void { Log::error('UpdateVmConfigJob: all retries exhausted', [ 'module_vmware_id' => $this->moduleVmwareId, 'vm_id' => $this->vmId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); ModuleVmware::where('id', $this->moduleVmwareId)->update(['status' => 0]); Service::where('vm_id', $this->moduleVmwareId)->update(['status' => 'failed']); try { $telegram = app(Telegram::class); $vm = ModuleVmware::with(['order.customer'])->find($this->moduleVmwareId); $customer = $vm?->order?->customer; $telegram->SendMessage( "🚨 UpdateVmConfigJob: Thất bại hoàn toàn\n" . "━━━━━━━━━━━━━━━━━━━━\n" . "🖥️ VM ID: {$this->vmId}\n" . "📦 Server: " . ($vm?->name ?? 'N/A') . "\n" . "👤 Khách hàng: " . ($customer ? trim($customer->firstname . ' ' . $customer->lastname) : 'N/A') . "\n" . "📧 Email: " . ($customer?->email ?? 'N/A') . "\n" . "❌ Lỗi: " . htmlspecialchars($e->getMessage()) . "\n" . "⚡ Trạng thái: VM đã bị đánh dấu inactive\n" . "🕒 " . Carbon::now()->format('d/m/Y H:i:s') . "\n" . "━━━━━━━━━━━━━━━━━━━━\n" . "⚠️ Vui lòng kiểm tra và xử lý thủ công!" ); } catch (\Throwable $te) { Log::warning('UpdateVmConfigJob: could not send Telegram failure notification', [ 'error' => $te->getMessage(), ]); } } // ───────────────────────────────────────────────────────────────────────── /** * Resolve cpu/ram qty from addons_meta. * Uses 'code' field saved by OrderService::resolveAddons() — no extra DB query needed. * Falls back to Addon::find() if 'code' is missing (legacy meta without code). * * @param array $addonsMeta * @return array{int, int} [cpu_count, ram_gb] */ private function resolveAddonCpuRam(array $addonsMeta): array { $cpu = 0; $ram = 0; foreach ($addonsMeta as $meta) { $qty = (int) ($meta['qty'] ?? 0); $code = strtolower((string) ($meta['code'] ?? '')); // Fallback: look up code from DB if not in meta (old orders) if ($code === '' && isset($meta['addon_id'])) { $addon = Addon::find($meta['addon_id']); $code = strtolower((string) ($addon?->code ?? '')); } if ($code === 'cpu') { $cpu += $qty; } elseif ($code === 'ram') { $ram += $qty; } } return [$cpu, $ram]; } private function nextStep(): void { ChangePasswordViaVsphereJob::dispatch( $this->moduleVmwareId, $this->vmId, $this->osFamily, ); } }