- Changed modal close event from `@click.away` to `@click.self` for better control over closing modals. - Updated various modal button styles for consistency and improved accessibility. - Adjusted transition durations for modal animations to enhance user experience. - Added aria-labels to close buttons for better screen reader support.
206 lines
7.2 KiB
PHP
206 lines
7.2 KiB
PHP
<?php
|
|
|
|
namespace Paymenter\Extensions\Servers\Proxmox\Livewire;
|
|
|
|
use Paymenter\Extensions\Servers\Proxmox\Models\Server as ServerModel;
|
|
use Paymenter\Extensions\Servers\Proxmox\Models\ServerStatistics;
|
|
use Paymenter\Extensions\Servers\Proxmox\Proxmox;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
class Server extends Component
|
|
{
|
|
protected int $maxRetries = 3;
|
|
public int $loadAttempts = 0;
|
|
|
|
public function mount(ServerModel $server): void
|
|
{
|
|
parent::mount($server);
|
|
$this->loadAttempts = 1;
|
|
|
|
// Check and enforce bandwidth throttling if exceeded (only if status loaded)
|
|
if ($this->status !== null) {
|
|
$this->checkAndEnforceBandwidthLimit();
|
|
}
|
|
}
|
|
|
|
public function getMonthlyTrafficProperty()
|
|
{
|
|
$startOfMonth = Carbon::now()->startOfMonth();
|
|
|
|
// Get the earliest and latest statistics for this month
|
|
$firstStat = $this->server->statistics()
|
|
->where('DateTime', '>=', $startOfMonth)
|
|
->orderBy('DateTime', 'asc')
|
|
->first();
|
|
|
|
$latestStat = $this->server->statistics()
|
|
->where('DateTime', '>=', $startOfMonth)
|
|
->orderBy('DateTime', 'desc')
|
|
->first();
|
|
|
|
if (!$firstStat || !$latestStat) {
|
|
return 0;
|
|
}
|
|
|
|
// Calculate total traffic (in + out) for the month
|
|
$netinDiff = max(0, $latestStat->netin - $firstStat->netin);
|
|
$netoutDiff = max(0, $latestStat->netout - $firstStat->netout);
|
|
$totalBytes = $netinDiff + $netoutDiff;
|
|
|
|
// Convert to GiB
|
|
return round($totalBytes / (1024 * 1024 * 1024), 2);
|
|
}
|
|
|
|
public function getBandwidthThreshold95Property()
|
|
{
|
|
// Cache for 15 minutes to avoid recalculating for every server
|
|
return Cache::remember('bandwidth_threshold_95_' . Carbon::now()->format('Y-m'), 900, function () {
|
|
$startOfMonth = Carbon::now()->startOfMonth();
|
|
|
|
// Get all servers
|
|
$allServers = ServerModel::all();
|
|
|
|
if ($allServers->isEmpty()) {
|
|
return 10; // Minimum threshold of 10 GiB
|
|
}
|
|
|
|
$trafficValues = [];
|
|
|
|
foreach ($allServers as $server) {
|
|
// Get the earliest and latest statistics for this month for each server
|
|
$firstStat = ServerStatistics::where('server_id', $server->id)
|
|
->where('DateTime', '>=', $startOfMonth)
|
|
->orderBy('DateTime', 'asc')
|
|
->first();
|
|
|
|
$latestStat = ServerStatistics::where('server_id', $server->id)
|
|
->where('DateTime', '>=', $startOfMonth)
|
|
->orderBy('DateTime', 'desc')
|
|
->first();
|
|
|
|
if ($firstStat && $latestStat) {
|
|
// Calculate total traffic (in + out) for the month
|
|
$netinDiff = max(0, $latestStat->netin - $firstStat->netin);
|
|
$netoutDiff = max(0, $latestStat->netout - $firstStat->netout);
|
|
$totalBytes = $netinDiff + $netoutDiff;
|
|
|
|
// Convert to GiB and collect
|
|
$trafficValues[] = $totalBytes / (1024 * 1024 * 1024);
|
|
}
|
|
}
|
|
|
|
if (empty($trafficValues)) {
|
|
return 10; // Minimum threshold of 10 GiB
|
|
}
|
|
|
|
// Calculate actual 95th percentile by sorting and taking the value at the 95th rank
|
|
sort($trafficValues);
|
|
$index = (int) ceil(0.95 * count($trafficValues)) - 1;
|
|
$threshold95Percent = $trafficValues[$index];
|
|
|
|
// Return the maximum of 95th percentile or 10 GiB minimum
|
|
return round(max($threshold95Percent, 10), 2);
|
|
});
|
|
}
|
|
|
|
protected function checkAndEnforceBandwidthLimit()
|
|
{
|
|
$monthlyTraffic = $this->monthlyTraffic;
|
|
$threshold = $this->bandwidthThreshold95;
|
|
|
|
// Check if bandwidth is exceeded
|
|
if ($monthlyTraffic > $threshold) {
|
|
// Throttle the server to 1 MiB/s
|
|
$this->throttleServerBandwidth();
|
|
}
|
|
}
|
|
|
|
protected function throttleServerBandwidth()
|
|
{
|
|
try {
|
|
$proxmox = new \Paymenter\Extensions\Servers\Proxmox\Proxmox;
|
|
|
|
// Get current VM config
|
|
$config = $proxmox->request('/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/config')->json()['data'] ?? [];
|
|
|
|
$updates = [];
|
|
|
|
// Update all network interfaces to rate limit of 1 MiB/s
|
|
foreach ($config as $key => $value) {
|
|
if (preg_match('/^net\d+$/', $key)) {
|
|
// Parse existing network config
|
|
$parts = explode(',', $value);
|
|
$newParts = [];
|
|
$hasRate = false;
|
|
|
|
foreach ($parts as $part) {
|
|
if (strpos($part, 'rate=') === 0) {
|
|
// Replace existing rate
|
|
$newParts[] = 'rate=1';
|
|
$hasRate = true;
|
|
} else {
|
|
$newParts[] = $part;
|
|
}
|
|
}
|
|
|
|
// Add rate if not present
|
|
if (!$hasRate) {
|
|
$newParts[] = 'rate=1';
|
|
}
|
|
|
|
$updates[$key] = implode(',', $newParts);
|
|
}
|
|
}
|
|
|
|
// Apply the updates if any network interfaces were found
|
|
if (!empty($updates)) {
|
|
$proxmox->request('/nodes/' . $this->server->node->name . '/qemu/' . $this->server->vm_id . '/config', 'put', $updates);
|
|
}
|
|
} catch (\Throwable $e) {
|
|
// Log the error but don't fail the page load
|
|
\Log::error('Failed to throttle bandwidth for server ' . $this->server->id, [
|
|
'error' => $e->getMessage(),
|
|
'server_id' => $this->server->id,
|
|
]);
|
|
}
|
|
}
|
|
|
|
public function retryLoadStatus(): void
|
|
{
|
|
$this->loadAttempts++;
|
|
$this->loadVmStatus();
|
|
|
|
// Check bandwidth throttling on successful load
|
|
if ($this->status !== null) {
|
|
$this->checkAndEnforceBandwidthLimit();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Auto-retry loading status via polling (called by wire:poll when status is null)
|
|
*/
|
|
public function autoRetryLoadStatus(): void
|
|
{
|
|
if ($this->status === null && $this->loadAttempts < $this->maxRetries) {
|
|
$this->loadAttempts++;
|
|
$this->loadVmStatus();
|
|
|
|
// Check bandwidth throttling on successful load
|
|
if ($this->status !== null) {
|
|
$this->checkAndEnforceBandwidthLimit();
|
|
}
|
|
}
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('proxmox::server');
|
|
}
|
|
|
|
public function placeholder(array $params = [])
|
|
{
|
|
return view('proxmox::serverplaceholder', $params);
|
|
}
|
|
}
|