Skip to content

Commit 4953608

Browse files
committed
Add build configuration to server management.
Allows modification of certain settings, as well as assigning additional IP addresses and ports.
1 parent 2c054e7 commit 4953608

File tree

4 files changed

+352
-17
lines changed

4 files changed

+352
-17
lines changed

app/Http/Controllers/Admin/ServersController.php

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,24 @@ public function getNew(Request $request)
5050

5151
public function getView(Request $request, $id)
5252
{
53+
$server = Models\Server::select(
54+
'servers.*',
55+
'nodes.name as a_nodeName',
56+
'users.email as a_ownerEmail',
57+
'locations.long as a_locationName',
58+
'services.name as a_serviceName',
59+
'service_options.name as a_servceOptionName'
60+
)->join('nodes', 'servers.node', '=', 'nodes.id')
61+
->join('users', 'servers.owner', '=', 'users.id')
62+
->join('locations', 'nodes.location', '=', 'locations.id')
63+
->join('services', 'servers.service', '=', 'services.id')
64+
->join('service_options', 'servers.option', '=', 'service_options.id')
65+
->first();
66+
5367
return view('admin.servers.view', [
54-
'server' => Models\Server::select(
55-
'servers.*',
56-
'nodes.name as a_nodeName',
57-
'users.email as a_ownerEmail',
58-
'locations.long as a_locationName',
59-
'services.name as a_serviceName',
60-
'service_options.name as a_servceOptionName'
61-
)->join('nodes', 'servers.node', '=', 'nodes.id')
62-
->join('users', 'servers.owner', '=', 'users.id')
63-
->join('locations', 'nodes.location', '=', 'locations.id')
64-
->join('services', 'servers.service', '=', 'services.id')
65-
->join('service_options', 'servers.option', '=', 'service_options.id')
66-
->first()
68+
'server' => $server,
69+
'assigned' => Models\Allocation::select('id', 'ip', 'port')->where('assigned_to', $id)->orderBy('ip', 'asc')->orderBy('port', 'asc')->get(),
70+
'unassigned' => Models\Allocation::select('id', 'ip', 'port')->where('node', $server->node)->whereNull('assigned_to')->orderBy('ip', 'asc')->orderBy('port', 'asc')->get()
6771
]);
6872
}
6973

@@ -225,4 +229,63 @@ public function postUpdateServerDetails(Request $request, $id)
225229
}
226230
}
227231

232+
public function postUpdateServerToggleBuild(Request $request, $id) {
233+
$server = Models\Server::findOrFail($id);
234+
$node = Models\Node::findOrFail($server->node);
235+
$client = Models\Node::guzzleRequest($server->node);
236+
237+
try {
238+
$res = $client->request('POST', '/server/rebuild', [
239+
'headers' => [
240+
'X-Access-Server' => $server->uuid,
241+
'X-Access-Token' => $node->daemonSecret
242+
]
243+
]);
244+
Alert::success('A rebuild has been queued successfully. It will run the next time this server is booted.')->flash();
245+
} catch (\GuzzleHttp\Exception\TransferException $ex) {
246+
Log::warning($ex);
247+
Alert::danger('An error occured while attempting to toggle a rebuild: ' . $ex->getMessage())->flash();
248+
}
249+
250+
return redirect()->route('admin.servers.view', [
251+
'id' => $id,
252+
'tab' => 'tab_manage'
253+
]);
254+
}
255+
256+
public function postUpdateServerUpdateBuild(Request $request, $id)
257+
{
258+
try {
259+
260+
$server = new ServerRepository;
261+
$server->changeBuild($id, [
262+
'default' => $request->input('default'),
263+
'add_additional' => $request->input('add_additional'),
264+
'remove_additional' => $request->input('remove_additional'),
265+
'memory' => $request->input('memory'),
266+
'swap' => $request->input('swap'),
267+
'io' => $request->input('io'),
268+
'cpu' => $request->input('cpu'),
269+
]);
270+
Alert::success('Server details were successfully updated.')->flash();
271+
} catch (\Exception $e) {
272+
273+
if ($e instanceof \Pterodactyl\Exceptions\DisplayValidationException) {
274+
return redirect()->route('admin.servers.view', [
275+
'id' => $id,
276+
'tab' => 'tab_build'
277+
])->withErrors(json_decode($e->getMessage()))->withInput();
278+
} else if ($e instanceof \Pterodactyl\Exceptions\DisplayException) {
279+
Alert::danger($e->getMessage())->flash();
280+
} else {
281+
Log::error($e);
282+
Alert::danger('An unhandled exception occured while attemping to add this server. Please try again.')->flash();
283+
}
284+
}
285+
return redirect()->route('admin.servers.view', [
286+
'id' => $id,
287+
'tab' => 'tab_build'
288+
]);
289+
}
290+
228291
}

app/Http/Routes/AdminRoutes.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public function map(Router $router) {
3131
$router->get('/view/{id}', [ 'as' => 'admin.servers.view', 'uses' => 'Admin\ServersController@getView' ]);
3232

3333
$router->post('/view/{id}/details', [ 'uses' => 'Admin\ServersController@postUpdateServerDetails' ]);
34+
$router->post('/view/{id}/rebuild', [ 'uses' => 'Admin\ServersController@postUpdateServerToggleBuild' ]);
35+
$router->post('/view/{id}/build', [ 'uses' => 'Admin\ServersController@postUpdateServerUpdateBuild' ]);
3436

3537
$router->post('/new', [ 'uses' => 'Admin\ServersController@postNewServer']);
3638
$router->post('/new/get-nodes', [ 'uses' => 'Admin\ServersController@postNewServerGetNodes' ]);

app/Repositories/ServerRepository.php

Lines changed: 160 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ public function updateDetails($id, array $data)
226226
// Validate Fields
227227
$validator = Validator::make($data, [
228228
'owner' => 'email|exists:users,email',
229-
'name' => 'regex:([\w -]{4,35})'
229+
'name' => 'regex:^([\w -]{4,35})$'
230230
]);
231231

232232
// Run validator, throw catchable and displayable exception if it fails.
@@ -304,11 +304,169 @@ public function updateDetails($id, array $data)
304304
throw new DisplayException('Daemon returned a a non HTTP/204 error code. HTTP/' + $res->getStatusCode());
305305
}
306306
} catch (\Exception $ex) {
307-
DB::rollback();
307+
DB::rollBack();
308308
Log::error($ex);
309309
throw new DisplayException('An error occured while attempting to update this server\'s information.');
310310
}
311311

312312
}
313313

314+
/**
315+
* [changeBuild description]
316+
* @param integer $id
317+
* @param array $data
318+
* @return boolean
319+
*/
320+
public function changeBuild($id, array $data)
321+
{
322+
323+
$validator = Validator::make($data, [
324+
'default' => [
325+
'string',
326+
'regex:/^(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5])):(\d{1,5})$/'
327+
],
328+
'add_additional' => 'array',
329+
'remove_additional' => 'array',
330+
'memory' => 'integer|min:0',
331+
'swap' => 'integer|min:0',
332+
'io' => 'integer|min:10|max:1000',
333+
'cpu' => 'integer|min:0',
334+
'disk' => 'integer|min:0'
335+
]);
336+
337+
// Run validator, throw catchable and displayable exception if it fails.
338+
// Exception includes a JSON result of failed validation rules.
339+
if ($validator->fails()) {
340+
throw new DisplayValidationException($validator->errors());
341+
}
342+
343+
DB::beginTransaction();
344+
$server = Models\Server::findOrFail($id);
345+
346+
if (isset($data['default'])) {
347+
list($ip, $port) = explode(':', $data['default']);
348+
if ($ip === $server->ip && $port === $server->port) {
349+
continue;
350+
}
351+
352+
$allocation = Models\Allocation::where('ip', $ip)->where('port', $port)->where('assigned_to', $server->id)->get();
353+
if (!$allocation) {
354+
throw new DisplayException('The assigned default connection (' . $ip . ':' . $prot . ') is not allocated to this server.');
355+
}
356+
357+
$server->ip = $ip;
358+
$server->port = $port;
359+
}
360+
361+
// Remove Assignments
362+
if (isset($data['remove_additional'])) {
363+
foreach ($data['remove_additional'] as $id => $combo) {
364+
list($ip, $port) = explode(':', $combo);
365+
// Invalid, not worth killing the whole thing, we'll just skip over it.
366+
if (!filter_var($ip, FILTER_VALIDATE_IP) || !preg_match('/^(\d{1,5})$/', $port)) {
367+
continue;
368+
}
369+
370+
// Can't remove the assigned IP/Port combo
371+
if ($ip === $server->ip && $port === $server->port) {
372+
continue;
373+
}
374+
375+
Models\Allocation::where('ip', $ip)->where('port', $port)->where('assigned_to', $server->id)->update([
376+
'assigned_to' => null
377+
]);
378+
}
379+
}
380+
381+
// Add Assignments
382+
if (isset($data['add_additional'])) {
383+
foreach ($data['add_additional'] as $id => $combo) {
384+
list($ip, $port) = explode(':', $combo);
385+
// Invalid, not worth killing the whole thing, we'll just skip over it.
386+
if (!filter_var($ip, FILTER_VALIDATE_IP) || !preg_match('/^(\d{1,5})$/', $port)) {
387+
continue;
388+
}
389+
390+
// Don't allow double port assignments
391+
if (Models\Allocation::where('port', $port)->where('assigned_to', $server->id)->count() !== 0) {
392+
continue;
393+
}
394+
395+
Models\Allocation::where('ip', $ip)->where('port', $port)->whereNull('assigned_to')->update([
396+
'assigned_to' => $server->id
397+
]);
398+
}
399+
}
400+
401+
// Loop All Assignments
402+
$additionalAssignments = [];
403+
$assignments = Models\Allocation::where('assigned_to', $server->id)->get();
404+
foreach ($assignments as &$assignment) {
405+
if (array_key_exists((string) $assignment->ip, $additionalAssignments)) {
406+
array_push($additionalAssignments[ (string) $assignment->ip ], (int) $assignment->port);
407+
} else {
408+
$additionalAssignments[ (string) $assignment->ip ] = [ (int) $assignment->port ];
409+
}
410+
}
411+
412+
// @TODO: verify that server can be set to this much memory without
413+
// going over node limits.
414+
if (isset($data['memory'])) {
415+
$server->memory = $data['memory'];
416+
}
417+
418+
if (isset($data['swap'])) {
419+
$server->swap = $data['swap'];
420+
}
421+
422+
// @TODO: verify that server can be set to this much disk without
423+
// going over node limits.
424+
if (isset($data['disk'])) {
425+
$server->disk = $data['disk'];
426+
}
427+
428+
if (isset($data['cpu'])) {
429+
$server->cpu = $data['cpu'];
430+
}
431+
432+
if (isset($data['io'])) {
433+
$server->io = $data['io'];
434+
}
435+
436+
try {
437+
438+
$node = Models\Node::getByID($server->node);
439+
$client = Models\Node::guzzleRequest($server->node);
440+
441+
$client->request('PATCH', '/server', [
442+
'headers' => [
443+
'X-Access-Server' => $server->uuid,
444+
'X-Access-Token' => $node->daemonSecret
445+
],
446+
'json' => [
447+
'build' => [
448+
'default' => [
449+
'ip' => $server->ip,
450+
'port' => (int) $server->port
451+
],
452+
'ports|overwrite' => $additionalAssignments,
453+
'memory' => (int) $server->memory,
454+
'swap' => (int) $server->swap,
455+
'io' => (int) $server->io,
456+
'cpu' => (int) $server->cpu,
457+
'disk' => (int) $server->disk
458+
]
459+
]
460+
]);
461+
462+
$server->save();
463+
DB::commit();
464+
return true;
465+
} catch (\GuzzleHttp\Exception\TransferException $ex) {
466+
DB::rollBack();
467+
throw new DisplayException('An error occured while attempting to update the configuration: ' . $ex->getMessage());
468+
}
469+
470+
}
471+
314472
}

0 commit comments

Comments
 (0)