Use client API to get resource use for a server
This commit is contained in:
parent
bcd3b055dd
commit
02b29a66ea
6 changed files with 113 additions and 68 deletions
|
@ -1,54 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Http\Controllers\Base;
|
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Pterodactyl\Models\User;
|
|
||||||
use Pterodactyl\Http\Controllers\Controller;
|
|
||||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
|
||||||
|
|
||||||
class DashboardController extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
|
||||||
*/
|
|
||||||
private $repository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DashboardController constructor.
|
|
||||||
*
|
|
||||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
|
||||||
*/
|
|
||||||
public function __construct(ServerRepositoryInterface $repository)
|
|
||||||
{
|
|
||||||
$this->repository = $repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function servers(Request $request)
|
|
||||||
{
|
|
||||||
$servers = $this->repository->setSearchTerm($request->input('query'))->filterUserAccessServers(
|
|
||||||
$request->user(), User::FILTER_LEVEL_ALL
|
|
||||||
);
|
|
||||||
|
|
||||||
$data = [];
|
|
||||||
foreach ($servers->items() as $server) {
|
|
||||||
$cleaned = collect($server)->only([
|
|
||||||
'uuidShort',
|
|
||||||
'uuid',
|
|
||||||
'name',
|
|
||||||
'cpu',
|
|
||||||
'memory',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$data[] = array_merge($cleaned->toArray(), [
|
|
||||||
'allocation' => [
|
|
||||||
'ip' => $server->allocation->ip,
|
|
||||||
'port' => $server->allocation->port,
|
|
||||||
],
|
|
||||||
'node_name' => $server->node->name,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json($data);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,6 +3,8 @@
|
||||||
namespace Pterodactyl\Transformers\Api\Client;
|
namespace Pterodactyl\Transformers\Api\Client;
|
||||||
|
|
||||||
use Pterodactyl\Models\Server;
|
use Pterodactyl\Models\Server;
|
||||||
|
use GuzzleHttp\Exception\RequestException;
|
||||||
|
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
||||||
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface;
|
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface;
|
||||||
|
|
||||||
class StatsTransformer extends BaseClientTransformer
|
class StatsTransformer extends BaseClientTransformer
|
||||||
|
@ -36,6 +38,8 @@ class StatsTransformer extends BaseClientTransformer
|
||||||
*
|
*
|
||||||
* @param \Pterodactyl\Models\Server $model
|
* @param \Pterodactyl\Models\Server $model
|
||||||
* @return array
|
* @return array
|
||||||
|
*
|
||||||
|
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
||||||
*/
|
*/
|
||||||
public function transform(Server $model)
|
public function transform(Server $model)
|
||||||
{
|
{
|
||||||
|
@ -61,7 +65,10 @@ class StatsTransformer extends BaseClientTransformer
|
||||||
'disk' => [
|
'disk' => [
|
||||||
'current' => round(object_get($object, 'proc.disk.used', 0)),
|
'current' => round(object_get($object, 'proc.disk.used', 0)),
|
||||||
'limit' => floatval($model->disk),
|
'limit' => floatval($model->disk),
|
||||||
|
'io' => $model->io,
|
||||||
],
|
],
|
||||||
|
'installed' => $model->installed === 1,
|
||||||
|
'suspended' => (bool) $model->suspended,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
v-for="(server, index) in servers.models"
|
v-for="(server, index) in servers.models"
|
||||||
v-bind:key="index"
|
v-bind:key="index"
|
||||||
v-bind:server="server"
|
v-bind:server="server"
|
||||||
v-bind:resources="resources[server.uuid]"
|
|
||||||
/>
|
/>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,14 +38,26 @@
|
||||||
loading: true,
|
loading: true,
|
||||||
search: '',
|
search: '',
|
||||||
servers: new ServerCollection,
|
servers: new ServerCollection,
|
||||||
resources: {},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted: function () {
|
/**
|
||||||
|
* Start loading the servers before the DOM $.el is created.
|
||||||
|
*/
|
||||||
|
created: function () {
|
||||||
this.loadServers();
|
this.loadServers();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Once the page is mounted set a function to run every 5 seconds that will
|
||||||
|
* iterate through the visible servers and fetch their resource usage.
|
||||||
|
*/
|
||||||
|
mounted: function () {
|
||||||
|
setInterval(() => {
|
||||||
|
this.servers.each(this.getResourceUse)
|
||||||
|
}, 10000);
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
/**
|
/**
|
||||||
* Load the user's servers and render them onto the dashboard.
|
* Load the user's servers and render them onto the dashboard.
|
||||||
|
@ -64,8 +75,9 @@
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.servers = new ServerCollection;
|
this.servers = new ServerCollection;
|
||||||
response.data.data.forEach(obj => {
|
response.data.data.forEach(obj => {
|
||||||
this.resources[obj.attributes.uuid] = { cpu: 0, memory: 0 };
|
this.getResourceUse(
|
||||||
this.servers.add(obj.attributes);
|
this.servers.add(obj.attributes)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.servers.models.length === 0) {
|
if (this.servers.models.length === 0) {
|
||||||
|
@ -93,6 +105,25 @@
|
||||||
onChange: _.debounce(function () {
|
onChange: _.debounce(function () {
|
||||||
this.loadServers(this.$data.search);
|
this.loadServers(this.$data.search);
|
||||||
}, 500),
|
}, 500),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get resource usage for an individual server for rendering purposes.
|
||||||
|
*
|
||||||
|
* @param {Server} server
|
||||||
|
*/
|
||||||
|
getResourceUse: function (server) {
|
||||||
|
window.axios.get(this.route('api.client.servers.resources', { server: server.identifier }))
|
||||||
|
.then(response => {
|
||||||
|
if (!(response.data instanceof Object)) {
|
||||||
|
throw new Error('Received an invalid response object back from status endpoint.');
|
||||||
|
}
|
||||||
|
|
||||||
|
window.events.$emit(`server:${server.uuid}::resources`, response.data.attributes);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="server-box animate fadein">
|
<div class="server-box animate fadein">
|
||||||
<router-link :to="{ name: 'server', params: { id: server.identifier }}" class="content">
|
<router-link :to="{ name: 'server', params: { id: server.identifier }}" class="content">
|
||||||
<div class="float-right">
|
<div class="float-right">
|
||||||
<div class="indicator"></div>
|
<div class="indicator" :class="status"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<div class="text-black font-bold text-xl">
|
<div class="text-black font-bold text-xl">
|
||||||
|
@ -19,11 +19,11 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 flex text-center">
|
<div class="mb-4 flex text-center">
|
||||||
<div class="inline-block border border-grey-lighter border-l-0 p-4 flex-1">
|
<div class="inline-block border border-grey-lighter border-l-0 p-4 flex-1">
|
||||||
<span class="font-bold text-xl">{{ resources.cpu > 0 ? resources.cpu : '—' }}</span>
|
<span class="font-bold text-xl">{{ cpu > 0 ? cpu : '—' }}</span>
|
||||||
<span class="font-light text-sm">%</span>
|
<span class="font-light text-sm">%</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="inline-block border border-grey-lighter border-l-0 border-r-0 p-4 flex-1">
|
<div class="inline-block border border-grey-lighter border-l-0 border-r-0 p-4 flex-1">
|
||||||
<span class="font-bold text-xl">{{ resources.memory > 0 ? resources.memory : '—' }}</span>
|
<span class="font-bold text-xl">{{ memory > 0 ? memory : '—' }}</span>
|
||||||
<span class="font-light text-sm">Mb</span>
|
<span class="font-light text-sm">Mb</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -38,13 +38,76 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Server } from '../../models/server';
|
import get from 'lodash/get';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'server-box',
|
name: 'server-box',
|
||||||
props: {
|
props: {
|
||||||
server: { type: Server, required: true },
|
server: { type: Object, required: true },
|
||||||
resources: { type: Object, required: true },
|
},
|
||||||
|
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
resources: undefined,
|
||||||
|
cpu: 0,
|
||||||
|
memory: 0,
|
||||||
|
status: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
created: function () {
|
||||||
|
window.events.$on(`server:${this.server.uuid}::resources`, data => {
|
||||||
|
this.resources = data;
|
||||||
|
this.status = this.getServerStatus();
|
||||||
|
|
||||||
|
this.memory = Number(get(data, 'memory.current', 0)).toFixed(0);
|
||||||
|
this.cpu = this._calculateCpu(
|
||||||
|
Number(get(data, 'cpu.current', 0)),
|
||||||
|
Number(this.server.limits.cpu)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Set the CSS to use for displaying the server's current status.
|
||||||
|
*/
|
||||||
|
getServerStatus: function () {
|
||||||
|
if (!(this.resources instanceof Object)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.resources.installed || this.resources.suspended) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.resources.state) {
|
||||||
|
case 'off':
|
||||||
|
return 'offline';
|
||||||
|
case 'on':
|
||||||
|
case 'starting':
|
||||||
|
case 'stopping':
|
||||||
|
return 'online';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the CPU usage for a given server relative to their set maximum.
|
||||||
|
*
|
||||||
|
* @param {Number} current
|
||||||
|
* @param {Number} max
|
||||||
|
* @return {Number}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_calculateCpu: function (current, max) {
|
||||||
|
if (max === 0) {
|
||||||
|
return parseFloat(current.toFixed(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseFloat((current / max * 100).toFixed(1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -23,7 +23,7 @@ Route::get('/', 'ClientController@index')->name('api.client.index');
|
||||||
Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateClientAccess::class]], function () {
|
Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateClientAccess::class]], function () {
|
||||||
Route::get('/', 'Servers\ServerController@index')->name('api.client.servers.view');
|
Route::get('/', 'Servers\ServerController@index')->name('api.client.servers.view');
|
||||||
Route::get('/utilization', 'Servers\ResourceUtilizationController@index')
|
Route::get('/utilization', 'Servers\ResourceUtilizationController@index')
|
||||||
->middleware(['throttle:15,1'])
|
->middleware(['throttle:20,1'])
|
||||||
->name('api.client.servers.resources');
|
->name('api.client.servers.resources');
|
||||||
|
|
||||||
Route::post('/command', 'Servers\CommandController@index')->name('api.client.servers.command');
|
Route::post('/command', 'Servers\CommandController@index')->name('api.client.servers.command');
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
* https://opensource.org/licenses/MIT
|
* https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
Route::get('/', 'IndexController@index')->name('index');
|
Route::get('/', 'IndexController@index')->name('index');
|
||||||
Route::get('/dashboard/servers', 'DashboardController@servers')->name('dashboard.servers');
|
|
||||||
Route::get('/status/{server}', 'IndexController@status')->name('index.status');
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue