feat: Implement VM console access, display VM power state, and add VM statistics retrieval.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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> </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
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user