176 lines
6.2 KiB
PHP
176 lines
6.2 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace App\Jobs\VirtualMachine\Create;
|
||
|
||
use App\Http\Services\vCenter;
|
||
use App\Jobs\VirtualMachine\Provision\AssignIpToGuestJob;
|
||
use App\Models\OrderItem;
|
||
use App\Services\VirtualMachine\VmAddonResolver;
|
||
use App\Services\VirtualMachine\VmInfrastructureResolver;
|
||
use App\Services\VirtualMachine\VmModuleService;
|
||
use App\Services\VirtualMachine\VmNotificationService;
|
||
use App\Services\VirtualMachine\VmPowerService;
|
||
use App\Services\VirtualMachine\VmProvisionLogger;
|
||
use App\Services\VirtualMachine\VmServiceSyncService;
|
||
use App\Services\VirtualMachine\VmVcenterErrorTranslator;
|
||
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 RuntimeException;
|
||
use Throwable;
|
||
|
||
class CreateVirtualMachineJob implements ShouldQueue
|
||
{
|
||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||
|
||
/** VM clone can take 5–8 minutes */
|
||
public int $timeout = 600;
|
||
|
||
/** No auto-retry — VM errors need manual review */
|
||
public int $tries = 1;
|
||
|
||
public function __construct(
|
||
public OrderItem $item,
|
||
public bool $isRetry = false,
|
||
) {
|
||
}
|
||
|
||
public function handle(
|
||
vCenter $vCenter,
|
||
VmProvisionLogger $vmLogger,
|
||
VmInfrastructureResolver $infraResolver,
|
||
VmAddonResolver $addonResolver,
|
||
VmPowerService $powerService,
|
||
VmModuleService $moduleService,
|
||
VmServiceSyncService $serviceSync,
|
||
VmNotificationService $notification,
|
||
VmVcenterErrorTranslator $errorTranslator,
|
||
): bool {
|
||
try {
|
||
$vmLogger->step('Create VM job started', [
|
||
'order_item_id' => $this->item->id,
|
||
'is_retry' => $this->isRetry,
|
||
]);
|
||
|
||
$product = $this->item->getProductAttribute();
|
||
|
||
if (!$product) {
|
||
throw new RuntimeException("Không tìm thấy sản phẩm từ OrderItem #{$this->item->id}");
|
||
}
|
||
|
||
$infra = $infraResolver->resolve($this->item, $product);
|
||
$addonData = $addonResolver->resolve($this->item);
|
||
|
||
if (!$vCenter->loginServer($infra->vcenter)) {
|
||
throw new RuntimeException("Login vào vCenter thất bại với host: {$infra->vcenter->host}");
|
||
}
|
||
|
||
$vmLogger->step('VM clone started', [
|
||
'order_item_id' => $this->item->id,
|
||
'product_id' => $product->id,
|
||
'template' => $infra->template,
|
||
'host_id' => $infra->host->id,
|
||
'vcenter_id' => $infra->vcenter->id,
|
||
]);
|
||
|
||
$result = $vCenter->CreateVirtualMachine([
|
||
'item' => $this->item,
|
||
'host' => $infra->host,
|
||
'datacenter' => $infra->datacenter,
|
||
'vcenter' => $infra->vcenter,
|
||
'template' => $infra->template,
|
||
'addon' => $addonData,
|
||
]);
|
||
|
||
if (is_array($result) && ($result['error'] ?? false)) {
|
||
throw new RuntimeException(
|
||
$errorTranslator->translate($result, $this->item->server_name)
|
||
);
|
||
}
|
||
|
||
if ($result === 'Login_Again') {
|
||
throw new RuntimeException('Tạo máy ảo thất bại: cần đăng nhập lại vCenter.');
|
||
}
|
||
|
||
$vmId = is_array($result) ? ($result['data'] ?? null) : $result;
|
||
|
||
if (!$vmId || in_array($vmId, ['unknown', 'UNKNOWN'], true)) {
|
||
throw new RuntimeException('Không lấy được vm_id sau khi tạo máy ảo.');
|
||
}
|
||
|
||
$vmLogger->success('VM clone success', [
|
||
'order_item_id' => $this->item->id,
|
||
'vm_id' => $vmId,
|
||
]);
|
||
|
||
$vmLogger->step('VM start started', ['vm_id' => $vmId]);
|
||
|
||
$startResult = $powerService->start($vCenter, $vmId);
|
||
|
||
if (is_array($startResult) && ($startResult['error'] ?? false)) {
|
||
throw new RuntimeException(
|
||
'Start VM thất bại: ' . json_encode($startResult['data'] ?? null)
|
||
);
|
||
}
|
||
if ($startResult === 'Login_Again') {
|
||
throw new RuntimeException('Start VM thất bại: cần đăng nhập lại vCenter.');
|
||
}
|
||
|
||
$vmLogger->success('VM start success', ['vm_id' => $vmId]);
|
||
|
||
$moduleVmware = $moduleService->create($this->item, $infra, $vmId);
|
||
|
||
$vmLogger->success('ModuleVmware created', [
|
||
'module_vmware_id' => $moduleVmware->id,
|
||
'vm_id' => $vmId,
|
||
]);
|
||
|
||
$serviceSync->sync($moduleVmware, $this->item);
|
||
|
||
$vmLogger->success('Service synced', [
|
||
'module_vmware_id' => $moduleVmware->id,
|
||
'order_item_id' => $this->item->id,
|
||
]);
|
||
|
||
AssignIpToGuestJob::dispatch(
|
||
$moduleVmware->id,
|
||
(string) $vmId,
|
||
$infra->isWindows ? VmProvisionService::OS_WINDOWS : VmProvisionService::OS_LINUX,
|
||
VmProvisionService::TEMPLATE_PASSWORD,
|
||
)->onQueue('default');
|
||
|
||
$vmLogger->success('AssignIpToGuestJob dispatched', [
|
||
'module_vmware_id' => $moduleVmware->id,
|
||
'vm_id' => $vmId,
|
||
'os_family' => $infra->isWindows ? 'WINDOWS' : 'LINUX',
|
||
]);
|
||
|
||
$notification->success($this->item, $moduleVmware, (string) $vmId);
|
||
|
||
$vmLogger->success('Create VM job success', [
|
||
'order_item_id' => $this->item->id,
|
||
'module_vmware_id' => $moduleVmware->id,
|
||
'vm_id' => $vmId,
|
||
]);
|
||
|
||
return true;
|
||
} catch (Throwable $e) {
|
||
$vmLogger->exception('Create VM job failed', $e, [
|
||
'order_item_id' => $this->item->id,
|
||
'is_retry' => $this->isRetry,
|
||
]);
|
||
|
||
if (!$this->isRetry) {
|
||
$notification->failed($this->item, $e->getMessage());
|
||
}
|
||
|
||
return false;
|
||
}
|
||
}
|
||
}
|