Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pterodactyl
GitHub Repository: pterodactyl/panel
Path: blob/1.0-develop/app/Services/Allocations/FindAssignableAllocationService.php
14042 views
1
<?php
2
3
namespace Pterodactyl\Services\Allocations;
4
5
use Webmozart\Assert\Assert;
6
use Pterodactyl\Models\Server;
7
use Pterodactyl\Models\Allocation;
8
use Pterodactyl\Exceptions\Service\Allocation\AutoAllocationNotEnabledException;
9
use Pterodactyl\Exceptions\Service\Allocation\NoAutoAllocationSpaceAvailableException;
10
11
class FindAssignableAllocationService
12
{
13
/**
14
* FindAssignableAllocationService constructor.
15
*/
16
public function __construct(private AssignmentService $service)
17
{
18
}
19
20
/**
21
* Finds an existing unassigned allocation and attempts to assign it to the given server. If
22
* no allocation can be found, a new one will be created with a random port between the defined
23
* range from the configuration.
24
*
25
* @throws \Pterodactyl\Exceptions\DisplayException
26
* @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException
27
* @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException
28
* @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException
29
* @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException
30
*/
31
public function handle(Server $server): Allocation
32
{
33
if (!config('pterodactyl.client_features.allocations.enabled')) {
34
throw new AutoAllocationNotEnabledException();
35
}
36
37
// Attempt to find a given available allocation for a server. If one cannot be found
38
// we will fall back to attempting to create a new allocation that can be used for the
39
// server.
40
/** @var Allocation|null $allocation */
41
$allocation = $server->node->allocations()
42
->lockForUpdate()
43
->where('ip', $server->allocation->ip)
44
->whereNull('server_id')
45
->inRandomOrder()
46
->first();
47
48
$allocation = $allocation ?? $this->createNewAllocation($server);
49
50
$allocation->update(['server_id' => $server->id]);
51
52
return $allocation->refresh();
53
}
54
55
/**
56
* Create a new allocation on the server's node with a random port from the defined range
57
* in the settings. If there are no matches in that range, or something is wrong with the
58
* range information provided an exception will be raised.
59
*
60
* @throws \Pterodactyl\Exceptions\DisplayException
61
* @throws \Pterodactyl\Exceptions\Service\Allocation\CidrOutOfRangeException
62
* @throws \Pterodactyl\Exceptions\Service\Allocation\InvalidPortMappingException
63
* @throws \Pterodactyl\Exceptions\Service\Allocation\PortOutOfRangeException
64
* @throws \Pterodactyl\Exceptions\Service\Allocation\TooManyPortsInRangeException
65
*/
66
protected function createNewAllocation(Server $server): Allocation
67
{
68
$start = config('pterodactyl.client_features.allocations.range_start', null);
69
$end = config('pterodactyl.client_features.allocations.range_end', null);
70
71
if (!$start || !$end) {
72
throw new NoAutoAllocationSpaceAvailableException();
73
}
74
75
Assert::integerish($start);
76
Assert::integerish($end);
77
78
// Get all of the currently allocated ports for the node so that we can figure out
79
// which port might be available.
80
$ports = $server->node->allocations()
81
->where('ip', $server->allocation->ip)
82
->whereBetween('port', [$start, $end])
83
->pluck('port');
84
85
// Compute the difference of the range and the currently created ports, finding
86
// any port that does not already exist in the database. We will then use this
87
// array of ports to create a new allocation to assign to the server.
88
$available = array_diff(range($start, $end), $ports->toArray());
89
90
// If we've already allocated all of the ports, just abort.
91
if (empty($available)) {
92
throw new NoAutoAllocationSpaceAvailableException();
93
}
94
95
// Pick a random port out of the remaining available ports.
96
/** @var int $port */
97
$port = $available[array_rand($available)];
98
99
$this->service->handle($server->node, [
100
'allocation_ip' => $server->allocation->ip,
101
'allocation_ports' => [$port],
102
]);
103
104
/** @var Allocation $allocation */
105
$allocation = $server->node->allocations()
106
->lockForUpdate()
107
->where('ip', $server->allocation->ip)
108
->where('port', $port)
109
->firstOrFail();
110
111
return $allocation;
112
}
113
}
114
115