fix: OS template type matching for LXC/KVM nodes
- Add is_lxc column to ext_proxmox_oses for tracking template type - OS model now has vmType() helper returning 'lxc' or 'qemu' - Install uses OS template's type for clone API path (not node type) - createServer validates OS type matches node type before provisioning - calculateNode filters nodes by LXC/KVM type based on selected OS - Reinstall OS list filtered to only show matching template types - Admin location OS repeater now has LXC Template checkbox
This commit is contained in:
@@ -75,9 +75,13 @@ class LocationResource extends Resource
|
|||||||
return $rule->where('location_id', $get('../../id'));
|
return $rule->where('location_id', $get('../../id'));
|
||||||
})
|
})
|
||||||
->placeholder('Enter the VM ID'),
|
->placeholder('Enter the VM ID'),
|
||||||
|
Forms\Components\Checkbox::make('is_lxc')
|
||||||
|
->label('LXC Template')
|
||||||
|
->default(false)
|
||||||
|
->helperText('Check if this is an LXC container template (not KVM)'),
|
||||||
])
|
])
|
||||||
->cloneable()
|
->cloneable()
|
||||||
->columns(2)
|
->columns(3)
|
||||||
->columnSpanFull(),
|
->columnSpanFull(),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
|||||||
@@ -83,9 +83,12 @@ class Reinstall extends Component
|
|||||||
|
|
||||||
// assuming Server has relation `node()` -> Node model
|
// assuming Server has relation `node()` -> Node model
|
||||||
$locationId = $this->server->node->location_id;
|
$locationId = $this->server->node->location_id;
|
||||||
|
$isLxc = $this->server->node->is_lxc ?? false;
|
||||||
|
|
||||||
|
// Filter OS templates by location AND type (LXC templates for LXC nodes, KVM for KVM)
|
||||||
$osList = \Paymenter\Extensions\Servers\Proxmox\Models\Location::findOrFail($locationId)
|
$osList = \Paymenter\Extensions\Servers\Proxmox\Models\Location::findOrFail($locationId)
|
||||||
->os()
|
->os()
|
||||||
|
->where('is_lxc', $isLxc)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
return view('proxmox::reinstall', [
|
return view('proxmox::reinstall', [
|
||||||
|
|||||||
@@ -13,5 +13,18 @@ class OS extends Model
|
|||||||
'name',
|
'name',
|
||||||
'vm_id',
|
'vm_id',
|
||||||
'location_id',
|
'location_id',
|
||||||
|
'is_lxc',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'is_lxc' => 'boolean',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the VM type string for Proxmox API paths.
|
||||||
|
*/
|
||||||
|
public function vmType(): string
|
||||||
|
{
|
||||||
|
return $this->is_lxc ? 'lxc' : 'qemu';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -470,10 +470,22 @@ class Proxmox extends ClassServer
|
|||||||
|
|
||||||
$server->save();
|
$server->save();
|
||||||
|
|
||||||
$vmType = $server->node->vmType();
|
// Use the OS template's type for cloning (source type determines API path)
|
||||||
|
// The template type should match the node type (LXC templates for LXC nodes, KVM for KVM)
|
||||||
|
$templateVmType = $server->os->vmType();
|
||||||
|
$nodeVmType = $server->node->vmType();
|
||||||
|
|
||||||
// Create the VM/CT
|
// Warn if there's a type mismatch (shouldn't happen if OS selection is filtered correctly)
|
||||||
$cloneResult = $this->requestAndWait('/nodes/' . $server->node->name . '/' . $vmType . '/' . $server->os->vm_id . '/clone', 'post', [
|
if ($templateVmType !== $nodeVmType) {
|
||||||
|
\Log::warning('OS template type does not match node type', [
|
||||||
|
'server_id' => $server->id,
|
||||||
|
'os_type' => $templateVmType,
|
||||||
|
'node_type' => $nodeVmType,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the VM/CT - use template's vmType for source path
|
||||||
|
$cloneResult = $this->requestAndWait('/nodes/' . $server->node->name . '/' . $templateVmType . '/' . $server->os->vm_id . '/clone', 'post', [
|
||||||
'newid' => $server->vm_id,
|
'newid' => $server->vm_id,
|
||||||
'hostname' => $server->hostname,
|
'hostname' => $server->hostname,
|
||||||
'target' => $server->node->name,
|
'target' => $server->node->name,
|
||||||
@@ -698,7 +710,7 @@ class Proxmox extends ClassServer
|
|||||||
ReinstallJob::dispatch($server);
|
ReinstallJob::dispatch($server);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function calculateNode(Location $location, Service $service, $settings)
|
private function calculateNode(Location $location, Service $service, $settings, ?bool $isLxc = null)
|
||||||
{
|
{
|
||||||
// What type of selection do we want to use?
|
// What type of selection do we want to use?
|
||||||
/* 'random' => 'Random Node',
|
/* 'random' => 'Random Node',
|
||||||
@@ -708,12 +720,18 @@ class Proxmox extends ClassServer
|
|||||||
'least_loaded_disk' => 'Least Loaded Node (based on disk)',
|
'least_loaded_disk' => 'Least Loaded Node (based on disk)',
|
||||||
'least_loaded_cpu' => 'Least Loaded Node (based on CPU)',
|
'least_loaded_cpu' => 'Least Loaded Node (based on CPU)',
|
||||||
*/
|
*/
|
||||||
$cpuNeeded = $settings['cores'] * $settings['sockets'];
|
$cpuNeeded = $settings['cores'] * ($settings['sockets'] ?? 1);
|
||||||
$memoryNeeded = (int) $settings['memory'];
|
$memoryNeeded = (int) $settings['memory'];
|
||||||
$diskNeeded = (int) $settings['disk'];
|
$diskNeeded = (int) $settings['disk'];
|
||||||
|
|
||||||
|
// Build base query for nodes, optionally filtering by type (LXC or KVM)
|
||||||
|
$nodeQuery = $location->nodes();
|
||||||
|
if ($isLxc !== null) {
|
||||||
|
$nodeQuery->where('is_lxc', $isLxc);
|
||||||
|
}
|
||||||
|
|
||||||
if ($location->server_placement == 'random') {
|
if ($location->server_placement == 'random') {
|
||||||
$nodes = $location->nodes()->inRandomOrder()->get();
|
$nodes = (clone $nodeQuery)->inRandomOrder()->get();
|
||||||
$node = null;
|
$node = null;
|
||||||
foreach ($nodes as $n) {
|
foreach ($nodes as $n) {
|
||||||
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
||||||
@@ -723,7 +741,7 @@ class Proxmox extends ClassServer
|
|||||||
}
|
}
|
||||||
} elseif ($location->server_placement == 'fill') {
|
} elseif ($location->server_placement == 'fill') {
|
||||||
// Get the nodes that have enough resources
|
// Get the nodes that have enough resources
|
||||||
$nodes = $location->nodes()->get();
|
$nodes = (clone $nodeQuery)->get();
|
||||||
$node = null;
|
$node = null;
|
||||||
foreach ($nodes as $n) {
|
foreach ($nodes as $n) {
|
||||||
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
||||||
@@ -733,7 +751,7 @@ class Proxmox extends ClassServer
|
|||||||
}
|
}
|
||||||
} elseif ($location->server_placement == 'least_loaded_server') {
|
} elseif ($location->server_placement == 'least_loaded_server') {
|
||||||
// Get the node with the least
|
// Get the node with the least
|
||||||
$nodes = $location->nodes()->withCount('servers')->orderBy('servers_count', 'asc')->get();
|
$nodes = (clone $nodeQuery)->withCount('servers')->orderBy('servers_count', 'asc')->get();
|
||||||
$node = null;
|
$node = null;
|
||||||
foreach ($nodes as $n) {
|
foreach ($nodes as $n) {
|
||||||
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
||||||
@@ -742,7 +760,7 @@ class Proxmox extends ClassServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif ($location->server_placement == 'least_loaded_ram') {
|
} elseif ($location->server_placement == 'least_loaded_ram') {
|
||||||
$nodes = $location->nodes()->withCount('servers')->orderBy('ram', 'asc')->get();
|
$nodes = (clone $nodeQuery)->withCount('servers')->orderBy('ram', 'asc')->get();
|
||||||
$nodeAndRam = [];
|
$nodeAndRam = [];
|
||||||
foreach ($nodes as $n) {
|
foreach ($nodes as $n) {
|
||||||
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
||||||
@@ -756,7 +774,7 @@ class Proxmox extends ClassServer
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} elseif ($location->server_placement == 'least_loaded_disk') {
|
} elseif ($location->server_placement == 'least_loaded_disk') {
|
||||||
$nodes = $location->nodes()->withCount('servers')->orderBy('disk', 'asc')->get();
|
$nodes = (clone $nodeQuery)->withCount('servers')->orderBy('disk', 'asc')->get();
|
||||||
$nodeAndDisk = [];
|
$nodeAndDisk = [];
|
||||||
foreach ($nodes as $n) {
|
foreach ($nodes as $n) {
|
||||||
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
||||||
@@ -770,7 +788,7 @@ class Proxmox extends ClassServer
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} elseif ($location->server_placement == 'least_loaded_cpu') {
|
} elseif ($location->server_placement == 'least_loaded_cpu') {
|
||||||
$nodes = $location->nodes()->withCount('servers')->orderBy('cpu', 'asc')->get();
|
$nodes = (clone $nodeQuery)->withCount('servers')->orderBy('cpu', 'asc')->get();
|
||||||
$nodeAndCpu = [];
|
$nodeAndCpu = [];
|
||||||
foreach ($nodes as $n) {
|
foreach ($nodes as $n) {
|
||||||
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
||||||
@@ -967,8 +985,18 @@ class Proxmox extends ClassServer
|
|||||||
$settings = array_merge($settings, $properties);
|
$settings = array_merge($settings, $properties);
|
||||||
|
|
||||||
$location = Location::findOrFail($settings['location_id']);
|
$location = Location::findOrFail($settings['location_id']);
|
||||||
$node = $this->calculateNode($location, $service, $settings);
|
|
||||||
|
// Get the OS first to determine if it's LXC or KVM
|
||||||
$os = $location->os()->findOrFail($settings['os']);
|
$os = $location->os()->findOrFail($settings['os']);
|
||||||
|
|
||||||
|
// Calculate node - pass the OS type so it selects a matching node (LXC or KVM)
|
||||||
|
$node = $this->calculateNode($location, $service, $settings, $os->is_lxc);
|
||||||
|
|
||||||
|
// Verify OS type matches node type
|
||||||
|
if ($os->is_lxc !== ($node->is_lxc ?? false)) {
|
||||||
|
throw new Exception('OS template type (' . ($os->is_lxc ? 'LXC' : 'KVM') . ') does not match available node type (' . ($node->is_lxc ? 'LXC' : 'KVM') . '). Please select a different OS or contact support.');
|
||||||
|
}
|
||||||
|
|
||||||
// Get ippools, then check if a pool has enough IPs
|
// Get ippools, then check if a pool has enough IPs
|
||||||
$ipv4 = (object) [
|
$ipv4 = (object) [
|
||||||
'ips' => collect(),
|
'ips' => collect(),
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('ext_proxmox_oses', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_lxc')->default(false)->after('location_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('ext_proxmox_oses', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_lxc');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user