- 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.
226 lines
8.3 KiB
PHP
226 lines
8.3 KiB
PHP
<?php
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
final class SPP_Activator
|
|
{
|
|
private const DB_VERSION = '0.7.0';
|
|
|
|
public static function activate(): void
|
|
{
|
|
self::install_schema();
|
|
self::seed_templates();
|
|
|
|
add_option('spp_proxmox_mode', 'mock');
|
|
add_option('spp_proxmox_node', 'pve-01');
|
|
add_option('spp_lxc_rootfs_storage', '');
|
|
add_option('spp_lxc_bridge', 'vmbr0');
|
|
add_option('spp_mock_next_vm_id', 10000);
|
|
add_option('spp_quota_user_memory_mb', 0);
|
|
add_option('spp_quota_global_memory_mb', 0);
|
|
self::schedule_expiration_check();
|
|
update_option('spp_db_version', self::DB_VERSION, false);
|
|
}
|
|
|
|
public static function deactivate(): void
|
|
{
|
|
wp_clear_scheduled_hook('spp_expire_deployments');
|
|
}
|
|
|
|
public static function maybe_upgrade(): void
|
|
{
|
|
if (get_option('spp_db_version') === self::DB_VERSION) {
|
|
return;
|
|
}
|
|
|
|
self::install_schema();
|
|
self::seed_templates();
|
|
add_option('spp_quota_user_memory_mb', 0);
|
|
add_option('spp_quota_global_memory_mb', 0);
|
|
self::schedule_expiration_check();
|
|
update_option('spp_db_version', self::DB_VERSION, false);
|
|
}
|
|
|
|
private static function install_schema(): void
|
|
{
|
|
global $wpdb;
|
|
|
|
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
|
|
|
$charset_collate = $wpdb->get_charset_collate();
|
|
$templates = self::table('templates');
|
|
$deployments = self::table('deployments');
|
|
$deployment_shares = self::table('deployment_shares');
|
|
$audit_logs = self::table('audit_logs');
|
|
|
|
dbDelta("CREATE TABLE {$templates} (
|
|
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
|
template_key varchar(80) NOT NULL,
|
|
name varchar(160) NOT NULL,
|
|
description text NOT NULL,
|
|
os_type varchar(24) NOT NULL,
|
|
cpu_cores int unsigned NOT NULL,
|
|
memory_mb int unsigned NOT NULL,
|
|
disk_gb int unsigned NOT NULL,
|
|
default_ttl_hours int unsigned NOT NULL,
|
|
provisioning_type varchar(16) NOT NULL DEFAULT 'qemu',
|
|
proxmox_template_id int unsigned NOT NULL,
|
|
proxmox_template_ref varchar(255) NULL,
|
|
is_active tinyint(1) NOT NULL DEFAULT 1,
|
|
created_at datetime NOT NULL,
|
|
updated_at datetime NOT NULL,
|
|
PRIMARY KEY (id),
|
|
UNIQUE KEY template_key (template_key)
|
|
) {$charset_collate};");
|
|
|
|
dbDelta("CREATE TABLE {$deployments} (
|
|
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
|
name varchar(160) NOT NULL,
|
|
status varchar(32) NOT NULL,
|
|
provisioning_type varchar(16) NOT NULL DEFAULT 'qemu',
|
|
proxmox_vm_id int unsigned DEFAULT NULL,
|
|
ip_addresses longtext NULL,
|
|
error_message text NULL,
|
|
expires_at datetime NULL,
|
|
created_at datetime NOT NULL,
|
|
updated_at datetime NOT NULL,
|
|
template_id bigint(20) unsigned NOT NULL,
|
|
requested_by bigint(20) unsigned NOT NULL,
|
|
PRIMARY KEY (id),
|
|
KEY status (status),
|
|
KEY expires_at (expires_at),
|
|
KEY template_id (template_id),
|
|
KEY requested_by (requested_by)
|
|
) {$charset_collate};");
|
|
|
|
$wpdb->query("ALTER TABLE {$deployments} MODIFY expires_at datetime NULL");
|
|
|
|
self::add_column_if_missing($templates, 'provisioning_type', "ALTER TABLE {$templates} ADD COLUMN provisioning_type varchar(16) NOT NULL DEFAULT 'qemu' AFTER default_ttl_hours");
|
|
self::add_column_if_missing($templates, 'proxmox_template_ref', "ALTER TABLE {$templates} ADD COLUMN proxmox_template_ref varchar(255) NULL AFTER proxmox_template_id");
|
|
self::add_column_if_missing($deployments, 'provisioning_type', "ALTER TABLE {$deployments} ADD COLUMN provisioning_type varchar(16) NOT NULL DEFAULT 'qemu' AFTER status");
|
|
|
|
$ip_column = $wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$deployments} LIKE %s", 'ip_addresses'));
|
|
if ($ip_column === null) {
|
|
$wpdb->query("ALTER TABLE {$deployments} ADD COLUMN ip_addresses longtext NULL AFTER proxmox_vm_id");
|
|
}
|
|
|
|
dbDelta("CREATE TABLE {$deployment_shares} (
|
|
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
|
deployment_id bigint(20) unsigned NOT NULL,
|
|
user_id bigint(20) unsigned NOT NULL,
|
|
created_by bigint(20) unsigned NOT NULL,
|
|
created_at datetime NOT NULL,
|
|
PRIMARY KEY (id),
|
|
UNIQUE KEY deployment_user (deployment_id, user_id),
|
|
KEY deployment_id (deployment_id),
|
|
KEY user_id (user_id)
|
|
) {$charset_collate};");
|
|
|
|
dbDelta("CREATE TABLE {$audit_logs} (
|
|
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
|
action varchar(80) NOT NULL,
|
|
entity_type varchar(80) NOT NULL,
|
|
entity_id bigint(20) unsigned NOT NULL,
|
|
actor_id bigint(20) unsigned NOT NULL,
|
|
metadata longtext NULL,
|
|
created_at datetime NOT NULL,
|
|
PRIMARY KEY (id),
|
|
KEY entity_lookup (entity_type, entity_id),
|
|
KEY created_at (created_at)
|
|
) {$charset_collate};");
|
|
|
|
}
|
|
|
|
private static function schedule_expiration_check(): void
|
|
{
|
|
if (!wp_next_scheduled('spp_expire_deployments')) {
|
|
wp_schedule_event(time() + 5 * MINUTE_IN_SECONDS, 'hourly', 'spp_expire_deployments');
|
|
}
|
|
}
|
|
|
|
public static function table(string $name): string
|
|
{
|
|
global $wpdb;
|
|
|
|
return $wpdb->prefix . 'spp_' . $name;
|
|
}
|
|
|
|
private static function add_column_if_missing(string $table, string $column, string $sql): void
|
|
{
|
|
global $wpdb;
|
|
|
|
$exists = $wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table} LIKE %s", $column));
|
|
if ($exists === null) {
|
|
$wpdb->query($sql);
|
|
}
|
|
}
|
|
|
|
private static function seed_templates(): void
|
|
{
|
|
global $wpdb;
|
|
|
|
$table = self::table('templates');
|
|
$now = current_time('mysql');
|
|
$templates = [
|
|
[
|
|
'template_key' => 'turnkey-pbx-test',
|
|
'name' => 'Turnkey PBX Test Appliance',
|
|
'description' => 'Small PBX appliance for call-flow reproduction and support testing.',
|
|
'os_type' => 'APPLIANCE',
|
|
'cpu_cores' => 2,
|
|
'memory_mb' => 2048,
|
|
'disk_gb' => 24,
|
|
'default_ttl_hours' => 72,
|
|
'provisioning_type' => 'qemu',
|
|
'proxmox_template_id' => 9001,
|
|
'proxmox_template_ref' => null,
|
|
],
|
|
[
|
|
'template_key' => 'windows-support-client',
|
|
'name' => 'Windows Support Client',
|
|
'description' => 'Standard Windows client VM with support tooling pre-installed.',
|
|
'os_type' => 'WINDOWS',
|
|
'cpu_cores' => 4,
|
|
'memory_mb' => 8192,
|
|
'disk_gb' => 80,
|
|
'default_ttl_hours' => 48,
|
|
'provisioning_type' => 'qemu',
|
|
'proxmox_template_id' => 9002,
|
|
'proxmox_template_ref' => null,
|
|
],
|
|
[
|
|
'template_key' => 'linux-utility-vm',
|
|
'name' => 'Linux Utility VM',
|
|
'description' => 'Lightweight Linux host for network checks, packet capture, and diagnostics.',
|
|
'os_type' => 'LINUX',
|
|
'cpu_cores' => 2,
|
|
'memory_mb' => 2048,
|
|
'disk_gb' => 32,
|
|
'default_ttl_hours' => 168,
|
|
'provisioning_type' => 'qemu',
|
|
'proxmox_template_id' => 9003,
|
|
'proxmox_template_ref' => null,
|
|
],
|
|
];
|
|
|
|
foreach ($templates as $template) {
|
|
$exists = (int) $wpdb->get_var(
|
|
$wpdb->prepare("SELECT id FROM {$table} WHERE template_key = %s", $template['template_key'])
|
|
);
|
|
|
|
$data = array_merge($template, [
|
|
'is_active' => 1,
|
|
'updated_at' => $now,
|
|
]);
|
|
|
|
if ($exists > 0) {
|
|
continue;
|
|
}
|
|
|
|
$wpdb->insert($table, array_merge($data, ['created_at' => $now]));
|
|
}
|
|
}
|
|
}
|