feat: Add IP address and DNS fields to module_vmware table
- Introduced `ip` and `dns` columns to the `module_vmware` table to store assigned IP addresses and DNS configurations. - Implemented `ChangePasswordViaSshJob` for secure password changes via SSH. - Created `ChangePasswordJob` for handling password changes during VM provisioning. - Developed `ChangePasswordViaVsphereJob` to reset VM guest OS passwords using PowerShell scripts executed on the vSphere host. - Added `SendVmCredentialsEmailJob` to send final VM credentials to customers. - Implemented `WaitVmReadyJob` to poll vSphere API until VMware Tools are running and guest IP is available. - Created `VmProvisionService` for managing VM provisioning tasks, including password generation and updates. - Developed `VsphereHostSshService` for executing PowerShell scripts on the vSphere host via SSH. - Added `IpPoolSeeder` to populate IP pools for VM service initialization.
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Jobs\VirtualMachine\Provision;
|
||||
|
||||
use App\Http\Facade\SignRSA;
|
||||
use App\Models\ModuleVmware;
|
||||
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;
|
||||
|
||||
/**
|
||||
* ChangePasswordViaVsphereJob
|
||||
*
|
||||
* Resets the VM guest OS password via:
|
||||
* Laravel → SSH (10.10.10.173) → PowerShell PS1 → Invoke-VMScript (guest OS)
|
||||
*
|
||||
* Flow:
|
||||
* [1] VM already exists, vm_id known
|
||||
* [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, dispatch email
|
||||
* [8] 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 set during VM creation
|
||||
$provision->updatePassword($this->moduleVmwareId, $newPassword);
|
||||
|
||||
// [8] Send credentials email (loads fresh VM data from DB)
|
||||
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]);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* 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}",
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user