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'));
|
||||
})
|
||||
->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()
|
||||
->columns(2)
|
||||
->columns(3)
|
||||
->columnSpanFull(),
|
||||
]),
|
||||
]),
|
||||
|
||||
@@ -83,9 +83,12 @@ class Reinstall extends Component
|
||||
|
||||
// assuming Server has relation `node()` -> Node model
|
||||
$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)
|
||||
->os()
|
||||
->where('is_lxc', $isLxc)
|
||||
->get();
|
||||
|
||||
return view('proxmox::reinstall', [
|
||||
|
||||
@@ -13,5 +13,18 @@ class OS extends Model
|
||||
'name',
|
||||
'vm_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();
|
||||
|
||||
$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
|
||||
$cloneResult = $this->requestAndWait('/nodes/' . $server->node->name . '/' . $vmType . '/' . $server->os->vm_id . '/clone', 'post', [
|
||||
// Warn if there's a type mismatch (shouldn't happen if OS selection is filtered correctly)
|
||||
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,
|
||||
'hostname' => $server->hostname,
|
||||
'target' => $server->node->name,
|
||||
@@ -698,7 +710,7 @@ class Proxmox extends ClassServer
|
||||
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?
|
||||
/* 'random' => 'Random Node',
|
||||
@@ -708,12 +720,18 @@ class Proxmox extends ClassServer
|
||||
'least_loaded_disk' => 'Least Loaded Node (based on disk)',
|
||||
'least_loaded_cpu' => 'Least Loaded Node (based on CPU)',
|
||||
*/
|
||||
$cpuNeeded = $settings['cores'] * $settings['sockets'];
|
||||
$cpuNeeded = $settings['cores'] * ($settings['sockets'] ?? 1);
|
||||
$memoryNeeded = (int) $settings['memory'];
|
||||
$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') {
|
||||
$nodes = $location->nodes()->inRandomOrder()->get();
|
||||
$nodes = (clone $nodeQuery)->inRandomOrder()->get();
|
||||
$node = null;
|
||||
foreach ($nodes as $n) {
|
||||
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
||||
@@ -723,7 +741,7 @@ class Proxmox extends ClassServer
|
||||
}
|
||||
} elseif ($location->server_placement == 'fill') {
|
||||
// Get the nodes that have enough resources
|
||||
$nodes = $location->nodes()->get();
|
||||
$nodes = (clone $nodeQuery)->get();
|
||||
$node = null;
|
||||
foreach ($nodes as $n) {
|
||||
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
||||
@@ -733,7 +751,7 @@ class Proxmox extends ClassServer
|
||||
}
|
||||
} elseif ($location->server_placement == 'least_loaded_server') {
|
||||
// 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;
|
||||
foreach ($nodes as $n) {
|
||||
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
||||
@@ -742,7 +760,7 @@ class Proxmox extends ClassServer
|
||||
}
|
||||
}
|
||||
} 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 = [];
|
||||
foreach ($nodes as $n) {
|
||||
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
||||
@@ -756,7 +774,7 @@ class Proxmox extends ClassServer
|
||||
break;
|
||||
}
|
||||
} 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 = [];
|
||||
foreach ($nodes as $n) {
|
||||
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
||||
@@ -770,7 +788,7 @@ class Proxmox extends ClassServer
|
||||
break;
|
||||
}
|
||||
} 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 = [];
|
||||
foreach ($nodes as $n) {
|
||||
if ($n->canStoreServer($memoryNeeded, $diskNeeded, $cpuNeeded)) {
|
||||
@@ -967,8 +985,18 @@ class Proxmox extends ClassServer
|
||||
$settings = array_merge($settings, $properties);
|
||||
|
||||
$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']);
|
||||
|
||||
// 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
|
||||
$ipv4 = (object) [
|
||||
'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