From 498a57b4999b95a871c39b6ca9b7e7b2b6d42863 Mon Sep 17 00:00:00 2001 From: pbao9 Date: Sat, 14 Feb 2026 16:28:13 +0700 Subject: [PATCH] feat: Implement VM console access, display VM power state, and add VM statistics retrieval. --- .../admin/vm/VirtualMachineController.php | 11 +- app/Http/Services/vCenter.php | 68 ++++ .../package/vm/datatable/action.blade.php | 72 +++- .../pages/package/vm/edit.blade.php | 78 ++++- .../administrator/pages/vm_console.blade.php | 314 ++++++++++++++++++ routes/admin.php | 90 ++++- 6 files changed, 610 insertions(+), 23 deletions(-) create mode 100644 resources/views/administrator/pages/vm_console.blade.php diff --git a/app/Http/Controllers/admin/vm/VirtualMachineController.php b/app/Http/Controllers/admin/vm/VirtualMachineController.php index 449212d..c63e075 100644 --- a/app/Http/Controllers/admin/vm/VirtualMachineController.php +++ b/app/Http/Controllers/admin/vm/VirtualMachineController.php @@ -66,6 +66,7 @@ class VirtualMachineController extends Controller if (!$vm) { return back()->withErrors('Invalid request.'); } + $vm->load('datacenter', 'host'); $title = 'Edit Virtual Machine'; return view('administrator.pages.package.vm.edit', compact('title', 'vm')); } @@ -137,10 +138,18 @@ class VirtualMachineController extends Controller ->editColumn('updated_at', function ($d) { return $d->updated_at ? $d->updated_at->format('H:i:s d/m/Y') : ''; }) + ->editColumn('power_state', function ($d) { + if ($d->power_state == 'POWERED_ON') { + return 'Đang bật'; + } elseif ($d->power_state == 'POWERED_OFF') { + return 'Đang tắt'; + } + return $d->power_state ?? 'N/A'; + }) ->editColumn('datacenter.name', function ($d) { return $d->datacenter ? $d->datacenter->name . ' - ' . $d->datacenter->datacenter_id : 'N/A'; }) - ->rawColumns(['action']) + ->rawColumns(['action', 'power_state']) ->make(true); } } \ No newline at end of file diff --git a/app/Http/Services/vCenter.php b/app/Http/Services/vCenter.php index 309bdb1..3898ece 100644 --- a/app/Http/Services/vCenter.php +++ b/app/Http/Services/vCenter.php @@ -1141,6 +1141,74 @@ PS; ]; } + public function GetVMStats($vmware_id = ''): array + { + $stats = [ + 'cpu' => null, + 'memory' => null, + 'disk' => null, + ]; + + try { + $hardwareResponse = Http::withoutVerifying() + ->withHeaders([ + 'vmware-api-session-id' => $this->token + ]) + ->get("https://$this->apiUri/rest/vcenter/vm/$vmware_id/hardware")->json(); + + if (is_array($hardwareResponse) && !isset($hardwareResponse['type']) && isset($hardwareResponse['value'])) { + foreach ($hardwareResponse['value'] as $hardware) { + if (isset($hardware['label']) && $hardware['label'] === 'CPU') { + $stats['cpu'] = [ + 'cores' => $hardware['spec']['num_cores'] ?? 0, + 'cores_per_socket' => $hardware['spec']['cores_per_socket'] ?? 0, + ]; + } + if (isset($hardware['label']) && $hardware['label'] === 'Memory') { + $stats['memory'] = [ + 'size_MiB' => $hardware['spec']['size_MiB'] ?? 0, + 'size_GB' => round(($hardware['spec']['size_MiB'] ?? 0) / 1024, 2), + ]; + } + if (isset($hardware['label']) && $hardware['label'] === 'Hard disk') { + $stats['disk'][] = [ + 'capacity' => $hardware['capacity'] ?? 0, + 'capacity_GB' => round(($hardware['capacity'] ?? 0) / (1024 * 1024 * 1024), 2), + ]; + } + } + } + + $guestMemoryResponse = Http::withoutVerifying() + ->withHeaders([ + 'vmware-api-session-id' => $this->token + ]) + ->get("https://$this->apiUri/rest/vcenter/vm/$vmware_id/guest/memory")->json(); + + if (is_array($guestMemoryResponse) && !isset($guestMemoryResponse['type']) && isset($guestMemoryResponse['value'])) { + $stats['memory']['used_MiB'] = $guestMemoryResponse['value']['used'] ?? 0; + $stats['memory']['used_GB'] = round(($guestMemoryResponse['value']['used'] ?? 0) / 1024, 2); + $stats['memory']['usage_percent'] = $stats['memory']['size_MiB'] > 0 + ? round(($guestMemoryResponse['value']['used'] ?? 0) / $stats['memory']['size_MiB'] * 100, 1) + : 0; + } + + $guestCpuResponse = Http::withoutVerifying() + ->withHeaders([ + 'vmware-api-session-id' => $this->token + ]) + ->get("https://$this->apiUri/rest/vcenter/vm/$vmware_id/guest/cpu")->json(); + + if (is_array($guestCpuResponse) && !isset($guestCpuResponse['type']) && isset($guestCpuResponse['value'])) { + $stats['cpu']['used_percent'] = $guestCpuResponse['value']['used'] ?? 0; + } + + return ['error' => false, 'data' => $stats]; + } catch (\Exception $e) { + return ['error' => true, 'message' => $e->getMessage(), 'data' => $stats]; + } + } + public function ConfigCentOSVersion8($vmware_id = '', $product = [], $password = ''): void { $this->SetConfig($product->IpAddress->Host->Datacenter->vCenter); diff --git a/resources/views/administrator/pages/package/vm/datatable/action.blade.php b/resources/views/administrator/pages/package/vm/datatable/action.blade.php index 0b48d43..87c432b 100644 --- a/resources/views/administrator/pages/package/vm/datatable/action.blade.php +++ b/resources/views/administrator/pages/package/vm/datatable/action.blade.php @@ -1,6 +1,68 @@ -
- Edit - Delete + + + \ No newline at end of file diff --git a/resources/views/administrator/pages/package/vm/edit.blade.php b/resources/views/administrator/pages/package/vm/edit.blade.php index a1fc033..a559448 100644 --- a/resources/views/administrator/pages/package/vm/edit.blade.php +++ b/resources/views/administrator/pages/package/vm/edit.blade.php @@ -1,46 +1,92 @@ @extends("administrator.layout.master") + +@section('breadcrumb') + @php + $breadcrumbItems = [ + ['label' => 'Modules'], + ['label' => 'Package vCenter'], + ['label' => 'Virtual Machine', 'route' => 'package.vm.index'], + ['label' => $vm->name ?? $vm->vm_id ?? 'Edit', 'active' => true], + ]; + @endphp + +@endsection + @section("content") @if(auth()->guard('admins')->user()->can("add module vcenter"))
-
+ @csrf @method('put')
- - - {{--Name--}}
- +
- {{--Host ID--}} + {{--VM ID--}}
- +
- +
- {{--Template ID--}} + {{--Power State--}}
- +
- + @if($vm->power_state == 'POWERED_ON') + Đang bật + @elseif($vm->power_state == 'POWERED_OFF') + Đang tắt + @else + {{ $vm->power_state }} + @endif
- {{--Console ID--}} + {{--CPU--}}
- +
- + +
+
+ {{--RAM (MB)--}} +
+ +
+ +
+
+ {{--Datacenter--}} +
+ +
+ +
+
+ {{--Host--}} +
+ +
+
diff --git a/resources/views/administrator/pages/vm_console.blade.php b/resources/views/administrator/pages/vm_console.blade.php new file mode 100644 index 0000000..55a3cb2 --- /dev/null +++ b/resources/views/administrator/pages/vm_console.blade.php @@ -0,0 +1,314 @@ +@extends('administrator.layout.master') + +@push('style') + + +@endpush + +@section('breadcrumb') + @php + $breadcrumbItems = [ + ['label' => 'Modules'], + ['label' => 'Package vCenter'], + ['label' => 'Virtual Machine', 'route' => 'package.vm.index'], + ['label' => $vm->name ?? $vm->vm_id ?? 'Console', 'active' => true], + ]; + @endphp + +@endsection + +@section('content') +
+
+
+ +
+
+
{{ $vm->name ?? 'VM Console' }} ({{ $vm->vm_id }})
+
+ +
+
+
+
+
+ Power State +

{{ $vm->power_state ?? 'N/A' }}

+
+
+ CPU + @if($vmStats && isset($vmStats['cpu'])) +

+ {{ $vmStats['cpu']['cores'] ?? $vm->cpu ?? 'N/A' }} vCore + @if(isset($vmStats['cpu']['used_percent'])) + + {{ $vmStats['cpu']['used_percent'] }}% + + @endif +

+ @else +

{{ $vm->cpu ?? 'N/A' }} vCore

+ @endif +
+
+ RAM + @if($vmStats && isset($vmStats['memory'])) +

+ {{ $vmStats['memory']['size_GB'] ?? round(($vm->ram_mb ?? 0) / 1024, 2) }} GB + @if(isset($vmStats['memory']['used_GB'])) + + {{ $vmStats['memory']['used_GB'] }} GB ({{ $vmStats['memory']['usage_percent'] }}%) + + @endif +

+ @else +

{{ $vm->ram_mb ? round($vm->ram_mb / 1024, 2) : 'N/A' }} GB

+ @endif +
+
+ Disk + @if($vmStats && isset($vmStats['disk']) && count($vmStats['disk']) > 0) +

+ @foreach($vmStats['disk'] as $disk) + {{ $disk['capacity_GB'] }} GB + @endforeach +

+ @else +

N/A

+ @endif +
+
+ Datacenter +

{{ $vm->datacenter->name ?? 'N/A' }}

+
+
+ vCenter +

+ @if($vm->datacenter && $vm->datacenter->vCenter) + {{ $vm->datacenter->vCenter->host }} + @else + N/A + @endif +

+
+
+
+
+ + +
+
+
Console Controls
+
+
+
+
+ + +
+
+ +
+ + + +
+
+
+
+
+ + +
+
+ +
+
+
+
+
+
Connecting to vCenter...
+

Initializing console

+
+ + + +
+
+ Setting up session... +
+
+
+ + +
+
+
+
+
+
+
+ + + + + + + +@endsection diff --git a/routes/admin.php b/routes/admin.php index 235edfc..86f76d0 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -37,8 +37,10 @@ use App\Http\Controllers\admin\UserController; use App\Http\Controllers\admin\vm\VirtualMachineController; use App\Http\Controllers\Api\VpsActionController; use App\Http\Controllers\rest\ModuleController; +use App\Http\Services\vCenter; +use App\Models\ModuleVmware; +use Illuminate\Http\Request; use Illuminate\Support\Facades\Cookie; -use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Route; @@ -381,6 +383,92 @@ Route::prefix('')->group(function () { Route::post('task-reinstall', 'Reinstall')->name('task.reinstall'); }); + //Console Ticket API for Admin - using vm_id from VirtualMachines + Route::get('admin-get-console-ticket', function (Request $request, vCenter $serviceVCenter) { + $vm_id = $request->get('vm_id'); + if (!$vm_id) { + return response()->json(['error' => true, 'message' => "Please provide vm_id"]); + } + $vm = \App\Models\VirtualMachines::with('datacenter.vCenter')->where('vm_id', $vm_id)->first(); + if (!$vm) { + return response()->json(['error' => true, 'message' => "Can't find VM with vm_id: " . $vm_id]); + } + + $vcenter = null; + if ($vm->datacenter && $vm->datacenter->vCenter) { + $vcenter = $vm->datacenter->vCenter; + } + + if (!$vcenter) { + return response()->json(['error' => true, 'message' => "Can't find vCenter information"]); + } + + if (!$serviceVCenter->loginServer($vcenter)) { + return response()->json([ + 'error' => true, + 'message' => 'Không thể đăng nhập vào vCenter' + ]); + } + + $ConsoleTicket = $serviceVCenter->GetConsoleTickets($vm->vm_id); + + if ($ConsoleTicket === 'Login_Again') { + if ($serviceVCenter->loginServer($vcenter)) { + $ConsoleTicket = $serviceVCenter->GetConsoleTickets($vm->vm_id); + } else { + return response()->json([ + 'error' => true, + 'message' => 'Không thể đăng nhập lại vào vCenter' + ]); + } + } + + if (is_string($ConsoleTicket)) { + return response()->json([ + 'error' => true, + 'message' => "Failed to get console ticket: $ConsoleTicket" + ]); + } + + if ($ConsoleTicket['error']) { + return response()->json([ + 'error' => true, + 'message' => $ConsoleTicket['data'] + ]); + } + + return response()->json([ + 'error' => false, + 'data' => $ConsoleTicket['data'], + 'host' => $vcenter->host, + 'vmware-api-session-id' => $serviceVCenter->token + ])->cookie('vmware-api-session-id', $serviceVCenter->token, 60, '/', null, false, false); + })->name('admin.getConsoleTicket'); + + //Admin VM Console View - using vm_id + Route::get('view-admin-console/{vm_id}', function (Request $request, $vm_id, vCenter $serviceVCenter) { + $vm = \App\Models\VirtualMachines::with('datacenter.vCenter', 'host')->where('vm_id', $vm_id)->first(); + if (!$vm) { + return back()->withErrors("Can't find VM with vm_id: " . $vm_id); + } + + $vmStats = null; + if ($vm->datacenter && $vm->datacenter->vCenter) { + $vcenter = $vm->datacenter->vCenter; + if ($serviceVCenter->loginServer($vcenter)) { + $vmStats = $serviceVCenter->GetVMStats($vm->vm_id); + if ($vmStats['error']) { + $vmStats = null; + } else { + $vmStats = $vmStats['data']; + } + } + } + + $title = 'Console - ' . ($vm->name ?? 'VM ' . $vm_id); + return view('administrator.pages.vm_console', compact('title', 'vm', 'vmStats')); + })->name('admin.vm.console'); + //Invoice Route::controller(InvoiceController::class)->group(function () { Route::get('list-all-invoice', 'index')->name('administrator.pages.invoice.index');