Add all files
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Servers\Proxmox\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Paymenter\Extensions\Servers\Proxmox\Models\Server;
|
||||
use Paymenter\Extensions\Servers\Proxmox\Models\ServerStatistics;
|
||||
use Paymenter\Extensions\Servers\Proxmox\Proxmox;
|
||||
|
||||
class PollServerStatistics extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'proxmox:poll-statistics';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Poll Proxmox server statistics hourly (runs in background)';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
// Poll all servers regardless of status or power condition
|
||||
$servers = Server::all();
|
||||
$this->info('Polling statistics for ' . $servers->count() . ' servers...');
|
||||
|
||||
$successCount = 0;
|
||||
$errorCount = 0;
|
||||
$maxAutomaticStatistics = 24;
|
||||
|
||||
foreach ($servers as $server) {
|
||||
try {
|
||||
$proxmox = new Proxmox;
|
||||
$status = $proxmox->request(
|
||||
'/nodes/' . $server->node->name . '/qemu/' . $server->vm_id . '/status/current'
|
||||
)->json()['data'] ?? null;
|
||||
|
||||
if ($status) {
|
||||
// Get disk I/O from rrddata (latest point)
|
||||
$diskRead = 0;
|
||||
$diskWrite = 0;
|
||||
$diskUsed = $status['disk'] ?? 0;
|
||||
$diskMax = $status['maxdisk'] ?? 0;
|
||||
|
||||
try {
|
||||
$rrdData = $proxmox->request(
|
||||
'/nodes/' . $server->node->name . '/qemu/' . $server->vm_id . '/rrddata',
|
||||
'get',
|
||||
['timeframe' => 'hour', 'cf' => 'AVERAGE']
|
||||
)->json()['data'] ?? null;
|
||||
|
||||
if ($rrdData && is_array($rrdData) && count($rrdData) > 0) {
|
||||
$latest = end($rrdData);
|
||||
$diskRead = (int)($latest['diskread'] ?? 0);
|
||||
$diskWrite = (int)($latest['diskwrite'] ?? 0);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// If rrddata fails, continue with 0 values
|
||||
}
|
||||
|
||||
// Always try to get diskmax from VM config (more reliable than status endpoint)
|
||||
// The status endpoint may not return maxdisk, especially if guest agent is not enabled
|
||||
try {
|
||||
$config = $proxmox->request(
|
||||
'/nodes/' . $server->node->name . '/qemu/' . $server->vm_id . '/config'
|
||||
)->json()['data'] ?? null;
|
||||
|
||||
if ($config) {
|
||||
// Try to find disk configuration (scsi0, virtio0, sata0, ide0, scsi1, etc.)
|
||||
$diskKeys = ['scsi0', 'scsi1', 'scsi2', 'virtio0', 'virtio1', 'virtio2', 'sata0', 'sata1', 'sata2', 'ide0', 'ide1', 'ide2'];
|
||||
$totalDiskSize = 0;
|
||||
|
||||
foreach ($diskKeys as $diskKey) {
|
||||
if (isset($config[$diskKey])) {
|
||||
// Parse disk size from format like "local-lvm:vm-100-disk-0,size=32G"
|
||||
$diskConfig = $config[$diskKey];
|
||||
if (preg_match('/size=(\d+)([TGMK]?)/i', $diskConfig, $matches)) {
|
||||
$size = (int)$matches[1];
|
||||
$unit = strtoupper($matches[2] ?? 'G');
|
||||
|
||||
// Convert to bytes
|
||||
switch ($unit) {
|
||||
case 'T':
|
||||
$totalDiskSize += $size * 1024 * 1024 * 1024 * 1024;
|
||||
break;
|
||||
case 'G':
|
||||
$totalDiskSize += $size * 1024 * 1024 * 1024;
|
||||
break;
|
||||
case 'M':
|
||||
$totalDiskSize += $size * 1024 * 1024;
|
||||
break;
|
||||
case 'K':
|
||||
$totalDiskSize += $size * 1024;
|
||||
break;
|
||||
default:
|
||||
$totalDiskSize += $size * 1024 * 1024 * 1024; // Default to GB
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use config disk size if we found it, otherwise keep status value
|
||||
if ($totalDiskSize > 0) {
|
||||
$diskMax = $totalDiskSize;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// If config fetch fails, continue with status value
|
||||
}
|
||||
|
||||
// Create the new automatic statistic
|
||||
ServerStatistics::create([
|
||||
'server_id' => $server->id,
|
||||
'netin' => $status['netin'] ?? 0,
|
||||
'netout' => $status['netout'] ?? 0,
|
||||
'diskread' => $diskRead,
|
||||
'diskwrite' => $diskWrite,
|
||||
'diskused' => $diskUsed,
|
||||
'diskmax' => $diskMax,
|
||||
'memoryused' => $status['mem'] ?? 0,
|
||||
'memorymax' => $status['maxmem'] ?? 0,
|
||||
'cpuused' => $status['cpu'] ?? 0,
|
||||
'cpumax' => 1.0, // CPU max is typically 1.0 (100%)
|
||||
'uptime' => $status['uptime'] ?? 0,
|
||||
'vmstatus' => $status['status'] ?? null,
|
||||
'DateTime' => now(),
|
||||
'is_automatic' => true,
|
||||
]);
|
||||
|
||||
// Maintain maximum of 24 automatic statistics per server
|
||||
// Remove oldest automatic statistics if limit is exceeded
|
||||
$automaticStatsCount = ServerStatistics::where('server_id', $server->id)
|
||||
->where('is_automatic', true)
|
||||
->count();
|
||||
|
||||
if ($automaticStatsCount > $maxAutomaticStatistics) {
|
||||
$excessCount = $automaticStatsCount - $maxAutomaticStatistics;
|
||||
$oldestStats = ServerStatistics::where('server_id', $server->id)
|
||||
->where('is_automatic', true)
|
||||
->orderBy('DateTime', 'asc')
|
||||
->limit($excessCount)
|
||||
->get();
|
||||
|
||||
foreach ($oldestStats as $oldStat) {
|
||||
$oldStat->delete();
|
||||
}
|
||||
}
|
||||
|
||||
$successCount++;
|
||||
} else {
|
||||
$this->warn('No status data for server ID: ' . $server->id);
|
||||
$errorCount++;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->error('Error polling server ID ' . $server->id . ': ' . $e->getMessage());
|
||||
$errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Statistics polling completed. Success: ' . $successCount . ', Errors: ' . $errorCount);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user