Use client API to get resource use for a server

This commit is contained in:
Dane Everitt 2018-06-02 19:08:53 -07:00
parent bcd3b055dd
commit 02b29a66ea
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
6 changed files with 113 additions and 68 deletions

View file

@ -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);
}
}

View file

@ -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,
]; ];
} }

View file

@ -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>

View file

@ -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 : '&mdash;' }}</span> <span class="font-bold text-xl">{{ cpu > 0 ? cpu : '&mdash;' }}</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 : '&mdash;' }}</span> <span class="font-bold text-xl">{{ memory > 0 ? memory : '&mdash;' }}</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>

View file

@ -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');

View file

@ -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');
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------