21 Commits

Author SHA1 Message Date
Luke S Thompson
0f0cce929a C'LOG for v1.3.4 2026-02-06 01:47:37 +11:00
Jose Domenech
b0c12c4fb1 Proxmox VE for WHMCS v1.3.4 (#190)
* Nodes: IPv6 support

* Autodiscover node of template

* Update WHMCS dedicated IP field with pvewhmcs assigned IP

* Configure network of cloned VM from template via cloud-init and start VM

* Update default nameservers and SCSI controller

* Run logModuleCall only if debug mode is enabled

* fix: add missing nameserver space delimiter and remove unused $amendment variable

* chore: bump version to v1.3.4 and update changelog

* ip pool reuse issue fix and send configs

* change select ip query in pvewhmcs_CreateAccount

* store ip in tblhosting,set password , ip and apply configs to proxmox

* start machine if onboot is enabled and send configs

* fix: resolve syntax error after integrating PR #182

* chore: tidy release title and group related node lookup helpers

* docs: add contributors for v1.3.4

* chore: update all nameservers entries

* Prevent duplicate IP allocation during concurrent provisioning

---------

Co-authored-by: Jose Domenech <jose.domenech@axarnet.es>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Hooman hamidpour <31413674+InnocentCivilian@users.noreply.github.com>
2026-02-06 01:40:07 +11:00
Luke S Thompson
c3d10228dd v1.3.3: Nodes Tidy; RRD Migration Tool 2026-02-05 18:33:41 +11:00
Luke S Thompson
3212d70db6 proxmox-rrd-migration-tool (#188)
- mention tool in GUI error to end-user

- README.md section with more info
2026-01-29 09:10:13 +11:00
Luke S Thompson
79a206e95b Admin > Nodes tab: Tidy-up 2026-01-14 17:47:26 +11:00
Luke S Thompson
928018285c Commit 400, honouring HTTP Status Dogs
- if no auth, image and logo display

- logo onto Support/Config panes

- improve plan add/edit wording

- Disk Cache, explicit "none"

- link to reference docs x2

- clarify re: data xfer cap

- clarify re: IPv6 "prefix"

- consistent icon sizing

- remove <pre></pre>

- improve ver. display

- "add" WS 2025
2026-01-10 13:47:29 +11:00
Luke S Thompson
df819f2d40 Fully remove Bobby Tables (#160)
- and link to the latest release directly
2026-01-09 21:28:37 +11:00
Luke S Thompson
1663668e7c If auth NOK, ensure CSS renders (#177) 2026-01-09 17:01:34 +11:00
Luke S Thompson
914acf3632 v1.3.2: TPL Node; VNC Resolve; Tidy-up 2026-01-09 16:28:13 +11:00
Luke S Thompson
fbf990d6af TPL_Node_QEMU/LXC (#185) & VNC Node Search (#183)
- new custom fields for TPL_Node_QEMU/LXC

- use above field, if exists, for API; else fallback

- VNC guest resolution now via root access

- VNC itself remains via VM.Console
2026-01-09 15:37:15 +11:00
Luke S Thompson
42cbc6d108 /cluster/nextid no-param (#185)
- previously, sent param which will not give nextid

- now trying sans-param to get nextid instead

- then if no bueno, attempt with vmid param
2026-01-08 10:28:52 +11:00
Luke S Thompson
d18c723884 v1.3.1: Relativity & Nodes 2025-12-12 23:33:32 +11:00
Chris
651abc4880 (Clusters) Resolve Node for VM/CT (#178)
* Resolve Proxmox node for each VM/CT via /cluster/resources instead of assuming first node

* Refactor guest node retrieval in pvewhmcs.php

* Fix issues from PR review

* fix indentation

* more indentation fixes

* Format comma spacing

* Check if `$guest` is null before performing actions.

* Handle errors in account termination

* Load js and PNGs with relative paths

This helps in setups where WHMCS is installed in a subfolder e.g. `/clientarea/`x

* fix docblock and property typo

* Bump version and add contributors

---------

Co-authored-by: hliasa <hliasant@gmail.com>
2025-12-12 23:27:34 +11:00
Luke S Thompson
b1052d87c9 Support PVE 9.x.x (re: #162)
Tested working OK on pve-manager 8.4.14.
Tested working OK on pve-manager 9.1.2.

Module version 1.3.0 on both (updated RRD).
2025-12-04 16:52:43 +11:00
Luke S Thompson
e21a7fc3e7 #166 KVM to QEMU (except SQL)
Plan Type still in DB as 'kvm' then swapped for API usage. Need to convert this to 'qemu' in future.
2025-12-03 21:10:41 +11:00
Luke S Thompson
0056719950 KVM -> QEMU (Client Area logo) 2025-12-03 19:44:49 +11:00
Luke S Thompson
719735b81c v1.3.0: RRD for Admins & Clients 2025-12-03 19:14:00 +11:00
Luke S Thompson
ef7752ab89 v1.2.20-dev: (Admin) Guests GUI 2025-12-03 17:47:06 +11:00
Luke S Thompson
6186b9f896 v1.2.20-dev: (Admin) Nodes GUI 2025-12-03 17:37:51 +11:00
Luke S Thompson
b86ba210f2 v1.2.20-dev: RRD try-catch, etc. 2025-12-03 17:26:48 +11:00
Luke S Thompson
1b56f4ac8e RRD Schema Update (pve9) - testing (#162) 2025-11-13 11:24:30 +11:00
19 changed files with 1695 additions and 750 deletions

View File

@@ -1,9 +1,63 @@
# Changelog
All notable changes to Proxmox VE for WHMCS will be documented in this file.
## [1.3.x] - TBC 2026-??-??
## [1.3.4] - 2026-02-06 - _"IPv6, Discovery & cloud-init"_
https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/milestones
### 🚀 Feature
- IPv6 Node: Added IPv6 support to node logic & client
- Clusters: Node auto-discovery from cloud-init templates
- IPv4 Pool: Service Link for any IPv4 that matches a Service
- Admin Area, Client Area: Dedicated IPv4 propagation into field
### 💅 Polish
- VMs: Network config of cloned VMs via cloud-init
- VMs: Default nameserver configuration
- VMs: SCSI controller defaults
- Post-clone: On-boot enact
## [1.3.3] - 2026-02-05 - _"Tidy Nodes & RRD"_
### 💅 Polish
- Nodes tab: Improve presentation of data points
- Client Area, RRD error: Mention migration tool (#188)
## [1.3.2] - 2026-01-10 - _"VNC, Cleaning, etc"_
### 🚀 Feature
- Custom Fields: TPL_Node_QEMU/LXC (Template Storage Node)
- Template Node: Honour Fields if set, else fallback (#186)
### 💅 Polish
- QEMU CPU: Add AMD EPYC-Milan-v2 processor model
- QEMU CPU: Link to Admin Guide for CPU comparisons
- Spacing: Clean-up all files to space concatenations
- Update Available: Links directly to the latest release
- Naming: $srv -> $pve; $res -> $resource; $v -> $guest_type
### 🐛 Bug Fix
- VNC: Resolve node as root, then connect VNC as limited user (#183)
- nextid: No param, so we get nextid; then declare as required (#185)
- CSS: If node authentication fails, close div so render is OK (#177)
## [1.3.1] - 2025-12-12 - _"Relativity & Nodes"_
### 💅 Polish
- Clusters: Requests are made to the node hosting Guest (#16)
### 🐛 Bug Fix
- Client Area: Images load in sub-dir installs (relative src)
## [1.3.0] - 2025-12-03 - _"RRD: Clients & Admins"_
### 🚀 Feature
- Nodes RRD: View CPU/RAM/Network/Disk graphs in Admin Area
### 💅 Polish
- Admin Area: Overhaul Nodes, Guests, Support, Config & Logs tabs
- Client Area: Overhaul Graphs, Specs Table, and Statistics display
### 🐛 Bug Fix
- RRD Schema Update: Adjustments to Client/Admin Areas (#162)
## [1.2.19] - 2025-10-24 - _"Remove TigerVNC (Java)"_

View File

@@ -10,8 +10,12 @@ This document seeks to say "cheers", "many thanks" & "love your work" to the peo
- [@nodespacehosting](https://github.com/nodespacehosting)
- [@WaldperlachFabi](https://github.com/WaldperlachFabi)
- [@is7Qin](https://github.com/is7Qin)
- [@chrismfz](https://github.com/chrismfz)
- [@hliasa](https://github.com/hliasa)
- [@InnocentCivilian](https://github.com/InnocentCivilian)
- [@jdomenechg](https://github.com/jdomenechg)
## Why not make it even better?
> [!TIP]
> You can always step up and write some improvements to the Module. Why not?
> You can always step up and write some improvements to the Module. Why not?

View File

@@ -22,13 +22,25 @@
https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/
**Client Area GUI - Landing:**
**Client Area GUI - w/ Stats:**
<img alt="Client Area GUI showing management of a powered-on VM" src="_images/zVMclientGUI.png">
<img alt="Client Area GUI showing management of a powered-on Guest, after the Statistics action has been clicked resulting in the graphs at the bottom." src="_images/zVMclientGUI.png">
**Admin Area GUI - Landing:**
> [!IMPORTANT]
> Nodes must be using RRD `PVE-$TYPE-9.0` format. You can use `proxmox-rrd-migration-tool` to migrate.
>
> Old RRD Dir for VMs: `/var/lib/rrdcached/db/pve2-vm/` <br>
> New RRD Dir for VMs: `/var/lib/rrdcached/db/pve-vm-9.0/`
>
> You can research more online, and <a href="https://www.mail-archive.com/pve-devel@lists.proxmox.com/msg29223.html" target="_blank">here is part of a patch series</a> showing the new logic.
<img alt="Admin Area GUI for the Module, showing the Nodes & Guests" src="_images/zClusterGuests.png">
**Admin Area GUI - PVE Nodes:**
<img alt="Admin Area GUI for the Module, showing the Nodes tab that you land on upon opening the Module." src="_images/zClusterNodes.png">
**Admin Area GUI - Guests:**
<img alt="Admin Area GUI for the Module, showing the Guests tab which lists and details Virtual Machines and Containers." src="_images/zClusterGuests.png">
# ❤️ RTFM: Read the Manual & Review the Module!
@@ -46,7 +58,7 @@ https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/
- **(PHP)** v8.x.x (latest stable version)
- **(PHP)** max_execution_time = 300
- **(Proxmox)** 2 users (API & VNC)
- **(Proxmox)** VE v8.x.x (current)
- **(Proxmox)** VE v8.4/v9 (latest)
Please note specific VNC & Network requirements below - read 100% of the README.md. :-)
@@ -211,25 +223,31 @@ This needs configuring for each `WHMCS Admin > Products & Services` entry.
Firstly, create the Template VM in PVE. You need its unique PVE ID.
Use that ID in the Custom Field `KVMTemplate`, as in `ID|Name`.
Secondly, use that ID in the Custom Field `KVMTemplate`, as in `ID|Name`.
> **Note**: `ID` is the Unique ID that your Template VM has in PVE.<br>
> **Note**: `Name` is what will be displayed to your Clients in WHMCS.
Thirdly, add another Custom Field `TPL_Node_QEMU` with the node short name.
### VM Option 2: QEMU, WHMCS Plan + PVE ISO
Firstly, create the Plan in WHMCS Module. Then too in WHMCS Config > Services.
> Under the Service, you need to add a Custom Field `ISO` with the full location.<br>
> This ISO must be located on the PVE Host, and not on the WHMCS installation side.
> This ISO must be located on all PVE Nodes, and not on the WHMCS installation side.
### CT Option 1: LXC, PVE Template File
Firstly, store the Template in PVE. You need its storage, folder & File Name.
> Use that prefixed file name in the Custom Field `Template`, as in:<br>
Secondly, use that prefixed file name in the Custom Field `Template`.
> Here is the syntax for that field, including display name:<br>
> `local:vztmpl/ubuntu-99.99-standard_amd64.tar.gz|Ubuntu 99`
Thirdly, add another Custom Field `TPL_Node_LXC` with the node short name.
### VM/CT Import/Associate Existing Guest
You can associate an existing PVE Guest through the WHMCS Module too, like this:
@@ -261,6 +279,14 @@ Create a 2nd Custom Field `Password` for the Container's root user on all CT Ser
### Updating to a newer release!
> [!WARNING]
> There are 2x states that new Proxmox VE for WHMCS releases typically go through.
>
> 1. Module shows Update Available, but GitHub repo does NOT have a published release.<br>
> In this state, it is ready for testing - but we do not recommend deploying to prod.
> 2. Module shows Update Available, and GitHub repo DOES have a published release.<br>
> In this state, it is tested and considered ready for production usage.
1. Download the new version
2. Upload it over the top (FTP)
3. Login to WHMCS Admin
@@ -269,14 +295,14 @@ Create a 2nd Custom Field `Password` for the Container's root user on all CT Ser
> **Logging in _should_ trigger the self-upgrade procedure for the SQL database.**
>
> (**Beta in v1.2.x:** for now, verify yourself that updates were successful)
> (**Beta Feature:** For now, verify yourself that updates were successful)
### SQL: Keeping your DB up-to-date
> [!IMPORTANT]
> Since v1.3.0, logging into WHMCS Admin & opening the module should run any needed SQL Ops.
> Since v1.3.x, logging into WHMCS Admin & opening the module should run any needed SQL Ops.
>
> v1.2.9 & below, consult the **_docs/UPDATE-SQL.md** file, open your SQL DB & run statements.
> v1.2.x & below, consult the **_docs/UPDATE-SQL.md** file, open your SQL DB & run statements.
Then you're done with each update!

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

After

Width:  |  Height:  |  Size: 113 KiB

BIN
_images/zClusterNodes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -46,7 +46,10 @@ class PVE2_API {
}
// Check hostname resolves.
if (gethostbyname($hostname) == $hostname && !filter_var($hostname, FILTER_VALIDATE_IP)) {
throw new PVE2_Exception("PVE2 API: Cannot resolve {$hostname}.", 2);
// Fallback: check for IPv6 (AAAA) records if IPv4 lookup failed
if (!checkdnsrr($hostname, 'AAAA')) {
throw new PVE2_Exception("PVE2 API: Cannot resolve {$hostname}.", 2);
}
}
// Check port is between 1 and 65535.
if (!filter_var($port, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 65535]])) {
@@ -81,7 +84,14 @@ class PVE2_API {
// Perform login request.
$prox_ch = curl_init();
curl_setopt($prox_ch, CURLOPT_URL, "https://{$this->hostname}:{$this->port}/api2/json/access/ticket");
// Handle IPv6 literals in URL
$host_url = $this->hostname;
if (filter_var($this->hostname, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$host_url = '[' . $this->hostname . ']';
}
curl_setopt($prox_ch, CURLOPT_URL, "https://{$host_url}:{$this->port}/api2/json/access/ticket");
curl_setopt($prox_ch, CURLOPT_POST, true);
curl_setopt($prox_ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($prox_ch, CURLOPT_POSTFIELDS, $login_postfields_string);
@@ -171,7 +181,7 @@ class PVE2_API {
private function action ($action_path, $http_method, $put_post_parameters = null) {
// Check if we have a prefixed / on the path, if not add one.
if (substr($action_path, 0, 1) != "/") {
$action_path = "/".$action_path;
$action_path = "/" . $action_path;
}
if (!$this->check_login_ticket()) {
@@ -180,7 +190,14 @@ class PVE2_API {
// Prepare cURL resource.
$prox_ch = curl_init();
curl_setopt($prox_ch, CURLOPT_URL, "https://{$this->hostname}:{$this->port}/api2/json{$action_path}");
// Handle IPv6 literals in URL
$host_url = $this->hostname;
if (filter_var($this->hostname, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$host_url = '[' . $this->hostname . ']';
}
curl_setopt($prox_ch, CURLOPT_URL, "https://{$host_url}:{$this->port}/api2/json{$action_path}");
$put_post_http_headers = array();
$put_post_http_headers[] = "CSRFPreventionToken: {$this->login_ticket['CSRFPreventionToken']}";
@@ -225,7 +242,7 @@ class PVE2_API {
curl_setopt($prox_ch, CURLOPT_HEADER, true);
curl_setopt($prox_ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($prox_ch, CURLOPT_COOKIE, "PVEAuthCookie=".$this->login_ticket['ticket']);
curl_setopt($prox_ch, CURLOPT_COOKIE, "PVEAuthCookie=" . $this->login_ticket['ticket']);
curl_setopt($prox_ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($prox_ch, CURLOPT_SSL_VERIFYHOST, false);

File diff suppressed because it is too large Load Diff

View File

@@ -1,146 +1,503 @@
<div class="row">
<div style="text-align : left;">
</div>
<div class="col col-md-12">
<div class="row">
<div class="col col-md-3">
<div class="row">
<div class="col col-md-12">
<img src="/modules/servers/pvewhmcs/img/{$vm_config['vtype']}.png"/>
</div>
</div>
<div class="row">
<div class="col col-md-12">
<img src="/modules/servers/pvewhmcs/img/os/{$vm_config['ostype']}.png"/>
</div>
</div>
{* Proxmox VE for WHMCS - Client Area Template *}
<style>
.pve-client-area {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
.pve-header-panel {
background: #fafafa;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.pve-status-section {
display: flex;
align-items: center;
gap: 20px;
flex-wrap: nowrap;
}
.pve-vm-icons {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.pve-vm-icons img {
max-width: 56px;
height: auto;
}
.pve-status-badge {
text-align: center;
padding: 14px 18px;
background: #fff;
border: 1px solid #e0e0e0;
border-radius: 8px;
min-width: 95px;
flex-shrink: 0;
}
.pve-status-badge img {
max-width: 44px;
margin-bottom: 7px;
}
.pve-status-badge .status-text {
display: block;
text-transform: uppercase;
font-weight: 700;
font-size: 13px;
color: #5c3d7a;
margin-bottom: 3px;
}
.pve-status-badge .uptime-text {
font-size: 12px;
color: #555;
}
.pve-gauges {
display: flex;
gap: 10px;
flex-wrap: nowrap;
flex: 1;
justify-content: flex-end;
}
.pve-gauge-item {
background: #fff;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 12px;
text-align: center;
min-width: 75px;
}
.pve-gauge-item .circle {
margin: 0 auto;
}
.pve-gauge-item strong {
display: block;
margin-top: 7px;
font-size: 12px;
color: #555;
}
/* Specs Table */
.pve-specs-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
background: #fff;
font-size: 14px;
}
.pve-specs-table tr:not(:last-child) {
border-bottom: 1px solid #eee;
}
.pve-specs-table td {
padding: 13px 16px;
vertical-align: top;
font-size: 14px;
line-height: 1.5;
}
.pve-specs-table td:first-child {
background: #f8f8f8;
width: 180px;
font-weight: 500;
color: #444;
border-right: 1px solid #eee;
}
.pve-specs-table td:last-child {
color: #333;
}
.pve-specs-table .spec-label {
font-weight: 600;
font-size: 14px;
color: #5c3d7a;
}
.pve-specs-table .spec-sublabel {
font-size: 12px;
color: #555;
font-weight: normal;
}
.pve-specs-table .spec-value {
font-weight: 600;
font-size: 14px;
color: #333;
}
.pve-specs-table .spec-detail {
font-size: 14px;
color: #555;
margin-top: 3px;
}
.pve-specs-table code {
background: #f4f0f7;
padding: 2px 6px;
border-radius: 3px;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 13px;
color: #5c3d7a;
}
/* Statistics Section */
.pve-stats-section {
margin-top: 25px;
}
.pve-stats-section h4 {
color: #5c3d7a;
font-weight: 600;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 2px solid #5c3d7a;
}
.pve-stats-tabs {
display: flex;
gap: 5px;
margin-bottom: 0;
padding: 0;
list-style: none;
border-bottom: 1px solid #ddd;
}
.pve-stats-tabs li {
margin: 0;
}
.pve-stats-tabs li a {
display: block;
padding: 10px 20px;
text-decoration: none;
color: #666;
background: #f5f5f5;
border: 1px solid #ddd;
border-bottom: none;
border-radius: 6px 6px 0 0;
font-weight: 500;
font-size: 13px;
transition: all 0.2s ease;
}
.pve-stats-tabs li a:hover {
background: #eee;
color: #5c3d7a;
}
.pve-stats-tabs li.active a {
background: #f5f5f5;
color: #666;
border-color: #ddd;
}
.pve-stats-content {
background: #fff;
border: 1px solid #ddd;
border-top: none;
border-radius: 0 0 8px 8px;
padding: 20px;
}
.pve-stats-content .tab-pane {
display: none;
}
.pve-stats-content .tab-pane.active {
display: block;
}
.pve-graphs-grid {
display: flex;
flex-direction: column;
gap: 15px;
}
.pve-graphs-grid img {
width: 100%;
border-radius: 6px;
border: 1px solid #eee;
}
/* Responsive: Large screens - maximize graph space */
@media (min-width: 1200px) {
.pve-status-section {
gap: 25px;
}
.pve-gauges {
gap: 15px;
}
.pve-gauge-item {
padding: 14px;
min-width: 80px;
}
.pve-graphs-grid img {
max-width: 100%;
}
}
/* Responsive: Medium screens (tablets) */
@media (min-width: 769px) and (max-width: 1199px) {
.pve-status-section {
gap: 15px;
}
.pve-vm-icons img {
max-width: 48px;
}
.pve-gauges {
gap: 8px;
}
.pve-gauge-item {
padding: 10px;
min-width: 65px;
}
}
/* Responsive: Small screens (mobile) */
@media (max-width: 768px) {
.pve-status-section {
flex-direction: column;
text-align: center;
gap: 15px;
}
.pve-status-section > * {
flex-shrink: 1;
}
.pve-vm-icons {
flex-direction: row;
gap: 15px;
}
.pve-gauges {
flex-wrap: wrap;
justify-content: center;
width: 100%;
}
.pve-gauge-item {
flex: 1 1 calc(50% - 8px);
max-width: calc(50% - 8px);
}
.pve-specs-table td:first-child {
width: 120px;
}
}
/* Alert styling */
.pve-alert-warning {
background: #fff3cd;
border: 1px solid #ffc107;
border-radius: 6px;
padding: 15px;
color: #856404;
font-size: 13px;
}
.pve-alert-warning i {
margin-right: 8px;
}
</style>
<div class="pve-client-area">
{* Header Panel with VM Type, Status, and Gauges *}
<div class="pve-header-panel">
<div class="pve-status-section">
{* VM Type & OS Icons *}
<div class="pve-vm-icons">
<img src="./modules/servers/pvewhmcs/img/{$vm_config['vtype']}.png" alt="{$vm_config['vtype']}" title="Type: {$vm_config['vtype']}"/>
<img src="./modules/servers/pvewhmcs/img/os/{$vm_config['ostype']}.png" alt="{$vm_config['ostype']}" title="OS: {$vm_config['ostype']}"/>
</div>
<div class="col col-md-2">
<img src="/modules/servers/pvewhmcs/img/{$vm_status['status']}.png"/><br/>
<span style="text-transform: uppercase"><strong><i>{$vm_status['status']}</i></strong></span><br/>
Up:&nbsp;{$vm_status['uptime']}
{* Status Badge *}
<div class="pve-status-badge">
<img src="./modules/servers/pvewhmcs/img/{$vm_status['status']}.png" alt="{$vm_status['status']}"/>
<span class="status-text">{$vm_status['status']}</span>
<span class="uptime-text">Up {$vm_status['uptime']}</span>
</div>
<div class="col col-md-7">
<div class="row">
<script src="/modules/servers/pvewhmcs/js/CircularLoader.js"></script>
<div class="col col-md-3" style="height:106px;">
<div id="c1" class="circle" data-percent="{$vm_status['cpu']}"><strong>CPU</strong></div>
</div>
<div class="col col-md-3">
<div id="c2" class="circle" data-percent="{$vm_status['memusepercent']}"><strong>RAM</strong></div>
</div>
<div class="col col-md-3">
<div id="c3" class="circle" data-percent="{$vm_status['diskusepercent']}"><strong>Disk</strong></div>
</div>
<div class="col col-md-3">
<div id="c4" class="circle" data-percent="{$vm_status['swapusepercent']}"><strong>Swap</strong></div>
</div>
{* Resource Gauges *}
<div class="pve-gauges">
<script src="./modules/servers/pvewhmcs/js/CircularLoader.js"></script>
<div class="pve-gauge-item">
<div id="c1" class="circle" data-percent="{$vm_status['cpu']}"></div>
<strong>CPU</strong>
</div>
<div class="pve-gauge-item">
<div id="c2" class="circle" data-percent="{$vm_status['memusepercent']}"></div>
<strong>RAM</strong>
</div>
<div class="pve-gauge-item">
<div id="c3" class="circle" data-percent="{$vm_status['diskusepercent']}"></div>
<strong>Disk</strong>
</div>
<div class="pve-gauge-item">
<div id="c4" class="circle" data-percent="{$vm_status['swapusepercent']}"></div>
<strong>Swap</strong>
</div>
<script>
$(document).ready(function() {
$('.circle').each(function(){
$(this).circularloader({
progressPercent: $(this).attr("data-percent"),
fontSize: "13px",
radius: 30,
progressBarWidth: 8,
progressBarBackground: "#D6B1F9",
progressBarColor: "#802DBC",
});
});
});
</script>
</div>
</div>
</div>
<script>
$(document).ready(function() {
$('.circle').each(function(){
$(this).circularloader({
progressPercent: $(this).attr("data-percent"),
fontSize: "12px",
radius: 27,
progressBarWidth: 7,
progressBarBackground: "#D6B1F9",
progressBarColor: "#802DBC",
});
});
});
</script>
<table class="table table-bordered table-striped">
{* Specifications Table *}
<table class="pve-specs-table">
<tr>
<td><strong>Memory</strong> (RAM)</td>
<td><strong>{$vm_config['memory']}MB</strong></td>
<td><span class="spec-label">Memory</span> <span class="spec-sublabel">(RAM)</span></td>
<td><span class="spec-value">{$vm_config['memory']}MB</span></td>
</tr>
<tr>
<td><strong>Compute</strong> (CPU)</td>
<td><strong>{$vm_config['cpu']}&nbsp;{$vm_config['cores']}&nbsp;core/s&nbsp;on&nbsp;{$vm_config['sockets']}&nbsp;socket/s
</strong></td>
</tr>
<tr>
<td><strong>Storage</strong> (SSD/HDD)</td>
<td><span class="spec-label">Compute</span> <span class="spec-sublabel">(CPU)</span></td>
<td>
{$rootfs=(","|explode:$vm_config['rootfs'])}
{$disk=(","|explode:$vm_config['ide0'])}
{$disk[1]}
{$rootfs[1]}
{($vm_config['scsi0']|replace:',':'<br/>')}
{($vm_config['virtio0']|replace:',':'<br/>')}
<span class="spec-value">{$vm_config['cores']} core(s)</span>
<div class="spec-detail">on {$vm_config['sockets']} socket(s)</div>
</td>
</tr>
<tr>
<td><strong>Boot Order</strong> (1st > 2nd)</td>
<td>{($vm_config['boot']|replace:'order=':''|replace:';':' > ')}</td>
<td><span class="spec-label">Storage</span> <span class="spec-sublabel">(SSD/HDD)</span></td>
<td>
{if $vm_config['rootfs']}
{assign var="rootfs_parts" value=","|explode:$vm_config['rootfs']}
{foreach from=$rootfs_parts item=rpart}
{if $rpart|strpos:"size=" !== false}<span class="spec-value">{$rpart|replace:'size=':''}</span> <span class="spec-detail">(rootfs)</span>{/if}
{/foreach}
{/if}
{if $vm_config['ide0']}
{assign var="ide0_parts" value=","|explode:$vm_config['ide0']}
{foreach from=$ide0_parts item=ipart}
{if $ipart|strpos:"size=" !== false}<div class="spec-detail"><span class="spec-value">{$ipart|replace:'size=':''}</span> (ide0)</div>{/if}
{/foreach}
{/if}
{if $vm_config['scsi0']}
{assign var="scsi0_parts" value=","|explode:$vm_config['scsi0']}
{foreach from=$scsi0_parts item=spart}
{if $spart|strpos:"size=" !== false}<div class="spec-detail"><span class="spec-value">{$spart|replace:'size=':''}</span> (scsi0)</div>{/if}
{/foreach}
{/if}
{if $vm_config['virtio0']}
{assign var="virtio0_parts" value=","|explode:$vm_config['virtio0']}
{foreach from=$virtio0_parts item=vpart}
{if $vpart|strpos:"size=" !== false}<div class="spec-detail"><span class="spec-value">{$vpart|replace:'size=':''}</span> (virtio0)</div>{/if}
{/foreach}
{/if}
</td>
</tr>
<tr>
<td><strong>IPv4</strong> (Networking)</td><td><strong>{$vm_config['ipv4']}</strong><br/>Mask:&nbsp;{$vm_config['netmask4']}<br/>Gateway:&nbsp;{$vm_config['gateway4']}</td>
<td><span class="spec-label">Boot Order</span></td>
<td><span class="spec-value">{($vm_config['boot']|replace:'order=':''|replace:';':' → ')}</span></td>
</tr>
<tr>
<td><strong>ipconfig</strong> (IPv4/v6)</td><td><strong>NIC #0</strong>: {$vm_config['ipconfig0']}<br><strong>NIC #1</strong>: {$vm_config['ipconfig1']}</td>
<td><span class="spec-label">IPv4</span> <span class="spec-sublabel">(Networking)</span></td>
<td>
<span class="spec-value">{$vm_config['ipv4']}</span>
<div class="spec-detail">Mask: {$vm_config['netmask4']} &bull; Gateway: {$vm_config['gateway4']}</div>
</td>
</tr>
<tr>
<td><strong>NIC #0</strong> (IPv4)</td>
<td>{($vm_config['net0']|replace:',':'<br/>'|replace:'=':': ')}</td>
<td><span class="spec-label">IP Config</span> <span class="spec-sublabel">(IPv4/v6)</span></td>
<td>
{if $vm_config['ipconfig0']}<div class="spec-detail"><strong>NIC #0:</strong> {($vm_config['ipconfig0']|replace:',':' &bull; '|replace:'=':': ')}</div>{/if}
{if $vm_config['ipconfig1']}<div class="spec-detail"><strong>NIC #1:</strong> {($vm_config['ipconfig1']|replace:',':' &bull; '|replace:'=':': ')}</div>{/if}
</td>
</tr>
<tr>
<td><strong>NIC #1</strong> (IPv6)</td>
<td>{($vm_config['net1']|replace:',':'<br/>'|replace:'=':': ')}</td>
<td><span class="spec-label">NIC #0</span> <span class="spec-sublabel">(Primary)</span></td>
<td>
{assign var="net0_parts" value=","|explode:$vm_config['net0']}
{foreach from=$net0_parts item=part name=netloop}
{if $part|strpos:"=" !== false}
{assign var="kv" value="="|explode:$part}
{if $kv[0] == 'virtio' || $kv[0] == 'e1000' || $kv[0] == 'rtl8139'}
<div class="spec-detail"><strong>{$kv[0]}</strong>: <code>{$kv[1]}</code></div>
{elseif $kv[0] == 'bridge' || $kv[0] == 'link_down' || $kv[0] == 'firewall' || $kv[0] == 'tag'}
<div class="spec-detail"><strong>{$kv[0]}</strong>: {$kv[1]}</div>
{/if}
{/if}
{/foreach}
</td>
</tr>
{if $vm_config['net1']}
<tr>
<td><strong>Config</strong> (Tweaks)</td>
<td>on_boot: {$vm_config['onboot']}</td>
<td><span class="spec-label">NIC #1</span> <span class="spec-sublabel">(Secondary)</span></td>
<td>
{assign var="net1_parts" value=","|explode:$vm_config['net1']}
{foreach from=$net1_parts item=part name=netloop}
{if $part|strpos:"=" !== false}
{assign var="kv" value="="|explode:$part}
{if $kv[0] == 'virtio' || $kv[0] == 'e1000' || $kv[0] == 'rtl8139'}
<div class="spec-detail"><strong>{$kv[0]}</strong>: <code>{$kv[1]}</code></div>
{elseif $kv[0] == 'bridge' || $kv[0] == 'link_down' || $kv[0] == 'firewall' || $kv[0] == 'tag'}
<div class="spec-detail"><strong>{$kv[0]}</strong>: {$kv[1]}</div>
{/if}
{/if}
{/foreach}
</td>
</tr>
{/if}
<tr>
<td><strong>SSH Keys</strong> (Public)</td>
<td>{$vm_config['sshkeys']}</td>
<td><span class="spec-label">Config</span> <span class="spec-sublabel">(Tweaks)</span></td>
<td><span class="spec-detail"><strong>On-boot?</strong> {if $vm_config['onboot']}Yes!{else}No (Contact Support){/if}</span></td>
</tr>
{if $vm_config['sshkeys']}
<tr>
<td><strong>OS</strong> (System Kernel)</td>
<td><strong>{$vm_config['ostype']}</strong></td>
<td><span class="spec-label">SSH Keys</span> <span class="spec-sublabel">(Public)</span></td>
<td><div class="spec-detail" style="word-break:break-all;">{$vm_config['sshkeys']}</div></td>
</tr>
{/if}
<tr>
<td><span class="spec-label">Kernel</span> <span class="spec-sublabel">(OS)</span></td>
<td><span class="spec-value">{$vm_config['ostype']}</span></td>
</tr>
</table>
{* Statistics Section *}
{if ($smarty.get.a eq 'vmStat')}
<h4>VM Statistics</h4>
<ul class="nav nav-tabs client-tabs" role="tab-list">
<li class="active"><a id="dailytab" data-toggle="tab" role="tab" href="#dailystat">Daily</a></li>
<li><a id="dailytab" data-toggle="tab" role="tab" href="#weeklystat">Weekly</a></li>
<li><a id="dailytab" data-toggle="tab" role="tab" href="#monthlystat">Monthly</a></li>
<li><a id="dailytab" data-toggle="tab" role="tab" href="#yearlystat">Yearly</a></li>
</ul>
<div class="tab-content admin-tabs">
<div id="dailystat" class="tab-pane active">
<img src="data:image/png;base64,{$vm_statistics['cpu']['day']}"/>
<img src="data:image/png;base64,{$vm_statistics['maxmem']['day']}"/>
<img src="data:image/png;base64,{$vm_statistics['netinout']['day']}"/>
<img src="data:image/png;base64,{$vm_statistics['diskrw']['day']}"/>
<div class="pve-stats-section">
<h4><i class="fa fa-line-chart"></i> Guest Statistics</h4>
{if $vm_statistics['cpu']['day']}
<ul class="pve-stats-tabs" role="tablist">
<li class="active"><a data-toggle="tab" role="tab" href="#dailystat">Daily</a></li>
<li><a data-toggle="tab" role="tab" href="#weeklystat">Weekly</a></li>
<li><a data-toggle="tab" role="tab" href="#monthlystat">Monthly</a></li>
<li><a data-toggle="tab" role="tab" href="#yearlystat">Yearly</a></li>
</ul>
<div class="pve-stats-content tab-content">
<div id="dailystat" class="tab-pane active">
<div class="pve-graphs-grid">
<img src="data:image/png;base64,{$vm_statistics['cpu']['day']}" alt="CPU (Daily)"/>
<img src="data:image/png;base64,{$vm_statistics['mem']['day']}" alt="Memory (Daily)"/>
<img src="data:image/png;base64,{$vm_statistics['netinout']['day']}" alt="Network I/O (Daily)"/>
<img src="data:image/png;base64,{$vm_statistics['diskrw']['day']}" alt="Disk I/O (Daily)"/>
</div>
</div>
<div id="weeklystat" class="tab-pane">
<div class="pve-graphs-grid">
<img src="data:image/png;base64,{$vm_statistics['cpu']['week']}" alt="CPU (Weekly)"/>
<img src="data:image/png;base64,{$vm_statistics['mem']['week']}" alt="Memory (Weekly)"/>
<img src="data:image/png;base64,{$vm_statistics['netinout']['week']}" alt="Network I/O (Weekly)"/>
<img src="data:image/png;base64,{$vm_statistics['diskrw']['week']}" alt="Disk I/O (Weekly)"/>
</div>
</div>
<div id="monthlystat" class="tab-pane">
<div class="pve-graphs-grid">
<img src="data:image/png;base64,{$vm_statistics['cpu']['month']}" alt="CPU (Monthly)"/>
<img src="data:image/png;base64,{$vm_statistics['mem']['month']}" alt="Memory (Monthly)"/>
<img src="data:image/png;base64,{$vm_statistics['netinout']['month']}" alt="Network I/O (Monthly)"/>
<img src="data:image/png;base64,{$vm_statistics['diskrw']['month']}" alt="Disk I/O (Monthly)"/>
</div>
</div>
<div id="yearlystat" class="tab-pane">
<div class="pve-graphs-grid">
<img src="data:image/png;base64,{$vm_statistics['cpu']['year']}" alt="CPU (Yearly)"/>
<img src="data:image/png;base64,{$vm_statistics['mem']['year']}" alt="Memory (Yearly)"/>
<img src="data:image/png;base64,{$vm_statistics['netinout']['year']}" alt="Network I/O (Yearly)"/>
<img src="data:image/png;base64,{$vm_statistics['diskrw']['year']}" alt="Disk I/O (Yearly)"/>
</div>
</div>
</div>
<div id="weeklystat" class="tab-pane">
<img src="data:image/png;base64,{$vm_statistics['cpu']['week']}"/>
<img src="data:image/png;base64,{$vm_statistics['maxmem']['week']}"/>
<img src="data:image/png;base64,{$vm_statistics['netinout']['week']}"/>
<img src="data:image/png;base64,{$vm_statistics['diskrw']['week']}"/>
</div>
<div id="monthlystat" class="tab-pane">
<img src="data:image/png;base64,{$vm_statistics['cpu']['month']}"/>
<img src="data:image/png;base64,{$vm_statistics['maxmem']['month']}"/>
<img src="data:image/png;base64,{$vm_statistics['netinout']['month']}"/>
<img src="data:image/png;base64,{$vm_statistics['diskrw']['month']}"/>
</div>
<div id="yearlystat" class="tab-pane">
<img src="data:image/png;base64,{$vm_statistics['cpu']['year']}"/>
<img src="data:image/png;base64,{$vm_statistics['maxmem']['year']}"/>
<img src="data:image/png;base64,{$vm_statistics['netinout']['year']}"/>
<img src="data:image/png;base64,{$vm_statistics['diskrw']['year']}"/>
{else}
<div class="pve-alert-warning">
<i class="fa fa-exclamation-triangle"></i>
Stats Error: RRD Unavailable. Ask Support to upgrade/migrate RRD Data using: <code>proxmox-rrd-migration-tool</code>
</div>
{/if}
</div>
{/if}
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
1.2.19
1.3.4