- 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:
@@ -222,22 +222,27 @@ final class SPP_REST_Controller
|
||||
return $quota_error;
|
||||
}
|
||||
|
||||
$provisioning_type = $this->normalise_template_type((string) ($template['provisioning_type'] ?? 'qemu'));
|
||||
|
||||
try {
|
||||
$clone = $this->proxmox->clone_vm([
|
||||
$instance = $this->proxmox->provision_instance([
|
||||
'provisioning_type' => $provisioning_type,
|
||||
'template_vm_id' => (int) $template['proxmox_template_id'],
|
||||
'lxc_template_ref' => (string) ($template['proxmox_template_ref'] ?? ''),
|
||||
'name' => $name,
|
||||
'cpu_cores' => (int) $template['cpu_cores'],
|
||||
'memory_mb' => (int) $template['memory_mb'],
|
||||
'disk_gb' => (int) $template['disk_gb'],
|
||||
'tags' => $this->deployment_tags(wp_get_current_user()),
|
||||
]);
|
||||
|
||||
$vm_id = (int) $clone['vm_id'];
|
||||
$vm_id = (int) $instance['vm_id'];
|
||||
$deployment = $this->repository->create_deployment(
|
||||
$template,
|
||||
$name,
|
||||
$ttl_hours,
|
||||
$vm_id,
|
||||
$this->safe_ip_addresses($vm_id),
|
||||
$this->safe_ip_addresses($provisioning_type, $vm_id),
|
||||
get_current_user_id()
|
||||
);
|
||||
} catch (Throwable $error) {
|
||||
@@ -278,17 +283,17 @@ final class SPP_REST_Controller
|
||||
|
||||
public function start_deployment(WP_REST_Request $request): WP_REST_Response|WP_Error
|
||||
{
|
||||
return $this->apply_lifecycle_action((int) $request['id'], 'RUNNING', 'DEPLOYMENT_STARTED', 'start_vm');
|
||||
return $this->apply_lifecycle_action((int) $request['id'], 'RUNNING', 'DEPLOYMENT_STARTED', 'start');
|
||||
}
|
||||
|
||||
public function stop_deployment(WP_REST_Request $request): WP_REST_Response|WP_Error
|
||||
{
|
||||
return $this->apply_lifecycle_action((int) $request['id'], 'STOPPED', 'DEPLOYMENT_STOPPED', 'stop_vm');
|
||||
return $this->apply_lifecycle_action((int) $request['id'], 'STOPPED', 'DEPLOYMENT_STOPPED', 'stop');
|
||||
}
|
||||
|
||||
public function delete_deployment(WP_REST_Request $request): WP_REST_Response|WP_Error
|
||||
{
|
||||
return $this->apply_lifecycle_action((int) $request['id'], 'DELETED', 'DEPLOYMENT_DELETED', 'delete_vm');
|
||||
return $this->apply_lifecycle_action((int) $request['id'], 'DELETED', 'DEPLOYMENT_DELETED', 'delete');
|
||||
}
|
||||
|
||||
public function prolong_deployment(WP_REST_Request $request): WP_REST_Response|WP_Error
|
||||
@@ -351,13 +356,16 @@ final class SPP_REST_Controller
|
||||
}
|
||||
|
||||
if (empty($record['proxmox_vm_id'])) {
|
||||
return new WP_Error('spp_missing_vm_id', 'Deployment is missing a Proxmox VM id.', ['status' => 409]);
|
||||
return new WP_Error('spp_missing_vm_id', 'Deployment is missing a Proxmox resource id.', ['status' => 409]);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->repository->update_deployment_ips(
|
||||
$id,
|
||||
$this->proxmox->get_ip_addresses((int) $record['proxmox_vm_id']),
|
||||
$this->proxmox->get_ip_addresses(
|
||||
(string) ($record['provisioning_type'] ?? 'qemu'),
|
||||
(int) $record['proxmox_vm_id']
|
||||
),
|
||||
get_current_user_id()
|
||||
);
|
||||
} catch (Throwable $error) {
|
||||
@@ -442,11 +450,11 @@ final class SPP_REST_Controller
|
||||
return new WP_Error('spp_not_found', 'Deployment not found.', ['status' => 404]);
|
||||
}
|
||||
|
||||
if ($method === 'delete_vm' && !$this->user_can_delete_deployment($id)) {
|
||||
if ($method === 'delete' && !$this->user_can_delete_deployment($id)) {
|
||||
return new WP_Error('spp_forbidden', 'Only the owner or a deployment manager can delete this deployment.', ['status' => 403]);
|
||||
}
|
||||
|
||||
if ($method === 'start_vm' && $record['status'] === 'EXPIRED') {
|
||||
if ($method === 'start' && $record['status'] === 'EXPIRED') {
|
||||
return new WP_Error(
|
||||
'spp_expired_deployment',
|
||||
'This deployment is expired. Prolong its TTL before starting it again.',
|
||||
@@ -455,27 +463,33 @@ final class SPP_REST_Controller
|
||||
}
|
||||
|
||||
if (empty($record['proxmox_vm_id'])) {
|
||||
return new WP_Error('spp_missing_vm_id', 'Deployment is missing a Proxmox VM id.', ['status' => 409]);
|
||||
return new WP_Error('spp_missing_vm_id', 'Deployment is missing a Proxmox resource id.', ['status' => 409]);
|
||||
}
|
||||
|
||||
$provisioning_type = $this->normalise_template_type((string) ($record['provisioning_type'] ?? 'qemu'));
|
||||
|
||||
try {
|
||||
$this->proxmox->{$method}((int) $record['proxmox_vm_id']);
|
||||
if ($method === 'start_vm') {
|
||||
if ($method === 'start') {
|
||||
$this->proxmox->start_instance($provisioning_type, (int) $record['proxmox_vm_id']);
|
||||
$this->repository->update_deployment_status_and_ips(
|
||||
$id,
|
||||
$status,
|
||||
$this->safe_ip_addresses((int) $record['proxmox_vm_id']),
|
||||
$this->safe_ip_addresses($provisioning_type, (int) $record['proxmox_vm_id']),
|
||||
$audit_action,
|
||||
get_current_user_id()
|
||||
);
|
||||
} elseif ($method === 'stop') {
|
||||
$this->proxmox->stop_instance($provisioning_type, (int) $record['proxmox_vm_id']);
|
||||
$this->repository->update_deployment_status($id, $status, $audit_action, get_current_user_id());
|
||||
} else {
|
||||
$this->proxmox->delete_instance($provisioning_type, (int) $record['proxmox_vm_id']);
|
||||
$this->repository->update_deployment_status($id, $status, $audit_action, get_current_user_id());
|
||||
}
|
||||
} catch (Throwable $error) {
|
||||
return new WP_Error('spp_proxmox_error', $error->getMessage(), ['status' => 502]);
|
||||
}
|
||||
|
||||
if ($method === 'delete_vm') {
|
||||
if ($method === 'delete') {
|
||||
return rest_ensure_response(['deleted' => true]);
|
||||
}
|
||||
|
||||
@@ -553,12 +567,32 @@ final class SPP_REST_Controller
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function safe_ip_addresses(int $vm_id): array
|
||||
private function safe_ip_addresses(string $provisioning_type, int $vm_id): array
|
||||
{
|
||||
try {
|
||||
return $this->proxmox->get_ip_addresses($vm_id);
|
||||
return $this->proxmox->get_ip_addresses($provisioning_type, $vm_id);
|
||||
} catch (Throwable) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function deployment_tags(WP_User $user): array
|
||||
{
|
||||
$login = $user->user_login !== '' ? $user->user_login : $user->display_name;
|
||||
$user_tag = strtolower(sanitize_title($login));
|
||||
|
||||
if ($user_tag === '') {
|
||||
$user_tag = (string) $user->ID;
|
||||
}
|
||||
|
||||
return ['support-portal', 'user-' . $user_tag];
|
||||
}
|
||||
|
||||
private function normalise_template_type(string $type): string
|
||||
{
|
||||
return strtolower($type) === 'lxc' ? 'lxc' : 'qemu';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user