Files

137 lines
4.8 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\Services\vCenter as VCenterService;
use App\Services\VmProvisionService;
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;
/**
* WaitVmReadyJob
*
* Polls vSphere REST API every 15 s until VMware Tools is running
* and the guest IP is available. No direct TCP port check is required
* because password changes are performed via Invoke-VMScript (vSphere
* path), not direct SSH/WinRM into the VM.
*
* Max 40 attempts × 15 s ≈ 10 minutes before giving up.
* On success → dispatches ChangePasswordViaVsphereJob.
*/
class WaitVmReadyJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** 40 attempts × 15 s release delay ≈ 10 min total */
public int $tries = 40;
/** Each poll attempt must finish within 30 s */
public int $timeout = 30;
public function __construct(
public readonly int $moduleVmwareId,
public readonly string $vmId,
public readonly int $vcenterId,
public readonly string $osFamily, // VmProvisionService::OS_LINUX | OS_WINDOWS
public readonly string $username, // root | Administrator
) {}
// -------------------------------------------------------------------------
public function handle(VCenterService $vCenterService, VmProvisionService $provision): void
{
$attempt = $this->attempts();
Log::info('WaitVmReadyJob: polling', [
'vm_id' => $this->vmId,
'attempt' => $attempt,
'os_family' => $this->osFamily,
]);
// ── 1. Login to vCenter ───────────────────────────────────────────────
$vcenter = \App\Models\vCenter::find($this->vcenterId);
if (!$vcenter) {
Log::error('WaitVmReadyJob: vCenter model not found', ['vcenter_id' => $this->vcenterId]);
$this->fail(new \RuntimeException("vCenter id={$this->vcenterId} not found"));
return;
}
try {
$provision->loginVCenter($vcenter, $vCenterService);
} catch (\Throwable $e) {
Log::warning('WaitVmReadyJob: vCenter login failed, will retry', [
'vm_id' => $this->vmId,
'error' => $e->getMessage(),
]);
$this->releaseOrFail(15, $attempt);
return;
}
// ── 2. Check VMware Tools + guest IP ─────────────────────────────────
// Invoke-VMScript requires VMware Tools to be running inside the guest.
$guest = $provision->getVmGuestInfo($vCenterService, $this->vmId);
Log::info('WaitVmReadyJob: guest info', [
'vm_id' => $this->vmId,
'attempt' => $attempt,
'tools_running' => $guest['tools_running'],
'ip_address' => $guest['ip_address'],
]);
if (!$guest['tools_running'] || $guest['ip_address'] === null) {
Log::info('WaitVmReadyJob: VMware Tools not ready yet', ['vm_id' => $this->vmId]);
$this->releaseOrFail(15, $attempt);
return;
}
$guestIp = $guest['ip_address'];
// ── 3. VM ready — dispatch password-change job ────────────────────────
Log::info('WaitVmReadyJob: VMware Tools ready, dispatching ChangePasswordViaVsphereJob', [
'vm_id' => $this->vmId,
'guest_ip' => $guestIp,
]);
ChangePasswordViaVsphereJob::dispatch(
$this->moduleVmwareId,
$this->vmId,
$this->osFamily,
);
}
// -------------------------------------------------------------------------
public function failed(\Throwable $e): void
{
Log::error('WaitVmReadyJob: exceeded max attempts, marking VM failed', [
'vm_id' => $this->vmId,
'module_vmware_id' => $this->moduleVmwareId,
'error' => $e->getMessage(),
]);
\App\Models\ModuleVmware::where('id', $this->moduleVmwareId)
->update(['status' => 0]);
}
// -------------------------------------------------------------------------
private function releaseOrFail(int $delaySeconds, int $attempt): void
{
if ($attempt >= $this->tries) {
$this->fail(new \RuntimeException(
"VM {$this->vmId} not ready after {$this->tries} attempts (~10 min)",
));
return;
}
$this->release($delaySeconds);
}
}