Refactor vCenter snapshot handling and VM operations

- Introduced a new method `resolveVCenterFromProduct` in `SnapshotService` to streamline vCenter resolution from product data.
- Updated `CreateSnapshot`, `RemoveSnapshot`, and `RevertSnapshot` jobs to utilize the new vCenter resolution method.
- Enhanced the `VmController` to support UUID-based service identification for VM operations.
- Added new actions for changing VM IP and RDP port, encapsulating the logic for these operations.
- Improved error handling and logging for VM operations, including detailed responses for API consumers.
- Removed unnecessary relationships in model queries to optimize performance.
This commit is contained in:
pbao9
2026-04-15 23:38:38 +07:00
parent 64a9d603e2
commit bb73d6c5a2
14 changed files with 987 additions and 120 deletions
+146 -48
View File
@@ -5,6 +5,7 @@ use App\Http\Controllers\Api\Client\ClientController;
use App\Http\Controllers\Api\Service\OrderController;
use App\Http\Controllers\Api\Service\ProductCategoryController;
use App\Http\Controllers\Api\Service\ServiceController;
use App\Http\Controllers\Api\Service\VmController;
use App\Http\Controllers\Api\Client\Service\ServiceController as ServiceClientController;
use App\Http\Controllers\Api\Transaction\TransactionController;
use Illuminate\Support\Facades\Route;
@@ -163,6 +164,14 @@ Route::prefix('v1')->as('api.')->group(function () {
Route::get('v1/payment/vnpay/return', [TransactionController::class, 'returnCallback']);
Route::prefix('v1/system')->middleware(['api.auth'])->group(function () {
// VM power & network operations
Route::controller(VmController::class)->group(function () {
Route::post('vm-power-on', 'powerOn') ->name('vm.power-on');
Route::post('vm-power-off', 'powerOff') ->name('vm.power-off');
Route::post('vm-change-ip', 'changeIp') ->name('vm.change-ip');
Route::post('vm-change-port','changePort') ->name('vm.change-port');
});
Route::get('get-ticket-console', function (\Illuminate\Http\Request $request, \App\Http\Services\vCenter $serviceVCenter) {
$product_id = $request->get('product');
if (!$product_id) {
@@ -249,15 +258,36 @@ Route::prefix('v1/system')->middleware(['api.auth'])->group(function () {
]);
});
Route::post('vm-create-snapshot', function (\Illuminate\Http\Request $request, \App\Http\Services\vCenter $serviceVCenter) {
Route::post('vm-create-snapshot', function (\Illuminate\Http\Request $request) {
$customer = $request->user();
$payload = $request->validate([
'product' => ['required', 'integer'],
'name' => ['nullable', 'string', 'max:191'],
'product' => ['required'],
'description' => ['nullable', 'string', 'max:1000'],
]);
$product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter')->find($payload['product']);
// Kiểm tra nếu là UUID (Service UUID)
$isUuid = preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $payload['product']);
if ($isUuid) {
// Tìm qua Service UUID
$service = \App\Models\Service::with('vmware.ipAddress.host.datacenter.vCenter', 'vmware.vCenter')
->where('uuid', $payload['product'])
->where('customer_id', $customer->id)
->first();
if (!$service || !$service->vmware) {
return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404);
}
$product = $service->vmware;
} elseif (is_numeric($payload['product'])) {
$product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter')->find((int)$payload['product']);
} else {
$product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter')
->where('vmware_id', $payload['product'])
->where('customer_id', $customer->id)
->first();
}
if (!$product) {
return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404);
}
@@ -265,44 +295,68 @@ Route::prefix('v1/system')->middleware(['api.auth'])->group(function () {
return response()->json(['error' => true, 'message' => 'Bạn không có quyền thao tác VM này'], 403);
}
$result = $serviceVCenter->CreateSnapShot($product, $payload['name'] ?? null);
if (is_string($result) || ($result['error'] ?? true)) {
return response()->json([
'error' => true,
'message' => is_string($result) ? $result : ($result['message'] ?? 'Không thể tạo snapshot'),
], 400);
}
// Auto-generate snapshot name: {vm_name}-{date}-{count}
$today = now()->format('d-m-Y');
$existingCount = \App\Models\ModuleSnapshot::where('module_vmware_id', $product->id)
->whereDate('created_at', today())
->count();
$snapshotName = sprintf('%s-%s-%d', $product->name, $today, $existingCount + 1);
$snapshotName = $result['data'] ?? ($payload['name'] ?? null);
// Create snapshot record with pending status
$snapshot = \App\Models\ModuleSnapshot::create([
'name' => $snapshotName,
'description' => $payload['description'] ?? null,
'customer_id' => $customer->id,
'module_vmware_id' => $product->id,
'status' => 'success',
'snapshot_ref' => $snapshotName,
'status' => 'pending',
'snapshot_ref' => null,
]);
// Dispatch job to create snapshot
\App\Jobs\VirtualMachine\Snapshot\CreateSnapshot::dispatch($snapshot->id);
return response()->json([
'error' => false,
'message' => 'Đã tạo snapshot thành công',
'message' => 'Đã gửi yêu cầu tạo snapshot vào hàng đợi',
'data' => [
'id' => $snapshot->id,
'name' => $snapshot->name,
'description' => $snapshot->description,
'snapshot_ref' => $snapshot->snapshot_ref,
'status' => 'pending',
],
]);
});
Route::get('vm-snapshots', function (\Illuminate\Http\Request $request, \App\Http\Services\vCenter $serviceVCenter) {
$customer = $request->user();
$productId = (int)$request->query('product');
if (!$productId) {
$productParam = $request->query('product');
if (!$productParam) {
return response()->json(['error' => true, 'message' => 'Thiếu product id'], 422);
}
$product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter')->find($productId);
// Kiểm tra nếu là UUID (Service UUID)
$isUuid = preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $productParam);
if ($isUuid) {
// Tìm qua Service UUID
$service = \App\Models\Service::with('vmware.ipAddress.host.datacenter.vCenter', 'vmware.vCenter')
->where('uuid', $productParam)
->where('customer_id', $customer->id)
->first();
if (!$service || !$service->vmware) {
return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404);
}
$product = $service->vmware;
} elseif (is_numeric($productParam)) {
$product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter')->find((int)$productParam);
} else {
$product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter')
->where('vmware_id', $productParam)
->where('customer_id', $customer->id)
->first();
}
if (!$product) {
return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404);
}
@@ -374,14 +428,35 @@ Route::prefix('v1/system')->middleware(['api.auth'])->group(function () {
]);
});
Route::post('vm-revert-snapshot', function (\Illuminate\Http\Request $request, \App\Http\Services\vCenter $serviceVCenter) {
Route::post('vm-revert-snapshot', function (\Illuminate\Http\Request $request) {
$customer = $request->user();
$payload = $request->validate([
'product' => ['required', 'integer'],
'snapshot' => ['required', 'string'],
'product' => ['required'],
'snapshot_id' => ['required', 'numeric'],
]);
$product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter')->find($payload['product']);
// Kiểm tra nếu là UUID (Service UUID)
$isUuid = preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $payload['product']);
if ($isUuid) {
// Tìm qua Service UUID
$service = \App\Models\Service::with('vmware')
->where('uuid', $payload['product'])
->where('customer_id', $customer->id)
->first();
if (!$service || !$service->vmware) {
return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404);
}
$product = $service->vmware;
} elseif (is_numeric($payload['product'])) {
$product = \App\Models\ModuleVmware::find((int)$payload['product']);
} else {
$product = \App\Models\ModuleVmware::where('vmware_id', $payload['product'])
->where('customer_id', $customer->id)
->first();
}
if (!$product) {
return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404);
}
@@ -389,28 +464,54 @@ Route::prefix('v1/system')->middleware(['api.auth'])->group(function () {
return response()->json(['error' => true, 'message' => 'Bạn không có quyền thao tác VM này'], 403);
}
$result = $serviceVCenter->RevertSnapShot($product, $payload['snapshot']);
if (is_string($result) || ($result['error'] ?? true)) {
return response()->json([
'error' => true,
'message' => is_string($result) ? $result : ($result['message'] ?? 'Không thể revert snapshot'),
], 400);
// Find snapshot
$snapshot = \App\Models\ModuleSnapshot::where('id', $payload['snapshot_id'])
->where('module_vmware_id', $product->id)
->where('customer_id', $customer->id)
->first();
if (!$snapshot) {
return response()->json(['error' => true, 'message' => 'Không tìm thấy snapshot'], 404);
}
// Dispatch job
\App\Jobs\VirtualMachine\Snapshot\RevertSnapshot::dispatch($product->id, $snapshot->id);
return response()->json([
'error' => false,
'message' => 'Đã gửi lệnh revert snapshot',
'message' => 'Đã gửi yêu cầu revert snapshot vào hàng đợi',
]);
});
Route::post('vm-delete-snapshot', function (\Illuminate\Http\Request $request, \App\Http\Services\vCenter $serviceVCenter) {
Route::post('vm-delete-snapshot', function (\Illuminate\Http\Request $request) {
$customer = $request->user();
$payload = $request->validate([
'product' => ['required', 'integer'],
'snapshot' => ['required', 'string'],
'product' => ['required'],
'snapshot_id' => ['required', 'numeric'],
]);
$product = \App\Models\ModuleVmware::with('ipAddress.host.datacenter.vCenter', 'vCenter')->find($payload['product']);
// Kiểm tra nếu là UUID (Service UUID)
$isUuid = preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $payload['product']);
if ($isUuid) {
// Tìm qua Service UUID
$service = \App\Models\Service::with('vmware')
->where('uuid', $payload['product'])
->where('customer_id', $customer->id)
->first();
if (!$service || !$service->vmware) {
return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404);
}
$product = $service->vmware;
} elseif (is_numeric($payload['product'])) {
$product = \App\Models\ModuleVmware::find((int)$payload['product']);
} else {
$product = \App\Models\ModuleVmware::where('vmware_id', $payload['product'])
->where('customer_id', $customer->id)
->first();
}
if (!$product) {
return response()->json(['error' => true, 'message' => 'Không tìm thấy VM'], 404);
}
@@ -418,25 +519,22 @@ Route::prefix('v1/system')->middleware(['api.auth'])->group(function () {
return response()->json(['error' => true, 'message' => 'Bạn không có quyền thao tác VM này'], 403);
}
$result = $serviceVCenter->RemoveSnapShot($product, $payload['snapshot']);
if (is_string($result) || ($result['error'] ?? true)) {
return response()->json([
'error' => true,
'message' => is_string($result) ? $result : ($result['message'] ?? 'Không thể xoá snapshot'),
], 400);
}
\App\Models\ModuleSnapshot::query()
// Find snapshot
$snapshot = \App\Models\ModuleSnapshot::where('id', $payload['snapshot_id'])
->where('module_vmware_id', $product->id)
->where('customer_id', $customer->id)
->where(function ($q) use ($payload) {
$q->where('snapshot_ref', $payload['snapshot'])->orWhere('name', $payload['snapshot']);
})
->delete();
->first();
if (!$snapshot) {
return response()->json(['error' => true, 'message' => 'Không tìm thấy snapshot'], 404);
}
// Dispatch job
\App\Jobs\VirtualMachine\Snapshot\RemoveSnapshot::dispatch($product->id, $snapshot->id);
return response()->json([
'error' => false,
'message' => 'Đã xoá snapshot thành công',
'message' => 'Đã gửi yêu cầu xóa snapshot vào hàng đợi',
]);
});