From 0634a17cdca6f2aae69e8895fccbcfba0bfcb298 Mon Sep 17 00:00:00 2001 From: Luke S Thompson Date: Tue, 19 Aug 2025 08:42:37 +1000 Subject: [PATCH] v1.2.14 (fix #50, fix #154, fix #155, fix #157) --- CHANGELOG.md | 19 +- modules/addons/pvewhmcs/pvewhmcs.php | 425 ++++++++++++++--------- modules/servers/pvewhmcs/img/stopped.png | Bin 4626 -> 5835 bytes modules/servers/pvewhmcs/pvewhmcs.php | 5 +- version | 2 +- 5 files changed, 279 insertions(+), 172 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0203bf..21c7c66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,24 @@ All notable changes to Proxmox VE for WHMCS will be documented in this file. ### 💅 Polish - SQL Expansion: Prepare for Nodes/ISOs/TPLs/Logs/SSH Keys/etc -- Client Area: Improved layout and formatting of guest details (#155) - -### 🐛 Bug Fix -- Client Area, Swap %: "NaN%" replaced with "0%" for QEMU cases (#154) https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/milestones +## [1.2.14] - 2025-08-19 - _"Client Area tidy"_ + +### 🚀 Feature +- Cluster Tasks: Show the cluster history in Admin GUI (#50) + +### 💅 Polish +- Admin Area, Server Test: Renamed to "Proxmox VE" for brevity +- Admin Area, Pane Titles: Renamed most panes to make it simpler +- Client Area: Improved layout and formatting of Guest Info (#155) +- Client Area: Improved naming and ordering of Actions menu (#157) +- Client Area: Updated 64x64px icons for Running/Suspended/Offline + +### 🐛 Bug Fix +- Client Area, Swap %: "NaN%" replaced with "0%" for QEMU (#154) + ## [1.2.13] - 2025-08-13 - _"Little Things"_ ### 💅 Polish diff --git a/modules/addons/pvewhmcs/pvewhmcs.php b/modules/addons/pvewhmcs/pvewhmcs.php index 4bd44a7..36b79e9 100644 --- a/modules/addons/pvewhmcs/pvewhmcs.php +++ b/modules/addons/pvewhmcs/pvewhmcs.php @@ -36,7 +36,7 @@ function pvewhmcs_config() { $configarray = array( "name" => "Proxmox VE for WHMCS", "description" => "Proxmox VE (Virtual Environment) & WHMCS, integrated & open-source! Provisioning & Management of VMs/CTs.".is_pvewhmcs_outdated(), - "version" => "1.2.13", + "version" => "1.2.14", "author" => "The Network Crew Pty Ltd", 'language' => 'English' ); @@ -45,7 +45,7 @@ function pvewhmcs_config() { // VERSION: also stored in repo/version (for update-available checker) function pvewhmcs_version(){ - return "1.2.13"; + return "1.2.14"; } // WHMCS MODULE: ACTIVATION of the ADDON MODULE @@ -166,19 +166,20 @@ function pvewhmcs_output($vars) { // Set the active tab based on the GET parameter, default to 'vmplans' if (!isset($_GET['tab'])) { - $_GET['tab'] = 'vmplans'; + $_GET['tab'] = 'nodes'; } // Start the HTML output for the Admin GUI echo '
@@ -204,7 +205,155 @@ function pvewhmcs_output($vars) { save_lxc_plan() ; } - // VM/CT PLANS tab in ADMIN GUI + // NODES / GUESTS tab in ADMIN GUI + echo '
' ; + echo ('

/cluster/resources

'); + + // Fetch all enabled Servers that use pvewhmcs + $servers = Capsule::table('tblservers') + ->where('type', '=', 'pvewhmcs') + ->where('disabled', '=', 0) + ->orderBy('id', 'asc') + ->get(); + + // Catch no-servers case early + if ($servers->isEmpty()) { + echo '
No enabled WHMCS servers found for module type pvewhmcs. Add/enable a server in Setup > Products/Services > Servers.
'; + } else { + foreach ($servers as $srv) { + // Decrypt server password (same approach as ClientArea) + $api_data = array('password2' => $srv->password); + $serverpassword = localAPI('DecryptPassword', $api_data); + $serverip = $srv->ipaddress; + $serverusername = $srv->username; + $serverlabel = !empty($srv->name) ? $srv->name : ('Server #'.$srv->id); + + // Login + get cluster/resources + $proxmox = new PVE2_API($serverip, $serverusername, "pam", $serverpassword['password']); + if (!$proxmox->login()) { + echo '
Unable to log in to PVE API on '.htmlspecialchars($serverip).'. Check credentials / connectivity.
'; + continue; + } + + $cluster_resources = $proxmox->get('/cluster/resources'); // returns nodes, qemu, lxc, storage, pools, etc. + + // Debug logging (same style as ClientArea) + if (Capsule::table('mod_pvewhmcs')->where('id', '1')->value('debug_mode') == 1) { + logModuleCall( + 'pvewhmcs', + __FUNCTION__, + 'CLUSTER RESOURCES ['.$serverlabel.']:', + json_encode($cluster_resources) + ); + } + + if (!is_array($cluster_resources) || empty($cluster_resources)) { + echo '
No resources returned.
'; + continue; + } + + // Split resources + $nodes = []; + $guests = []; // qemu + lxc + foreach ($cluster_resources as $res) { + if (!isset($res['type'])) { + continue; + } + if ($res['type'] === 'node') { + $nodes[] = $res; + } elseif ($res['type'] === 'qemu' || $res['type'] === 'lxc') { + $guests[] = $res; + } + } + + // -------- Nodes table -------- + echo ''; + echo ' + + + + + + + + '; + + foreach ($nodes as $n) { + $n_cpu_pct = isset($n['cpu']) ? round($n['cpu'] * 100, 2) : 0; + $n_mem_pct = (isset($n['maxmem']) && $n['maxmem'] > 0) + ? intval(($n['mem'] ?? 0) * 100 / $n['maxmem']) + : 0; + $n_uptime = isset($n['uptime']) ? time2format($n['uptime']) : '—'; + $n_status = isset($n['status']) ? $n['status'] : 'unknown'; + $n_name = isset($n['node']) ? $n['node'] : '(node)'; + $n_version = $proxmox->get_version(); + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + echo '
NodeVersionUptimeStatusIPv4CPU %RAM %
'.htmlspecialchars($n_name).''.htmlspecialchars($n_version).''.htmlspecialchars($n_uptime).''.htmlspecialchars($n_status).''.htmlspecialchars($serverip).''.$n_cpu_pct.''.$n_mem_pct.'
'; + + // -------- Active Guests (running only) -------- + echo '

Active Guests

'; + echo ''; + echo ' + + + + + + + + + + '; + + foreach ($guests as $g) { + // Only running guests for the "active" overview + if (!isset($g['status']) || $g['status'] !== 'running') { + continue; + } + $g_node = $g['node'] ?? '—'; + $g_type = $g['type'] ?? '—'; + $g_vmid = isset($g['vmid']) ? (int)$g['vmid'] : 0; + $g_name = $g['name'] ?? ''; + $g_uptime = isset($g['uptime']) ? time2format($g['uptime']) : '—'; + + $g_cpu_pct = isset($g['cpu']) ? round($g['cpu'] * 100, 2) : 0; + $g_mem_pct = (isset($g['maxmem']) && $g['maxmem'] > 0) + ? intval(($g['mem'] ?? 0) * 100 / $g['maxmem']) + : 0; + $g_dsk_pct = (isset($g['maxdisk']) && $g['maxdisk'] > 0) + ? intval(($g['disk'] ?? 0) * 100 / $g['maxdisk']) + : 0; + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + echo '
NodeTypeVMIDNameUptimeStatusCPU %RAM %Disk %
'.htmlspecialchars($g_node).''.htmlspecialchars($g_type).''.$g_vmid.''.htmlspecialchars($g_name).''.htmlspecialchars($g_uptime).''.htmlspecialchars($g['status']).''.$g_cpu_pct.''.$g_mem_pct.''.$g_dsk_pct.'
'; + + echo '
'; + } + } + echo '
'; + + // VM / CT PLANS tab in ADMIN GUI echo '
@@ -248,7 +397,7 @@ function pvewhmcs_output($vars) { lxc_plan_add() ; } - // List of VM/CT Plans + // List of VM / CT Plans if ($_GET['action']=='planlist') { echo ' @@ -349,162 +498,16 @@ function pvewhmcs_output($vars) { '; - // NODES / CLUSTER tab in ADMIN GUI - echo '
' ; - echo ('

PVE: /cluster/resources

'); - - // Fetch all enabled servers that use this provisioning module - $servers = Capsule::table('tblservers') - ->where('type', '=', 'pvewhmcs') // module system name - ->where('disabled', '=', 0) - ->orderBy('id', 'asc') - ->get(); - - if ($servers->isEmpty()) { - echo '
No enabled WHMCS servers found for module type pvewhmcs. Add/enable a server in Setup > Products/Services > Servers.
'; - } else { - foreach ($servers as $srv) { - // Decrypt server password (same approach as ClientArea) - $api_data = array('password2' => $srv->password); - $serverpassword = localAPI('DecryptPassword', $api_data); - $serverip = $srv->ipaddress; - $serverusername = $srv->username; - $serverlabel = !empty($srv->name) ? $srv->name : ('Server #'.$srv->id); - - // Login + get cluster/resources - $proxmox = new PVE2_API($serverip, $serverusername, "pam", $serverpassword['password']); - if (!$proxmox->login()) { - echo '
Unable to log in to PVE API on '.htmlspecialchars($serverip).'. Check credentials / connectivity.
'; - continue; - } - - $cluster_resources = $proxmox->get('/cluster/resources'); // returns nodes, qemu, lxc, storage, pools, etc. - - // Debug logging (same style as ClientArea) - if (Capsule::table('mod_pvewhmcs')->where('id', '1')->value('debug_mode') == 1) { - logModuleCall( - 'pvewhmcs', - __FUNCTION__, - 'CLUSTER RESOURCES ['.$serverlabel.']:', - json_encode($cluster_resources) - ); - } - - if (!is_array($cluster_resources) || empty($cluster_resources)) { - echo '
No resources returned.
'; - continue; - } - - // Split resources - $nodes = []; - $guests = []; // qemu + lxc - foreach ($cluster_resources as $res) { - if (!isset($res['type'])) { - continue; - } - if ($res['type'] === 'node') { - $nodes[] = $res; - } elseif ($res['type'] === 'qemu' || $res['type'] === 'lxc') { - $guests[] = $res; - } - } - - // -------- Nodes table -------- - echo '
'; - echo ' - - - - - - - '; - - foreach ($nodes as $n) { - $n_cpu_pct = isset($n['cpu']) ? round($n['cpu'] * 100, 2) : 0; - $n_mem_pct = (isset($n['maxmem']) && $n['maxmem'] > 0) - ? intval(($n['mem'] ?? 0) * 100 / $n['maxmem']) - : 0; - $n_uptime = isset($n['uptime']) ? time2format($n['uptime']) : '—'; - $n_status = isset($n['status']) ? $n['status'] : 'unknown'; - $n_name = isset($n['node']) ? $n['node'] : '(node)'; - - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - } - echo '
NodeStatusIPv4UptimeCPU %RAM %
'.htmlspecialchars($n_name).''.htmlspecialchars($n_status).''.htmlspecialchars($serverip).''.htmlspecialchars($n_uptime).''.$n_cpu_pct.''.$n_mem_pct.'
'; - - // -------- Active Guests (running only) -------- - echo '

Active Guests (running)

'; - echo ''; - echo ' - - - - - - - - - - '; - - foreach ($guests as $g) { - // Only running guests for the "active" overview - if (!isset($g['status']) || $g['status'] !== 'running') { - continue; - } - $g_node = $g['node'] ?? '—'; - $g_type = $g['type'] ?? '—'; - $g_vmid = isset($g['vmid']) ? (int)$g['vmid'] : 0; - $g_name = $g['name'] ?? ''; - $g_uptime = isset($g['uptime']) ? time2format($g['uptime']) : '—'; - - $g_cpu_pct = isset($g['cpu']) ? round($g['cpu'] * 100, 2) : 0; - $g_mem_pct = (isset($g['maxmem']) && $g['maxmem'] > 0) - ? intval(($g['mem'] ?? 0) * 100 / $g['maxmem']) - : 0; - $g_dsk_pct = (isset($g['maxdisk']) && $g['maxdisk'] > 0) - ? intval(($g['disk'] ?? 0) * 100 / $g['maxdisk']) - : 0; - - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - } - echo '
NodeTypeVMIDNameUptimeStatusCPU %RAM %Disk %
'.htmlspecialchars($g_node).''.htmlspecialchars($g_type).''.$g_vmid.''.htmlspecialchars($g_name).''.htmlspecialchars($g_uptime).''.htmlspecialchars($g['status']).''.$g_cpu_pct.''.$g_mem_pct.''.$g_dsk_pct.'
'; - - echo '
'; - } - } - echo '
'; - - // ACTIONS / LOGS tab in ADMIN GUI + // ACTIONS tab in ADMIN GUI echo '
' ; - echo ('

WHMCS: Module Logging

'); - echo ('Click here
(Module Config > Debug Mode = ON)'); echo ('

Module: Action History

'); echo ('Coming in v1.3.x'); echo ('

Module: Failed Actions

'); echo ('Coming in v1.3.x
View the milestones/versions on GitHub'); echo '
'; - // SUPPORT / HEALTH tab in ADMIN GUI - echo ('
') ; + // SUPPORT tab in ADMIN GUI + echo ('
') ; echo ('❤️ Proxmox for WHMCS is open-source and free to use & improve on!
https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/

'); echo ('Your 5-star review on WHMCS Marketplace will help the module grow!
*****: https://marketplace.whmcs.com/product/6935-proxmox-ve-for-whmcs

'); echo ('

System Environment

Proxmox VE for WHMCS v' . pvewhmcs_version() . ' (GitHub reports latest as v' . get_pvewhmcs_latest_version() . ')' . '
PHP v' . phpversion() . ' running on ' . $_SERVER['SERVER_SOFTWARE'] . ' Web Server (' . $_SERVER['SERVER_NAME'] . ')

'); @@ -531,10 +534,10 @@ function pvewhmcs_output($vars) { - Debugging? + Debug? @@ -545,9 +548,103 @@ function pvewhmcs_output($vars) {
'; - echo '
'; + // LOGS tab in ADMIN GUI + echo '
'; + echo '

Cluster History

'; + + try { + // If a client exists already, reuse it; else initialise once from the first enabled pvewhmcs server + if (!isset($proxmox)) { + $srv = Capsule::table('tblservers') + ->where('type', 'pvewhmcs') + ->where('disabled', 0) + ->orderBy('id', 'asc') + ->first(); + + if (!$srv) { + throw new Exception('No enabled WHMCS server found for module type pvewhmcs.'); + } + + $dec = localAPI('DecryptPassword', ['password2' => $srv->password]); + $serverpassword = $dec['password'] ?? ''; + if (!$serverpassword) { + throw new Exception('Could not decrypt Proxmox server password.'); + } + + $proxmox = new PVE2_API($srv->ipaddress, $srv->username, 'pam', $serverpassword); + if (!$proxmox->login()) { + throw new Exception('Login to Proxmox API failed.'); + } + } + + // Fetch recent cluster-wide tasks once + $limit = 150; + $tasks = $proxmox->get('/cluster/tasks', ['limit' => $limit]); + + // Optional debug logging + if (Capsule::table('mod_pvewhmcs')->where('id', '1')->value('debug_mode') == 1) { + logModuleCall('pvewhmcs', 'ADMIN LOGS: /cluster/tasks', 'limit=' . $limit, json_encode($tasks)); + } + + if (!is_array($tasks) || empty($tasks)) { + echo '
No recent cluster tasks were returned.
'; + } else { + // Sort newest first (defensive) + usort($tasks, function ($a, $b) { + return (intval($b['starttime'] ?? 0)) <=> (intval($a['starttime'] ?? 0)); + }); + + echo ''; + echo ' + + + + + + + + '; + + foreach ($tasks as $t) { + $node = $t['node'] ?? '—'; + $type = $t['type'] ?? ''; + $user = $t['user'] ?? ''; + $startTs = (int)($t['starttime'] ?? 0); + $endTs = isset($t['endtime']) ? (int)$t['endtime'] : null; + + $start = $startTs ? date('Y-m-d H:i:s', $startTs) : '—'; + $end = $endTs ? date('Y-m-d H:i:s', $endTs) : '—'; + + $durSec = $startTs ? (is_null($endTs) ? (time() - $startTs) : max(0, $endTs - $startTs)) : null; + $durH = is_null($durSec) + ? '—' + : sprintf('%02d:%02d:%02d', intdiv($durSec, 3600), intdiv($durSec % 3600, 60), $durSec % 60); + + $status = $t['status'] ?? (is_null($endTs) ? 'running' : ''); + $badge = ($status === 'OK') + ? '✅' + : ((preg_match('/(error|fail|aborted|unknown)/i', (string)$status)) ? '❌' : '⏳'); + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + echo '
NodeTypeStatusUserDurationStartEnd
' . htmlspecialchars($node) . '' . htmlspecialchars($type) . '' . $badge . htmlspecialchars($status) . '' . htmlspecialchars($user) . '' . htmlspecialchars($durH) . '' . htmlspecialchars($start) . '' . htmlspecialchars($end) . '
'; + // Always close the tab-pane div + echo '
'; + } + } catch (Throwable $e) { + echo '
Could not retrieve PVE Cluster history: ' + . htmlspecialchars($e->getMessage()) . '
'; + } echo '
'; // End of tabbed content @@ -670,7 +767,7 @@ function import_guest() { echo ''; // Guest Type dropdown - echo 'VM/CT'; echo ''; echo ''; echo ''; diff --git a/modules/servers/pvewhmcs/img/stopped.png b/modules/servers/pvewhmcs/img/stopped.png index 8271acd2ac6b5756711db6fc6fd23ffae996a97e..cf7e18beee8e51288e685298b3001224845c4440 100644 GIT binary patch delta 5392 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'id', '=', $params['serviceid'])->get()[0]; $pve_cmdparam = array(); // Stop the service if it is not already stopped - $guest_specific = $proxmox->get('/nodes/'.$first_node.'/'.$guest->vtype.'/'.$guest->vmid.'/status/current'); + $guest_specific = $proxmox->get('/nodes/' . $first_node . '/' . $guest->vtype . '/' . $guest->vmid . '/status/current'); if ($guest_specific['status'] != 'stopped') { $proxmox->post('/nodes/' . $first_node . '/' . $guest->vtype . '/' . $guest->vmid . '/status/stop' , $pve_cmdparam); sleep(30); diff --git a/version b/version index 9579e1f..d79a5f8 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.2.13 \ No newline at end of file +1.2.14 \ No newline at end of file