2018-01-28 23:14:14 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Pterodactyl\Services\Deployment;
|
|
|
|
|
|
|
|
use Webmozart\Assert\Assert;
|
2020-10-10 05:01:25 +00:00
|
|
|
use Pterodactyl\Models\Node;
|
2018-01-28 23:14:14 +00:00
|
|
|
use Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException;
|
|
|
|
|
|
|
|
class FindViableNodesService
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $locations = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $disk;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $memory;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the locations that should be searched through to locate available nodes.
|
|
|
|
*
|
|
|
|
* @param array $locations
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function setLocations(array $locations): self
|
|
|
|
{
|
2020-10-17 18:52:21 +00:00
|
|
|
Assert::allIntegerish($locations, 'An array of location IDs should be provided when calling setLocations.');
|
2020-10-10 05:01:25 +00:00
|
|
|
|
2018-01-28 23:14:14 +00:00
|
|
|
$this->locations = $locations;
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the amount of disk that will be used by the server being created. Nodes will be
|
|
|
|
* filtered out if they do not have enough available free disk space for this server
|
|
|
|
* to be placed on.
|
|
|
|
*
|
|
|
|
* @param int $disk
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function setDisk(int $disk): self
|
|
|
|
{
|
|
|
|
$this->disk = $disk;
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the amount of memory that this server will be using. As with disk space, nodes that
|
|
|
|
* do not have enough free memory will be filtered out.
|
|
|
|
*
|
|
|
|
* @param int $memory
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function setMemory(int $memory): self
|
|
|
|
{
|
|
|
|
$this->memory = $memory;
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an array of nodes that meet the provided requirements and can then
|
|
|
|
* be passed to the AllocationSelectionService to return a single allocation.
|
|
|
|
*
|
|
|
|
* This functionality is used for automatic deployments of servers and will
|
|
|
|
* attempt to find all nodes in the defined locations that meet the disk and
|
|
|
|
* memory availability requirements. Any nodes not meeting those requirements
|
|
|
|
* are tossed out, as are any nodes marked as non-public, meaning automatic
|
2018-05-13 14:50:56 +00:00
|
|
|
* deployments should not be done against them.
|
2018-01-28 23:14:14 +00:00
|
|
|
*
|
2021-01-10 21:08:43 +00:00
|
|
|
* @param int|null $page If provided the results will be paginated by returning
|
|
|
|
* up to 50 nodes at a time starting at the provided page.
|
|
|
|
* If "null" is provided as the value no pagination will
|
|
|
|
* be used.
|
|
|
|
* @return \Illuminate\Support\Collection|\Illuminate\Contracts\Pagination\LengthAwarePaginator
|
|
|
|
*
|
2018-01-28 23:14:14 +00:00
|
|
|
* @throws \Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException
|
|
|
|
*/
|
2021-01-10 21:08:43 +00:00
|
|
|
public function handle(int $page = null)
|
2018-01-28 23:14:14 +00:00
|
|
|
{
|
2020-10-10 05:01:25 +00:00
|
|
|
Assert::integer($this->disk, 'Disk space must be an int, got %s');
|
|
|
|
Assert::integer($this->memory, 'Memory usage must be an int, got %s');
|
2018-01-28 23:14:14 +00:00
|
|
|
|
2020-10-10 05:01:25 +00:00
|
|
|
$query = Node::query()->select('nodes.*')
|
|
|
|
->selectRaw('IFNULL(SUM(servers.memory), 0) as sum_memory')
|
|
|
|
->selectRaw('IFNULL(SUM(servers.disk), 0) as sum_disk')
|
|
|
|
->leftJoin('servers', 'servers.node_id', '=', 'nodes.id')
|
|
|
|
->where('nodes.public', 1);
|
2018-01-28 23:14:14 +00:00
|
|
|
|
2020-10-10 05:01:25 +00:00
|
|
|
if (! empty($this->locations)) {
|
|
|
|
$query = $query->whereIn('nodes.location_id', $this->locations);
|
2018-01-28 23:14:14 +00:00
|
|
|
}
|
|
|
|
|
2020-10-10 05:01:25 +00:00
|
|
|
$results = $query->groupBy('nodes.id')
|
2020-10-17 18:52:21 +00:00
|
|
|
->havingRaw('(IFNULL(SUM(servers.memory), 0) + ?) <= (nodes.memory * (1 + (nodes.memory_overallocate / 100)))', [$this->memory])
|
2021-01-10 21:08:43 +00:00
|
|
|
->havingRaw('(IFNULL(SUM(servers.disk), 0) + ?) <= (nodes.disk * (1 + (nodes.disk_overallocate / 100)))', [$this->disk]);
|
|
|
|
|
|
|
|
if (! is_null($page)) {
|
|
|
|
$results = $results->paginate(50, ['*'], 'page', $page);
|
|
|
|
} else {
|
|
|
|
$results = $results->get()->toBase();
|
|
|
|
}
|
2020-10-10 05:01:25 +00:00
|
|
|
|
|
|
|
if ($results->isEmpty()) {
|
2018-01-28 23:14:14 +00:00
|
|
|
throw new NoViableNodeException(trans('exceptions.deployment.no_viable_nodes'));
|
|
|
|
}
|
|
|
|
|
2020-10-10 05:01:25 +00:00
|
|
|
return $results;
|
2018-01-28 23:14:14 +00:00
|
|
|
}
|
|
|
|
}
|