update bug
This commit is contained in:
@@ -3,18 +3,23 @@
|
||||
namespace App\Jobs\VirtualMachine\Create;
|
||||
|
||||
use App\Http\Facade\SignRSA;
|
||||
use App\Http\Services\Discord;
|
||||
use App\Http\Services\Telegram;
|
||||
use App\Http\Services\vCenter;
|
||||
use App\Jobs\Mail\Customer\VMCreationFailedJob;
|
||||
use App\Jobs\VirtualMachine\Create\AssignIPJob;
|
||||
use App\Jobs\VirtualMachine\Create\ConfigCLICommand;
|
||||
use App\Models\OrderItem;
|
||||
use App\Models\Os;
|
||||
use App\Services\VMDataService;
|
||||
use App\Models\ModuleVmware;
|
||||
use App\Models\ModuleIpAddress;
|
||||
use App\Models\IpPool;
|
||||
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;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class CreateVirtualMachineJob implements ShouldQueue
|
||||
{
|
||||
@@ -35,7 +40,7 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(vCenter $vCenter, Discord $discord, VMDataService $vmDataService)
|
||||
public function handle(vCenter $vCenter, Telegram $telegram)
|
||||
{
|
||||
try {
|
||||
$product = $this->item->getProductAttribute(); // dùng helper trong OrderItem
|
||||
@@ -43,7 +48,7 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
if (!$product) {
|
||||
$errorMessage = "Không tìm thấy sản phẩm từ OrderItem #{$this->item->id}";
|
||||
Log::warning($errorMessage);
|
||||
$discord->SendErrorMessage($errorMessage, 'CreateVirtualMachineJob - Missing Product');
|
||||
$telegram->SendErrorMessage($errorMessage, 'CreateVirtualMachineJob - Missing Product');
|
||||
|
||||
if (!$this->isRetry) {
|
||||
$this->sendFailureNotification($errorMessage);
|
||||
@@ -60,7 +65,7 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
if (!$vcenter || !$host || !$datacenter) {
|
||||
$errorMessage = "Thiếu thông tin host/datacenter/vcenter từ sản phẩm ID #{$product->id}";
|
||||
Log::error($errorMessage);
|
||||
$discord->SendErrorMessage([
|
||||
$telegram->SendErrorMessage([
|
||||
'error' => $errorMessage,
|
||||
'product_id' => $product->id,
|
||||
'order_item_id' => $this->item->id,
|
||||
@@ -79,7 +84,7 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
if (!$vCenter->loginServer($vcenter)) {
|
||||
$errorMessage = "Login vào vCenter thất bại với host: {$vcenter->host}";
|
||||
Log::error($errorMessage);
|
||||
$discord->SendErrorMessage([
|
||||
$telegram->SendErrorMessage([
|
||||
'error' => $errorMessage,
|
||||
'vcenter_host' => $vcenter->host,
|
||||
'vcenter_user' => $vcenter->user,
|
||||
@@ -102,70 +107,28 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
|
||||
// Lấy addon từ order item
|
||||
if ($this->item->addons) {
|
||||
Log::info("🔍 Tìm thấy addons cho order item", [
|
||||
'order_item_id' => $this->item->id,
|
||||
'addons_count' => $this->item->addons->count(),
|
||||
'addons' => $this->item->addons->map(function($addon) {
|
||||
return [
|
||||
'id' => $addon->id,
|
||||
'addon_id' => $addon->addon_id,
|
||||
'qty' => $addon->qty,
|
||||
'price' => $addon->price,
|
||||
'addon_name' => $addon->addonItems?->name ?? 'Unknown'
|
||||
];
|
||||
})
|
||||
]);
|
||||
|
||||
foreach ($this->item->addons as $orderAddon) {
|
||||
$addon = $orderAddon->addonItems;
|
||||
if (!$addon) {
|
||||
Log::warning("⚠️ Addon không tìm thấy addonItems", [
|
||||
'order_addon_id' => $orderAddon->id,
|
||||
'addon_id' => $orderAddon->addon_id
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
if (!$addon) continue;
|
||||
|
||||
$addonName = strtolower(trim($addon->name));
|
||||
$quantity = (int) $orderAddon->qty;
|
||||
|
||||
Log::info("🔍 Xử lý addon", [
|
||||
'addon_name' => $addon->name,
|
||||
'addon_name_lower' => $addonName,
|
||||
'quantity' => $quantity,
|
||||
'addon_id' => $addon->id
|
||||
]);
|
||||
|
||||
switch ($addonName) {
|
||||
case 'cpu':
|
||||
$addonData['cpu'] += $quantity;
|
||||
Log::info("✅ Thêm CPU addon: +{$quantity}");
|
||||
break;
|
||||
case 'ram':
|
||||
$addonData['ram'] += $quantity;
|
||||
Log::info("✅ Thêm RAM addon: +{$quantity}GB");
|
||||
break;
|
||||
case 'disk':
|
||||
case 'ssd':
|
||||
$addonData['disk'] += $quantity;
|
||||
Log::info("✅ Thêm Disk addon: +{$quantity}GB");
|
||||
break;
|
||||
default:
|
||||
Log::warning("⚠️ Addon không được nhận diện: {$addonName}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log::info("ℹ️ Không có addons cho order item {$this->item->id}");
|
||||
}
|
||||
|
||||
Log::info('📊 Thông tin addon được xử lý', [
|
||||
'order_item_id' => $this->item->id,
|
||||
'addon_data' => $addonData,
|
||||
'addons_count' => $this->item->addons?->count() ?? 0,
|
||||
'has_addons' => !empty($addonData) && array_sum($addonData) > 0
|
||||
]);
|
||||
|
||||
$result = $vCenter->CreateVirtualMachine([
|
||||
'item' => $this->item,
|
||||
'host' => $host,
|
||||
@@ -194,7 +157,7 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
if (is_array($result) && ($result['error'] ?? false)) {
|
||||
$errorMessage = 'Tạo máy ảo thất bại: ' . json_encode($result);
|
||||
Log::warning($errorMessage);
|
||||
$discord->SendErrorMessage([
|
||||
$telegram->SendErrorMessage([
|
||||
'error' => $result['error'] ?? 'Unknown error',
|
||||
'result' => $result,
|
||||
'product_id' => $product->id,
|
||||
@@ -211,7 +174,7 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
} elseif ($result === 'Login_Again') {
|
||||
$errorMessage = 'Tạo máy ảo thất bại: cần đăng nhập lại.';
|
||||
Log::error($errorMessage);
|
||||
$discord->SendErrorMessage([
|
||||
$telegram->SendErrorMessage([
|
||||
'error' => $errorMessage,
|
||||
'product_id' => $product->id,
|
||||
'order_item_id' => $this->item->id,
|
||||
@@ -234,91 +197,86 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
$invoice = \App\Models\Invoice::where('order_id', $this->item->order->id)->first();
|
||||
if ($invoice) {
|
||||
$invoice->update(['vm_id' => $vmId]);
|
||||
Log::info('✅ Đã lưu vm_id vào bảng invoice', [
|
||||
'vm_id' => $vmId,
|
||||
'invoice_id' => $invoice->id,
|
||||
'order_id' => $this->item->order->id
|
||||
]);
|
||||
} else {
|
||||
Log::warning('⚠️ Không tìm thấy invoice cho order', [
|
||||
'order_id' => $this->item->order->id,
|
||||
'vm_id' => $vmId
|
||||
]);
|
||||
}
|
||||
|
||||
// Start VM ngay sau khi tạo thành công
|
||||
Log::info('🚀 Bắt đầu khởi động VM sau khi tạo thành công', [
|
||||
'vm_id' => $vmId,
|
||||
'order_item_id' => $this->item->id
|
||||
]);
|
||||
|
||||
$startResult = $vCenter->StartVirtualMachine($vmId);
|
||||
|
||||
if ($startResult === 'Login_Again') {
|
||||
// Thử đăng nhập lại và start VM
|
||||
Log::info('🔄 Thử đăng nhập lại vCenter để start VM', [
|
||||
'vm_id' => $vmId,
|
||||
'vcenter_host' => $vcenter->host
|
||||
]);
|
||||
|
||||
if ($vCenter->loginServer($vcenter)) {
|
||||
$startResult = $vCenter->StartVirtualMachine($vmId);
|
||||
Log::info('✅ Đăng nhập lại thành công, thử start VM lần nữa', [
|
||||
'vm_id' => $vmId,
|
||||
'start_result' => $startResult
|
||||
]);
|
||||
} else {
|
||||
Log::error('❌ Không thể đăng nhập lại vCenter để start VM', [
|
||||
'vm_id' => $vmId,
|
||||
'vcenter_host' => $vcenter->host
|
||||
]);
|
||||
}
|
||||
}
|
||||
// Start VM ngay sau khi tạo thành công (sử dụng API trực tiếp)
|
||||
$startResult = $this->startVirtualMachineDirectly($vCenter, $vmId);
|
||||
|
||||
if (is_array($startResult) && $startResult['error'] === false) {
|
||||
Log::info('✅ Khởi động VM thành công', [
|
||||
'vm_id' => $vmId,
|
||||
'order_item_id' => $this->item->id,
|
||||
'result' => $startResult['data']
|
||||
]);
|
||||
|
||||
// Dispatch ConfigCLICommand để cấu hình VM và lấy password
|
||||
$configJob = new ConfigCLICommand($vmId, $this->item, $vCenter);
|
||||
dispatch($configJob)->delay(now()->addMinutes(2)); // Chờ 2 phút để VM boot hoàn toàn
|
||||
}
|
||||
|
||||
// Lưu dữ liệu VM vào database
|
||||
try {
|
||||
$customer = $this->item->order->customer;
|
||||
$serverName = $this->item->server_name ?? 'VM-' . $vmId;
|
||||
|
||||
Log::info('✅ Đã dispatch ConfigCLICommand job', [
|
||||
'vm_id' => $vmId,
|
||||
'order_item_id' => $this->item->id
|
||||
// 1. Lấy IP available từ IPPool
|
||||
$networkId = $product->ipAddress?->module_network_id ?? 1;
|
||||
$availableIP = $this->getAvailableIPFromPool($networkId);
|
||||
|
||||
// 2. Tạo ModuleIpAddress với IP từ pool
|
||||
$moduleIpAddress = ModuleIpAddress::create([
|
||||
'vm_name' => $serverName,
|
||||
'module_host_id' => $host->id,
|
||||
'module_network_id' => $networkId,
|
||||
'ip' => $availableIP,
|
||||
'gateway' => $product->ipAddress?->gateway ?? '0.0.0.0',
|
||||
'netmask' => $product->ipAddress?->netmask ?? '255.255.255.0',
|
||||
'dns' => $product->ipAddress?->dns ?? '8.8.8.8',
|
||||
'status' => 1 // 1 = Assigned
|
||||
]);
|
||||
} else {
|
||||
Log::warning('⚠️ Khởi động VM thất bại', [
|
||||
'vm_id' => $vmId,
|
||||
'order_item_id' => $this->item->id,
|
||||
'error' => $startResult['data']
|
||||
|
||||
// 3. Tạo ModuleVmware với IP address vừa tạo
|
||||
$moduleVmware = ModuleVmware::create([
|
||||
'name' => $serverName,
|
||||
'module_ip_address_id' => $moduleIpAddress->id,
|
||||
'port' => 22,
|
||||
'order_id' => $this->item->order->id,
|
||||
'first_payment' => $this->item->order->first_payment ?? 0,
|
||||
'next_payment' => $this->item->order->next_payment ?? 0,
|
||||
'next_due_date' => $this->item->order->next_due_date ?? null,
|
||||
'next_invoice' => $this->item->order->next_invoice ?? 0,
|
||||
'user' => 'root',
|
||||
'password' => '',
|
||||
'hostname' => $serverName,
|
||||
'vmware_id' => $vmId,
|
||||
'os_id' => $this->item->os_id ?? 1,
|
||||
'customer_id' => $customer->id,
|
||||
'status' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
// Lưu dữ liệu VM và IP address vào database
|
||||
$ipAddresses = [];
|
||||
|
||||
if (is_array($result)) {
|
||||
$ipAddresses = $result['ip_addresses'] ?? [];
|
||||
}
|
||||
|
||||
$saveResult = $vmDataService->saveVMData($this->item, $vmId, $ipAddresses);
|
||||
|
||||
if ($saveResult['success']) {
|
||||
// 4. Cập nhật product với module_vmware_id
|
||||
$product->module_vmware_id = $moduleVmware->id;
|
||||
$product->save();
|
||||
|
||||
Log::info('✅ Lưu dữ liệu VM và IP address thành công', [
|
||||
'vm_id' => $saveResult['vm']->id,
|
||||
'vmware_id' => $vmId,
|
||||
'ip_address_id' => $saveResult['ip_address']->id,
|
||||
'module_vmware_id' => $moduleVmware->id,
|
||||
'module_ip_address_id' => $moduleIpAddress->id,
|
||||
'assigned_ip' => $availableIP,
|
||||
'name' => $serverName,
|
||||
'order_item_id' => $this->item->id
|
||||
]);
|
||||
} else {
|
||||
Log::error('❌ Lỗi khi lưu dữ liệu VM và IP address', [
|
||||
|
||||
// Dispatch AssignIPJob để chạy sau 5 phút
|
||||
AssignIPJob::dispatch($vmId, $moduleVmware->id, $availableIP)
|
||||
->delay(now()->addMinutes(5));
|
||||
|
||||
Log::info('✅ Đã dispatch AssignIPJob để chạy sau 5 phút', [
|
||||
'vm_id' => $vmId,
|
||||
'module_vmware_id' => $moduleVmware->id,
|
||||
'assigned_ip' => $availableIP,
|
||||
'delay_minutes' => 5
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('❌ Lỗi khi lưu dữ liệu VM và IP address: ' . $e->getMessage(), [
|
||||
'vmware_id' => $vmId,
|
||||
'error' => $saveResult['message'],
|
||||
'order_item_id' => $this->item->id
|
||||
'order_item_id' => $this->item->id,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -331,17 +289,8 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
}
|
||||
}
|
||||
|
||||
// Log thông tin addon đã được áp dụng
|
||||
if (!empty($addonData) && array_sum($addonData) > 0) {
|
||||
Log::info('Addon đã được áp dụng thành công', [
|
||||
'vm_id' => $vmId,
|
||||
'addon_data' => $addonData,
|
||||
'order_item_id' => $this->item->id
|
||||
]);
|
||||
}
|
||||
|
||||
// Gửi thông báo thành công
|
||||
$discord->SendJobRunInfo(
|
||||
$telegram->SendJobRunInfo(
|
||||
'CreateVirtualMachineJob',
|
||||
'success',
|
||||
[
|
||||
@@ -364,7 +313,7 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
$discord->SendErrorMessage([
|
||||
$telegram->SendErrorMessage([
|
||||
'error' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
@@ -379,6 +328,88 @@ class CreateVirtualMachineJob implements ShouldQueue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available IP from IPPool that doesn't exist in ModuleIpAddress
|
||||
*/
|
||||
private function getAvailableIPFromPool(int $networkId): string
|
||||
{
|
||||
// Lấy tất cả IP từ IPPool theo network_id, order by ip_address
|
||||
$ipPoolList = IpPool::where('module_network_id', $networkId)
|
||||
->orderBy('ip_address')
|
||||
->get();
|
||||
|
||||
if ($ipPoolList->isEmpty()) {
|
||||
// Nếu không có IP trong pool, trả về IP default
|
||||
return '0.0.0.0';
|
||||
}
|
||||
|
||||
// Lấy tất cả IP đã được assign trong ModuleIpAddress
|
||||
$assignedIPs = ModuleIpAddress::where('module_network_id', $networkId)
|
||||
->pluck('ip')
|
||||
->toArray();
|
||||
|
||||
// Tìm IP đầu tiên chưa được assign
|
||||
foreach ($ipPoolList as $ipPool) {
|
||||
if (!in_array($ipPool->ip_address, $assignedIPs)) {
|
||||
Log::info('✅ Lấy IP available từ pool', [
|
||||
'ip' => $ipPool->ip_address,
|
||||
'network_id' => $networkId
|
||||
]);
|
||||
return $ipPool->ip_address;
|
||||
}
|
||||
}
|
||||
|
||||
// Nếu tất cả IP đã được assign, log warning và trả về IP default
|
||||
Log::warning('⚠️ Không có IP available trong pool', [
|
||||
'network_id' => $networkId,
|
||||
'total_ips_in_pool' => $ipPoolList->count(),
|
||||
'assigned_ips' => count($assignedIPs)
|
||||
]);
|
||||
|
||||
return '0.0.0.0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Start VM directly using vCenter API
|
||||
*/
|
||||
private function startVirtualMachineDirectly(vCenter $vCenter, string $vmId): array|string
|
||||
{
|
||||
try {
|
||||
// Kiểm tra xem đã có session chưa
|
||||
$check = $vCenter->checkApiExits();
|
||||
if (!$check) {
|
||||
return 'Login_Again';
|
||||
}
|
||||
|
||||
// Gọi API start VM trực tiếp
|
||||
$response = Http::withoutVerifying()
|
||||
->withHeaders([
|
||||
'vmware-api-session-id' => $vCenter->token
|
||||
])
|
||||
->post("https://{$vCenter->apiUri}/rest/vcenter/vm/{$vmId}/power/start");
|
||||
|
||||
if ($response->status() === 200) {
|
||||
return [
|
||||
'error' => false,
|
||||
'data' => 'success'
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'error' => true,
|
||||
'data' => $response->body()
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Lỗi khi start VM: ' . $e->getMessage());
|
||||
return [
|
||||
'error' => true,
|
||||
'data' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Send failure notification to customer
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user