Files
portal-api/app/Jobs/VirtualMachine/Provision/ChangePasswordViaVsphereJob.php
baodev0andClaude Sonnet 5.5 55a834a2f8 feat: nhắc gia hạn QR theo dịch vụ, failover server hosting, CCCD admin, log cấp phát dịch vụ
- Mail campaign: mẫu nhắc gia hạn thanh toán QR, chọn dịch vụ + chu kỳ từng dòng (renewal_services)
- Hosting: nhiều server Plesk cho một gói (service_plan_servers), chuyển server khi server chết
- Admin xem/sửa giấy tờ định danh khách, tuỳ chọn gia hạn cho khách, nhật ký cấp phát dịch vụ
- Đánh dấu mẫu email dịch vụ là non-marketing

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 18:36:41 +07:00

142 lines
5.4 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\Jobs\VirtualMachine\Provision;
use App\Http\Facade\SignRSA;
use App\Models\ModuleVmware;
use App\Models\Service;
use App\Models\vCenter;
use App\Services\VmProvisionService;
use App\Services\VsphereHostSshService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use App\Support\ServiceProvisionLog;
/**
* ChangePasswordViaVsphereJob
*
* Resets the VM guest OS password via:
* Laravel → SSH (10.10.10.173) → PowerShell PS1 → Invoke-VMScript (guest OS)
*
* Dispatched by AssignIpToGuestJob after IP is confirmed. Flow:
* [1] VM already exists, vm_id known, IP already assigned
* [2] Generate random 16-char password (plain text)
* [3] Load vCenter credentials from module_vcenter
* [4] SSH into vSphere host
* [5] Execute changePassword{Linux|Windows}.ps1 with 5 params
* [6] Parse "SUCCESS" / "FAIL" from script output
* [7] On SUCCESS → save encrypted password to DB
* [8] Dispatch SendVmCredentialsEmailJob (IP + password both ready)
* [9] On FAIL → throw exception → job retries
*/
class ChangePasswordViaVsphereJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 5;
public int $timeout = 180;
public int $backoff = 60;
public function __construct(
public readonly int $moduleVmwareId,
public readonly string $vmId,
public readonly string $osFamily, // VmProvisionService::OS_LINUX | OS_WINDOWS
) {}
// ─────────────────────────────────────────────────────────────────────────
public function handle(VmProvisionService $provision, VsphereHostSshService $ssh): void
{
Log::info('ChangePasswordViaVsphereJob: starting', [
'module_vmware_id' => $this->moduleVmwareId,
'vm_id' => $this->vmId,
'os_family' => $this->osFamily,
]);
// [2] Generate plain-text password (not hashed)
$newPassword = $provision->generatePassword(16);
// [3] Load vCenter credentials from database
$vcenter = vCenter::first();
if (!$vcenter) {
throw new \RuntimeException('No vCenter record found in module_vcenter');
}
$vcenterHost = $vcenter->host;
$vcenterUser = $vcenter->user;
$vcenterPass = SignRSA::DecryptFunc($vcenter->password);
// [4–5] SSH → vSphere host → run PS1 with 5 params
$output = $ssh->changePassword(
$this->vmId,
$newPassword,
$this->osFamily,
$vcenterHost,
$vcenterUser,
$vcenterPass,
);
// [6] Parse output — throw on FAIL so job retries
$this->assertSuccess($output);
// [7] Save encrypted + plain password (IP already assigned by AssignIpToGuestJob)
$provision->updatePassword($this->moduleVmwareId, $newPassword);
// Provisioning complete — activate the service record
Service::where('vm_id', $this->moduleVmwareId)->update(['status' => 'active']);
ServiceProvisionLog::forVm($this->moduleVmwareId, 'provision_step', 'Đã đặt mật khẩu máy chủ');
// [8] Send credentials email — IP + new password are both ready at this point
SendVmCredentialsEmailJob::dispatch($this->moduleVmwareId);
Log::info('ChangePasswordViaVsphereJob: completed', [
'module_vmware_id' => $this->moduleVmwareId,
'vm_id' => $this->vmId,
]);
}
// ─────────────────────────────────────────────────────────────────────────
public function failed(\Throwable $e): void
{
Log::error('ChangePasswordViaVsphereJob: all retries exhausted', [
'module_vmware_id' => $this->moduleVmwareId,
'vm_id' => $this->vmId,
'error' => $e->getMessage(),
]);
ModuleVmware::where('id', $this->moduleVmwareId)->update(['status' => 0]);
Service::where('vm_id', $this->moduleVmwareId)->update(['status' => 'failed']);
ServiceProvisionLog::forVm($this->moduleVmwareId, 'provision_failed', 'Đặt mật khẩu máy chủ thất bại — ' . $e->getMessage(), ['stage' => 'set_password']);
}
// ─────────────────────────────────────────────────────────────────────────
/**
* PS1 scripts output exactly "SUCCESS" or "FAIL".
* Throw RuntimeException on FAIL → triggers job retry.
*
* @throws \RuntimeException
*/
private function assertSuccess(string $output): void
{
$trimmed = trim($output);
if (str_contains(strtoupper($trimmed), 'SUCCESS')) {
return;
}
throw new \RuntimeException(
"PS1 script returned failure for VM {$this->vmId}. Output: {$trimmed}",
);
}
}