input('vm_id'); $vm = VirtualMachines::find($vmId); if (!$vm) { return response()->json(['error' => true, 'message' => 'VM not found'], 404); } return response()->json(['error' => false, 'data' => ['power_state' => $vm->power_state]]); } public function powerOn(Request $request) { $vmId = (int) $request->input('vm_id'); $vm = VirtualMachines::find($vmId); if (!$vm) { return response()->json(['error' => true, 'message' => 'VM not found'], 404); } $vmwareId = $vm->vm_id; if (!$vmwareId) { return response()->json(['error' => true, 'message' => 'VM VMware ID not found'], 400); } if ($vm->power_state === 'POWERED_ON') { return response()->json(['error' => true, 'message' => 'VM is already powered on'], 400); } $vcenter = null; if ($vm->datacenter?->vCenter) { $vcenter = $vm->datacenter->vCenter; } if (!$vcenter && $vm->host?->datacenter?->vCenter) { $vcenter = $vm->host->datacenter->vCenter; } if (!$vcenter) { return response()->json(['error' => true, 'message' => 'vCenter not found'], 400); } $vCenterService = new \App\Http\Services\vCenter(); if (!$vCenterService->loginServer($vcenter)) { return response()->json(['error' => true, 'message' => 'Cannot login to vCenter'], 400); } $response = TlsHttpClientFactory::vCenter() ->withHeaders([ 'vmware-api-session-id' => $vCenterService->token ]) ->post("https://{$vCenterService->apiUri}/rest/vcenter/vm/$vmwareId/power/start"); if ($response->status() === 200) { $vm->update(['power_state' => 'POWERED_ON']); return response()->json(['error' => false, 'message' => 'Power on command sent successfully']); } $errorData = $response->json(); if (isset($errorData['error_type']) && $errorData['error_type'] === 'ALREADY_IN_DESIRED_STATE') { $vm->update(['power_state' => 'POWERED_ON']); return response()->json(['error' => false, 'message' => 'VM is already powered on']); } return response()->json(['error' => true, 'message' => $errorData['value'] ?? $errorData['error_type'] ?? 'Failed to power on VM'], 400); } public function powerOff(Request $request) { $vmId = (int) $request->input('vm_id'); $vm = VirtualMachines::find($vmId); if (!$vm) { return response()->json(['error' => true, 'message' => 'VM not found'], 404); } $vmwareId = $vm->vm_id; if (!$vmwareId) { return response()->json(['error' => true, 'message' => 'VM VMware ID not found'], 400); } if ($vm->power_state === 'POWERED_OFF') { return response()->json(['error' => true, 'message' => 'VM is already powered off'], 400); } $vcenter = null; if ($vm->datacenter?->vCenter) { $vcenter = $vm->datacenter->vCenter; } if (!$vcenter && $vm->host?->datacenter?->vCenter) { $vcenter = $vm->host->datacenter->vCenter; } if (!$vcenter) { return response()->json(['error' => true, 'message' => 'vCenter not found'], 400); } $vCenterService = new \App\Http\Services\vCenter(); if (!$vCenterService->loginServer($vcenter)) { return response()->json(['error' => true, 'message' => 'Cannot login to vCenter'], 400); } $response = TlsHttpClientFactory::vCenter() ->withHeaders([ 'vmware-api-session-id' => $vCenterService->token ]) ->post("https://{$vCenterService->apiUri}/rest/vcenter/vm/$vmwareId/power/stop"); if ($response->status() === 200) { $vm->update(['power_state' => 'POWERED_OFF']); return response()->json(['error' => false, 'message' => 'Power off command sent successfully']); } $errorData = $response->json(); if (isset($errorData['error_type']) && $errorData['error_type'] === 'ALREADY_IN_DESIRED_STATE') { $vm->update(['power_state' => 'POWERED_OFF']); return response()->json(['error' => false, 'message' => 'VM is already powered off']); } return response()->json(['error' => true, 'message' => $errorData['value'] ?? $errorData['error_type'] ?? 'Failed to power off VM'], 400); } public function restart(Request $request) { $vmId = (int) $request->input('vm_id'); $vm = VirtualMachines::find($vmId); if (!$vm) { return response()->json(['error' => true, 'message' => 'VM not found'], 404); } $vmwareId = $vm->vm_id; if (!$vmwareId) { return response()->json(['error' => true, 'message' => 'VM VMware ID not found'], 400); } $vcenter = $vm->datacenter?->vCenter ?? $vm->host?->datacenter?->vCenter; if (!$vcenter) { return response()->json(['error' => true, 'message' => 'vCenter not found'], 400); } $vCenterService = new \App\Http\Services\vCenter(); if (!$vCenterService->loginServer($vcenter)) { return response()->json(['error' => true, 'message' => 'Cannot login to vCenter'], 400); } $response = TlsHttpClientFactory::vCenter() ->withHeaders(['vmware-api-session-id' => $vCenterService->token]) ->post("https://{$vCenterService->apiUri}/rest/vcenter/vm/$vmwareId/power/reset"); if ($response->status() === 200) { return response()->json(['error' => false, 'message' => 'Restart command sent successfully']); } $errorData = $response->json(); return response()->json([ 'error' => true, 'message' => $errorData['value'] ?? $errorData['error_type'] ?? 'Failed to restart VM', ], 400); } public function snapshot(Request $request) { $request->validate([ 'vm_id' => 'required|integer', 'name' => 'required|string|max:255', 'description' => 'nullable|string|max:1000', ]); $vmId = (int) $request->input('vm_id'); $vm = VirtualMachines::find($vmId); if (!$vm) { return response()->json(['error' => true, 'message' => 'VM not found'], 404); } $vmwareId = $vm->vm_id; if (!$vmwareId) { return response()->json(['error' => true, 'message' => 'VM VMware ID not found'], 400); } $vcenter = $vm->datacenter?->vCenter ?? $vm->host?->datacenter?->vCenter; if (!$vcenter) { return response()->json(['error' => true, 'message' => 'vCenter not found'], 400); } $vCenterService = new \App\Http\Services\vCenter(); if (!$vCenterService->loginServer($vcenter)) { return response()->json(['error' => true, 'message' => 'Cannot login to vCenter'], 400); } $response = TlsHttpClientFactory::vCenter() ->withHeaders(['vmware-api-session-id' => $vCenterService->token]) ->post("https://{$vCenterService->apiUri}/rest/vcenter/vm/$vmwareId/snapshot", [ 'spec' => [ 'name' => $request->input('name'), 'description' => $request->input('description', ''), 'memory' => false, 'quiesce' => false, ], ]); if ($response->status() === 200) { return response()->json(['error' => false, 'message' => 'Snapshot created successfully']); } $errorData = $response->json(); return response()->json([ 'error' => true, 'message' => $errorData['value'] ?? $errorData['error_type'] ?? 'Failed to create snapshot', ], 400); } public function stats(Request $request) { $vmId = (int) $request->input('vm_id'); $vm = VirtualMachines::find($vmId); if (!$vm) { return response()->json(['error' => true, 'message' => 'VM not found'], 404); } $vmwareId = $vm->vm_id; if (!$vmwareId) { return response()->json(['error' => true, 'message' => 'VM VMware ID not found'], 400); } if ($vm->power_state !== 'POWERED_ON') { return response()->json(['error' => true, 'message' => 'VM is not powered on'], 400); } try { $stats = $this->vmMetrics->get($vmwareId, $vm->ram_mb, $vm->cpu); } catch (RuntimeException $exception) { $status = $exception->getCode() === 404 ? 404 : 502; return response()->json([ 'error' => true, 'message' => $status === 404 ? 'VM metrics are unavailable' : 'Unable to fetch VM metrics', ], $status); } return response()->json([ 'error' => false, 'data' => $stats, ]); } public function getPoweredOffVms() { $vms = VirtualMachines::whereNotNull('vm_id') ->where('power_state', 'POWERED_OFF') ->get() ->map(function ($vm) { return [ 'id' => $vm->id, 'name' => $vm->name, 'vm_id' => $vm->vm_id, 'power_state' => $vm->power_state, ]; }); return response()->json([ 'error' => false, 'data' => $vms ]); } public function suspend(Request $request) { $vmId = (int) $request->input('vm_id'); // vm_id from frontend = services.vm_id = module_vmware.id (FK) $vmware = ModuleVmware::with('vCenter')->find($vmId); if (!$vmware) { return response()->json(['error' => true, 'message' => 'VM not found'], 404); } $vmwareId = $vmware->vmware_id; if (!$vmwareId) { return response()->json(['error' => true, 'message' => 'VM VMware ID not found'], 400); } $vm = VirtualMachines::where('vm_id', $vmwareId)->first(); if ($vm?->power_state === 'SUSPENDED') { return response()->json(['error' => true, 'message' => 'VM is already suspended'], 400); } $vcenter = $vmware->vCenter; if (!$vcenter) { return response()->json(['error' => true, 'message' => 'vCenter not found'], 400); } $vCenterService = new \App\Http\Services\vCenter(); if (!$vCenterService->loginServer($vcenter)) { return response()->json(['error' => true, 'message' => 'Cannot login to vCenter'], 400); } $response = TlsHttpClientFactory::vCenter() ->withHeaders(['vmware-api-session-id' => $vCenterService->token]) ->post("https://{$vCenterService->apiUri}/rest/vcenter/vm/$vmwareId/power/suspend"); if ($response->status() === 200) { $vm?->update(['power_state' => 'SUSPENDED']); $this->sendSuspendNotification($vmware, $vm); return response()->json(['error' => false, 'message' => 'Suspend command sent successfully']); } $errorData = $response->json(); if (isset($errorData['error_type']) && $errorData['error_type'] === 'ALREADY_IN_DESIRED_STATE') { $vm?->update(['power_state' => 'SUSPENDED']); $this->sendSuspendNotification($vmware, $vm); return response()->json(['error' => false, 'message' => 'VM is already suspended']); } return response()->json([ 'error' => true, 'message' => $errorData['value'] ?? $errorData['error_type'] ?? 'Failed to suspend VM', ], 400); } public function updateConfig(Request $request) { $request->validate([ 'vm_name' => 'required|string', 'cpu' => 'required|integer|min:1', 'ram' => 'required|integer|min:1', ]); $vmware = ModuleVmware::where('name', $request->input('vm_name'))->first(); if (!$vmware) { return response()->json(['error' => true, 'message' => 'VM not found'], 404); } \App\Jobs\VirtualMachine\Provision\UpdateVmConfigJob::dispatch( $vmware->id, (string) $vmware->vmware_id, '', (int) $request->input('cpu'), (int) $request->input('ram'), ); return response()->json(['error' => false, 'message' => 'Đã gửi yêu cầu cập nhật cấu hình VM']); } public function deleteVM(Request $request) { $request->validate([ 'vm_id' => 'required|integer', 'confirm_name' => 'required|string', ]); $vmId = (int) $request->input('vm_id'); $vm = VirtualMachines::find($vmId); if (!$vm) { return response()->json(['error' => true, 'message' => 'VM not found'], 404); } if ($request->input('confirm_name') !== $vm->name) { return response()->json(['error' => true, 'message' => "Tên xác nhận không khớp. Vui lòng nhập đúng: {$vm->name}"], 422); } if ($vm->power_state !== 'POWERED_OFF') { return response()->json(['error' => true, 'message' => 'Vui lòng tắt máy ảo trước khi xoá'], 400); } $vcenter = $vm->datacenter?->vCenter ?? $vm->host?->datacenter?->vCenter; if (!$vcenter) { return response()->json(['error' => true, 'message' => 'vCenter not found'], 400); } $powerShellService = app(\App\Http\Services\vCenter\PowerShellService::class); $q = fn(string $v) => str_replace("'", "''", $v); $vmName = $vm->name; $host = $vcenter->host; $user = $vcenter->user; $password = \App\Http\Facade\SignRSA::DecryptFunc($vcenter->password); $command = "& 'C:\\CLISave\\DeleteVM\\deleteVM.ps1' -VmName '" . $q($vmName) . "' -VCenterHost '" . $q($host) . "' -VCenterUser '" . $q($user) . "' -VCenterPassword '" . $q($password) . "'"; $cmdResult = $powerShellService->runPsCommand($command); if (strpos($cmdResult, 'Login_Fail') !== false) { return response()->json(['error' => true, 'message' => 'SSH Login to PowerShell server failed'], 400); } if (strpos($cmdResult, 'SUCCESS') === false) { return response()->json(['error' => true, 'message' => 'Xoá VM thất bại: ' . $cmdResult], 400); } $vm->delete(); return response()->json(['error' => false, 'message' => 'VM đã được xoá thành công']); } private function sendSuspendNotification(ModuleVmware $vmware, ?VirtualMachines $vm = null): void { $vmware->loadMissing('customer'); if (!$vmware->customer || !$vmware->customer->email) { return; } try { Mail::to($vmware->customer->email) ->queue(new VmSuspendedMail($vmware->customer, $vm?->name ?? $vmware->name, $vmware->vmware_id)); } catch (\Throwable) { // Email failure must not block the suspend response } } }