- Added template typing so approved templates can represent either QEMU VMs or LXC containers.
- Added LXC template discovery from Proxmox storage `vztmpl` content in the admin template manager. - Added live LXC container provisioning through the Proxmox API with configurable rootfs storage and optional DHCP bridge. - Routed start, stop, delete, expiration, status, and IP refresh operations through typed Proxmox VM/LXC API paths. - Added Proxmox tags to newly created VMs and containers, including a sanitized per-user tag for easier PVE administration. - Updated the admin and portal UI to show VM versus LXC template/deployment types and generic Proxmox resource IDs. - Added schema upgrades for template provisioning type, LXC template references, and deployment resource type. - Documented LXC setup, storage permissions, and the new Proxmox settings.
This commit is contained in:
@@ -157,6 +157,7 @@ final class SPP_Repository
|
||||
$wpdb->insert($table, [
|
||||
'name' => $name,
|
||||
'status' => 'STOPPED',
|
||||
'provisioning_type' => $this->normalise_template_type((string) ($template['provisioning_type'] ?? 'qemu')),
|
||||
'proxmox_vm_id' => $vm_id,
|
||||
'ip_addresses' => wp_json_encode(array_values($ip_addresses)),
|
||||
'expires_at' => $expires_at,
|
||||
@@ -169,6 +170,7 @@ final class SPP_Repository
|
||||
$deployment_id = (int) $wpdb->insert_id;
|
||||
$this->audit('DEPLOYMENT_CREATED', 'deployment', $deployment_id, $actor_id, [
|
||||
'template_id' => (int) $template['id'],
|
||||
'provisioning_type' => $this->normalise_template_type((string) ($template['provisioning_type'] ?? 'qemu')),
|
||||
'proxmox_vm_id' => $vm_id,
|
||||
'ip_addresses' => $ip_addresses,
|
||||
'ttl_hours' => $ttl_hours,
|
||||
@@ -439,15 +441,11 @@ final class SPP_Repository
|
||||
|
||||
$table = SPP_Activator::table('templates');
|
||||
$now = current_time('mysql');
|
||||
$template_key = $this->unique_template_key((string) $data['template_key'], (int) $data['proxmox_template_id']);
|
||||
$existing_id = (int) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT id FROM {$table} WHERE proxmox_template_id = %d OR template_key = %s ORDER BY proxmox_template_id = %d DESC LIMIT 1",
|
||||
(int) $data['proxmox_template_id'],
|
||||
$template_key,
|
||||
(int) $data['proxmox_template_id']
|
||||
)
|
||||
);
|
||||
$provisioning_type = $this->normalise_template_type((string) ($data['provisioning_type'] ?? 'qemu'));
|
||||
$proxmox_template_id = $provisioning_type === 'qemu' ? absint($data['proxmox_template_id']) : 0;
|
||||
$proxmox_template_ref = $provisioning_type === 'lxc' ? sanitize_text_field((string) ($data['proxmox_template_ref'] ?? '')) : null;
|
||||
$template_key = $this->unique_template_key((string) $data['template_key'], $provisioning_type, $proxmox_template_id, (string) $proxmox_template_ref);
|
||||
$existing_id = $this->template_existing_id($provisioning_type, $proxmox_template_id, (string) $proxmox_template_ref, $template_key);
|
||||
|
||||
$row = [
|
||||
'template_key' => $template_key,
|
||||
@@ -458,7 +456,9 @@ final class SPP_Repository
|
||||
'memory_mb' => max(128, absint($data['memory_mb'])),
|
||||
'disk_gb' => max(1, absint($data['disk_gb'])),
|
||||
'default_ttl_hours' => max(1, min(720, absint($data['default_ttl_hours']))),
|
||||
'proxmox_template_id' => absint($data['proxmox_template_id']),
|
||||
'provisioning_type' => $provisioning_type,
|
||||
'proxmox_template_id' => $proxmox_template_id,
|
||||
'proxmox_template_ref' => $proxmox_template_ref,
|
||||
'is_active' => 1,
|
||||
'updated_at' => $now,
|
||||
];
|
||||
@@ -474,7 +474,9 @@ final class SPP_Repository
|
||||
}
|
||||
|
||||
$this->audit($action, 'template', $template_id, $actor_id, [
|
||||
'provisioning_type' => (string) $row['provisioning_type'],
|
||||
'proxmox_template_id' => (int) $row['proxmox_template_id'],
|
||||
'proxmox_template_ref' => (string) ($row['proxmox_template_ref'] ?? ''),
|
||||
'name' => (string) $row['name'],
|
||||
]);
|
||||
|
||||
@@ -489,6 +491,9 @@ final class SPP_Repository
|
||||
global $wpdb;
|
||||
|
||||
$table = SPP_Activator::table('templates');
|
||||
$provisioning_type = $this->normalise_template_type((string) ($data['provisioning_type'] ?? 'qemu'));
|
||||
$proxmox_template_id = $provisioning_type === 'qemu' ? absint($data['proxmox_template_id']) : 0;
|
||||
$proxmox_template_ref = $provisioning_type === 'lxc' ? sanitize_text_field((string) ($data['proxmox_template_ref'] ?? '')) : null;
|
||||
$row = [
|
||||
'name' => sanitize_text_field((string) $data['name']),
|
||||
'description' => sanitize_textarea_field((string) $data['description']),
|
||||
@@ -497,7 +502,9 @@ final class SPP_Repository
|
||||
'memory_mb' => max(128, absint($data['memory_mb'])),
|
||||
'disk_gb' => max(1, absint($data['disk_gb'])),
|
||||
'default_ttl_hours' => max(1, min(720, absint($data['default_ttl_hours']))),
|
||||
'proxmox_template_id' => absint($data['proxmox_template_id']),
|
||||
'provisioning_type' => $provisioning_type,
|
||||
'proxmox_template_id' => $proxmox_template_id,
|
||||
'proxmox_template_ref' => $proxmox_template_ref,
|
||||
'is_active' => empty($data['is_active']) ? 0 : 1,
|
||||
'updated_at' => current_time('mysql'),
|
||||
];
|
||||
@@ -505,7 +512,9 @@ final class SPP_Repository
|
||||
$wpdb->update($table, $row, ['id' => $id]);
|
||||
|
||||
$this->audit('TEMPLATE_UPDATED', 'template', $id, $actor_id, [
|
||||
'provisioning_type' => (string) $row['provisioning_type'],
|
||||
'proxmox_template_id' => (int) $row['proxmox_template_id'],
|
||||
'proxmox_template_ref' => (string) ($row['proxmox_template_ref'] ?? ''),
|
||||
'name' => (string) $row['name'],
|
||||
'is_active' => (int) $row['is_active'],
|
||||
]);
|
||||
@@ -529,16 +538,28 @@ final class SPP_Repository
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, int>
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function active_proxmox_template_ids(): array
|
||||
public function active_template_identity_keys(): array
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
$table = SPP_Activator::table('templates');
|
||||
$ids = $wpdb->get_col("SELECT proxmox_template_id FROM {$table} WHERE is_active = 1");
|
||||
$rows = $wpdb->get_results(
|
||||
"SELECT provisioning_type, proxmox_template_id, proxmox_template_ref FROM {$table} WHERE is_active = 1",
|
||||
ARRAY_A
|
||||
);
|
||||
$keys = [];
|
||||
|
||||
return array_values(array_map('intval', is_array($ids) ? $ids : []));
|
||||
foreach (is_array($rows) ? $rows : [] as $row) {
|
||||
$keys[] = $this->template_identity_key(
|
||||
(string) ($row['provisioning_type'] ?? 'qemu'),
|
||||
(int) ($row['proxmox_template_id'] ?? 0),
|
||||
(string) ($row['proxmox_template_ref'] ?? '')
|
||||
);
|
||||
}
|
||||
|
||||
return array_values(array_unique($keys));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -617,17 +638,58 @@ final class SPP_Repository
|
||||
return is_array($row) ? $this->template_dto($row) : null;
|
||||
}
|
||||
|
||||
private function unique_template_key(string $raw_key, int $proxmox_template_id): string
|
||||
private function unique_template_key(string $raw_key, string $provisioning_type, int $proxmox_template_id, string $proxmox_template_ref): string
|
||||
{
|
||||
$key = sanitize_title($raw_key);
|
||||
|
||||
if ($key === '') {
|
||||
$key = 'pve-template-' . $proxmox_template_id;
|
||||
$key = $this->template_identity_key($provisioning_type, $proxmox_template_id, $proxmox_template_ref);
|
||||
}
|
||||
|
||||
return substr($key, 0, 80);
|
||||
}
|
||||
|
||||
private function template_existing_id(string $provisioning_type, int $proxmox_template_id, string $proxmox_template_ref, string $template_key): int
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
$table = SPP_Activator::table('templates');
|
||||
|
||||
if ($provisioning_type === 'lxc') {
|
||||
return (int) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT id FROM {$table}
|
||||
WHERE (provisioning_type = 'lxc' AND proxmox_template_ref = %s) OR template_key = %s
|
||||
LIMIT 1",
|
||||
$proxmox_template_ref,
|
||||
$template_key
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (int) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT id FROM {$table}
|
||||
WHERE (provisioning_type = 'qemu' AND proxmox_template_id = %d) OR template_key = %s
|
||||
LIMIT 1",
|
||||
$proxmox_template_id,
|
||||
$template_key
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function template_identity_key(string $provisioning_type, int $proxmox_template_id, string $proxmox_template_ref): string
|
||||
{
|
||||
return $this->normalise_template_type($provisioning_type) === 'lxc'
|
||||
? 'lxc:' . $proxmox_template_ref
|
||||
: 'qemu:' . $proxmox_template_id;
|
||||
}
|
||||
|
||||
private function normalise_template_type(string $type): string
|
||||
{
|
||||
return strtolower($type) === 'lxc' ? 'lxc' : 'qemu';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $metadata
|
||||
*/
|
||||
@@ -661,7 +723,9 @@ final class SPP_Repository
|
||||
'memoryMb' => (int) $row['memory_mb'],
|
||||
'diskGb' => (int) $row['disk_gb'],
|
||||
'defaultTtlHours' => (int) $row['default_ttl_hours'],
|
||||
'provisioningType' => $this->normalise_template_type((string) ($row['provisioning_type'] ?? 'qemu')),
|
||||
'proxmoxTemplateId' => (int) $row['proxmox_template_id'],
|
||||
'proxmoxTemplateRef' => (string) ($row['proxmox_template_ref'] ?? ''),
|
||||
'isActive' => (int) $row['is_active'] === 1,
|
||||
];
|
||||
}
|
||||
@@ -676,6 +740,7 @@ final class SPP_Repository
|
||||
'id' => (int) $row['id'],
|
||||
'name' => (string) $row['name'],
|
||||
'status' => (string) $row['status'],
|
||||
'provisioningType' => $this->normalise_template_type((string) ($row['provisioning_type'] ?? 'qemu')),
|
||||
'templateName' => (string) $row['template_name'],
|
||||
'requestedById' => (int) $row['requested_by'],
|
||||
'requestedByName' => (string) $row['requested_by_name'],
|
||||
@@ -695,6 +760,8 @@ final class SPP_Repository
|
||||
return array_merge($this->deployment_summary_dto($row), [
|
||||
'templateId' => (int) $row['template_id'],
|
||||
'proxmoxVmId' => isset($row['proxmox_vm_id']) ? (int) $row['proxmox_vm_id'] : null,
|
||||
'proxmoxResourceType' => $this->normalise_template_type((string) ($row['provisioning_type'] ?? 'qemu')),
|
||||
'proxmoxResourceId' => isset($row['proxmox_vm_id']) ? (int) $row['proxmox_vm_id'] : null,
|
||||
'cpuCores' => (int) $row['cpu_cores'],
|
||||
'memoryMb' => (int) $row['memory_mb'],
|
||||
'diskGb' => (int) $row['disk_gb'],
|
||||
|
||||
Reference in New Issue
Block a user