feat: Implement VM console access, display VM power state, and add VM statistics retrieval.

This commit is contained in:
pbao9
2026-02-14 16:28:13 +07:00
parent 449502e290
commit 498a57b499
6 changed files with 610 additions and 23 deletions
@@ -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 '<span class="badge bg-success">Đang bật</span>';
} elseif ($d->power_state == 'POWERED_OFF') {
return '<span class="badge bg-secondary">Đang tắt</span>';
}
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);
}
}
+68
View File
@@ -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);
@@ -1,6 +1,68 @@
<div class="d-flex gap-2">
<x-button-crm type="button" variant="primary" size="sm" route="package.vm.edit" :params="['id' => $row->id]"
icon="pencil">Edit</x-button-crm>
<x-button-crm type="button" variant="danger" size="sm" route="package.vm.destroy" :params="['id' => $row->id]"
icon="trash">Delete</x-button-crm>
<div class="dt-action-dropdown dropdown">
<button class="btn btn-sm btn-light d-flex align-items-center gap-1" type="button" data-bs-toggle="dropdown"
aria-expanded="false">
<i data-lucide="more-vertical" width="14" height="14"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="{{ route('package.vm.edit', ['id' => $row->id]) }}">
<i data-lucide="eye" width="14" height="14" class="me-2"></i>
View
</a>
</li>
<li>
<a class="dropdown-item" href="{{ route('package.vm.edit', ['id' => $row->id]) }}">
<i data-lucide="pencil" width="14" height="14" class="me-2"></i>
Edit
</a>
</li>
@if($row->power_state == 'POWERED_ON')
<li>
<a class="dropdown-item" href="{{ route('admin.vm.console', ['vm_id' => $row->vm_id]) }}" target="_blank">
<i data-lucide="monitor" width="14" height="14" class="me-2"></i>
Console
</a>
</li>
@else
<li>
<span class="dropdown-item disabled">
<i data-lucide="monitor" width="14" height="14" class="me-2"></i>
Console (Off)
</span>
</li>
@endif
<li>
<hr class="dropdown-divider">
</li>
<li>
<button type="button" class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal{{ $row->id }}">
<i data-lucide="trash-2" width="14" height="14" class="me-2 text-danger"></i>
Delete
</button>
</li>
</ul>
</div>
<div class="modal fade" id="deleteModal{{ $row->id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<form action="{{ route('package.vm.destroy', ['id' => $row->id]) }}" method="POST" class="d-inline">
@csrf
@method('DELETE')
<input type="hidden" name="id" value="{{ $row->id }}">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Delete VM</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Are you sure you want to delete this VM?
<strong>{{ $row->name ?? $row->vm_id }}</strong>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-danger">Delete</button>
</div>
</div>
</form>
</div>
</div>
@@ -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
<x-admin-breadcrumb :items="$breadcrumbItems" />
@endsection
@section("content")
@if(auth()->guard('admins')->user()->can("add module vcenter"))
<div class="card card-outline card-primary">
<form action="{{ route('package.host.update',['id' => $host->id]) }}" method="post">
<form action="{{ route('package.vm.update',['id' => $vm->id]) }}" method="post">
@csrf
@method('put')
<div class="card-body">
<x-module.v-center :model="$host->datacenter" />
<x-module.datacenter :model="$host" />
{{--Name--}}
<div class="row form-group align-items-center mb-0">
<label for="name" class="col-md-3 text-right">Name</label>
<div class="col-md-9 p-1 bg-customer">
<input type="text" id="name" name="name" placeholder="" value="{{ $host->name}}" class="form-control">
<input type="text" id="name" name="name" placeholder="" value="{{ $vm->name}}" class="form-control">
</div>
</div>
{{--Host ID--}}
{{--VM ID--}}
<div class="row form-group align-items-center mb-0">
<label for="host_id" class="col-md-3 text-right">Host ID</label>
<label for="vm_id" class="col-md-3 text-right">VM ID</label>
<div class="col-md-9 p-1 bg-customer">
<input type="text" id="host_id" name="host_id" placeholder="" value="{{ $host->host_id}}" class="form-control">
<input type="text" id="vm_id" name="vm_id" placeholder="" value="{{ $vm->vm_id}}" class="form-control">
</div>
</div>
{{--Template ID--}}
{{--Power State--}}
<div class="row form-group align-items-center mb-0">
<label for="template" class="col-md-3 text-right">Template ID</label>
<label for="power_state" class="col-md-3 text-right">Power State</label>
<div class="col-md-9 p-1 bg-customer">
<input type="text" id="template" name="template" placeholder="" value="{{ $host->template}}" class="form-control">
@if($vm->power_state == 'POWERED_ON')
<span class="badge bg-success">Đang bật</span>
@elseif($vm->power_state == 'POWERED_OFF')
<span class="badge bg-secondary">Đang tắt</span>
@else
{{ $vm->power_state }}
@endif
</div>
</div>
{{--Console ID--}}
{{--CPU--}}
<div class="row form-group align-items-center mb-0">
<label for="console" class="col-md-3 text-right">Console Name</label>
<label for="cpu" class="col-md-3 text-right">CPU</label>
<div class="col-md-9 p-1 bg-customer">
<input type="text" id="console" name="console" placeholder="consolenode129.tgs.com.vn" value="{{ $host->console}}" class="form-control">
<input type="text" id="cpu" name="cpu" placeholder="" value="{{ $vm->cpu}}" class="form-control">
</div>
</div>
{{--RAM (MB)--}}
<div class="row form-group align-items-center mb-0">
<label for="ram_mb" class="col-md-3 text-right">RAM (MB)</label>
<div class="col-md-9 p-1 bg-customer">
<input type="text" id="ram_mb" name="ram_mb" placeholder="" value="{{ $vm->ram_mb}}" class="form-control">
</div>
</div>
{{--Datacenter--}}
<div class="row form-group align-items-center mb-0">
<label for="datacenter_id" class="col-md-3 text-right">Datacenter</label>
<div class="col-md-9 p-1 bg-customer">
<input type="text" id="datacenter_id" name="datacenter_id" placeholder="" value="{{ $vm->datacenter_id}}" class="form-control">
</div>
</div>
{{--Host--}}
<div class="row form-group align-items-center mb-0">
<label for="host_id" class="col-md-3 text-right">Host</label>
<div class="col-md-9 p-1 bg-customer">
<input type="text" id="host_id" name="host_id" placeholder="" value="{{ $vm->host_id}}" class="form-control">
</div>
</div>
</div>
<div class="card-footer">
<a href="{{ route('package.host.index') }}" class="btn btn-outline-secondary">Trở về</a>
<button type="submit" class="btn btn-outline-primary">Lưu dữ liệu</button>
<a href="{{ route('package.vm.index') }}" class="btn btn-secondary">Back</a>
<button type="submit" class="btn btn-primary">Save</button>
@if($vm->vm_id && $vm->power_state == 'POWERED_ON')
<a href="{{ route('admin.vm.console', ['vm_id' => $vm->vm_id]) }}" target="_blank" class="btn btn-info">
<i class="fas fa-terminal"></i> Console
</a>
@elseif($vm->vm_id)
<span class="btn btn-secondary disabled">
<i class="fas fa-terminal"></i> Console (Off)
</span>
@endif
</div>
</form>
</div>
@@ -0,0 +1,314 @@
@extends('administrator.layout.master')
@push('style')
<link rel="stylesheet" href="{{ asset('lib/wmks/css/wmks-all.css') }}">
<link rel="stylesheet" href="{{ asset('css/service/console.css') }}">
@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
<x-admin-breadcrumb :items="$breadcrumbItems" />
@endsection
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-12">
<!-- VM Info Card -->
<div class="card card-outline card-secondary mb-3">
<div class="card-header d-flex bg-white align-items-center justify-content-between">
<h5 class="card-title">{{ $vm->name ?? 'VM Console' }} ({{ $vm->vm_id }})</h5>
<div class="card-tools">
<x-button-c-r-m route="package.vm.index" label="Tất cả VM"/>
</div>
</div>
<div class="card-body p-2">
<div class="row">
<div class="col-md-2">
<small class="text-muted">Power State</small>
<p class="mb-0">{{ $vm->power_state ?? 'N/A' }}</p>
</div>
<div class="col-md-2">
<small class="text-muted">CPU</small>
@if($vmStats && isset($vmStats['cpu']))
<p class="mb-0">
{{ $vmStats['cpu']['cores'] ?? $vm->cpu ?? 'N/A' }} vCore
@if(isset($vmStats['cpu']['used_percent']))
<span class="badge bg-{{ $vmStats['cpu']['used_percent'] > 80 ? 'danger' : 'success' }} ml-1">
{{ $vmStats['cpu']['used_percent'] }}%
</span>
@endif
</p>
@else
<p class="mb-0">{{ $vm->cpu ?? 'N/A' }} vCore</p>
@endif
</div>
<div class="col-md-2">
<small class="text-muted">RAM</small>
@if($vmStats && isset($vmStats['memory']))
<p class="mb-0">
{{ $vmStats['memory']['size_GB'] ?? round(($vm->ram_mb ?? 0) / 1024, 2) }} GB
@if(isset($vmStats['memory']['used_GB']))
<span class="badge bg-{{ $vmStats['memory']['usage_percent'] > 80 ? 'danger' : 'success' }} ml-1">
{{ $vmStats['memory']['used_GB'] }} GB ({{ $vmStats['memory']['usage_percent'] }}%)
</span>
@endif
</p>
@else
<p class="mb-0">{{ $vm->ram_mb ? round($vm->ram_mb / 1024, 2) : 'N/A' }} GB</p>
@endif
</div>
<div class="col-md-2">
<small class="text-muted">Disk</small>
@if($vmStats && isset($vmStats['disk']) && count($vmStats['disk']) > 0)
<p class="mb-0">
@foreach($vmStats['disk'] as $disk)
{{ $disk['capacity_GB'] }} GB
@endforeach
</p>
@else
<p class="mb-0">N/A</p>
@endif
</div>
<div class="col-md-2">
<small class="text-muted">Datacenter</small>
<p class="mb-0">{{ $vm->datacenter->name ?? 'N/A' }}</p>
</div>
<div class="col-md-2">
<small class="text-muted">vCenter</small>
<p class="mb-0">
@if($vm->datacenter && $vm->datacenter->vCenter)
{{ $vm->datacenter->vCenter->host }}
@else
N/A
@endif
</p>
</div>
</div>
</div>
</div>
<!-- Control Panel -->
<div class="card mb-3">
<div class="card-header bg-white">
<h5 class="card-title fw-bold">Console Controls</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3">
<label for="sendStringText">Send Text:</label>
<input id="sendStringText" class="form-control" type="text"
placeholder="Enter text...">
</div>
<div class="col-md-6 mb-3">
<label>&nbsp;</label>
<div class="btn-group d-flex" role="group">
<button onclick="enterFullScreen()" class="btn btn-primary btn-sm flex-fill">
<i class="fas fa-expand"></i> Fullscreen
</button>
<button onclick="sendCAD()" class="btn btn-info btn-sm flex-fill">
<i class="fas fa-keyboard"></i> Ctrl+Alt+Del
</button>
<button onclick="sendInputString()" class="btn btn-success btn-sm flex-fill">
<i class="fas fa-paper-plane"></i> Send
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Console Container -->
<div class="card">
<div class="card-body p-0 position-relative">
<!-- Loading State -->
<div id="loadingState"
class="loading-container d-flex align-items-center justify-content-center text-dark fade-transition"
style="height: 70vh;">
<div class="text-center">
<div class="spinner-box">
<div class="three-quarter-spinner"></div>
</div>
<h5 class="mb-3">Connecting to vCenter...</h5>
<p class="text-muted" id="loadingText">Initializing console</p>
<div class="loading-dots justify-content-center">
<span></span>
<span></span>
<span></span>
</div>
<div class="mt-3">
<small class="text-muted" id="connectionStatus">Setting up session...</small>
</div>
</div>
</div>
<!-- Console Display -->
<div id="wmksContainer"
class="console-container d-flex justify-content-center align-items-center bg-dark fade-transition"
style="height: 70vh; opacity: 0; position: absolute; top: 0; left: 0; right: 0;">
</div>
</div>
</div>
</div>
</div>
</div>
<script src="{{ asset('lib/jquery/jquery.min.js') }}"></script>
<script src="{{ asset('lib/jquery-ui/jquery-ui.min.js') }}"></script>
<script src="{{ asset('lib/wmks/wmks.min.js') }}"></script>
<script src="{{ asset('lib/wmks/wmks-sdk-example.js') }}"></script>
<script type="text/javascript">
let loadingSteps = [
'{{ __('Khởi tạo kết nối...') }}',
'{{ __('Đang xác thực...') }}',
'{{ __('Thiết lập phiên làm việc...') }}',
'{{ __('Đang kết nối Console...') }}',
'{{ __('Sẵn sàng!') }}'
];
let currentStep = 0;
$(document).ready(function() {
showLoadingState();
animateLoadingText();
});
function showLoadingState() {
$('#loadingState').css('opacity', '1').show();
$('#wmksContainer').css('opacity', '0').hide();
}
function hideLoadingAndShowConsole() {
$('#loadingText').text('{{ __('Kết nối thành công!') }}');
$('#connectionStatus').text('{{ __('Console đã sẵn sàng') }}');
setTimeout(() => {
$('#loadingState').animate({ opacity: 0 }, 400, function() {
$(this).hide();
$('#wmksContainer').show().animate({ opacity: 1 }, 400);
});
}, 800);
}
function showErrorState(errorMessage) {
$('#loadingState').animate({ opacity: 0 }, 300, function() {
$(this).hide();
$('#wmksContainer').html(`
<div class="d-flex align-items-center justify-content-center w-100 h-100">
<div class="text-center text-black">
<div class="mb-3">
<i class="fas fa-exclamation-triangle text-danger" style="font-size: 3rem;"></i>
</div>
<h5>{{ __('Kết nối thất bại') }}</h5>
<p class="text-muted">${errorMessage || '{{ __('Máy chủ không sẵn sàng hoặc có lỗi kết nối') }}'}</p>
<button onclick="location.reload()" class="btn btn-primary">
<i class="fas fa-redo"></i> {{ __('Thử lại') }}
</button>
</div>
</div>
`).show().animate({ opacity: 1 }, 400);
});
}
function animateLoadingText() {
setInterval(() => {
if (currentStep < loadingSteps.length - 1) {
currentStep++;
$('#loadingText').fadeOut(200, function() {
$(this).text(loadingSteps[currentStep]).fadeIn(200);
});
}
}, 1500);
}
function updateConnectionStatus(status) {
$('#connectionStatus').fadeOut(200, function() {
$(this).text(status).fadeIn(200);
});
}
let WMKSContainer = $('#wmksContainer');
var wmks = WMKS.createWMKS("wmksContainer", {})
.register(WMKS.CONST.Events.CONNECTION_STATE_CHANGE, function(event, data) {
if (data.state === WMKS.CONST.ConnectionState.CONNECTED) {
console.log("connection state change : connected");
hideLoadingAndShowConsole();
}
});
const getTicket = new Promise((resolve, reject) => {
updateConnectionStatus('{{ __('Đang lấy ticket xác thực...') }}');
$.ajax({
url: '{{ route('admin.getConsoleTicket') }}',
type: 'get',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json'
},
data: {
vm_id: '{{ $vm->vm_id }}'
},
success: function(response) {
if (response.error === false) {
updateConnectionStatus('{{ __('Đang thiết lập kết nối Console...') }}');
resolve(response);
} else {
reject(response.message);
}
},
error: function(xhr, status, error) {
reject(`{{ __('Lỗi kết nối:') }} ${error}`);
}
});
});
getTicket
.then(result => {
if (result !== false) {
updateConnectionStatus('{{ __('Đang kết nối đến Console...') }}');
const wmksHost = '{{ config('vmware.wmks_host', '10.10.10.120') }}';
wmks.connect(`wss://${wmksHost}/ticket/${result.data}`);
}
})
.catch(error => {
console.log(`Có lỗi xảy ra: ${error}.`);
showErrorState(error);
});
function enterFullScreen() {
if (wmks && wmks.canFullScreen()) {
wmks.enterFullScreen();
}
}
function sendCAD() {
if (wmks) {
wmks.sendCAD();
}
}
function sendInputString() {
const inputText = $('#sendStringText').val().trim();
if (inputText && wmks) {
wmks.sendInputString(inputText);
$('#sendStringText').val('');
} else if (!inputText) {
alert('{{ __('Vui lòng nhập văn bản cần gửi') }}');
}
}
$('#sendStringText').on('keypress', function(e) {
if (e.which == 13) {
sendInputString();
}
});
</script>
@endsection
+89 -1
View File
@@ -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');