Skip to content

Commit ee033d6

Browse files
authored
Telemetry (pterodactyl#4564)
1 parent df9a7f7 commit ee033d6

File tree

5 files changed

+255
-2
lines changed

5 files changed

+255
-2
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Pterodactyl\Console\Commands;
4+
5+
use Illuminate\Console\Command;
6+
use Symfony\Component\VarDumper\VarDumper;
7+
use Pterodactyl\Services\Telemetry\TelemetryCollectionService;
8+
9+
class TelemetryCommand extends Command
10+
{
11+
protected $description = 'Displays all the data that would be sent to the Pterodactyl Telemetry Service if telemetry collection is enabled.';
12+
13+
protected $signature = 'p:telemetry';
14+
15+
/**
16+
* TelemetryCommand constructor.
17+
*/
18+
public function __construct(private TelemetryCollectionService $telemetryCollectionService)
19+
{
20+
parent::__construct();
21+
}
22+
23+
/**
24+
* Handle execution of command.
25+
*
26+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
27+
*/
28+
public function handle()
29+
{
30+
$this->output->info('Collecting telemetry data, this may take a while...');
31+
32+
VarDumper::dump($this->telemetryCollectionService->collect());
33+
}
34+
}

app/Console/Kernel.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
namespace Pterodactyl\Console;
44

5+
use Ramsey\Uuid\Uuid;
56
use Pterodactyl\Models\ActivityLog;
67
use Illuminate\Console\Scheduling\Schedule;
78
use Illuminate\Database\Console\PruneCommand;
9+
use Pterodactyl\Repositories\Eloquent\SettingsRepository;
810
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
11+
use Pterodactyl\Services\Telemetry\TelemetryCollectionService;
912
use Pterodactyl\Console\Commands\Schedule\ProcessRunnableCommand;
1013
use Pterodactyl\Console\Commands\Maintenance\PruneOrphanedBackupsCommand;
1114
use Pterodactyl\Console\Commands\Maintenance\CleanServiceBackupFilesCommand;
@@ -37,5 +40,34 @@ protected function schedule(Schedule $schedule)
3740
if (config('activity.prune_days')) {
3841
$schedule->command(PruneCommand::class, ['--model' => [ActivityLog::class]])->daily();
3942
}
43+
44+
if (config('pterodactyl.telemetry.enabled')) {
45+
$this->registerTelemetry($schedule);
46+
}
47+
}
48+
49+
/**
50+
* I wonder what this does.
51+
*
52+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
53+
* @throws \Illuminate\Contracts\Container\BindingResolutionException
54+
*/
55+
private function registerTelemetry(Schedule $schedule): void
56+
{
57+
$settingsRepository = app()->make(SettingsRepository::class);
58+
59+
$uuid = $settingsRepository->get('app:uuid');
60+
if (is_null($uuid)) {
61+
$uuid = Uuid::uuid4()->toString();
62+
$settingsRepository->set('app:uuid', $uuid);
63+
}
64+
65+
// Calculate a fixed time to run the data push at, this will be the same time every day.
66+
$time = hexdec(str_replace('-', '', substr($uuid, 27))) % 1440;
67+
$hour = floor($time / 60);
68+
$minute = $time % 60;
69+
70+
// Run the telemetry collector.
71+
$schedule->call(app()->make(TelemetryCollectionService::class))->description('Collect Telemetry')->dailyAt("$hour:$minute");
4072
}
4173
}

app/Repositories/Wings/DaemonConfigurationRepository.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ class DaemonConfigurationRepository extends DaemonRepository
1414
*
1515
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
1616
*/
17-
public function getSystemInformation(): array
17+
public function getSystemInformation(?int $version = null): array
1818
{
1919
try {
20-
$response = $this->getHttpClient()->get('/api/system');
20+
$response = $this->getHttpClient()->get('/api/system' . (!is_null($version) ? '?v=' . $version : ''));
2121
} catch (TransferException $exception) {
2222
throw new DaemonConnectionException($exception);
2323
}
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<?php
2+
3+
namespace Pterodactyl\Services\Telemetry;
4+
5+
use PDO;
6+
use Exception;
7+
use Ramsey\Uuid\Uuid;
8+
use Illuminate\Support\Arr;
9+
use Pterodactyl\Models\Egg;
10+
use Pterodactyl\Models\Nest;
11+
use Pterodactyl\Models\Node;
12+
use Pterodactyl\Models\User;
13+
use Pterodactyl\Models\Mount;
14+
use Pterodactyl\Models\Backup;
15+
use Pterodactyl\Models\Server;
16+
use Pterodactyl\Models\Location;
17+
use Illuminate\Support\Facades\DB;
18+
use Pterodactyl\Models\Allocation;
19+
use Illuminate\Support\Facades\Http;
20+
use Pterodactyl\Repositories\Eloquent\SettingsRepository;
21+
use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository;
22+
23+
class TelemetryCollectionService
24+
{
25+
/**
26+
* TelemetryCollectionService constructor.
27+
*/
28+
public function __construct(
29+
private DaemonConfigurationRepository $daemonConfigurationRepository,
30+
private SettingsRepository $settingsRepository
31+
) {
32+
}
33+
34+
/**
35+
* Collects telemetry data and sends it to the Pterodactyl Telemetry Service.
36+
*/
37+
public function __invoke(): void
38+
{
39+
try {
40+
$data = $this->collect();
41+
} catch (Exception) {
42+
return;
43+
}
44+
45+
Http::post('https://telemetry.pterodactyl.io', $data);
46+
}
47+
48+
/**
49+
* Collects telemetry data and returns it as an array.
50+
*
51+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
52+
*/
53+
public function collect(): array
54+
{
55+
$uuid = $this->settingsRepository->get('app:uuid');
56+
if (is_null($uuid)) {
57+
$uuid = Uuid::uuid4()->toString();
58+
$this->settingsRepository->set('app:uuid', $uuid);
59+
}
60+
61+
$nodes = Node::all()->map(function ($node) {
62+
try {
63+
$info = $this->daemonConfigurationRepository->setNode($node)->getSystemInformation(2);
64+
} catch (Exception) {
65+
return null;
66+
}
67+
68+
return [
69+
'id' => $node->uuid,
70+
'version' => Arr::get($info, 'version', ''),
71+
72+
'docker' => [
73+
'version' => Arr::get($info, 'docker.version', ''),
74+
75+
'cgroups' => [
76+
'driver' => Arr::get($info, 'docker.cgroups.driver', ''),
77+
'version' => Arr::get($info, 'docker.cgroups.version', ''),
78+
],
79+
80+
'containers' => [
81+
'total' => Arr::get($info, 'docker.containers.total', -1),
82+
'running' => Arr::get($info, 'docker.containers.running', -1),
83+
'paused' => Arr::get($info, 'docker.containers.paused', -1),
84+
'stopped' => Arr::get($info, 'docker.containers.stopped', -1),
85+
],
86+
87+
'storage' => [
88+
'driver' => Arr::get($info, 'docker.storage.driver', ''),
89+
'filesystem' => Arr::get($info, 'docker.storage.filesystem', ''),
90+
],
91+
92+
'runc' => [
93+
'version' => Arr::get($info, 'docker.runc.version', ''),
94+
],
95+
],
96+
97+
'system' => [
98+
'architecture' => Arr::get($info, 'system.architecture', ''),
99+
'cpuThreads' => Arr::get($info, 'system.cpu_threads', ''),
100+
'memoryBytes' => Arr::get($info, 'system.memory_bytes', ''),
101+
'kernelVersion' => Arr::get($info, 'system.kernel_version', ''),
102+
'os' => Arr::get($info, 'system.os', ''),
103+
'osType' => Arr::get($info, 'system.os_type', ''),
104+
],
105+
];
106+
})->filter(fn ($node) => !is_null($node))->toArray();
107+
108+
return [
109+
'id' => $uuid,
110+
111+
'panel' => [
112+
'version' => config('app.version'),
113+
'phpVersion' => phpversion(),
114+
115+
'drivers' => [
116+
'backup' => [
117+
'type' => config('backups.default'),
118+
],
119+
'cache' => [
120+
'type' => config('cache.default'),
121+
],
122+
'database' => [
123+
'type' => config('database.default'),
124+
'version' => DB::getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION),
125+
],
126+
],
127+
],
128+
129+
'resources' => [
130+
'allocations' => [
131+
'count' => Allocation::count(),
132+
'used' => Allocation::whereNotNull('server_id')->count(),
133+
],
134+
135+
'backups' => [
136+
'count' => Backup::count(),
137+
'bytes' => Backup::sum('bytes'),
138+
],
139+
140+
'eggs' => [
141+
'count' => Egg::count(),
142+
'ids' => Egg::pluck('uuid')->toArray(),
143+
],
144+
145+
'locations' => [
146+
'count' => Location::count(),
147+
],
148+
149+
'mounts' => [
150+
'count' => Mount::count(),
151+
],
152+
153+
'nests' => [
154+
'count' => Nest::count(),
155+
],
156+
157+
'nodes' => [
158+
'count' => Node::count(),
159+
],
160+
161+
'servers' => [
162+
'count' => Server::count(),
163+
'suspended' => Server::where('status', Server::STATUS_SUSPENDED)->count(),
164+
],
165+
166+
'users' => [
167+
'count' => User::count(),
168+
'admins' => User::where('root_admin', true)->count(),
169+
],
170+
],
171+
172+
'nodes' => $nodes,
173+
];
174+
}
175+
}

config/pterodactyl.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,16 @@
177177
// Should an email be sent to a server owner whenever their server is reinstalled?
178178
'send_reinstall_notification' => env('PTERODACTYL_SEND_REINSTALL_NOTIFICATION', true),
179179
],
180+
181+
/*
182+
|--------------------------------------------------------------------------
183+
| Telemetry Settings
184+
|--------------------------------------------------------------------------
185+
|
186+
| This section controls the telemetry sent by Pterodactyl.
187+
*/
188+
189+
'telemetry' => [
190+
'enabled' => env('PTERODACTYL_TELEMETRY_ENABLED', false),
191+
],
180192
];

0 commit comments

Comments
 (0)