Add VM rebuild pipeline with isolated job chain and admin log API
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\VirtualMachine\Rebuild;
|
||||
|
||||
use App\DTOs\VirtualMachine\Rebuild\RebuildContext;
|
||||
use App\Http\Facade\SignRSA;
|
||||
use App\Models\ModuleVmware;
|
||||
use App\Models\ReinstallLog;
|
||||
use App\Models\vCenter as VCenterModel;
|
||||
use App\Services\VsphereHostSshService;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* VmRebuildPasswordService
|
||||
*
|
||||
* Generates a new random password for the rebuilt VM and pushes it through
|
||||
* VsphereHostSshService::changePassword(). Uses the same low-level SSH helper
|
||||
* as the create flow but never dispatches ChangePasswordViaVsphereJob.
|
||||
*
|
||||
* The new credential is persisted on the EXISTING ModuleVmware row — the
|
||||
* service identity is preserved across rebuild.
|
||||
*/
|
||||
final class VmRebuildPasswordService
|
||||
{
|
||||
public function __construct(private readonly VsphereHostSshService $ssh)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $reinstallLogId If supplied, the customer-supplied
|
||||
* password (if any) is pulled from this
|
||||
* log row and the same row is updated
|
||||
* with the credential that was applied.
|
||||
* @return string the plaintext password actually applied
|
||||
*/
|
||||
public function rotate(RebuildContext $ctx, ?int $reinstallLogId = null): string
|
||||
{
|
||||
if ($ctx->newVmId === null) {
|
||||
throw new RuntimeException('Rebuild password: newVmId is null');
|
||||
}
|
||||
|
||||
$vcenter = VCenterModel::find($ctx->vcenterId);
|
||||
if (!$vcenter) {
|
||||
throw new RuntimeException("Rebuild password: vCenter #{$ctx->vcenterId} not found");
|
||||
}
|
||||
|
||||
// Prefer a customer-supplied credential when one was provided.
|
||||
$newPassword = null;
|
||||
$log = $reinstallLogId ? ReinstallLog::find($reinstallLogId) : null;
|
||||
if ($log && $log->password_supplied_by_customer && $log->password) {
|
||||
try {
|
||||
$newPassword = SignRSA::DecryptFunc($log->password);
|
||||
} catch (\Throwable $e) {
|
||||
$newPassword = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_string($newPassword) || $newPassword === '') {
|
||||
$newPassword = $this->generatePassword(16);
|
||||
}
|
||||
|
||||
$output = $this->ssh->changePassword(
|
||||
$ctx->newVmId,
|
||||
$newPassword,
|
||||
$ctx->osFamily,
|
||||
(string) $vcenter->host,
|
||||
(string) $vcenter->user,
|
||||
SignRSA::DecryptFunc($vcenter->password),
|
||||
);
|
||||
|
||||
if (!str_contains(strtoupper(trim($output)), 'SUCCESS')) {
|
||||
throw new RuntimeException(
|
||||
"Rebuild password: changePassword script failed — " . trim($output),
|
||||
);
|
||||
}
|
||||
|
||||
$encrypted = SignRSA::EncryptFunc($newPassword);
|
||||
|
||||
ModuleVmware::where('id', $ctx->moduleVmwareId)->update([
|
||||
'password' => $encrypted,
|
||||
'plain_password' => $newPassword,
|
||||
]);
|
||||
|
||||
if ($log) {
|
||||
$log->update([
|
||||
'password' => $encrypted,
|
||||
'plain_password' => $newPassword,
|
||||
]);
|
||||
}
|
||||
|
||||
Log::info('VmRebuildPasswordService: password rotated', [
|
||||
'module_vmware_id' => $ctx->moduleVmwareId,
|
||||
'new_vm_id' => $ctx->newVmId,
|
||||
'reinstall_log_id' => $log?->id,
|
||||
]);
|
||||
|
||||
return $newPassword;
|
||||
}
|
||||
|
||||
private function generatePassword(int $length): string
|
||||
{
|
||||
$sets = [
|
||||
'ABCDEFGHJKLMNPQRSTUVWXYZ',
|
||||
'abcdefghjkmnpqrstuvwxyz',
|
||||
'23456789',
|
||||
'!@#$*',
|
||||
];
|
||||
$password = '';
|
||||
$allChars = implode('', $sets);
|
||||
foreach ($sets as $set) {
|
||||
$password .= $set[random_int(0, strlen($set) - 1)];
|
||||
}
|
||||
for ($i = count($sets); $i < $length; $i++) {
|
||||
$password .= $allChars[random_int(0, strlen($allChars) - 1)];
|
||||
}
|
||||
$chars = str_split($password);
|
||||
for ($i = count($chars) - 1; $i > 0; $i--) {
|
||||
$j = random_int(0, $i);
|
||||
[$chars[$i], $chars[$j]] = [$chars[$j], $chars[$i]];
|
||||
}
|
||||
return implode('', $chars);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user