159 lines
5.5 KiB
PHP
159 lines
5.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\Mailbox;
|
|
use App\Models\User;
|
|
use App\Models\UserResourceUsage;
|
|
use App\Models\UserSetting;
|
|
use App\Services\Agent\AgentClient;
|
|
use Exception;
|
|
use Illuminate\Console\Command;
|
|
|
|
class CollectUserUsage extends Command
|
|
{
|
|
protected $signature = 'jabali:collect-user-usage {--user=} {--retain=90}';
|
|
|
|
protected $description = 'Collect per-user resource usage snapshots';
|
|
|
|
public function handle(): int
|
|
{
|
|
$users = User::query()
|
|
->where('is_admin', false)
|
|
->where('is_active', true);
|
|
|
|
$userFilter = $this->option('user');
|
|
if ($userFilter) {
|
|
$users->where(function ($query) use ($userFilter) {
|
|
$query->where('id', $userFilter)
|
|
->orWhere('username', $userFilter);
|
|
});
|
|
}
|
|
|
|
$users = $users->get();
|
|
if ($users->isEmpty()) {
|
|
$this->info('No users found for usage collection.');
|
|
|
|
return 0;
|
|
}
|
|
|
|
$agent = new AgentClient;
|
|
$capturedAt = now();
|
|
|
|
foreach ($users as $user) {
|
|
$diskBytes = $user->getDiskUsageBytes();
|
|
$mailBytes = (int) Mailbox::where('user_id', $user->id)->sum('quota_used_bytes');
|
|
$dbBytes = $this->getDatabaseUsage($agent, $user);
|
|
$bandwidthTotal = $this->getBandwidthTotal($agent, $user);
|
|
$resourceStats = $this->getUserResourceStats($agent, $user);
|
|
$cpuPercent = (int) round($resourceStats['cpu_percent'] ?? 0);
|
|
$memoryBytes = (int) ($resourceStats['memory_bytes'] ?? 0);
|
|
$diskIoTotal = (int) ($resourceStats['disk_io_total_bytes'] ?? 0);
|
|
$lastBandwidthTotal = (int) UserSetting::getForUser($user->id, 'bandwidth_total_bytes', 0);
|
|
$lastDiskIoTotal = (int) UserSetting::getForUser($user->id, 'disk_io_total_bytes', 0);
|
|
$bandwidthDelta = $bandwidthTotal >= $lastBandwidthTotal
|
|
? $bandwidthTotal - $lastBandwidthTotal
|
|
: $bandwidthTotal;
|
|
$diskIoDelta = $diskIoTotal >= $lastDiskIoTotal
|
|
? $diskIoTotal - $lastDiskIoTotal
|
|
: $diskIoTotal;
|
|
|
|
$this->storeMetric($user->id, 'disk_bytes', $diskBytes, $capturedAt);
|
|
$this->storeMetric($user->id, 'mail_bytes', $mailBytes, $capturedAt);
|
|
$this->storeMetric($user->id, 'database_bytes', $dbBytes, $capturedAt);
|
|
$this->storeMetric($user->id, 'bandwidth_bytes', $bandwidthDelta, $capturedAt);
|
|
$this->storeMetric($user->id, 'cpu_percent', $cpuPercent, $capturedAt);
|
|
$this->storeMetric($user->id, 'memory_bytes', $memoryBytes, $capturedAt);
|
|
$this->storeMetric($user->id, 'disk_io_bytes', $diskIoDelta, $capturedAt);
|
|
|
|
UserSetting::setForUser($user->id, 'bandwidth_total_bytes', $bandwidthTotal);
|
|
UserSetting::setForUser($user->id, 'disk_io_total_bytes', $diskIoTotal);
|
|
|
|
$this->line("Collected usage for {$user->username}");
|
|
}
|
|
|
|
$retainDays = (int) $this->option('retain');
|
|
if ($retainDays > 0) {
|
|
UserResourceUsage::where('captured_at', '<', now()->subDays($retainDays))->delete();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
protected function getDatabaseUsage(AgentClient $agent, User $user): int
|
|
{
|
|
try {
|
|
$result = $agent->mysqlListDatabases($user->username);
|
|
$databases = $result['databases'] ?? [];
|
|
$total = 0;
|
|
|
|
foreach ($databases as $database) {
|
|
$total += (int) ($database['size_bytes'] ?? 0);
|
|
}
|
|
|
|
return $total;
|
|
} catch (Exception $e) {
|
|
$this->warn("Failed to fetch database usage for {$user->username}: {$e->getMessage()}");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
protected function getBandwidthTotal(AgentClient $agent, User $user): int
|
|
{
|
|
try {
|
|
$result = $agent->send('usage.bandwidth_total', [
|
|
'username' => $user->username,
|
|
]);
|
|
|
|
if ($result['success'] ?? false) {
|
|
return (int) ($result['total_bytes'] ?? 0);
|
|
}
|
|
} catch (Exception $e) {
|
|
$this->warn("Failed to fetch bandwidth usage for {$user->username}: {$e->getMessage()}");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @return array{cpu_percent: float, memory_bytes: int, disk_io_total_bytes: int}
|
|
*/
|
|
protected function getUserResourceStats(AgentClient $agent, User $user): array
|
|
{
|
|
try {
|
|
$result = $agent->send('usage.user_resources', [
|
|
'username' => $user->username,
|
|
]);
|
|
|
|
if ($result['success'] ?? false) {
|
|
return [
|
|
'cpu_percent' => (float) ($result['cpu_percent'] ?? 0),
|
|
'memory_bytes' => (int) ($result['memory_bytes'] ?? 0),
|
|
'disk_io_total_bytes' => (int) ($result['disk_io_total_bytes'] ?? 0),
|
|
];
|
|
}
|
|
} catch (Exception $e) {
|
|
$this->warn("Failed to fetch CPU/memory/disk IO for {$user->username}: {$e->getMessage()}");
|
|
}
|
|
|
|
return [
|
|
'cpu_percent' => 0.0,
|
|
'memory_bytes' => 0,
|
|
'disk_io_total_bytes' => 0,
|
|
];
|
|
}
|
|
|
|
protected function storeMetric(int $userId, string $metric, int $value, $capturedAt): void
|
|
{
|
|
UserResourceUsage::create([
|
|
'user_id' => $userId,
|
|
'metric' => $metric,
|
|
'value' => max(0, $value),
|
|
'captured_at' => $capturedAt,
|
|
]);
|
|
}
|
|
}
|