From 021710aa1c6b726dc4eba2549703800c1d169906 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Fri, 2 Mar 2018 20:58:58 -0600 Subject: [PATCH] Add bulk power management via CLI --- CHANGELOG.md | 1 + .../Server/BulkPowerActionCommand.php | 119 ++++++++++++++++++ .../Repository/ServerRepositoryInterface.php | 19 +++ .../Eloquent/ServerRepository.php | 39 ++++++ resources/lang/en/command/messages.php | 4 + 5 files changed, 182 insertions(+) create mode 100644 app/Console/Commands/Server/BulkPowerActionCommand.php diff --git a/CHANGELOG.md b/CHANGELOG.md index d182ef4a2..947098b6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines. * Added proper transformer for Packs and re-enabled missing includes on server. * Added support for using Filesystem as a caching driver, although not recommended. * Added support for user management of server databases. +* **Added bulk power management CLI interface to send start, stop, kill, restart actions to servers across configurable nodes.** ## v0.7.3 (Derelict Dermodactylus) ### Fixed diff --git a/app/Console/Commands/Server/BulkPowerActionCommand.php b/app/Console/Commands/Server/BulkPowerActionCommand.php new file mode 100644 index 000000000..dbe36718d --- /dev/null +++ b/app/Console/Commands/Server/BulkPowerActionCommand.php @@ -0,0 +1,119 @@ +powerRepository = $powerRepository; + $this->repository = $repository; + $this->validator = $validator; + } + + /** + * Handle the bulk power request. + * + * @throws \Pterodactyl\Exceptions\Repository\Daemon\InvalidPowerSignalException + */ + public function handle() + { + $action = $this->argument('action'); + $nodes = empty($this->option('nodes')) ? [] : explode(',', $this->option('nodes')); + $servers = empty($this->option('servers')) ? [] : explode(',', $this->option('servers')); + + $validator = $this->validator->make([ + 'action' => $action, + 'nodes' => $nodes, + 'servers' => $servers, + ], [ + 'action' => 'string|in:start,stop,kill,restart', + 'nodes' => 'array', + 'nodes.*' => 'integer|min:1', + 'servers' => 'array', + 'servers.*' => 'integer|min:1', + ]); + + if ($validator->fails()) { + foreach ($validator->getMessageBag()->all() as $message) { + $this->output->error($message); + } + + return; + } + + $count = $this->repository->getServersForPowerActionCount($servers, $nodes); + if (! $this->confirm(trans('command/messages.server.power.confirm', ['action' => $action, 'count' => $count]))) { + return; + } + + $bar = $this->output->createProgressBar($count); + $servers = $this->repository->getServersForPowerAction($servers, $nodes); + + foreach ($servers as $server) { + $bar->clear(); + + try { + $this->powerRepository->setServer($server)->sendSignal($action); + } catch (RequestException $exception) { + $this->output->error(trans('command/messages.server.power.action_failed', [ + 'name' => $server->name, + 'id' => $server->id, + 'node' => $server->node->name, + 'message' => $exception->getMessage(), + ])); + } + + $bar->advance(); + $bar->display(); + } + + $this->line(''); + } +} diff --git a/app/Contracts/Repository/ServerRepositoryInterface.php b/app/Contracts/Repository/ServerRepositoryInterface.php index 0ca74bf40..983cf7e6e 100644 --- a/app/Contracts/Repository/ServerRepositoryInterface.php +++ b/app/Contracts/Repository/ServerRepositoryInterface.php @@ -117,4 +117,23 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function getByUuid(string $uuid): Server; + + /** + * Return all of the servers that should have a power action performed aganist them. + * + * @param int[] $servers + * @param int[] $nodes + * @param bool $returnCount + * @return int|\Generator + */ + public function getServersForPowerAction(array $servers = [], array $nodes = [], bool $returnCount = false); + + /** + * Return the total number of servers that will be affected by the query. + * + * @param int[] $servers + * @param int[] $nodes + * @return int + */ + public function getServersForPowerActionCount(array $servers = [], array $nodes = []): int; } diff --git a/app/Repositories/Eloquent/ServerRepository.php b/app/Repositories/Eloquent/ServerRepository.php index 7bca12691..5a53d33f0 100644 --- a/app/Repositories/Eloquent/ServerRepository.php +++ b/app/Repositories/Eloquent/ServerRepository.php @@ -264,6 +264,45 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt } } + /** + * Return all of the servers that should have a power action performed aganist them. + * + * @param int[] $servers + * @param int[] $nodes + * @param bool $returnCount + * @return int|\Generator + */ + public function getServersForPowerAction(array $servers = [], array $nodes = [], bool $returnCount = false) + { + $instance = $this->getBuilder(); + + if (! empty($nodes) && ! empty($servers)) { + $instance->whereIn('id', $servers)->orWhereIn('node_id', $nodes); + } elseif (empty($nodes) && ! empty($servers)) { + $instance->whereIn('id', $servers); + } elseif (! empty($nodes) && empty($servers)) { + $instance->whereIn('node_id', $nodes); + } + + if ($returnCount) { + return $instance->count(); + } + + return $instance->with('node')->cursor(); + } + + /** + * Return the total number of servers that will be affected by the query. + * + * @param int[] $servers + * @param int[] $nodes + * @return int + */ + public function getServersForPowerActionCount(array $servers = [], array $nodes = []): int + { + return $this->getServersForPowerAction($servers, $nodes, true); + } + /** * Return an array of server IDs that a given user can access based * on owner and subuser permissions. diff --git a/resources/lang/en/command/messages.php b/resources/lang/en/command/messages.php index 77f67c663..4a5250327 100644 --- a/resources/lang/en/command/messages.php +++ b/resources/lang/en/command/messages.php @@ -37,6 +37,10 @@ return [ ], 'server' => [ 'rebuild_failed' => 'Rebuild request for ":name" (#:id) on node ":node" failed with error: :message', + 'power' => [ + 'confirm' => 'You are about to perform a :action aganist :count servers. Do you wish to continue?', + 'action_failed' => 'Power action request for ":name" (#:id) on node ":node" failed with error: :message', + ], ], 'environment' => [ 'mail' => [