Enhance VM provisioning logging by integrating VmProvisionLogger into CreateVirtualMachineJob and AssignIpToGuestJob; update composer dependencies for Plesk integration

This commit is contained in:
baodev0
2026-07-13 13:45:22 +07:00
parent fdb5714e93
commit d283a2c694
5 changed files with 83 additions and 33 deletions
@@ -58,6 +58,8 @@ class CreateVirtualMachineJob implements ShouldQueue
$ipPoolId = null;
$lock = null;
$vmLogger->for('CreateVirtualMachineJob');
try {
$customerId = $this->item->order->customer_id;
$lock = VmProvisionLock::updateOrCreate(
@@ -13,6 +13,7 @@ use App\Models\Service;
use App\Models\VirtualMachines;
use App\Http\Services\vCenter as vCenterService;
use App\Models\vCenter;
use App\Services\VirtualMachine\VmProvisionLogger;
use App\Services\VmProvisionService;
use App\Services\VsphereHostSshService;
use Illuminate\Bus\Queueable;
@@ -64,9 +65,11 @@ class AssignIpToGuestJob implements ShouldQueue
// ─────────────────────────────────────────────────────────────────────────
public function handle(VsphereHostSshService $ssh, vCenterService $vCenterService): void
public function handle(VsphereHostSshService $ssh, vCenterService $vCenterService, VmProvisionLogger $logger): void
{
Log::info('AssignIpToGuestJob: start', [
$logger->for('AssignIpToGuestJob');
$logger->step('start', [
'module_vmware_id' => $this->moduleVmwareId,
'vm_id' => $this->vmId,
'os_family' => $this->osFamily,
@@ -79,7 +82,7 @@ class AssignIpToGuestJob implements ShouldQueue
])->find($this->moduleVmwareId);
if (!$moduleVmware) {
Log::error('AssignIpToGuestJob: ModuleVmware not found', [
$logger->error('ModuleVmware not found', [
'module_vmware_id' => $this->moduleVmwareId,
]);
return;
@@ -110,7 +113,7 @@ class AssignIpToGuestJob implements ShouldQueue
$maxAttempts = $this->reservationId ? 1 : self::MAX_ATTEMPTS;
for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
Log::info('AssignIpToGuestJob: attempt', [
$logger->step('attempt', [
'attempt' => $attempt,
'module_vmware_id' => $this->moduleVmwareId,
'network_id' => $networkId,
@@ -119,15 +122,15 @@ class AssignIpToGuestJob implements ShouldQueue
// [3] Use upstream reservation if provided, else reserve a fresh IP.
$picked = $this->reservationId
? $this->loadReservation($this->reservationId, $this->reservedIpPoolId)
? $this->loadReservation($this->reservationId, $this->reservedIpPoolId, $logger)
: $this->reserveIp($networkId, $hostId, $dns);
if (!$picked) {
Log::error('AssignIpToGuestJob: no free IPs in pool', [
$logger->error('no free IPs in pool', [
'network_id' => $networkId,
'attempt' => $attempt,
]);
break; // No point retrying — no IPs available
break;
}
$reservation = $picked['reservation'];
@@ -153,19 +156,19 @@ class AssignIpToGuestJob implements ShouldQueue
// [5] If assign failed → release and try next IP
if (!$this->isSuccess($assignOutput)) {
Log::warning('AssignIpToGuestJob: assignIp FAIL', [
$logger->warning('assignIp FAIL', [
'ip' => $ip,
'attempt' => $attempt,
'output' => trim($assignOutput),
]);
$this->releaseIp($reservation, $ipPoolId);
$this->releaseIp($reservation, $ipPoolId, $logger);
continue;
}
if (strtoupper($this->osFamily) === VmProvisionService::OS_WINDOWS) {
$this->persistIp($reservation, $moduleVmware, $ipPoolId, $serviceId);
Log::info('AssignIpToGuestJob: Windows IP assigned', [
$logger->success('Windows IP assigned', [
'module_vmware_id' => $this->moduleVmwareId,
'vm_id' => $this->vmId,
'ip' => $ip,
@@ -195,19 +198,19 @@ class AssignIpToGuestJob implements ShouldQueue
// [7] If verification failed → release and try next IP
if (!$this->isSuccess($verifyOutput)) {
Log::warning('AssignIpToGuestJob: verifyIpInGuest FAIL', [
$logger->warning('verifyIpInGuest FAIL', [
'ip' => $ip,
'attempt' => $attempt,
'output' => trim($verifyOutput),
]);
$this->releaseIp($reservation, $ipPoolId);
$this->releaseIp($reservation, $ipPoolId, $logger);
continue;
}
// [8] SUCCESS — persist confirmed IP to module_vmware + virtual_machines + ip_pools
$this->persistIp($reservation, $moduleVmware, $ipPoolId, $serviceId);
Log::info('AssignIpToGuestJob: IP assigned and verified', [
$logger->success('IP assigned and verified', [
'module_vmware_id' => $this->moduleVmwareId,
'vm_id' => $this->vmId,
'ip' => $ip,
@@ -225,7 +228,7 @@ class AssignIpToGuestJob implements ShouldQueue
}
// [9] All attempts exhausted
Log::error('AssignIpToGuestJob: all attempts failed — marking VM inactive', [
$logger->error('all attempts failed — marking VM inactive', [
'module_vmware_id' => $this->moduleVmwareId,
'vm_id' => $this->vmId,
'attempts' => $maxAttempts,
@@ -246,7 +249,7 @@ class AssignIpToGuestJob implements ShouldQueue
$guestIp = $guestInfo['data']['ip_address'] ?? null;
if ($guestIp) {
$moduleVmware->update(['ip' => $guestIp, 'status' => 1]);
Log::info('AssignIpToGuestJob: recovered IP from guest identity', [
$logger->step('recovered IP from guest identity', [
'module_vmware_id' => $this->moduleVmwareId,
'ip' => $guestIp,
]);
@@ -255,7 +258,7 @@ class AssignIpToGuestJob implements ShouldQueue
}
}
} catch (\Throwable $e) {
Log::error('AssignIpToGuestJob: failed to recover IP after exhausted attempts', [
$logger->error('failed to recover IP after exhausted attempts', [
'module_vmware_id' => $this->moduleVmwareId,
'error' => $e->getMessage(),
]);
@@ -348,11 +351,11 @@ class AssignIpToGuestJob implements ShouldQueue
*
* @return array{reservation:ModuleIpAddress,ip_pool_id:int,gateway:string,cidr:int}|null
*/
private function loadReservation(int $reservationId, ?int $ipPoolId): ?array
private function loadReservation(int $reservationId, ?int $ipPoolId, VmProvisionLogger $logger): ?array
{
$reservation = ModuleIpAddress::find($reservationId);
if (!$reservation) {
Log::error('AssignIpToGuestJob: upstream reservation missing', [
$logger->error('upstream reservation missing', [
'reservation_id' => $reservationId,
]);
return null;
@@ -374,16 +377,16 @@ class AssignIpToGuestJob implements ShouldQueue
* Release a failed reservation by deleting the pending ModuleIpAddress row.
* This returns the IP to the available pool.
*/
private function releaseIp(ModuleIpAddress $reservation, ?int $ipPoolId): void
private function releaseIp(ModuleIpAddress $reservation, ?int $ipPoolId, VmProvisionLogger $logger): void
{
try {
$reservation->delete();
if ($ipPoolId) {
IpPool::where('id', $ipPoolId)->update(['status' => 'free']);
}
Log::info('AssignIpToGuestJob: IP reservation released', ['ip' => $reservation->ip]);
$logger->step('IP reservation released', ['ip' => $reservation->ip]);
} catch (\Throwable $e) {
Log::warning('AssignIpToGuestJob: could not release IP reservation', [
$logger->warning('could not release IP reservation', [
'ip' => $reservation->ip,
'error' => $e->getMessage(),
]);
@@ -462,13 +465,4 @@ class AssignIpToGuestJob implements ShouldQueue
return is_array($data) && ($data['status'] ?? '') === 'ok';
}
/** Convert dotted-decimal netmask to CIDR prefix length (e.g. 255.255.255.0 → 24). */
private function netmaskToCidr(string $netmask): int
{
$bits = 0;
foreach (explode('.', $netmask) as $octet) {
$bits += substr_count(decbin((int) $octet), '1');
}
return $bits;
}
}
@@ -10,7 +10,7 @@ use Throwable;
class VmProvisionLogger
{
private const JOB_NAME = 'CreateVirtualMachineJob';
private string $jobName = 'System';
private const REDACTED_KEYS = [
'password',
@@ -29,6 +29,12 @@ class VmProvisionLogger
{
}
public function for(string $jobName): static
{
$this->jobName = $jobName;
return $this;
}
public function step(string $title, array $context = []): void
{
$safe = $this->redact($context);
@@ -90,7 +96,7 @@ class VmProvisionLogger
private function jobLabel(): string
{
return self::JOB_NAME;
return $this->jobName;
}
private function redact(array $context): array
+1
View File
@@ -21,6 +21,7 @@
"nwidart/laravel-modules": "^10.0",
"opcodesio/log-viewer": "^3.18",
"phpseclib/phpseclib": "*",
"plesk/ext-laravel-integration": "^7.0",
"predis/predis": "^2.2",
"pusher/pusher-php-server": "^7.2",
"spatie/laravel-permission": "^6.3",
Generated
+48 -1
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "cfc535c27971a90c45e31a2ddd47afe3",
"content-hash": "4996106ea97079078f843b42993edb6f",
"packages": [
{
"name": "barryvdh/laravel-dompdf",
@@ -4181,6 +4181,53 @@
],
"time": "2026-06-14T23:24:10+00:00"
},
{
"name": "plesk/ext-laravel-integration",
"version": "7.0.0",
"source": {
"type": "git",
"url": "https://github.com/plesk/ext-laravel-integration.git",
"reference": "73dc4ea3f99e033396497f147aa033eb0602f943"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/plesk/ext-laravel-integration/zipball/73dc4ea3f99e033396497f147aa033eb0602f943",
"reference": "73dc4ea3f99e033396497f147aa033eb0602f943",
"shasum": ""
},
"require": {
"laravel/framework": ">=7.0.0",
"php": ">=7.4"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"PleskExtLaravel\\Providers\\ConsoleServiceProvider"
]
},
"component": "package"
},
"autoload": {
"psr-4": {
"PleskExtLaravel\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"description": "Plesk Laravel Toolkit integration with Laravel applications.",
"keywords": [
"laravel",
"plesk"
],
"support": {
"issues": "https://github.com/plesk/ext-laravel-integration/issues",
"source": "https://github.com/plesk/ext-laravel-integration/tree/7.0.0"
},
"time": "2023-01-20T09:40:57+00:00"
},
{
"name": "predis/predis",
"version": "v2.4.1",