From 2d43e958558b6102c0d5e830522dcac9903a5ff2 Mon Sep 17 00:00:00 2001 From: baodev0 Date: Tue, 21 Jul 2026 11:10:45 +0700 Subject: [PATCH] Add datastore_id to OS model and related functionality; update validation and relationships --- .../Api/Admin/IpPool/IpPoolController.php | 12 ++++++--- .../Api/Admin/OsManager/OsController.php | 9 ++++++- .../Api/Admin/VCenter/VCenterController.php | 8 +++++- app/Http/Services/vCenter.php | 2 +- .../vCenter/VirtualMachineService.php | 15 ++++++----- .../Create/CreateVirtualMachineJob.php | 2 ++ app/Models/Os.php | 6 +++++ ...21_120000_add_datastore_id_to_os_table.php | 27 +++++++++++++++++++ database/seeders/DatabaseSeeder.php | 3 +-- 9 files changed, 69 insertions(+), 15 deletions(-) create mode 100644 database/migrations/2026_07_21_120000_add_datastore_id_to_os_table.php diff --git a/app/Http/Controllers/Api/Admin/IpPool/IpPoolController.php b/app/Http/Controllers/Api/Admin/IpPool/IpPoolController.php index 1376b2a..0a8acd1 100644 --- a/app/Http/Controllers/Api/Admin/IpPool/IpPoolController.php +++ b/app/Http/Controllers/Api/Admin/IpPool/IpPoolController.php @@ -109,18 +109,19 @@ class IpPoolController extends Controller } /** - * Bulk create: ip_address may be comma-separated (e.g. "10.0.0.1,10.0.0.2"). + * Bulk create: ip_address may be separated by commas and/or whitespace (e.g. "10.0.0.1, 10.0.0.2 10.0.0.3"). */ public function store(Request $request): JsonResponse { $request->validate([ 'ip_address' => 'required|string', 'module_network_id' => 'required|integer|exists:module_network,id', + 'gateway' => 'nullable|string|max:45', ]); $networkId = (int) $request->module_network_id; $rawIps = $request->ip_address; - $ips = array_filter(array_map('trim', explode(',', $rawIps))); + $ips = array_filter(array_map('trim', preg_split('/[\s,]+/', $rawIps))); if (empty($ips)) { return response()->json(['error' => true, 'message' => 'Không có địa chỉ IP hợp lệ'], 422); @@ -161,6 +162,7 @@ class IpPoolController extends Controller $created[] = IpPool::create([ 'ip_address' => $ip, 'module_network_id' => $networkId, + 'gateway' => $request->gateway, ]); } DB::commit(); @@ -183,6 +185,7 @@ class IpPoolController extends Controller $request->validate([ 'ip_address' => 'required|string|max:45', 'module_network_id' => 'required|integer|exists:module_network,id', + 'gateway' => 'nullable|string|max:45', ]); if (!filter_var($request->ip_address, FILTER_VALIDATE_IP)) { @@ -204,7 +207,7 @@ class IpPoolController extends Controller DB::beginTransaction(); try { - $pool->update($request->only(['ip_address', 'module_network_id'])); + $pool->update($request->only(['ip_address', 'module_network_id', 'gateway'])); DB::commit(); return response()->json(['error' => false, 'data' => $pool->fresh('network')]); @@ -278,7 +281,8 @@ class IpPoolController extends Controller public function networks(): JsonResponse { - $networks = ModuleNetwork::select(['id', 'name', 'type', 'network']) + $networks = ModuleNetwork::select(['id', 'name', 'type', 'network', 'module_host_id']) + ->with(['host:id,name,module_datacenter_id', 'host.datacenter:id,name']) ->orderBy('name') ->get(); diff --git a/app/Http/Controllers/Api/Admin/OsManager/OsController.php b/app/Http/Controllers/Api/Admin/OsManager/OsController.php index 033e7d8..5610ef0 100644 --- a/app/Http/Controllers/Api/Admin/OsManager/OsController.php +++ b/app/Http/Controllers/Api/Admin/OsManager/OsController.php @@ -17,6 +17,7 @@ class OsController extends Controller 'os_group_id' => 'required|integer|exists:os_group,id', 'template_id' => 'nullable|string|max:255', 'module_host_id' => 'nullable|integer|exists:module_host,id', + 'datastore_id' => 'nullable|integer|exists:module_datastore,id', 'is_active' => 'boolean', ]); @@ -25,6 +26,7 @@ class OsController extends Controller 'os_group_id' => $validated['os_group_id'], 'template_id' => $validated['template_id'] ?? null, 'module_host_id' => $validated['module_host_id'] ?? null, + 'datastore_id' => $validated['datastore_id'] ?? null, 'status' => ($validated['is_active'] ?? true) ? 1 : 0, ]); @@ -48,6 +50,7 @@ class OsController extends Controller 'os_group_id' => 'sometimes|integer|exists:os_group,id', 'template_id' => 'nullable|string|max:255', 'module_host_id' => 'nullable|integer|exists:module_host,id', + 'datastore_id' => 'nullable|integer|exists:module_datastore,id', 'is_active' => 'boolean', ]); @@ -56,6 +59,7 @@ class OsController extends Controller 'os_group_id' => $validated['os_group_id'] ?? $os->os_group_id, 'template_id' => array_key_exists('template_id', $validated) ? $validated['template_id'] : $os->template_id, 'module_host_id' => array_key_exists('module_host_id', $validated) ? $validated['module_host_id'] : $os->module_host_id, + 'datastore_id' => array_key_exists('datastore_id', $validated) ? $validated['datastore_id'] : $os->datastore_id, 'status' => isset($validated['is_active']) ? ($validated['is_active'] ? 1 : 0) : $os->status, ]); @@ -83,8 +87,9 @@ class OsController extends Controller private function formatOs(Os $os): array { - $os->loadMissing('host.datacenter.vCenter'); + $os->loadMissing('host.datacenter.vCenter', 'datastore'); $host = $os->host; + $datastore = $os->datastore; return [ 'id' => $os->id, @@ -93,6 +98,8 @@ class OsController extends Controller 'is_active' => (bool) $os->status, 'os_group_id' => $os->os_group_id, 'module_host_id' => $os->module_host_id, + 'datastore_id' => $os->datastore_id, + 'datastore_name' => $datastore?->name, 'host_name' => $host ? $host->name : null, 'datacenter_name' => $host?->datacenter?->name, 'vcenter_name' => $host?->datacenter?->vCenter?->host, diff --git a/app/Http/Controllers/Api/Admin/VCenter/VCenterController.php b/app/Http/Controllers/Api/Admin/VCenter/VCenterController.php index 9037cbb..100a20b 100644 --- a/app/Http/Controllers/Api/Admin/VCenter/VCenterController.php +++ b/app/Http/Controllers/Api/Admin/VCenter/VCenterController.php @@ -18,6 +18,8 @@ use Illuminate\Http\Request; class VCenterController extends Controller { + private const CONSOLE_HOST = 'console.tgs.com.vn'; + public function store(Request $request): JsonResponse { $request->validate([ @@ -77,7 +79,11 @@ class VCenterController extends Controller return response()->json([ 'error' => false, - 'data' => $ticket['data'], + 'data' => preg_replace( + '#^wss://[^/]+#', + 'wss://' . self::CONSOLE_HOST, + $ticket['data'] + ), 'host' => $vcenter->host, 'vmware-api-session-id' => $serviceVCenter->token, ])->cookie('vmware-api-session-id', $serviceVCenter->token, 60, '/', null, false, false); diff --git a/app/Http/Services/vCenter.php b/app/Http/Services/vCenter.php index 4a30527..971b791 100644 --- a/app/Http/Services/vCenter.php +++ b/app/Http/Services/vCenter.php @@ -142,7 +142,7 @@ class vCenter public function getAllVirtualMachine($datacenter = '', $host = '') { - return $this->vm->getAllVirtualMachine($datacenter); + return $this->vm->getAllVirtualMachine($datacenter, $host); } public function findVirtualMachineByName(string $vmName): ?array { diff --git a/app/Http/Services/vCenter/VirtualMachineService.php b/app/Http/Services/vCenter/VirtualMachineService.php index 6ff6eca..e7d94f3 100644 --- a/app/Http/Services/vCenter/VirtualMachineService.php +++ b/app/Http/Services/vCenter/VirtualMachineService.php @@ -7,7 +7,6 @@ use Illuminate\Support\Facades\Log; use Illuminate\Http\Client\ConnectionException; use Illuminate\Support\Str; use App\Http\Services\Discord; -use App\Models\ModuleDatastore; use App\Services\VirtualMachine\VmProviderSettingsService; use Exception; @@ -22,13 +21,16 @@ class VirtualMachineService $this->powerShellService = $powerShellService; } - public function getAllVirtualMachine($datacenter = '') + public function getAllVirtualMachine($datacenter = '', $host = '') { if ($this->client->checkApiExits()) { $params = []; if (!empty($datacenter)) { $params['filter.datacenters'] = $datacenter; } + if (!empty($host)) { + $params['filter.hosts'] = $host; + } $response = Http::withoutVerifying() ->withHeaders([ @@ -81,10 +83,11 @@ class VirtualMachineService /** @var VmProviderSettingsService $providerSettings */ $providerSettings = app(VmProviderSettingsService::class); - $activeDatastore = ModuleDatastore::where('module_host_id', $host->id) - ->where('status', 1) - ->first(); - $datastoreId = $activeDatastore?->datastore ?? $providerSettings->defaultDatastore(); + $datastore = $product_data['datastore'] ?? null; + if (!$datastore) { + throw new Exception("Host id={$host->id} chưa có datastore active để tạo VM."); + } + $datastoreId = $datastore->datastore; $body = [ 'spec' => [ diff --git a/app/Jobs/VirtualMachine/Create/CreateVirtualMachineJob.php b/app/Jobs/VirtualMachine/Create/CreateVirtualMachineJob.php index fcf735d..3428e02 100644 --- a/app/Jobs/VirtualMachine/Create/CreateVirtualMachineJob.php +++ b/app/Jobs/VirtualMachine/Create/CreateVirtualMachineJob.php @@ -123,6 +123,7 @@ class CreateVirtualMachineJob implements ShouldQueue 'product_id' => $product->id, 'template' => $infra->template, 'host_id' => $infra->host->id, + 'datastore_id' => $infra->datastore?->id, 'vcenter_id' => $infra->vcenter->id, ]); @@ -133,6 +134,7 @@ class CreateVirtualMachineJob implements ShouldQueue 'vcenter' => $infra->vcenter, 'template' => $infra->template, 'addon' => $addonData, + 'datastore' => $infra->datastore, ]); if (is_array($result) && ($result['error'] ?? false)) { diff --git a/app/Models/Os.php b/app/Models/Os.php index 0869348..c5954a7 100644 --- a/app/Models/Os.php +++ b/app/Models/Os.php @@ -16,6 +16,7 @@ class Os extends Model 'os_group_id', 'template_id', 'module_host_id', + 'datastore_id', 'status', ]; @@ -28,6 +29,11 @@ class Os extends Model { return $this->belongsTo(\App\Models\ModuleHost::class, 'module_host_id'); } + + public function datastore() + { + return $this->belongsTo(\App\Models\ModuleDatastore::class, 'datastore_id'); + } public function getStatus() { return match ($this->attributes['status']){ diff --git a/database/migrations/2026_07_21_120000_add_datastore_id_to_os_table.php b/database/migrations/2026_07_21_120000_add_datastore_id_to_os_table.php new file mode 100644 index 0000000..f793d11 --- /dev/null +++ b/database/migrations/2026_07_21_120000_add_datastore_id_to_os_table.php @@ -0,0 +1,27 @@ +unsignedBigInteger('datastore_id')->nullable()->after('module_host_id'); + $table->foreign('datastore_id') + ->references('id') + ->on('module_datastore') + ->nullOnDelete(); + }); + } + + public function down(): void + { + Schema::table('os', function (Blueprint $table) { + $table->dropForeign(['datastore_id']); + $table->dropColumn('datastore_id'); + }); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index bacaea6..f42fa54 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -23,7 +23,6 @@ class DatabaseSeeder extends Seeder $user->assignRole('Full Administrator'); $this->call(TemplateSeeder::class); $this->call(Setting::class); - // $this->call(ControlPanelSeeder::class); - // $this->call(VPSProductSeeder::class); + } }