Files
Proxmox-VE-for-WHMCS/modules/addons/pvewhmcs/pvewhmcs.php
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

2399 lines
99 KiB
PHP

<?php
/*
Proxmox VE for WHMCS - Addon/Server Modules for WHMCS (& PVE)
https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/
File: /modules/addons/pvewhmcs/pvewhmcs.php (GUI Work)
Copyright (C) The Network Crew Pty Ltd (TNC) & Co.
For other Contributors to PVEWHMCS, see CONTRIBUTORS.md
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// Pull in the WHMCS database handler Capsule for SQL
use Illuminate\Database\Capsule\Manager as Capsule;
// Define where the module operates in the Admin GUI
define( 'pvewhmcs_BASEURL', 'addonmodules.php?module=pvewhmcs' );
// DEP: Require the PHP API Class to interact with Proxmox VE
require_once('proxmox.php');
// CONFIG: Declare key options to the WHMCS Addon Module framework.
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" => pvewhmcs_version(),
"author" => "The Network Crew Pty Ltd",
'language' => 'English'
);
return $configarray;
}
// VERSION: also stored in repo/version (for update-available checker)
function pvewhmcs_version(){
return "1.3.4";
}
// WHMCS MODULE: ACTIVATION of the ADDON MODULE
// This consists of importing the SQL structure, and then crudely returning yay or nay (needs improving)
function pvewhmcs_activate() {
// Pull in the SQL structure (includes VNC/etc tweaks)
$sql = file_get_contents(__DIR__ . '/db.sql');
if (!$sql) {
return array('status'=>'error','description'=>'The db.sql file was not found.');
}
// SQL file is good, let's proceed with pulling it in
$err=false;
$i=0;
$query_array=explode(';',$sql) ;
$query_count=count($query_array) ;
// Iterate through the SQL commands to finalise init.
foreach ( $query_array as $query) {
if ($i<$query_count-1)
if (!Capsule::statement($query . ';'))
$err=true;
$i++ ;
}
// Return success or error.
if (!$err)
return array('status'=>'success','description'=>'Proxmox VE for WHMCS was installed successfully!');
return array('status'=>'error','description'=>'Proxmox VE for WHMCS was not activated properly.');
}
// WHMCS MODULE: DEACTIVATION
function pvewhmcs_deactivate() {
// Return the assumed result (change?)
return array('status'=>'success','description'=>'Proxmox VE for WHMCS successfully deactivated. Database tables/data retained.');
}
// WHMCS MODULE: Upgrade
function pvewhmcs_upgrade($vars) {
// This function gets passed the old ver once post-update, hence lt check
$currentlyInstalledVersion = $vars['version'];
// SQL Operations for v1.2.9/10 version
if (version_compare($currentlyInstalledVersion, '1.2.10', 'lt')) {
$schema = Capsule::schema();
// Add the column "start_vmid" to the mod_pvewhmcs table
if (!$schema->hasColumn('mod_pvewhmcs', 'start_vmid')) {
$schema->table('mod_pvewhmcs', function ($table) {
$table->integer('start_vmid')->default(100)->after('vnc_secret');
});
}
// Add the column "vmid" to the mod_pvewhmcs_vms table
if (!$schema->hasColumn('mod_pvewhmcs_vms', 'vmid')) {
$schema->table('mod_pvewhmcs_vms', function ($table) {
$table->integer('vmid')->default(0)->after('id');
});
// Populate ID into VMID for all previous guests
Capsule::table('mod_pvewhmcs_vms')
->where('vmid', 0)
->update(['vmid' => Capsule::raw('id')]);
}
}
// SQL Operations for v1.2.12 version
if (version_compare($currentlyInstalledVersion, '1.2.12', 'lt')) {
$schema = Capsule::schema();
// Add the column "unpriv" to the mod_pvewhmcs_plans table
if (!$schema->hasColumn('mod_pvewhmcs_plans', 'unpriv')) {
$schema->table('mod_pvewhmcs_plans', function ($table) {
$table->integer('unpriv')->default(0)->after('balloon');
});
}
}
// SQL Operations for v1.2.17 version
if (version_compare($currentlyInstalledVersion, '1.2.17', 'lt')) {
try {
Capsule::schema()->table('mod_pvewhmcs_plans', function ($table) {
$table->smallInteger('cpus')->unsigned()->nullable()->change();
$table->smallInteger('cores')->unsigned()->nullable()->change();
$table->integer('memory')->unsigned()->default(0)->change();
$table->integer('swap')->unsigned()->nullable()->change();
$table->integer('disk')->unsigned()->nullable()->change();
$table->tinyInteger('vmbr')->unsigned()->nullable()->change();
$table->integer('netrate')->default(0)->change();
$table->integer('bw')->unsigned()->default(0)->change();
$table->integer('vlanid')->nullable()->change();
$table->integer('balloon')->default(0)->change();
$table->tinyInteger('unpriv')->unsigned()->default(0)->change();
});
} catch (\Throwable $e) {
// Debug logging (same style as ClientArea)
if (Capsule::table('mod_pvewhmcs')->where('id', '1')->value('debug_mode') == 1) {
logModuleCall(
'pvewhmcs',
__FUNCTION__,
'Attempting v1.2.17 database upgrade failed.',
$e->getMessage()
);
}
}
}
}
// UPDATE CHECKER: live vs repo
function is_pvewhmcs_outdated(){
if(get_pvewhmcs_latest_version() > pvewhmcs_version()){
return "<br><span style='float:right;'><b>Proxmox VE for WHMCS is outdated: <a style='color:red' href='https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/releases/latest'>Download the new version!</a></span>";
}
}
// UPDATE CHECKER: return latest ver
function get_pvewhmcs_latest_version(){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://raw.githubusercontent.com/The-Network-Crew/Proxmox-VE-for-WHMCS/master/version");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close ($ch);
return str_replace("\n", "", $result);
}
/**
* Fetch RRD statistics from Proxmox with graceful error handling (Admin Area).
*
* Proxmox RRD schema changed in PVE 9 from pve2-{type} to pve-{type}-9.0.
* The ds parameter names (cpu, mem, netin, netout, etc.) remain valid.
*
* RRD data may be unavailable when:
* - Node/VM was just created (RRD takes ~60s to populate)
* - RRD schema migration is incomplete on the PVE host
* - RRD files are corrupted or missing
*
* @param PVE2_API $proxmox The Proxmox API client instance
* @param string $path The RRD API path (e.g., /nodes/{node}/rrd)
* @param string $timeframe RRD timeframe: 'hour', 'day', 'week', 'month', 'year'
* @param string $ds Data source(s): 'cpu', 'mem', 'netin,netout', etc.
* @return string|null Base64-encoded PNG image, or null if unavailable
*/
function pvewhmcs_addon_fetch_rrd($proxmox, $path, $timeframe, $ds) {
$rrd_params = '?timeframe=' . $timeframe . '&ds=' . $ds . '&cf=AVERAGE';
try {
$rrd_data = $proxmox->get($path . $rrd_params);
if (isset($rrd_data['image']) && !empty($rrd_data['image'])) {
$image = utf8_decode($rrd_data['image']);
return base64_encode($image);
}
} catch (Exception $e) {
// RRD data unavailable - log if debug mode on
if (Capsule::table('mod_pvewhmcs')->where('id', '1')->value('debug_mode') == 1) {
logModuleCall(
'pvewhmcs',
'pvewhmcs_addon_fetch_rrd',
'RRD fetch failed: ' . $path . ' (' . $ds . ', ' . $timeframe . ')',
$e->getMessage()
);
}
}
return null;
}
// ADMIN MODULE GUI: output (HTML etc)
function pvewhmcs_output($vars) {
$modulelink = $vars['modulelink'];
// Check for update and report if available
if (!empty(is_pvewhmcs_outdated())) {
$_SESSION['pvewhmcs']['infomsg']['title']='Proxmox VE for WHMCS: New version available!' ;
$_SESSION['pvewhmcs']['infomsg']['message']='<a href="https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/releases/latest" target="_blank">https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/releases/latest</a>' ;
}
// Print Messages to GUI before anything else
if (isset($_SESSION['pvewhmcs']['infomsg'])) {
echo '
<div class="infobox">
<strong>
<span class="title">' . $_SESSION['pvewhmcs']['infomsg']['title'] . '</span>
</strong><br/>
' . $_SESSION['pvewhmcs']['infomsg']['message'] . '
</div>
';
unset($_SESSION['pvewhmcs']);
}
// Set the active tab based on the GET parameter, default to 'vmplans'
if (!isset($_GET['tab'])) {
$_GET['tab'] = 'nodes';
}
// Start the HTML output for the Admin GUI
echo '
<div id="clienttabs">
<ul class="nav nav-tabs admin-tabs">
<li class="'.($_GET['tab']=="nodes" ? "active" : "").'"><a id="tabLink1" data-toggle="tab" role="tab" href="#nodes">Nodes</a></li>
<li class="'.($_GET['tab']=="guests" ? "active" : "").'"><a id="tabLink2" data-toggle="tab" role="tab" href="#guests">Guests</a></li>
<li class="'.($_GET['tab']=="vmplans" ? "active" : "").'"><a id="tabLink3" data-toggle="tab" role="tab" href="#plans">Plans</a></li>
<li class="'.($_GET['tab']=="ippools" ? "active" : "").'"><a id="tabLink4" data-toggle="tab" role="tab" href="#ippools">IPv4</a></li>
<li class="'.($_GET['tab']=="actions" ? "active" : "").'"><a id="tabLink5" data-toggle="tab" role="tab" href="#actions">Actions</a></li>
<li class="'.($_GET['tab']=="support" ? "active" : "").'"><a id="tabLink6" data-toggle="tab" role="tab" href="#support">Support</a></li>
<li class="'.($_GET['tab']=="config" ? "active" : "").'"><a id="tabLink7" data-toggle="tab" role="tab" href="#config">Config</a></li>
<li class="'.($_GET['tab']=="logs" ? "active" : "").'"><a id="tabLink8" data-toggle="tab" role="tab" href="#logs">Logs</a></li>
</ul>
</div>
<style>
.pve-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
background: #fff;
font-size: 13px;
}
.pve-table thead th {
background: #f8f8f8;
color: #333;
font-weight: 600;
padding: 12px 15px;
text-align: left;
border-bottom: 2px solid #e0e0e0;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.pve-table tbody tr {
transition: background 0.15s ease;
}
.pve-table tbody tr:hover {
background: #faf8fc;
}
.pve-table tbody tr:not(:last-child) td {
border-bottom: 1px solid #eee;
}
.pve-table tbody td {
padding: 10px 15px;
color: #333;
vertical-align: middle;
}
.pve-table code {
background: #f4f0f7;
padding: 2px 8px;
border-radius: 3px;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 12px;
color: #5c3d7a;
font-weight: 600;
}
</style>
<div class="tab-content admin-tabs">';
// Handle form submissions for saving or updating plans
if (isset($_POST['plan_save_qemu']))
{
save_qemu_plan() ;
}
if (isset($_POST['plan_update_qemu']))
{
update_qemu_plan() ;
}
if (isset($_POST['plan_save_lxc']))
{
save_lxc_plan() ;
}
if (isset($_POST['plan_update_lxc']))
{
update_lxc_plan() ;
}
// NODES / GUESTS tab in ADMIN GUI
echo '<div id="nodes" class="tab-pane '.($_GET['tab']=="nodes" ? "active" : "").'" >' ;
// 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 '<div class="alert alert-warning">No enabled WHMCS servers found for module type <code>pvewhmcs</code>. Add/enable a server in <em>Setup &gt; Products/Services &gt; Servers</em>.</div>';
} else {
foreach ($servers as $pve) {
// Decrypt server password (same approach as ClientArea)
$api_data = array('password2' => $pve->password);
$serverpassword = localAPI('DecryptPassword', $api_data);
$serverip = $pve->ipaddress;
$serverusername = $pve->username;
$serverlabel = !empty($pve->name) ? $pve->name : ('Server #' . $pve->id);
// Login + get cluster/resources
$proxmox = new PVE2_API($serverip, $serverusername, "pam", $serverpassword['password']);
if (!$proxmox->login()) {
echo '<div class="alert alert-danger">Unable to log in to PVE API on ' . htmlspecialchars($serverip) . '. Check credentials, connectivity & configurations.</div><center><img src="../modules/addons/pvewhmcs/img/forbidden.png"><br><a href="https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS" target="_blank"><img src="../modules/addons/pvewhmcs/img/logo-stacked.png" style="max-height:150px;"></a></center>';
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 '<div class="alert alert-info">No resources returned.</div>';
continue;
}
// Split resources
$nodes = [];
$guests = []; // qemu + lxc
foreach ($cluster_resources as $resource) {
if (!isset($resource['type'])) {
continue;
}
if ($resource['type'] === 'node') {
$nodes[] = $resource;
} elseif ($resource['type'] === 'qemu' || $resource['type'] === 'lxc') {
$guests[] = $resource;
}
}
// Count running guests
$running_guests = array_filter($guests, function($g) {
return isset($g['status']) && $g['status'] === 'running';
});
// ======== CLUSTER HEADER PANEL ========
echo '<div class="panel panel-default" style="margin-bottom:20px;">';
echo '<div class="panel-heading" style="background:#5c3d7a;color:#fff;">';
echo '<h3 class="panel-title" style="margin:0;"><i class="fa fa-server"></i> '.htmlspecialchars($serverlabel).' <small style="color:#ccc;">('.htmlspecialchars($serverip).')</small></h3>';
echo '</div>';
echo '<div class="panel-body">';
// -------- Per-Node Info with RRD Graphs --------
foreach ($nodes as $n) {
$n_name = isset($n['node']) ? $n['node'] : '(node)';
$n_status = isset($n['status']) ? $n['status'] : 'unknown';
$n_uptime = isset($n['uptime']) ? time2format($n['uptime']) : '—';
$n_version = $proxmox->get_version();
$n_cpu_pct = isset($n['cpu']) ? round($n['cpu'] * 100, 1) : 0;
$n_maxcpu = isset($n['maxcpu']) ? $n['maxcpu'] : 0;
$n_mem_pct = (isset($n['mem']) && isset($n['maxmem']) && $n['maxmem'] > 0)
? round($n['mem'] * 100 / $n['maxmem'], 1)
: 0;
$n_mem_used = isset($n['mem']) ? round($n['mem'] / 1073741824, 1) : 0;
$n_mem_max = isset($n['maxmem']) ? round($n['maxmem'] / 1073741824, 1) : 0;
$status_color = ($n_status === 'online') ? '#5cb85c' : '#d9534f';
echo '<div style="border:1px solid #ddd;border-radius:4px;padding:15px;margin-bottom:15px;background:#fafafa;">';
// Node Header Row
echo '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:15px;border-bottom:1px solid #eee;padding-bottom:10px;">';
echo '<div>';
echo '<h4 style="margin:0 0 5px 0;"><i class="fa fa-cube" style="color:#5c3d7a;"></i> <strong>' . htmlspecialchars($n_name) . '</strong> [<code>v' . htmlspecialchars($n_version) . '</code>]</h4>';
echo '<span style="color:#555;font-size:12px;"><strong>Last Booted:</strong> <code>' . htmlspecialchars($n_uptime) . '</code></span>';
echo '</div>';
echo '<div style="text-align:right;">';
echo '<span style="display:inline-block;padding:4px 12px;border-radius:3px;background:' . $status_color . ';color:#fff;font-weight:bold;text-transform:uppercase;font-size:11px;">' . htmlspecialchars($n_status) . '</span>';
echo '</div>';
echo '</div>';
// Live Stats Row
echo '<div style="display:flex;gap:20px;margin-bottom:15px;">';
echo '<div style="flex:1;text-align:center;padding:10px;background:#fff;border-radius:4px;border:1px solid #eee;">';
echo '<div style="font-size:24px;font-weight:bold;color:#5c3d7a;">CPU: <code>' . $n_cpu_pct . '%</code></div>';
echo '<div style="font-size:11px;color:#555;"><strong>' . $n_maxcpu . ' Cores</strong></div>';
echo '</div>';
echo '<div style="flex:1;text-align:center;padding:10px;background:#fff;border-radius:4px;border:1px solid #eee;">';
echo '<div style="font-size:24px;font-weight:bold;color:#5c3d7a;">RAM: <code>' . $n_mem_pct . '%</code></div>';
echo '<div style="font-size:11px;color:#555;"><strong>' . $n_mem_used . ' of ' . $n_mem_max . 'GB</strong></div>';
echo '</div>';
echo '</div>';
// RRD Graphs Section
$rrd_path = '/nodes/' . $n_name . '/rrd';
$rrd_cpu = pvewhmcs_addon_fetch_rrd($proxmox, $rrd_path, 'hour', 'cpu');
$rrd_mem = pvewhmcs_addon_fetch_rrd($proxmox, $rrd_path, 'hour', 'memused');
$rrd_net = pvewhmcs_addon_fetch_rrd($proxmox, $rrd_path, 'hour', 'netin,netout');
$rrd_io = pvewhmcs_addon_fetch_rrd($proxmox, $rrd_path, 'hour', 'iowait');
if ($rrd_cpu || $rrd_mem || $rrd_net || $rrd_io) {
echo '<div style="margin-top:10px;">';
echo '<div style="font-size:12px;color:#555;margin-bottom:8px;"><i class="fa fa-line-chart"></i> <strong>Performance Graphs</strong> (Last Hour)</div>';
// Row 1: CPU and Memory
echo '<div style="display:flex;flex-wrap:wrap;gap:10px;margin-bottom:10px;">';
if ($rrd_cpu) {
echo '<div style="flex:1;min-width:45%;"><img src="data:image/png;base64,' . $rrd_cpu . '" style="width:100%;border-radius:3px;border:1px solid #ddd;"/></div>';
}
if ($rrd_mem) {
echo '<div style="flex:1;min-width:45%;"><img src="data:image/png;base64,' . $rrd_mem . '" style="width:100%;border-radius:3px;border:1px solid #ddd;"/></div>';
}
echo '</div>';
// Row 2: Network and I/O
echo '<div style="display:flex;flex-wrap:wrap;gap:10px;">';
if ($rrd_net) {
echo '<div style="flex:1;min-width:45%;"><img src="data:image/png;base64,' . $rrd_net . '" style="width:100%;border-radius:3px;border:1px solid #ddd;"/></div>';
}
if ($rrd_io) {
echo '<div style="flex:1;min-width:45%;"><img src="data:image/png;base64,' . $rrd_io . '" style="width:100%;border-radius:3px;border:1px solid #ddd;"/></div>';
}
echo '</div>';
echo '</div>';
} else {
echo '<div class="alert alert-warning" style="margin:10px 0 0 0;padding:10px;font-size:12px;">';
echo '<i class="fa fa-exclamation-triangle"></i> RRD data unavailable. Please ask Support to upgrade RRD stored data from 2.x to 9.0 format on their Nodes.';
echo '</div>';
}
echo '</div>'; // End node panel
}
echo '</div>'; // panel-body
echo '</div>'; // panel
}
}
echo '</div>';
// ======== GUESTS TAB ========
echo '<div id="guests" class="tab-pane '.($_GET['tab']=="guests" ? "active" : "").'" >';
// Re-use servers data for guests tab
$servers = Capsule::table('tblservers')
->where('type', '=', 'pvewhmcs')
->where('disabled', '=', 0)
->orderBy('id', 'asc')
->get();
if ($servers->isEmpty()) {
echo '<div class="alert alert-warning">No enabled WHMCS servers found for module type <code>pvewhmcs</code>.</div>';
} else {
foreach ($servers as $pve) {
$api_data = array('password2' => $pve->password);
$serverpassword = localAPI('DecryptPassword', $api_data);
$serverip = $pve->ipaddress;
$serverusername = $pve->username;
$serverlabel = !empty($pve->name) ? $pve->name : ('Server #' . $pve->id);
$proxmox = new PVE2_API($serverip, $serverusername, "pam", $serverpassword['password']);
if (!$proxmox->login()) {
echo '<div class="alert alert-danger">Unable to log in to PVE API on ' . htmlspecialchars($serverip) . '. Check credentials, connectivity & configurations.</div><center><img src="../modules/addons/pvewhmcs/img/forbidden.png"><br><a href="https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS" target="_blank"><img src="../modules/addons/pvewhmcs/img/logo-stacked.png" style="max-height:150px;"></a></center>';
continue;
}
$cluster_resources = $proxmox->get('/cluster/resources');
if (!is_array($cluster_resources) || empty($cluster_resources)) {
echo '<div class="alert alert-info">No resources returned.</div>';
continue;
}
// Filter guests only
$guests = [];
foreach ($cluster_resources as $resource) {
if (isset($resource['type']) && ($resource['type'] === 'qemu' || $resource['type'] === 'lxc')) {
$guests[] = $resource;
}
}
$running_count = count(array_filter($guests, function($g) { return ($g['status'] ?? '') === 'running'; }));
$stopped_count = count(array_filter($guests, function($g) { return ($g['status'] ?? '') === 'stopped'; }));
echo '<div class="panel panel-default" style="margin-bottom:20px;">';
echo '<div class="panel-heading" style="background:#5c3d7a;color:#fff;">';
echo '<h3 class="panel-title" style="margin:0;"><i class="fa fa-desktop"></i> ' . htmlspecialchars($serverlabel) . ' <small style="color:#ddd;">(' . count($guests) . ' guests: ' . $running_count . ' running, ' . $stopped_count . ' stopped)</small></h3>';
echo '</div>';
echo '<div class="panel-body" style="padding:0;padding-top:8px;">';
if (count($guests) > 0) {
echo '<table class="pve-table">';
echo '<thead><tr>
<th>VMID</th>
<th>Name</th>
<th>Status</th>
<th>Type</th>
<th>Node</th>
<th>CPU %</th>
<th>RAM %</th>
<th>Disk %</th>
<th>Uptime</th>
</tr></thead><tbody>';
foreach ($guests as $g) {
$g_node = $g['node'] ?? '—';
$g_type = $g['type'] ?? '—';
$g_vmid = isset($g['vmid']) ? (int)$g['vmid'] : 0;
$g_name = $g['name'] ?? '';
$g_status = $g['status'] ?? 'unknown';
$g_uptime = isset($g['uptime']) ? time2format($g['uptime']) : '—';
$g_cpu_pct = isset($g['cpu']) ? round($g['cpu'] * 100, 1) : 0;
$g_mem_pct = (isset($g['maxmem']) && $g['maxmem'] > 0)
? round(($g['mem'] ?? 0) * 100 / $g['maxmem'], 1)
: 0;
$g_dsk_pct = (isset($g['maxdisk']) && $g['maxdisk'] > 0)
? round(($g['disk'] ?? 0) * 100 / $g['maxdisk'], 1)
: 0;
$type_icon = ($g_type === 'qemu') ? 'fa-desktop' : 'fa-cube';
$status_color = ($g_status === 'running') ? '#5cb85c' : '#999';
echo '<tr>';
echo '<td><code>' . $g_vmid . '</code></td>';
echo '<td><i class="fa ' . $type_icon . '" style="color:#666;"></i> <strong>' . htmlspecialchars($g_name) . '</strong></td>';
echo '<td><span style="display:inline-block;padding:2px 8px;border-radius:3px;background:' . $status_color . ';color:#fff;font-size:10px;text-transform:uppercase;">' . htmlspecialchars($g_status) . '</span></td>';
echo '<td><span style="text-transform:uppercase;font-size:10px;background:#eee;padding:2px 6px;border-radius:3px;">' . htmlspecialchars($g_type) . '</span></td>';
echo '<td>' . htmlspecialchars($g_node) . '</td>';
echo '<td>' . $g_cpu_pct . '%</td>';
echo '<td>' . $g_mem_pct . '%</td>';
echo '<td>' . $g_dsk_pct . '%</td>';
echo '<td>' . htmlspecialchars($g_uptime) . '</td>';
echo '</tr>';
}
echo '</tbody></table>';
} else {
echo '<div class="alert alert-info" style="margin:15px;">No guests found on this cluster.</div>';
}
echo '</div>'; // panel-body
echo '</div>'; // panel
}
}
echo '</div>';
// VM / CT PLANS tab in ADMIN GUI
echo '
<div id="plans" class="tab-pane '.($_GET['tab']=="vmplans" ? "active" : "").'">
<div class="btn-group" role="group" aria-label="...">
<a class="btn btn-default" href="'. pvewhmcs_BASEURL .'&amp;tab=vmplans&amp;action=planlist">
<i class="fa fa-list"></i>&nbsp; List: Guest Plans
</a>
<a class="btn btn-default" href="'. pvewhmcs_BASEURL .'&amp;tab=vmplans&amp;action=add_qemu_plan">
<i class="fa fa-plus-square"></i>&nbsp; Add: QEMU Plan
</a>
<a class="btn btn-default" href="'. pvewhmcs_BASEURL .'&amp;tab=vmplans&amp;action=add_lxc_plan">
<i class="fa fa-plus-square"></i>&nbsp; Add: LXC Plan
</a>
<a class="btn btn-default" href="'. pvewhmcs_BASEURL .'&amp;tab=vmplans&amp;action=import_guest">
<i class="fa fa-upload"></i>&nbsp; Import: Guest
</a>
</div>
';
// Handle actions based on the 'action' GET parameter
if ($_GET['action']=='import_guest') {
import_guest() ;
}
if ($_GET['action']=='add_qemu_plan') {
qemu_plan_add() ;
}
if ($_GET['action']=='editplan') {
if ($_GET['vmtype']=='kvm')
qemu_plan_edit($_GET['id']) ;
else
lxc_plan_edit($_GET['id']) ;
}
if($_GET['action']=='removeplan') {
remove_plan($_GET['id']) ;
}
if ($_GET['action']=='add_lxc_plan') {
lxc_plan_add() ;
}
// List of VM / CT Plans
if ($_GET['action']=='planlist') {
echo '
<table class="datatable" border="0" cellpadding="3" cellspacing="1" width="100%">
<tbody>
<tr>
<th>ID</th>
<th>Name</th>
<th>Guest</th>
<th>OS Type</th>
<th>CPUs</th>
<th>Cores</th>
<th>RAM</th>
<th>Balloon</th>
<th>Swap</th>
<th>Disk</th>
<th>Disk Type</th>
<th>Disk I/O</th>
<th>PVE Store</th>
<th>Net Mode</th>
<th>Bridge</th>
<th>NIC</th>
<th>VLAN ID</th>
<th>Net Rate</th>
<th>Net BW</th>
<th>IPv6</th>
<th>Unpriv.</th>
<th>Actions</th>
</tr>';
foreach (Capsule::table('mod_pvewhmcs_plans')->get() as $vm) {
echo '<tr>';
echo '<td>' . $vm->id . '</td>';
echo '<td>' . $vm->title . '</td>';
echo '<td>' . $vm->vmtype . '</td>';
echo '<td>' . $vm->ostype . '</td>';
echo '<td>' . $vm->cpus . '</td>';
echo '<td>' . $vm->cores . '</td>';
echo '<td>' . $vm->memory . '</td>';
echo '<td>' . $vm->balloon . '</td>';
echo '<td>' . $vm->swap . '</td>';
echo '<td>' . $vm->disk . '</td>';
echo '<td>' . $vm->disktype . '</td>';
echo '<td>' . $vm->diskio . '</td>';
echo '<td>' . $vm->storage . '</td>';
echo '<td>' . $vm->netmode . '</td>';
echo '<td>' . $vm->bridge . $vm->vmbr . '</td>';
echo '<td>' . $vm->netmodel . '</td>';
echo '<td>' . $vm->vlanid . '</td>';
echo '<td>' . $vm->netrate . '</td>';
echo '<td>' . $vm->bw . '</td>';
echo '<td>' . $vm->ipv6 . '</td>';
echo '<td>' . $vm->unpriv . '</td>';
echo '<td>
<a href="' . pvewhmcs_BASEURL . '&amp;tab=vmplans&amp;action=editplan&amp;id=' . $vm->id . '&amp;vmtype=' . $vm->vmtype . '"><img height="16" width="16" border="0" alt="Edit" src="images/edit.gif"></a>
<a href="' . pvewhmcs_BASEURL . '&amp;tab=vmplans&amp;action=removeplan&amp;id=' . $vm->id . '" onclick="return confirm(\'Plan will be deleted, continue?\')"><img height="16" width="16" border="0" alt="Edit" src="images/delete.gif"></a>
</td>';
echo '</tr>';
}
echo '</tbody></table>';
}
echo '
</div>
';
// IPv4 POOLS tab in ADMIN GUI
echo '
<div id="ippools" class="tab-pane '.($_GET['tab']=="ippools" ? "active" : "").'" >
<div class="btn-group">
<a class="btn btn-default" href="'. pvewhmcs_BASEURL .'&amp;tab=ippools&amp;action=list_ip_pools">
<i class="fa fa-list"></i>&nbsp; List: IPv4 Pools
</a>
<a class="btn btn-default" href="'. pvewhmcs_BASEURL .'&amp;tab=ippools&amp;action=newip">
<i class="fa fa-plus"></i>&nbsp; Add: IPv4 to Pool
</a>
</div>
';
if ($_GET['action']=='list_ip_pools') {
list_ip_pools() ;
}
if ($_GET['action']=='new_ip_pool') {
add_ip_pool() ;
}
if ($_GET['action']=='newip') {
add_ip_2_pool() ;
}
if (isset($_POST['newIPpool'])) {
save_ip_pool() ;
}
if ($_GET['action']=='removeippool') {
removeIpPool($_GET['id']) ;
}
if ($_GET['action']=='list_ips') {
list_ips();
}
if ($_GET['action']=='removeip') {
removeip($_GET['id'],$_GET['pool_id']);
}
echo'
</div>
';
// ACTIONS tab in ADMIN GUI
echo '<div id="actions" class="tab-pane '.($_GET['tab']=="actions" ? "active" : "").'" >' ;
echo ('<strong><h2>Module: Action History</h2></strong>');
echo ('Coming soon!<br><br>');
echo ('<strong><h2>Module: Failed Actions</h2></strong>');
echo ('Coming soon!<br><br>');
echo '</div>';
// SUPPORT tab in ADMIN GUI
echo '<div id="support" class="tab-pane '.($_GET['tab']=="support" ? "active" : "").'" >';
echo '
<div style="max-width:800px;">
<div style="background:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:25px;margin-bottom:20px;">
<h3 style="margin:0 0 15px 0;color:#5c3d7a;font-weight:600;"><span style="font-size:24px;">&#9881;</span> System Environment</h3>
<table style="width:100%;font-size:14px;">
<tr><td style="padding:6px 0;color:#666;width:150px;"><strong>Module Version</strong></td><td style="padding:6px 0;"><code style="background:#f4f0f7;padding:3px 8px;border-radius:3px;color:#5c3d7a;">v' . pvewhmcs_version() . '</code></td></tr>
<tr><td style="padding:6px 0;color:#666;"><strong>Latest Version</strong></td><td style="padding:6px 0;"><code style="background:#f4f0f7;padding:3px 8px;border-radius:3px;color:#5c3d7a;">v' . get_pvewhmcs_latest_version() . '</code></td></tr>
<tr><td style="padding:6px 0;color:#666;"><strong>Web Server</strong></td><td style="padding:6px 0;"><code style="background:#f4f0f7;padding:3px 8px;border-radius:3px;color:#5c3d7a;">' . htmlspecialchars($_SERVER['SERVER_SOFTWARE']) . '</code></td></tr>
<tr><td style="padding:6px 0;color:#666;"><strong>PHP Version</strong></td><td style="padding:6px 0;"><code style="background:#f4f0f7;padding:3px 8px;border-radius:3px;color:#5c3d7a;">v' . phpversion() . '</code></td></tr>
<tr><td style="padding:6px 0;color:#666;"><strong>Server Name</strong></td><td style="padding:6px 0;"><code style="background:#f4f0f7;padding:3px 8px;border-radius:3px;color:#5c3d7a;">' . htmlspecialchars($_SERVER['SERVER_NAME']) . '</code></td></tr>
</table>
</div>
<div style="background:#faf8fc;border:1px solid #e0d4e8;border-radius:8px;padding:25px;margin-bottom:20px;">
<h3 style="margin:0 0 15px 0;color:#5c3d7a;font-weight:600;"><span style="font-size:24px;">&#9829;</span> Open Source</h3>
<p style="margin:0 0 12px 0;font-size:14px;line-height:1.6;color:#333;">PVEWHMCS is open-source and free to use &amp; improve on!</p>
<p style="margin:0;">
<a href="https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/" target="_blank" style="color:#5c3d7a;">&#10132; GitHub Repository</a>
</p>
</div>
<div style="background:#f8fff8;border:1px solid #c3e6c3;border-radius:8px;padding:25px;margin-bottom:20px;">
<h3 style="margin:0 0 15px 0;color:#2d7a2d;font-weight:600;"><span style="font-size:24px;">&#9733;</span> Leave a Review</h3>
<p style="margin:0 0 12px 0;font-size:14px;line-height:1.6;color:#333;">Your 5-star review on WHMCS Marketplace helps the module grow!</p>
<p style="margin:0;">
<a href="https://marketplace.whmcs.com/product/6935-proxmox-ve-for-whmcs" target="_blank" style="color:#2d7a2d;">&#9733;&#9733;&#9733;&#9733;&#9733; Rate on WHMCS Marketplace</a>
</p>
</div>
<div style="background:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:25px;">
<h3 style="margin:0 0 15px 0;color:#5c3d7a;font-weight:600;"><span style="font-size:24px;">&#9881;</span> Technical Support</h3>
<p style="margin:0 0 12px 0;font-size:14px;line-height:1.6;">Our README contains a wealth of information. Please review it before raising issues.</p>
<p style="margin:0 0 15px 0;">
<a href="https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/" target="_blank" style="color:#5c3d7a;">&#10132; View Documentation</a>
</p>
<p style="margin:0 0 12px 0;font-size:14px;line-height:1.6;">Only raise a GitHub Issue &mdash; including logs &mdash; if you have properly tried to resolve it first.</p>
<p style="margin:0 0 15px 0;">
<a href="https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/issues/new/choose" target="_blank" style="color:#5c3d7a;">&#10132; Open an Issue</a>
</p>
<p style="margin:0;padding:12px;background:#fff8f0;border-radius:6px;font-size:13px;color:#856404;border:1px solid #ffc107;">&#9888; Help is not guaranteed (FOSS). We will need your assistance to troubleshoot.</p>
</div>
<a href="https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS" target="_blank"><img src="../modules/addons/pvewhmcs/img/logo-stacked.png" style="max-height:150px;"></a>
</div>';
echo '</div>';
// Config Tab
$config= Capsule::table('mod_pvewhmcs')->where('id', '=', '1')->get()[0];
echo '<div id="config" class="tab-pane '.($_GET['tab']=="config" ? "active" : "").'" >' ;
echo '
<div style="max-width:800px;">
<div style="background:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:25px;">
<h3 style="margin:0 0 20px 0;color:#5c3d7a;font-weight:600;"><span style="font-size:24px;">&#9881;</span> Module Configuration</h3>
<form method="post">
<table style="width:100%;border-collapse:collapse;">
<tr>
<td style="padding:15px 0;border-bottom:1px solid #eee;width:150px;vertical-align:top;">
<label style="font-weight:600;color:#333;">VNC Secret</label>
</td>
<td style="padding:15px 0;border-bottom:1px solid #eee;">
<input type="text" style="width:100%;max-width:300px;padding:8px 12px;border:1px solid #ddd;border-radius:4px;font-size:14px;" name="vnc_secret" id="vnc_secret" value="' . $config->vnc_secret . '">
<p style="margin:8px 0 0 0;font-size:13px;color:#666;">Password for <code style="background:#f4f0f7;padding:2px 6px;border-radius:3px;color:#5c3d7a;">vnc@pve</code> user. Required for VNC proxying. <a href="https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/" target="_blank" style="color:#5c3d7a;"><u>View README</u></a></p>
</td>
</tr>
<tr>
<td style="padding:15px 0;border-bottom:1px solid #eee;vertical-align:top;">
<label style="font-weight:600;color:#333;">VMID Start</label>
</td>
<td style="padding:15px 0;border-bottom:1px solid #eee;">
<input type="text" style="width:100%;max-width:300px;padding:8px 12px;border:1px solid #ddd;border-radius:4px;font-size:14px;" name="start_vmid" id="start_vmid" value="' . $config->start_vmid . '">
<p style="margin:8px 0 0 0;font-size:13px;color:#666;">For Guests. Increments until a vacant VMID found. Default is <code style="background:#f4f0f7;padding:2px 6px;border-radius:3px;color:#5c3d7a;">100</code></p>
</td>
</tr>
<tr>
<td style="padding:15px 0;vertical-align:top;">
<label style="font-weight:600;color:#333;">Debug Mode</label>
</td>
<td style="padding:15px 0;">
<label style="display:flex;align-items:center;gap:10px;cursor:pointer;">
<input type="checkbox" name="debug_mode" value="1" '. ($config->debug_mode=="1" ? "checked" : "").' style="width:18px;height:18px;">
<span style="font-size:14px;color:#333;">Enable Debug Logging</span>
</label>
<p style="margin:8px 0 0 0;font-size:13px;color:#666;">Must also enable WHMCS Module Log. <a href="/admin/index.php?rp=/admin/logs/module-log" style="color:#5c3d7a;"><u>View Module Logs</u></a></p>
</td>
</tr>
</table>
<div style="margin-top:25px;padding-top:20px;border-top:1px solid #eee;">
<input type="submit" style="background:#5c3d7a;color:#fff;border:none;padding:10px 24px;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;margin-right:10px;" value="Save Changes" name="save_config" id="save_config">
<input type="reset" style="background:#f5f5f5;color:#333;border:1px solid #ddd;padding:10px 24px;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;" value="Cancel">
</div>
</form>
</div>
<a href="https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS" target="_blank"><img src="../modules/addons/pvewhmcs/img/logo-stacked.png" style="max-height:150px;"></a>
</div>
';
echo '</div>';
// LOGS tab in ADMIN GUI
echo '<div id="logs" class="tab-pane ' . (isset($_GET['tab']) && $_GET['tab'] === 'logs' ? 'active' : '') . '">';
try {
// If a client exists already, reuse it; else initialise once from the first enabled pvewhmcs server
if (!isset($proxmox)) {
$pve = Capsule::table('tblservers')
->where('type', 'pvewhmcs')
->where('disabled', 0)
->orderBy('id', 'asc')
->first();
if (!$pve) {
throw new Exception('No enabled WHMCS server found for module type pvewhmcs.');
}
$dec = localAPI('DecryptPassword', ['password2' => $pve->password]);
$serverpassword = $dec['password'] ?? '';
if (!$serverpassword) {
throw new Exception('Could not decrypt Proxmox server password.');
}
$proxmox = new PVE2_API($pve->ipaddress, $pve->username, "pam", $serverpassword);
if (!$proxmox->login()) {
throw new Exception('Unable to log in to PVE API on ' . htmlspecialchars($pve->ipaddress) . '. Check credentials, connectivity & configurations.');
}
}
// 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 '<div class="alert alert-info">No recent cluster tasks were returned.</div>';
} else {
// Sort newest first (defensive)
usort($tasks, function ($a, $b) {
return (intval($b['starttime'] ?? 0)) <=> (intval($a['starttime'] ?? 0));
});
echo '<table class="pve-table">';
echo '<thead><tr>
<th>Task</th>
<th>VMID</th>
<th>Status</th>
<th>Node</th>
<th>User</th>
<th>Duration</th>
<th>Start</th>
<th>End</th>
</tr></thead><tbody>';
foreach ($tasks as $t) {
$node = $t['node'] ?? '—';
$type = $t['type'] ?? '';
$user = $t['user'] ?? '';
$upid = $t['upid'] ?? '';
// Derive VMID:
// 1) Prefer numeric $t['id'] when available
// 2) Otherwise parse from UPID ("...:type:<vmid>:user@realm:")
$vmid = '—';
if (isset($t['id']) && preg_match('/^\d+$/', (string)$t['id'])) {
$vmid = (string)$t['id'];
} elseif (is_string($upid) && $upid !== '') {
// UPID format: UPID:node:pid:pstart:starttime:type:vmid:user@realm:
if (preg_match('/^UPID:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):/', $upid, $m)) {
if ($m[1] !== '' && ctype_digit($m[1])) {
$vmid = $m[1];
}
}
}
$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 '<tr>';
echo '<td><code>' . htmlspecialchars($type) . '</code></td>';
echo '<td><code>' . htmlspecialchars($vmid) . '</code></td>';
echo '<td>' . $badge . ' ' . htmlspecialchars($status) . '</td>';
echo '<td>' . htmlspecialchars($node) . '</td>';
echo '<td>' . htmlspecialchars($user) . '</td>';
echo '<td>' . htmlspecialchars($durH) . '</td>';
echo '<td>' . htmlspecialchars($start) . '</td>';
echo '<td>' . htmlspecialchars($end) . '</td>';
echo '</tr>';
}
echo '</tbody></table>';
}
} catch (Throwable $e) {
echo '<div class="alert alert-danger">Could not retrieve PVE Cluster history: '
. htmlspecialchars($e->getMessage()) . '</div>';
}
echo '</div></div>';
// End of tabbed content
// Handle saving the configuration if the form was submitted
if (isset($_POST['save_config'])) {
save_config() ;
}
}
// Import Guest sub-page handler (standalone, outside pvewhmcs_output)
// This function associates an existing PVE Guest in WHMCS as a new Client Service.
function import_guest() {
$resultMsg = '';
if (!empty($_POST['import_existing_guest'])) {
$vmid = intval($_POST['import_vmid']);
$userid = intval($_POST['import_clientid']);
$productid = intval($_POST['import_productid']);
$ipaddress = trim($_POST['import_ipv4']);
$subnetmask = trim($_POST['import_subnet']);
$gateway = trim($_POST['import_gateway']);
$hostname = trim($_POST['import_hostname']);
$vtype = ($_POST['import_vtype'] === 'lxc') ? 'lxc' : 'qemu';
// Validate Client ID
$client = Capsule::table('tblclients')->where('id', $userid)->where('status', 'Active')->first();
if (!$client) {
$resultMsg = '<div class="errorbox">No active WHMCS Client found with ID ' . $userid . '</div>';
} else {
// Validate Product
$product = Capsule::table('tblproducts')->where('id', $productid)->where('retired', 0)->first();
if (!$product) {
$resultMsg = '<div class="errorbox">No active WHMCS Product found with ID ' . $productid . '</div>';
} else {
// Create WHMCS Service (Order)
try {
// First, get the first Server ID that matches the product's server group
$serverRel = Capsule::table('tblservergroupsrel')->where('groupid', $product->servergroup)->first();
$serverID = $serverRel ? $serverRel->serverid : 0;
// Do the insertion to the tblhosting table
$serviceID = Capsule::table('tblhosting')->insertGetId([
'userid' => $userid,
'packageid' => $productid,
'regdate' => date('Y-m-d'),
'domain' => $hostname,
'paymentmethod' => 'banktransfer',
'firstpaymentamount' => '0.00',
'amount' => '0.00',
'billingcycle' => 'Monthly',
'nextduedate' => date('Y-m-d'),
'nextinvoicedate' => date('Y-m-d'),
'orderid' => 0,
'domainstatus' => 'Active',
'username' => 'root',
'password' => '',
'subscriptionid' => '',
'promoid' => 0,
'server' => $serverID,
'dedicatedip' => $ipaddress,
'assignedips' => $ipaddress,
'ns1' => '',
'ns2' => '',
'diskusage' => 0,
'disklimit' => 0,
'bwusage' => 0,
'bwlimit' => 0,
'lastupdate' => date('Y-m-d H:i:s'),
'suspendreason' => '',
'overideautosuspend' => 0,
'overidesuspenduntil' => '',
'notes' => 'PVEWHMCS: Imported from Proxmox Guest VMID ' . $vmid,
]);
} catch (Exception $e) {
$resultMsg = '<div class="errorbox">Could not create WHMCS service: ' . htmlspecialchars($e->getMessage()) . '</div>';
$serviceID = false;
}
if ($serviceID) {
// Insert into module VMs table
try {
Capsule::table('mod_pvewhmcs_vms')->insert([
'id' => $serviceID,
'vmid' => $vmid,
'user_id' => $userid,
'vtype' => $vtype,
'ipaddress' => $ipaddress,
'subnetmask' => $subnetmask,
'gateway' => $gateway,
'created' => date('Y-m-d H:i:s'),
]);
$resultMsg = '<div class="successbox">Successfully imported PVE VMID ' . $vmid . ' (' . $vtype . ') as Service ' . $serviceID . ' (' . $product->name . ') for ' . $client->firstname . ' ' . $client->lastname . '. ' . $client->company . '</div>';
} catch (Exception $e) {
$resultMsg = '<div class="errorbox">Database error: ' . htmlspecialchars($e->getMessage()) . '</div>';
}
}
}
}
}
// Always show the form for easy further imports
if (!empty($resultMsg)) echo $resultMsg;
echo '<form method="post">';
echo '<table class="form" border="0" cellpadding="3" cellspacing="1" width="100%">';
echo '<tr><td class="fieldlabel">PVE VMID</td><td class="fieldarea"><input type="text" name="import_vmid" required></td></tr>';
echo '<tr><td class="fieldlabel">Hostname</td><td class="fieldarea"><input type="text" name="import_hostname" required></td></tr>';
// Active clients dropdown
$clients = Capsule::table('tblclients')->where('status', 'Active')->orderBy('companyname')->orderBy('firstname')->orderBy('lastname')->get();
echo '<tr><td class="fieldlabel">Target Client</td><td class="fieldarea"><select name="import_clientid" required>';
foreach ($clients as $client) {
$label = $client->id . ' - ' . ($client->companyname ? $client->companyname . ' - ' : '') . $client->firstname . ' ' . $client->lastname;
echo '<option value="' . $client->id . '">' . htmlspecialchars($label) . '</option>';
}
echo '</select></td></tr>';
// Product/Service dropdown (only Active products of Server type)
$products = Capsule::table('tblproducts')->where('type', 'server')->where('retired', 0)->orderBy('name')->get();
echo '<tr><td class="fieldlabel">Service</td><td class="fieldarea"><select name="import_productid" required>';
foreach ($products as $product) {
echo '<option value="' . $product->id . '">' . htmlspecialchars($product->name) . '</option>';
}
echo '</select></td></tr>';
// Guest Type dropdown
echo '<tr><td class="fieldlabel">VM / CT</td><td class="fieldarea"><select name="import_vtype" required>';
echo '<option value="qemu">(VM) QEMU</option>';
echo '<option value="lxc">(CT) LXC</option>';
echo '</select></td></tr>';
// IPv4, Subnet, Gateway
echo '<tr><td class="fieldlabel">IPv4</td><td class="fieldarea"><input type="text" name="import_ipv4" required></td></tr>';
echo '<tr><td class="fieldlabel">Subnet</td><td class="fieldarea"><input type="text" name="import_subnet" required></td></tr>';
echo '<tr><td class="fieldlabel">Gateway</td><td class="fieldarea"><input type="text" name="import_gateway" required></td></tr>';
echo '</table>';
echo '<div class="btn-container"><input type="submit" class="btn btn-primary" value="Import Guest" name="import_existing_guest" id="import_existing_guest"></div>';
echo '</form>';
}
// MODULE CONFIG: Commit changes to the database
function save_config() {
try {
Capsule::connection()->transaction(
function ($connectionManager)
{
/** @var \Illuminate\Database\Connection $connectionManager */
$connectionManager->table('mod_pvewhmcs')->update(
[
'vnc_secret' => $_POST['vnc_secret'],
'start_vmid' => $_POST['start_vmid'],
'debug_mode' => $_POST['debug_mode'],
]
);
}
);
$_SESSION['pvewhmcs']['infomsg']['title']='Module Config saved.' ;
$_SESSION['pvewhmcs']['infomsg']['message']='New options have been successfully saved.' ;
header("Location: ".pvewhmcs_BASEURL."&tab=config");
} catch (\Exception $e) {
echo "Uh oh! That didn't work, but I was able to rollback. {$e->getMessage()}";
}
}
// MODULE FORM: Add new QEMU Plan
function qemu_plan_add() {
echo '
<form method="post">
<table class="form" border="0" cellpadding="3" cellspacing="1" width="100%">
<tr>
<td class="fieldlabel">Plan Title</td>
<td class="fieldarea">
<input type="text" size="35" name="title" id="title" required>
</td>
</tr>
<tr>
<td class="fieldlabel">OS - Type</td>
<td class="fieldarea">
<select class="form-control select-inline" name="ostype">
<option value="l26">Linux 6.x - 2.6 Kernel</option>
<option value="l24">Linux 2.4 Kernel</option>
<option value="solaris">Solaris Kernel</option>
<option value="win11">Windows 11 / 2022 / 2025</option>
<option value="win10">Windows 10 / 2016 / 2019</option>
<option value="win8">Windows 8 / 2012 / 2012r2</option>
<option value="win7">Windows 7 / 2008r2</option>
<option value="wvista">Windows Vista / 2008</option>
<option value="wxp">Windows XP / 2003</option>
<option value="w2k">Windows 2000</option>
<option value="other">Other</option>
</select>
Kernel type (Linux, Windows, etc).
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Emulation</td>
<td class="fieldarea">
<select class="form-control select-inline" name="cpuemu">
<option value="host">(Host) Host</option>
<option value="kvm32">(QEMU) kvm32</option>
<option value="kvm64">(QEMU) kvm64</option>
<option value="max">(QEMU) Max</option>
<option value="qemu32">(QEMU) qemu32</option>
<option value="qemu64">(QEMU) qemu64</option>
<option value="x86-64-v2">(x86-64 psABI) v2 (Nehalem/Opteron_G3 on)</option>
<option value="x86-64-v2-AES" selected="">(x86-64 psABI) v2-AES (Westmere/Opteron_G4 on)</option>
<option value="x86-64-v3">(x86-64 psABI) v3 (Broadwell/EPYC on)</option>
<option value="x86-64-v4">(x86-64 psABI) v4 (Skylake/EPYCv4 on)</option>
<option value="486">(Intel) 486</option>
<option value="Broadwell">(Intel) Broadwell</option>
<option value="Broadwell-IBRS">(Intel) Broadwell-IBRS</option>
<option value="Broadwell-noTSX">(Intel) Broadwell-noTSX</option>
<option value="Broadwell-noTSX-IBRS">(Intel) Broadwell-noTSX-IBRS</option>
<option value="Cascadelake-Server">(Intel) Cascadelake-Server</option>
<option value="Cascadelake-Server-noTSX">(Intel) Cascadelake-Server-noTSX</option>
<option value="Cascadelake-Server-v2">(Intel) Cascadelake-Server-v2</option>
<option value="Cascadelake-Server-v4">(Intel) Cascadelake-Server-v4</option>
<option value="Cascadelake-Server-v5">(Intel) Cascadelake-Server-v5</option>
<option value="Conroe">(Intel) Conroe</option>
<option value="Cooperlake">(Intel) Cooperlake</option>
<option value="Cooperlake-v2">(Intel) Cooperlake-v2</option>
<option value="Haswell">(Intel) Haswell</option>
<option value="Haswell-IBRS">(Intel) Haswell-IBRS</option>
<option value="Haswell-noTSX">(Intel) Haswell-noTSX</option>
<option value="Haswell-noTSX-IBRS">(Intel) Haswell-noTSX-IBRS</option>
<option value="Icelake-Client">(Intel) Icelake-Client</option>
<option value="Icelake-Client-noTSX">(Intel) Icelake-Client-noTSX</option>
<option value="Icelake-Server">(Intel) Icelake-Server</option>
<option value="Icelake-Server-noTSX">(Intel) Icelake-Server-noTSX</option>
<option value="Icelake-Server-v3">(Intel) Icelake-Server-v3</option>
<option value="Icelake-Server-v4">(Intel) Icelake-Server-v4</option>
<option value="Icelake-Server-v5">(Intel) Icelake-Server-v5</option>
<option value="Icelake-Server-v6">(Intel) Icelake-Server-v6</option>
<option value="IvyBridge">(Intel) IvyBridge</option>
<option value="IvyBridge-IBRS">(Intel) IvyBridge-IBRS</option>
<option value="KnightsMill">(Intel) KnightsMill</option>
<option value="Nehalem">(Intel) Nehalem</option>
<option value="Nehalem-IBRS">(Intel) Nehalem-IBRS</option>
<option value="Penryn">(Intel) Penryn</option>
<option value="SandyBridge">(Intel) SandyBridge</option>
<option value="SandyBridge-IBRS">(Intel) SandyBridge-IBRS</option>
<option value="SapphireRapids">(Intel) SapphireRapids</option>
<option value="Skylake-Client">(Intel) Skylake-Client</option>
<option value="Skylake-Client-IBRS">(Intel) Skylake-Client-IBRS</option>
<option value="Skylake-Client-noTSX-IBRS">(Intel) Skylake-Client-noTSX-IBRS</option>
<option value="Skylake-Client-v4">(Intel) Skylake-Client-v4</option>
<option value="Skylake-Server">(Intel) Skylake-Server</option>
<option value="Skylake-Server-IBRS">(Intel) Skylake-Server-IBRS</option>
<option value="Skylake-Server-noTSX-IBRS">(Intel) Skylake-Server-noTSX-IBRS</option>
<option value="Skylake-Server-v4">(Intel) Skylake-Server-v4</option>
<option value="Skylake-Server-v5">(Intel) Skylake-Server-v5</option>
<option value="Westmere">(Intel) Westmere</option>
<option value="Westmere-IBRS">(Intel) Westmere-IBRS</option>
<option value="pentium">(Intel) Pentium I</option>
<option value="pentium2">(Intel) Pentium II</option>
<option value="pentium3">(Intel) Pentium III</option>
<option value="coreduo">(Intel) Core Duo</option>
<option value="core2duo">(Intel) Core 2 Duo</option>
<option value="athlon">(AMD) Athlon</option>
<option value="phenom">(AMD) Phenom</option>
<option value="EPYC">(AMD) EPYC</option>
<option value="EPYC-IBPB">(AMD) EPYC-IBPB</option>
<option value="EPYC-Milan">(AMD) EPYC-Milan</option>
<option value="EPYC-Milan-v2">(AMD) EPYC-Milan-v2</option>
<option value="EPYC-Rome">(AMD) EPYC-Rome</option>
<option value="EPYC-Rome-v2">(AMD) EPYC-Rome-v2</option>
<option value="EPYC-v3">(AMD) EPYC-v3</option>
<option value="Opteron_G1">(AMD) Opteron_G1</option>
<option value="Opteron_G2">(AMD) Opteron_G2</option>
<option value="Opteron_G3">(AMD) Opteron_G3</option>
<option value="Opteron_G4">(AMD) Opteron_G4</option>
<option value="Opteron_G5">(AMD) Opteron_G5</option>
</select>
Host is best. Read the <a href="https://pve.proxmox.com/pve-docs/pve-admin-guide.html#_qemu_vcpu_list" target="_blank" style="color:#5c3d7a;"><u>Docs</u></a>.
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Sockets</td>
<td class="fieldarea">
<input type="text" size="8" name="cpus" id="cpus" value="1" required>
The number of CPU Sockets (typically 1-4). Governed by your physical Server.
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Cores</td>
<td class="fieldarea">
<input type="text" size="8" name="cores" id="cores" value="1" required>
The number of CPU Cores per Socket (1-N). Guest Compute = allocated Sockets * Cores.
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Limit</td>
<td class="fieldarea">
<input type="text" size="8" name="cpulimit" id="cpulimit" value="0" required>
Limit of CPU Usage. Note if the Server has 2 CPUs, it has total of "2" CPU time. Value "0" indicates no CPU limit.
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Weighting</td>
<td class="fieldarea">
<input type="text" size="8" name="cpuunits" id="cpuunits" value="1024" required>
Number is relative to weights of all the other running VMs. 8 - 500000, recommend 1024. Disable fair-scheduler by setting this to 0.
</td>
</tr>
<tr>
<td class="fieldlabel">RAM - Memory</td>
<td class="fieldarea">
<input type="text" size="8" name="memory" id="memory" value="2048" required>
RAM capacity in Megabytes eg. 1024 = 1GB (default is 2GB)
</td>
</tr>
<tr>
<td class="fieldlabel">RAM - Balloon</td>
<td class="fieldarea">
<input type="text" size="8" name="balloon" id="balloon" value="0" required>
Balloon capacity in Megabytes eg. 1024 = 1GB (0 = disabled)
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - Capacity</td>
<td class="fieldarea">
<input type="text" size="8" name="disk" id="disk" value="10240" required>
HDD/SSD storage in Gigabytes eg. 1024 = 1TB (default is 10GB)
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - Format</td>
<td class="fieldarea">
<select class="form-control select-inline" name="diskformat">
<option value="raw">Disk Image (raw)</option>
<option selected="" value="qcow2">QEMU Image (qcow2)</option>
<option value="vmdk">VMware Image (vmdk)</option>
</select>
Recommend "QEMU/qcow2" (supports Snapshots)
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - Cache</td>
<td class="fieldarea">
<select class="form-control select-inline" name="diskcache">
<option selected="" value="none">No Cache</option>
<option value="directsync">Direct Sync</option>
<option value="writethrough">Write Through</option>
<option value="writeback">Write Back</option>
<option value="unsafe">Write Back (Unsafe)</option>
</select>
Before overriding the default, read &amp; understand the <a href="https://pve.proxmox.com/wiki/Performance_Tweaks#Disk_Cache" target="_blank" style="color:#5c3d7a;"><u>Docs</u></a>.
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - Type</td>
<td class="fieldarea">
<select class="form-control select-inline" name="disktype">
<option selected="" value="virtio">Virtio</option>
<option value="scsi">SCSI</option>
<option value="sata">SATA</option>
<option value="ide">IDE</option>
</select>
Virtio is the fastest option, then SCSI, then SATA, etc.
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - I/O Cap</td>
<td class="fieldarea">
<input type="text" size="8" name="diskio" id="diskio" value="0" required>
Limit of Disk I/O in KiB/s. 0 for unrestricted storage access for Guests.
</td>
</tr>
<tr>
<td class="fieldlabel">PVE Store - Name</td>
<td class="fieldarea">
<input type="text" size="8" name="storage" id="storage" value="local" required>
Name of VM/CT Storage on Proxmox VE hypervisor. <code>local/local-lvm/etc</code>
</td>
</tr>
<tr>
<td class="fieldlabel">Network - NIC Type</td>
<td class="fieldarea">
<select class="form-control select-inline" name="netmodel">
<option selected="" value="virtio">VirtIO (Paravirtualised)</option>
<option value="e1000">Intel E1000 (Stable)</option>
<option value="rtl8139">Realtek RTL8139</option>
<option value="vmxnet3">VMware vmxnet3</option>
</select>
Recommend VirtIO, unless you need others.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Rate</td>
<td class="fieldarea">
<input type="text" size="8" name="netrate" id="netrate" value="0">
Network Rate Limit in Megabits/Second. Zero for unlimited.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Cap</td>
<td class="fieldarea">
<input type="text" size="8" name="bw" id="bw">
Monthly Data Transfer Cap in Gigabytes. Blank for unlimited.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - IPv6</td>
<td class="fieldarea">
<select class="form-control select-inline" name="ipv6">
<option value="0">Off</option>
<option value="auto">SLAAC</option>
<option value="dhcp">DHCPv6</option>
<option value="prefix">Prefix</option>
</select>
SLAAC & DHCPv6 working. Prefix in future.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Mode</td>
<td class="fieldarea">
<select class="form-control select-inline" name="netmode">
<option value="bridge">Bridge</option>
<option value="nat">NAT</option>
<option value="none">No Network</option>
</select>
Bridge, NAT or disconnect (no link) the Guest.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Interface</td>
<td class="fieldarea">
<input type="text" size="8" name="bridge" id="bridge" value="vmbr">
Network / Bridge / NIC name. PVE default bridge prefix is "vmbr".
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Bridge/NIC ID</td>
<td class="fieldarea">
<input type="text" size="8" name="vmbr" id="vmbr" value="0">
Interface ID. PVE Bridge default is 0, for "vmbr0". PVE SDN, leave blank.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - VLAN ID</td>
<td class="fieldarea">
<input type="text" size="8" name="vlanid" id="vlanid">
VLAN ID for Plan Services. Default forgoes tagging (VLAN ID), blank for untagged.
</td>
</tr>
<tr>
<td class="fieldlabel">
Hardware Virt?
</td>
<td class="fieldarea">
<label class="checkbox-inline">
<input type="checkbox" name="kvm" value="1" checked> Enable KVM hardware virtualisation. Requires support/enablement in BIOS. (Recommended)
</label>
</td>
</tr>
<tr>
<td class="fieldlabel">
On-boot VM?
</td>
<td class="fieldarea">
<label class="checkbox-inline">
<input type="checkbox" name="onboot" value="1" checked> Specifies whether a VM will be started during hypervisor boot-up. (Recommended)
</label>
</td>
</tr>
</table>
<div class="btn-container">
<input type="submit" class="btn btn-primary" value="Save Changes" name="plan_save_qemu" id="plan_save_qemu">
<input type="reset" class="btn btn-default" value="Cancel Changes">
</div>
</form>
';
}
// MODULE FORM: Edit a QEMU Plan
function qemu_plan_edit($id) {
$plan= Capsule::table('mod_pvewhmcs_plans')->where('id', '=', $id)->get()[0];
if (empty($plan)) {
echo 'Plan Not found' ;
return false ;
}
echo '
<form method="post">
<table class="form" border="0" cellpadding="3" cellspacing="1" width="100%">
<tr>
<td class="fieldlabel">Plan Title</td>
<td class="fieldarea">
<input type="text" size="35" name="title" id="title" required value="' . $plan->title . '">
</td>
</tr>
<tr>
<td class="fieldlabel">OS - Type</td>
<td class="fieldarea">
<select class="form-control select-inline" name="ostype">
<option value="l26" ' . ($plan->ostype == "l26" ? "selected" : "") . '>Linux 6.x - 2.6 Kernel</option>
<option value="l24" ' . ($plan->ostype == "l24" ? "selected" : "") . '>Linux 2.4 Kernel</option>
<option value="solaris" ' . ($plan->ostype == "solaris" ? "selected" : "") . '>Solaris Kernel</option>
<option value="win11" ' . ($plan->ostype == "win11" ? "selected" : "") . '>Windows 11 / 2022 / 2025</option>
<option value="win10" ' . ($plan->ostype == "win10" ? "selected" : "") . '>Windows 10 / 2016 / 2019</option>
<option value="win8" ' . ($plan->ostype == "win8" ? "selected" : "") . '>Windows 8 / 2012 / 2012r2</option>
<option value="win7" ' . ($plan->ostype == "win7" ? "selected" : "") . '>Windows 7 / 2008r2</option>
<option value="wvista" ' . ($plan->ostype == "wvista" ? "selected" : "") . '>Windows Vista / 2008</option>
<option value="wxp" ' . ($plan->ostype == "wxp" ? "selected" : "") . '>Windows XP / 2003</option>
<option value="w2k" ' . ($plan->ostype == "w2k" ? "selected" : "") . '>Windows 2000</option>
<option value="other" ' . ($plan->ostype == "other" ? "selected" : "") . '>Other</option>
</select>
Kernel type (Linux, Windows, etc).
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Emulation</td>
<td class="fieldarea">
<select class="form-control select-inline" name="cpuemu">
<option value="host" ' . ($plan->cpuemu == "host" ? "selected" : "") . '>Host</option>
<option value="kvm32" ' . ($plan->cpuemu == "kvm32" ? "selected" : "") . '>(QEMU) kvm32</option>
<option value="kvm64" ' . ($plan->cpuemu == "kvm64" ? "selected" : "") . '>(QEMU) kvm64</option>
<option value="max" ' . ($plan->cpuemu == "max" ? "selected" : "") . '>(QEMU) Max</option>
<option value="qemu32" ' . ($plan->cpuemu == "qemu32" ? "selected" : "") . '>(QEMU) qemu32</option>
<option value="qemu64" ' . ($plan->cpuemu == "qemu64" ? "selected" : "") . '>(QEMU) qemu64</option>
<option value="x86-64-v2" ' . ($plan->cpuemu == "x86-64-v2" ? "selected" : "") . '>(x86-64 psABI) v2 (Nehalem/Opteron_G3 on)</option>
<option value="x86-64-v2-AES" ' . ($plan->cpuemu == "x86-64-v2-AES" ? "selected" : "") . '>(x86-64 psABI) v2-AES (Westmere/Opteron_G4 on)</option>
<option value="x86-64-v3" ' . ($plan->cpuemu == "x86-64-v3" ? "selected" : "") . '>(x86-64 psABI) v3 (Broadwell/EPYC on)</option>
<option value="x86-64-v4" ' . ($plan->cpuemu == "x86-64-v4" ? "selected" : "") . '>(x86-64 psABI) v4 (Skylake/EPYCv4 on)</option>
<option value="486" ' . ($plan->cpuemu == "486" ? "selected" : "") . '>(Intel) 486</option>
<option value="Broadwell" ' . ($plan->cpuemu == "Broadwell" ? "selected" : "") . '>(Intel) Broadwell</option>
<option value="Broadwell-IBRS" ' . ($plan->cpuemu == "Broadwell-IBRS" ? "selected" : "") . '>(Intel) Broadwell-IBRS</option>
<option value="Broadwell-noTSX" ' . ($plan->cpuemu == "Broadwell-noTSX" ? "selected" : "") . '>(Intel) Broadwell-noTSX</option>
<option value="Broadwell-noTSX-IBRS" ' . ($plan->cpuemu == "Broadwell-noTSX-IBRS" ? "selected" : "") . '>(Intel) Broadwell-noTSX-IBRS</option>
<option value="Cascadelake-Server" ' . ($plan->cpuemu == "Cascadelake-Server" ? "selected" : "") . '>(Intel) Cascadelake-Server</option>
<option value="Cascadelake-Server-noTSX" ' . ($plan->cpuemu == "Cascadelake-Server-noTSX" ? "selected" : "") . '>(Intel) Cascadelake-Server-noTSX</option>
<option value="Cascadelake-Server-v2" ' . ($plan->cpuemu == "Cascadelake-Server-v2" ? "selected" : "") . '>(Intel) Cascadelake-Server V2</option>
<option value="Cascadelake-Server-v4" ' . ($plan->cpuemu == "Cascadelake-Server-v4" ? "selected" : "") . '>(Intel) Cascadelake-Server V4</option>
<option value="Cascadelake-Server-v5" ' . ($plan->cpuemu == "Cascadelake-Server-v5" ? "selected" : "") . '>(Intel) Cascadelake-Server V5</option>
<option value="Conroe" ' . ($plan->cpuemu == "Conroe" ? "selected" : "") . '>(Intel) Conroe</option>
<option value="Cooperlake" ' . ($plan->cpuemu == "Cooperlake" ? "selected" : "") . '>(Intel) Cooperlake</option>
<option value="Cooperlake-v2" ' . ($plan->cpuemu == "Cooperlake-v2" ? "selected" : "") . '>(Intel) Cooperlake V2</option>
<option value="Haswell" ' . ($plan->cpuemu == "Haswell" ? "selected" : "") . '>(Intel) Haswell</option>
<option value="Haswell-IBRS" ' . ($plan->cpuemu == "Haswell-IBRS" ? "selected" : "") . '>(Intel) Haswell-IBRS</option>
<option value="Haswell-noTSX" ' . ($plan->cpuemu == "Haswell-noTSX" ? "selected" : "") . '>(Intel) Haswell-noTSX</option>
<option value="Haswell-noTSX-IBRS" ' . ($plan->cpuemu == "Haswell-noTSX-IBRS" ? "selected" : "") . '>(Intel) Haswell-noTSX-IBRS</option>
<option value="Icelake-Client" ' . ($plan->cpuemu == "Icelake-Client" ? "selected" : "") . '>(Intel) Icelake-Client</option>
<option value="Icelake-Client-noTSX" ' . ($plan->cpuemu == "Icelake-Client-noTSX" ? "selected" : "") . '>(Intel) Icelake-Client-noTSX</option>
<option value="Icelake-Server" ' . ($plan->cpuemu == "Icelake-Server" ? "selected" : "") . '>(Intel) Icelake-Server</option>
<option value="Icelake-Server-noTSX" ' . ($plan->cpuemu == "Icelake-Server-noTSX" ? "selected" : "") . '>(Intel) Icelake-Server-noTSX</option>
<option value="Icelake-Server-v3" ' . ($plan->cpuemu == "Icelake-Server-v3" ? "selected" : "") . '>(Intel) Icelake-Server V3</option>
<option value="Icelake-Server-v4" ' . ($plan->cpuemu == "Icelake-Server-v4" ? "selected" : "") . '>(Intel) Icelake-Server V4</option>
<option value="Icelake-Server-v5" ' . ($plan->cpuemu == "Icelake-Server-v5" ? "selected" : "") . '>(Intel) Icelake-Server V5</option>
<option value="Icelake-Server-v6" ' . ($plan->cpuemu == "Icelake-Server-v6" ? "selected" : "") . '>(Intel) Icelake-Server V6</option>
<option value="IvyBridge" ' . ($plan->cpuemu == "IvyBridge" ? "selected" : "") . '>(Intel) IvyBridge</option>
<option value="IvyBridge-IBRS" ' . ($plan->cpuemu == "IvyBridge-IBRS" ? "selected" : "") . '>(Intel) IvyBridge-IBRS</option>
<option value="KnightsMill" ' . ($plan->cpuemu == "KnightsMill" ? "selected" : "") . '>(Intel) KnightsMill</option>
<option value="Nehalem" ' . ($plan->cpuemu == "Nehalem" ? "selected" : "") . '>(Intel) Nehalem</option>
<option value="Nehalem-IBRS" ' . ($plan->cpuemu == "Nehalem-IBRS" ? "selected" : "") . '>(Intel) Nehalem-IBRS</option>
<option value="Penryn" ' . ($plan->cpuemu == "Penryn" ? "selected" : "") . '>(Intel) Penryn</option>
<option value="SandyBridge" ' . ($plan->cpuemu == "SandyBridge" ? "selected" : "") . '>(Intel) SandyBridge</option>
<option value="SandyBridge-IBRS" ' . ($plan->cpuemu == "SandyBridge-IBRS" ? "selected" : "") . '>(Intel) SandyBridge-IBRS</option>
<option value="SapphireRapids" ' . ($plan->cpuemu == "SapphireRapids" ? "selected" : "") . '>(Intel) Sapphire Rapids</option>
<option value="Skylake-Client" ' . ($plan->cpuemu == "Skylake-Client" ? "selected" : "") . '>(Intel) Skylake-Client</option>
<option value="Skylake-Client-IBRS" ' . ($plan->cpuemu == "Skylake-Client-IBRS" ? "selected" : "") . '>(Intel) Skylake-Client-IBRS</option>
<option value="Skylake-Client-noTSX-IBRS" ' . ($plan->cpuemu == "Skylake-Client-noTSX-IBRS" ? "selected" : "") . '>(Intel) Skylake-Client-noTSX-IBRS</option>
<option value="Skylake-Client-v4" ' . ($plan->cpuemu == "Skylake-Client-v4" ? "selected" : "") . '>(Intel) Skylake-Client V4</option>
<option value="Skylake-Server" ' . ($plan->cpuemu == "Skylake-Server" ? "selected" : "") . '>(Intel) Skylake-Server</option>
<option value="Skylake-Server-IBRS" ' . ($plan->cpuemu == "Skylake-Server-IBRS" ? "selected" : "") . '>(Intel) Skylake-Server-IBRS</option>
<option value="Skylake-Server-noTSX-IBRS" ' . ($plan->cpuemu == "Skylake-Server-noTSX-IBRS" ? "selected" : "") . '>(Intel) Skylake-Server-noTSX-IBRS</option>
<option value="Skylake-Server-v4" ' . ($plan->cpuemu == "Skylake-Server-v4" ? "selected" : "") . '>(Intel) Skylake-Server V4</option>
<option value="Skylake-Server-v5" ' . ($plan->cpuemu == "Skylake-Server-v5" ? "selected" : "") . '>(Intel) Skylake-Server V5</option>
<option value="Westmere" ' . ($plan->cpuemu == "Westmere" ? "selected" : "") . '>(Intel) Westmere</option>
<option value="Westmere-IBRS" ' . ($plan->cpuemu == "Westmere-IBRS" ? "selected" : "") . '>(Intel) Westmere-IBRS</option>
<option value="pentium" ' . ($plan->cpuemu == "pentium" ? "selected" : "") . '>(Intel) Pentium I</option>
<option value="pentium2" ' . ($plan->cpuemu == "pentium2" ? "selected" : "") . '>(Intel) Pentium II</option>
<option value="pentium3" ' . ($plan->cpuemu == "pentium3" ? "selected" : "") . '>(Intel) Pentium III</option>
<option value="coreduo" ' . ($plan->cpuemu == "coreduo" ? "selected" : "") . '>(Intel) Core Duo</option>
<option value="core2duo" ' . ($plan->cpuemu == "core2duo" ? "selected" : "") . '>(Intel) Core 2 Duo</option>
<option value="athlon" ' . ($plan->cpuemu == "athlon" ? "selected" : "") . '>(AMD) Athlon</option>
<option value="phenom" ' . ($plan->cpuemu == "phenom" ? "selected" : "") . '>(AMD) Phenom</option>
<option value="EPYC" ' . ($plan->cpuemu == "EPYC" ? "selected" : "") . '>(AMD) EPYC</option>
<option value="EPYC-IBPB" ' . ($plan->cpuemu == "EPYC-IBPB" ? "selected" : "") . '>(AMD) EPYC-IBPB</option>
<option value="EPYC-Milan" ' . ($plan->cpuemu == "EPYC-Milan" ? "selected" : "") . '>(AMD) EPYC-Milan</option>
<option value="EPYC-Milan-v2" ' . ($plan->cpuemu == "EPYC-Milan-v2" ? "selected" : "") . '>(AMD) EPYC-Milan-v2</option>
<option value="EPYC-Rome" ' . ($plan->cpuemu == "EPYC-Rome" ? "selected" : "") . '>(AMD) EPYC-Rome</option>
<option value="EPYC-Rome-v2" ' . ($plan->cpuemu == "EPYC-Rome-v2" ? "selected" : "") . '>(AMD) EPYC-Rome-v2</option>
<option value="EPYC-v3" ' . ($plan->cpuemu == "EPYC-v3" ? "selected" : "") . '>(AMD) EPYC-v3</option>
<option value="Opteron_G1" ' . ($plan->cpuemu == "Opteron_G1" ? "selected" : "") . '>(AMD) Opteron_G1</option>
<option value="Opteron_G2" ' . ($plan->cpuemu == "Opteron_G2" ? "selected" : "") . '>(AMD) Opteron_G2</option>
<option value="Opteron_G3" ' . ($plan->cpuemu == "Opteron_G3" ? "selected" : "") . '>(AMD) Opteron_G3</option>
<option value="Opteron_G4" ' . ($plan->cpuemu == "Opteron_G4" ? "selected" : "") . '>(AMD) Opteron_G4</option>
<option value="Opteron_G5" ' . ($plan->cpuemu == "Opteron_G5" ? "selected" : "") . '>(AMD) Opteron_G5</option>
</select>
Host is best. Read the <a href="https://pve.proxmox.com/pve-docs/pve-admin-guide.html#_qemu_vcpu_list" target="_blank" style="color:#5c3d7a;"><u>Docs</u></a>.
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Sockets</td>
<td class="fieldarea">
<input type="text" size="8" name="cpus" id="cpus" value="' . $plan->cpus . '" required>
The number of CPU Sockets (typically 1-4). Governed by your physical Server.
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Cores</td>
<td class="fieldarea">
<input type="text" size="8" name="cores" id="cores" value="' . $plan->cores . '" required>
The number of CPU Cores per Socket (1-N). Guest Compute = allocated Sockets * Cores.
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Limit</td>
<td class="fieldarea">
<input type="text" size="8" name="cpulimit" id="cpulimit" value="' . $plan->cpulimit . '" required>
Limit of CPU usage. Note if the computer has 2 CPUs, it has total of "2" CPU time. Value "0" indicates no CPU limit.
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Weighting</td>
<td class="fieldarea">
<input type="text" size="8" name="cpuunits" id="cpuunits" value="' . $plan->cpuunits . '" required>
Number is relative to weights of all the other running VMs. 8 - 500000 recommended 1024. Disable fair-scheduler by setting this to 0.
</td>
</tr>
<tr>
<td class="fieldlabel">RAM - Memory</td>
<td class="fieldarea">
<input type="text" size="8" name="memory" id="memory" required value="' . $plan->memory . '">
RAM capacity in Megabytes eg. 1024 = 1GB
</td>
</tr>
<tr>
<td class="fieldlabel">RAM - Balloon</td>
<td class="fieldarea">
<input type="text" size="8" name="balloon" id="balloon" required value="' . $plan->balloon . '">
Balloon capacity in Megabytes eg. 1024 = 1GB (0 = disabled)
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - Capacity</td>
<td class="fieldarea">
<input type="text" size="8" name="disk" id="disk" required value="' . $plan->disk . '">
HDD/SSD storage in Gigabytes eg. 1024 = 1TB
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - Format</td>
<td class="fieldarea">
<select class="form-control select-inline" name="diskformat">
<option value="raw" ' . ($plan->diskformat == "raw" ? "selected" : "") . '>Disk Image (raw)</option>
<option value="qcow2" ' . ($plan->diskformat == "qcow2" ? "selected" : "") . '>QEMU image (qcow2)</option>
<option value="vmdk" ' . ($plan->diskformat == "vmdk" ? "selected" : "") . '>VMware image (vmdk)</option>
</select>
Recommend "QEMU/qcow2 format" (supports Snapshots)
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - Cache</td>
<td class="fieldarea">
<select class="form-control select-inline" name="diskcache">
<option value="none" ' . ($plan->diskcache == "none" ? "selected" : "") . '>No Cache</option>
<option value="directsync" ' . ($plan->diskcache == "directsync" ? "selected" : "") . '>Direct Sync</option>
<option value="writethrough" ' . ($plan->diskcache == "writethrough" ? "selected" : "") . '>Write Through</option>
<option value="writeback" ' . ($plan->diskcache == "writeback" ? "selected" : "") . '>Write Back</option>
<option value="unsafe" ' . ($plan->diskcache == "unsafe" ? "selected" : "") . '>Write Back (Unsafe)</option>
</select>
Before overriding the default, read &amp; understand the <a href="https://pve.proxmox.com/wiki/Performance_Tweaks#Disk_Cache" target="_blank" style="color:#5c3d7a;"><u>Docs</u></a>.
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - Type</td>
<td class="fieldarea">
<select class="form-control select-inline" name="disktype">
<option value="virtio" ' . ($plan->disktype == "virtio" ? "selected" : "") . '>Virtio</option>
<option value="scsi" ' . ($plan->disktype == "scsi" ? "selected" : "") . '>SCSI</option>
<option value="sata" ' . ($plan->disktype == "sata" ? "selected" : "") . '>SATA</option>
<option value="ide" ' . ($plan->disktype == "ide" ? "selected" : "") . '>IDE</option>
</select>
Virtio is the fastest option, then SCSI, then SATA, etc.
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - I/O Cap</td>
<td class="fieldarea">
<input type="text" size="8" name="diskio" id="diskio" required value="' . $plan->diskio . '">
Limit of Disk I/O in KiB/s. 0 for unrestricted storage access for Guests.
</td>
</tr>
<tr>
<td class="fieldlabel">PVE Store - Name</td>
<td class="fieldarea">
<input type="text" size="8" name="storage" id="storage" required value="' . $plan->storage . '">
Name of VM/CT Storage on Proxmox VE hypervisor. <code>local/local-lvm/etc</code>
</td>
</tr>
<tr>
<td class="fieldlabel">Network - NIC Type</td>
<td class="fieldarea">
<select class="form-control select-inline" name="netmodel">
<option value="virtio" ' . ($plan->netmodel == "virtio" ? "selected" : "") . '>VirtIO (Paravirtualised)</option>
<option value="e1000" ' . ($plan->netmodel == "e1000" ? "selected" : "") . '>Intel E1000 (Stable)</option>
<option value="rtl8139" ' . ($plan->netmodel == "rtl8139" ? "selected" : "") . '>Realtek RTL8139</option>
<option value="vmxnet3" ' . ($plan->netmodel == "vmxnet3" ? "selected" : "") . '>VMware vmxnet3</option>
</select>
Recommend VirtIO, unless you need others.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Rate</td>
<td class="fieldarea">
<input type="text" size="8" name="netrate" id="netrate" value="' . $plan->netrate . '">
Network Rate Limit in Megabits/Second. Zero for unlimited.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Cap</td>
<td class="fieldarea">
<input type="text" size="8" name="bw" id="bw" value="' . $plan->bw . '">
Monthly Data Transfer Cap in Gigabytes. Blank for unlimited.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - IPv6</td>
<td class="fieldarea">
<select class="form-control select-inline" name="ipv6">
<option value="0" ' . ($plan->ipv6 == "0" ? "selected" : "") . '>Off</option>
<option value="auto" ' . ($plan->ipv6 == "auto" ? "selected" : "") . '>SLAAC</option>
<option value="dhcp" ' . ($plan->ipv6 == "dhcp" ? "selected" : "") . '>DHCPv6</option>
<option value="prefix" ' . ($plan->ipv6 == "prefix" ? "selected" : "") . '>Prefix</option>
</select>
SLAAC & DHCPv6 working. Prefix in future.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Mode</td>
<td class="fieldarea">
<select class="form-control select-inline" name="netmode">
<option value="bridge" ' . ($plan->netmode == "bridge" ? "selected" : "") . '>Bridge</option>
<option value="nat" ' . ($plan->netmode == "nat" ? "selected" : "") . '>NAT</option>
<option value="none" ' . ($plan->netmode == "none" ? "selected" : "") . '>No network</option>
</select>
Bridge, NAT or disconnect (no link) the Guest.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Interface</td>
<td class="fieldarea">
<input type="text" size="8" name="bridge" id="bridge" value="' . $plan->bridge . '">
Network / Bridge / NIC name. PVE default bridge prefix is "vmbr".
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Bridge/NIC ID</td>
<td class="fieldarea">
<input type="text" size="8" name="vmbr" id="vmbr" value="' . $plan->vmbr . '">
Interface ID. PVE Bridge default is 0, for "vmbr0". PVE SDN, leave blank.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - VLAN ID</td>
<td class="fieldarea">
<input type="text" size="8" name="vlanid" id="vlanid">
VLAN ID for Plan Services. Default forgoes tagging (VLAN ID), blank for untagged.
</td>
</tr>
<tr>
<td class="fieldlabel">
Hardware Virt?
</td>
<td class="fieldarea">
<label class="checkbox-inline">
<input type="checkbox" name="kvm" value="1" ' . ($plan->kvm == "1" ? "checked" : "") . '> Enable KVM hardware virtualisation. Requires support/enablement in BIOS. (Recommended)
</label>
</td>
</tr>
<tr>
<td class="fieldlabel">
On-boot VM?
</td>
<td class="fieldarea">
<label class="checkbox-inline">
<input type="checkbox" name="onboot" value="1" ' . ($plan->onboot == "1" ? "checked" : "") . '> Specifies whether a VM will be started during hypervisor boot-up. (Recommended)
</label>
</td>
</tr>
</table>
<div class="btn-container">
<input type="submit" class="btn btn-primary" value="Save Changes" name="plan_update_qemu" id="plan_update_qemu">
<input type="reset" class="btn btn-default" value="Cancel Changes">
</div>
</form>
';
}
// MODULE FORM: Add an LXC Plan
function lxc_plan_add() {
echo '
<form method="post">
<table class="form" border="0" cellpadding="3" cellspacing="1" width="100%">
<tr>
<td class="fieldlabel">Plan Title</td>
<td class="fieldarea">
<input type="text" size="35" name="title" id="title" required>
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Limit</td>
<td class="fieldarea">
<input type="text" size="8" name="cpulimit" id="cpulimit" value="1" required>
Limit of CPU usage. Default is 1. If the computer has 2 CPUs, it has total of "2" CPU time. Value "0" indicates no CPU limit.
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Weighting</td>
<td class="fieldarea">
<input type="text" size="8" name="cpuunits" id="cpuunits" value="1024" required>
Number is relative to weights of all the other running VMs. 8 - 500000, recommend 1024.
</td>
</tr>
<tr>
<td class="fieldlabel">RAM - Memory</td>
<td class="fieldarea">
<input type="text" size="8" name="memory" id="memory" required>
RAM capacity in Megabytes eg. 1024 = 1GB
</td>
</tr>
<tr>
<td class="fieldlabel">Swap - Space</td>
<td class="fieldarea">
<input type="text" size="8" name="swap" id="swap">
Swap capacity in Megabytes eg. 1024 = 1GB
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - Capacity</td>
<td class="fieldarea">
<input type="text" size="8" name="disk" id="disk" required>
HDD/SSD storage in Gigabytes eg. 1024 = 1TB
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - I/O Cap</td>
<td class="fieldarea">
<input type="text" size="8" name="diskio" id="diskio" value="0" required>
Limit of Disk I/O in KiB/s. 0 for unrestricted storage access for Guests.
</td>
</tr>
<tr>
<td class="fieldlabel">PVE Store - Name</td>
<td class="fieldarea">
<input type="text" size="8" name="storage" id="storage" value="local" required>
Name of VM/CT Storage on Proxmox VE hypervisor. <code>local/local-lvm/etc</code>
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Interface</td>
<td class="fieldarea">
<input type="text" size="8" name="bridge" id="bridge" value="vmbr">
Network / Bridge / NIC name. PVE default bridge prefix is "vmbr".
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Bridge/NIC ID</td>
<td class="fieldarea">
<input type="text" size="8" name="vmbr" id="vmbr" value="0">
Interface ID. PVE Bridge default is 0, for "vmbr0". PVE SDN, leave blank.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - VLAN ID</td>
<td class="fieldarea">
<input type="text" size="8" name="vlanid" id="vlanid">
VLAN ID for Plan Services. Default forgoes tagging (VLAN ID), blank for untagged.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Rate</td>
<td class="fieldarea">
<input type="text" size="8" name="netrate" id="netrate" value="0">
Network Rate Limit in Megabits/Second. Zero for unlimited.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Cap</td>
<td class="fieldarea">
<input type="text" size="8" name="bw" id="bw">
Monthly Data Transfer Cap in Gigabytes. Blank for unlimited.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - IPv6</td>
<td class="fieldarea">
<select class="form-control select-inline" name="ipv6">
<option value="0">Off</option>
<option value="auto">SLAAC</option>
<option value="dhcp">DHCPv6</option>
<option value="prefix">Prefix</option>
</select>
SLAAC & DHCPv6 working. Prefix in future.
</td>
</tr>
<tr>
<td class="fieldlabel">
On-boot CT?
</td>
<td class="fieldarea">
<label class="checkbox-inline">
<input type="checkbox" name="onboot" value="1" checked> Specifies whether a CT will be started during hypervisor boot-up. (Recommended)
</label>
</td>
</tr>
<tr>
<td class="fieldlabel">
Unpriv.
</td>
<td class="fieldarea">
<label class="checkbox-inline">
<input type="checkbox" name="unpriv" value="0"> Specifies whether a CT will be unprivileged. (Recommended) <strong>Set at-create only!</strong>
</label>
</td>
</tr>
</table>
<div class="btn-container">
<input type="submit" class="btn btn-primary" value="Save Changes" name="plan_save_lxc" id="plan_save_lxc">
<input type="reset" class="btn btn-default" value="Cancel Changes">
</div>
</form>
';
}
// MODULE FORM: Edit an LXC Plan
function lxc_plan_edit($id) {
$plan= Capsule::table('mod_pvewhmcs_plans')->where('id', '=', $id)->get()[0];
if (empty($plan)) {
echo 'Plan Not found' ;
return false ;
}
echo '
<form method="post">
<table class="form" border="0" cellpadding="3" cellspacing="1" width="100%">
<tr>
<td class="fieldlabel">Plan Title</td>
<td class="fieldarea">
<input type="text" size="35" name="title" id="title" required value="' . $plan->title . '">
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Limit</td>
<td class="fieldarea">
<input type="text" size="8" name="cpulimit" id="cpulimit" value="' . $plan->cpulimit . '" required>
Limit of CPU usage. Default is 1. If the computer has 2 CPUs, it has total of "2" CPU time. Value "0" indicates no CPU limit.
</td>
</tr>
<tr>
<td class="fieldlabel">CPU - Weighting</td>
<td class="fieldarea">
<input type="text" size="8" name="cpuunits" id="cpuunits" value="' . $plan->cpuunits . '" required>
Number is relative to weights of all the other running VMs. 8 - 500000, recommend 1024.
</td>
</tr>
<tr>
<td class="fieldlabel">RAM - Memory</td>
<td class="fieldarea">
<input type="text" size="8" name="memory" id="memory" required value="' . $plan->memory . '">
RAM capacity in Megabytes eg. 1024 = 1GB
</td>
</tr>
<tr>
<td class="fieldlabel">Swap - Space</td>
<td class="fieldarea">
<input type="text" size="8" name="swap" id="swap" value="' . $plan->swap . '">
Swap capacity in Megabytes eg. 1024 = 1GB
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - Capacity</td>
<td class="fieldarea">
<input type="text" size="8" name="disk" id="disk" value="' . $plan->disk . '" required>
HDD/SSD storage in Gigabytes eg. 1024 = 1TB
</td>
</tr>
<tr>
<td class="fieldlabel">Disk - I/O Cap</td>
<td class="fieldarea">
<input type="text" size="8" name="diskio" id="diskio" value="' . $plan->diskio . '" required>
Limit of Disk I/O in KiB/s. 0 for unrestricted storage access for Guests.
</td>
</tr>
<tr>
<td class="fieldlabel">PVE Store - Name</td>
<td class="fieldarea">
<input type="text" size="8" name="storage" id="storage" value="' . $plan->storage . '" required>
Name of VM/CT Storage on Proxmox VE hypervisor. <code>local/local-lvm/etc</code>
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Interface</td>
<td class="fieldarea">
<input type="text" size="8" name="bridge" id="bridge" value="' . $plan->bridge . '">
Network / Bridge / NIC name. PVE default bridge prefix is "vmbr".
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Bridge/NIC ID</td>
<td class="fieldarea">
<input type="text" size="8" name="vmbr" id="vmbr" value="' . $plan->vmbr . '">
Interface ID. PVE Bridge default is 0, for "vmbr0". PVE SDN, leave blank.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - VLAN ID</td>
<td class="fieldarea">
<input type="text" size="8" name="vlanid" id="vlanid">
VLAN ID for Plan Services. Default forgoes tagging (VLAN ID), blank for untagged.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Rate</td>
<td class="fieldarea">
<input type="text" size="8" name="netrate" id="netrate" value="' . $plan->netrate . '">
Network Rate Limit in Megabits/Second. Zero for unlimited.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - Cap</td>
<td class="fieldarea">
<input type="text" size="8" name="bw" id="bw" value="' . $plan->bw . '">
Monthly Data Transfer Cap in Gigabytes. Blank for unlimited.
</td>
</tr>
<tr>
<td class="fieldlabel">Network - IPv6</td>
<td class="fieldarea">
<select class="form-control select-inline" name="ipv6">
<option value="0" ' . ($plan->ipv6 == "0" ? "selected" : "") . '>Off</option>
<option value="auto" ' . ($plan->ipv6 == "auto" ? "selected" : "") . '>SLAAC</option>
<option value="dhcp" ' . ($plan->ipv6 == "dhcp" ? "selected" : "") . '>DHCPv6</option>
<option value="prefix" ' . ($plan->ipv6 == "prefix" ? "selected" : "") . '>Prefix</option>
</select>
SLAAC & DHCPv6 working. Prefix in future.
</td>
</tr>
<tr>
<td class="fieldlabel">
On-boot CT?
</td>
<td class="fieldarea">
<label class="checkbox-inline">
<input type="checkbox" value="1" name="onboot" ' . ($plan->onboot == "1" ? "checked" : "") . '> Specifies whether a CT will be started during hypervisor boot-up. (Recommended)
</label>
</td>
</tr>
<tr>
<td class="fieldlabel">
Unpriv.
</td>
<td class="fieldarea">
<label class="checkbox-inline">
<input type="checkbox" value="1" name="unpriv" ' . ($plan->unpriv == "1" ? "checked" : "") . '> Specifies whether a CT will be unprivileged. (Recommended) <strong>Set at-create only!</strong>
</label>
</td>
</tr>
</table>
<div class="btn-container">
<input type="submit" class="btn btn-primary" value="Save Changes" name="plan_update_lxc" id="plan_update_lxc">
<input type="reset" class="btn btn-default" value="Cancel Changes">
</div>
</form>
';
}
// MODULE FORM ACTION: Save QEMU Plan
function save_qemu_plan() {
try {
Capsule::connection()->transaction(
function ($connectionManager)
{
/** @var \Illuminate\Database\Connection $connectionManager */
$connectionManager->table('mod_pvewhmcs_plans')->insert(
[
'title' => $_POST['title'],
'vmtype' => 'kvm',
'ostype' => $_POST['ostype'],
'cpus' => $_POST['cpus'],
'cpuemu' => $_POST['cpuemu'],
'cores' => $_POST['cores'],
'cpulimit' => $_POST['cpulimit'],
'cpuunits' => $_POST['cpuunits'],
'memory' => $_POST['memory'],
'balloon' => $_POST['balloon'],
'disk' => $_POST['disk'],
'diskformat' => $_POST['diskformat'],
'diskcache' => $_POST['diskcache'],
'disktype' => $_POST['disktype'],
'diskio' => $_POST['diskio'],
'storage' => $_POST['storage'],
'netmode' => $_POST['netmode'],
'bridge' => $_POST['bridge'],
'vmbr' => $_POST['vmbr'],
'netmodel' => $_POST['netmodel'],
'vlanid' => $_POST['vlanid'],
'netrate' => $_POST['netrate'],
'bw' => $_POST['bw'],
'ipv6' => $_POST['ipv6'],
'kvm' => $_POST['kvm'],
'onboot' => $_POST['onboot'],
]
);
}
);
$_SESSION['pvewhmcs']['infomsg']['title']='QEMU Plan added.' ;
$_SESSION['pvewhmcs']['infomsg']['message']='Saved the QEMU Plan successfully.' ;
header("Location: ".pvewhmcs_BASEURL."&tab=vmplans&action=planlist");
} catch (\Exception $e) {
echo "Uh oh! Inserting didn't work, but I was able to rollback. {$e->getMessage()}";
}
}
// MODULE FORM ACTION: Update QEMU Plan
function update_qemu_plan() {
Capsule::table('mod_pvewhmcs_plans')
->where('id', $_GET['id'])
->update(
[
'title' => $_POST['title'],
'vmtype' => 'kvm',
'ostype' => $_POST['ostype'],
'cpus' => $_POST['cpus'],
'cpuemu' => $_POST['cpuemu'],
'cores' => $_POST['cores'],
'cpulimit' => $_POST['cpulimit'],
'cpuunits' => $_POST['cpuunits'],
'memory' => $_POST['memory'],
'balloon' => $_POST['balloon'],
'disk' => $_POST['disk'],
'diskformat' => $_POST['diskformat'],
'diskcache' => $_POST['diskcache'],
'disktype' => $_POST['disktype'],
'diskio' => $_POST['diskio'],
'storage' => $_POST['storage'],
'netmode' => $_POST['netmode'],
'bridge' => $_POST['bridge'],
'vmbr' => $_POST['vmbr'],
'netmodel' => $_POST['netmodel'],
'vlanid' => $_POST['vlanid'],
'netrate' => $_POST['netrate'],
'bw' => $_POST['bw'],
'ipv6' => $_POST['ipv6'],
'kvm' => $_POST['kvm'],
'onboot' => $_POST['onboot'],
]
);
$_SESSION['pvewhmcs']['infomsg']['title']='QEMU Plan updated.' ;
$_SESSION['pvewhmcs']['infomsg']['message']='Updated the QEMU Plan successfully. (Updating plans will not alter existing VMs)' ;
header("Location: ".pvewhmcs_BASEURL."&tab=vmplans&action=planlist");
}
// MODULE FORM ACTION: Remove Plan
function remove_plan($id) {
Capsule::table('mod_pvewhmcs_plans')->where('id', '=', $id)->delete();
header("Location: ".pvewhmcs_BASEURL."&tab=vmplans&action=planlist");
$_SESSION['pvewhmcs']['infomsg']['title']='Plan Deleted.' ;
$_SESSION['pvewhmcs']['infomsg']['message']='Selected Item deleted successfully.' ;
}
// MODULE FORM ACTION: Save LXC Plan
function save_lxc_plan() {
try {
Capsule::connection()->transaction(
function ($connectionManager)
{
/** @var \Illuminate\Database\Connection $connectionManager */
$connectionManager->table('mod_pvewhmcs_plans')->insert(
[
'title' => $_POST['title'],
'vmtype' => 'lxc',
'cores' => $_POST['cores'],
'cpulimit' => $_POST['cpulimit'],
'cpuunits' => $_POST['cpuunits'],
'memory' => $_POST['memory'],
'swap' => $_POST['swap'],
'disk' => $_POST['disk'],
'diskio' => $_POST['diskio'],
'storage' => $_POST['storage'],
'bridge' => $_POST['bridge'],
'vmbr' => $_POST['vmbr'],
'netmodel' => $_POST['netmodel'],
'vlanid' => $_POST['vlanid'],
'netrate' => $_POST['netrate'],
'bw' => $_POST['bw'],
'ipv6' => $_POST['ipv6'],
'onboot' => $_POST['onboot'],
'unpriv' => $_POST['unpriv'],
]
);
}
);
$_SESSION['pvewhmcs']['infomsg']['title']='New LXC Plan added.' ;
$_SESSION['pvewhmcs']['infomsg']['message']='Saved the LXC Plan successfully.' ;
header("Location: ".pvewhmcs_BASEURL."&tab=vmplans&action=planlist");
} catch (\Exception $e) {
echo "Uh oh! Inserting didn't work, but I was able to rollback. {$e->getMessage()}";
}
}
// MODULE FORM ACTION: Update LXC Plan
function update_lxc_plan() {
Capsule::table('mod_pvewhmcs_plans')
->where('id', $_GET['id'])
->update(
[
'title' => $_POST['title'],
'vmtype' => 'lxc',
'cores' => $_POST['cores'],
'cpulimit' => $_POST['cpulimit'],
'cpuunits' => $_POST['cpuunits'],
'memory' => $_POST['memory'],
'swap' => $_POST['swap'],
'disk' => $_POST['disk'],
'diskio' => $_POST['diskio'],
'storage' => $_POST['storage'],
'bridge' => $_POST['bridge'],
'vmbr' => $_POST['vmbr'],
'netmodel' => $_POST['netmodel'],
'vlanid' => $_POST['vlanid'],
'netrate' => $_POST['netrate'],
'bw' => $_POST['bw'],
'ipv6' => $_POST['ipv6'],
'onboot' => $_POST['onboot'],
'unpriv' => $_POST['unpriv'],
]
);
$_SESSION['pvewhmcs']['infomsg']['title']='LXC Plan updated.' ;
$_SESSION['pvewhmcs']['infomsg']['message']='Updated the LXC Plan successfully. (Updating plans will not alter existing CTs)' ;
header("Location: ".pvewhmcs_BASEURL."&tab=vmplans&action=planlist");
}
// IP POOLS: List all Pools
function list_ip_pools() {
echo '<a class="btn btn-default" href="' . pvewhmcs_BASEURL . '&amp;tab=ippools&amp;action=new_ip_pool"><i class="fa fa-plus-square"></i>&nbsp; New IPv4 Pool</a>';
echo '<table class="datatable"><tr><th>ID</th><th>Pool</th><th>Gateway</th><th>Action</th></tr>';
foreach (Capsule::table('mod_pvewhmcs_ip_pools')->get() as $pool) {
echo '<tr>';
echo '<td>' . $pool->id . '</td>';
echo '<td>' . $pool->title . '</td>';
echo '<td>' . $pool->gateway . '</td>';
echo '<td>
<a href="' . pvewhmcs_BASEURL . '&amp;tab=ippools&amp;action=list_ips&amp;id=' . $pool->id . '"><img height="16" width="16" border="0" alt="Info" src="images/edit.gif"></a>
<a href="' . pvewhmcs_BASEURL . '&amp;tab=ippools&amp;action=removeippool&amp;id=' . $pool->id . '" onclick="return confirm(\'Pool and all IPv4 Addresses assigned to it will be deleted, continue?\')"><img height="16" width="16" border="0" alt="Remove" src="images/delete.gif"></a>
</td>';
echo '</tr>';
}
echo '</table>';
}
// IP POOL FORM: Add IP Pool
function add_ip_pool() {
echo '
<form method="post">
<table class="form" border="0" cellpadding="3" cellspacing="1" width="100%">
<tr>
<td class="fieldlabel">Pool Title</td>
<td class="fieldarea">
<input type="text" size="35" name="title" id="title" required>
</td>
<td class="fieldlabel">IPv4 Gateway</td>
<td class="fieldarea">
<input type="text" size="25" name="gateway" id="gateway" required>
Gateway address of the pool
</td>
</tr>
</table>
<input type="submit" class="btn btn-primary" name="newIPpool" value="Save"/>
</form>
';
}
// IP POOL FORM ACTION: Save Pool
function save_ip_pool() {
try {
Capsule::connection()->transaction(
function ($connectionManager)
{
/** @var \Illuminate\Database\Connection $connectionManager */
$connectionManager->table('mod_pvewhmcs_ip_pools')->insert(
[
'title' => $_POST['title'],
'gateway' => $_POST['gateway'],
]
);
}
);
$_SESSION['pvewhmcs']['infomsg']['title']='New IPv4 Pool added.' ;
$_SESSION['pvewhmcs']['infomsg']['message']='New IPv4 Pool saved successfully.' ;
header("Location: ".pvewhmcs_BASEURL."&tab=ippools&action=list_ip_pools");
} catch (\Exception $e) {
echo "Uh oh! Inserting didn't work, but I was able to rollback. {$e->getMessage()}";
}
}
// IP POOL FORM ACTION: Remove Pool
function removeIpPool($id) {
Capsule::table('mod_pvewhmcs_ip_addresses')->where('pool_id', '=', $id)->delete();
Capsule::table('mod_pvewhmcs_ip_pools')->where('id', '=', $id)->delete();
header("Location: ".pvewhmcs_BASEURL."&tab=ippools&action=list_ip_pools");
$_SESSION['pvewhmcs']['infomsg']['title']='IPv4 Pool Deleted.' ;
$_SESSION['pvewhmcs']['infomsg']['message']='Deleted the IPv4 Pool successfully.' ;
}
// IP POOL FORM ACTION: Add IP to Pool
function add_ip_2_pool() {
require_once(ROOTDIR.'/modules/addons/pvewhmcs/Ipv4/Subnet.php');
echo '<form method="post">
<table class="form" border="0" cellpadding="3" cellspacing="1" width="100%">
<tr>
<td class="fieldlabel">IPv4 Pool</td>
<td class="fieldarea">
<select class="form-control select-inline" name="pool_id">';
foreach (Capsule::table('mod_pvewhmcs_ip_pools')->get() as $pool) {
echo '<option value="' . $pool->id . '">' . $pool->title . '</option>';
$gateways[] = $pool->gateway;
}
echo '</select>
</td>
</tr>
<tr>
<td class="fieldlabel">Address/Prefix</td>
<td class="fieldarea">
<input type="text" name="ipblock"/>
IPv4 prefix with CIDR e.g. 172.16.255.230/27, or for single /32 address don\'t use CIDR
</td>
</tr>
</table>
<input type="submit" name="assignIP2pool" value="Add"/>
</form>';
if (isset($_POST['assignIP2pool'])) {
// check if single IP address
if ((strpos($_POST['ipblock'],'/'))!=false) {
$subnet=Ipv4_Subnet::fromString($_POST['ipblock']);
$ips = $subnet->getIterator();
foreach($ips as $ip) {
if (!in_array($ip, $gateways)) {
Capsule::table('mod_pvewhmcs_ip_addresses')->insert(
[
'pool_id' => $_POST['pool_id'],
'ipaddress' => $ip,
'mask' => $subnet->getNetmask(),
]
);
}
}
}
else {
if (!in_array($_POST['ipblock'], $gateways)) {
Capsule::table('mod_pvewhmcs_ip_addresses')->insert(
[
'pool_id' => $_POST['pool_id'],
'ipaddress' => $_POST['ipblock'],
'mask' => '255.255.255.255',
]
);
}
}
header("Location: " . pvewhmcs_BASEURL . "&tab=ippools&action=list_ips&id=" . $_POST['pool_id']);
$_SESSION['pvewhmcs']['infomsg']['title'] = 'IPv4 Address/Blocks added to Pool.';
$_SESSION['pvewhmcs']['infomsg']['message'] = 'You can remove IPv4 Addresses from the pool.';
}
}
// IP POOL FORM: List IPs in Pool
function list_ips() {
// Determine the WHMCS Admin Directory URL for the link
$adminUrl = 'clientsservices.php';
echo '<table class="datatable">
<tr>
<th>IPv4 Address</th>
<th>Subnet Mask</th>
<th>Action</th>
</tr>';
// Loop through IPs in the pool
foreach (Capsule::table('mod_pvewhmcs_ip_addresses')->where('pool_id', '=', $_GET['id'])->get() as $ip) {
// Query tblhosting to see if this IP is currently "occupied"
// Occupied = assigned to a service that is Active, Suspended, or Completed
$service = Capsule::table('tblhosting')
->where('dedicatedip', '=', $ip->ipaddress)
->whereIn('domainstatus', ['Active', 'Suspended', 'Completed'])
->first();
echo '<tr>
<td>' . $ip->ipaddress . '</td>
<td>' . $ip->mask . '</td>
<td>';
if ($service) {
// IP is in use: Create a link to the related service
$serviceLink = $adminUrl . '?userid=' . $service->userid . '&id=' . $service->id;
echo 'In use: <a href="' . $serviceLink . '" target="_blank">Service #' . $service->id . '</a>';
} else {
// IP is free (not in tblhosting OR status is Terminated/Cancelled/Fraud/Pending)
echo '<a href="' . pvewhmcs_BASEURL . '&amp;tab=ippools&amp;action=removeip&amp;pool_id=' . $ip->pool_id . '&amp;id=' . $ip->id . '" onclick="return confirm(\'IPv4 Address will be deleted from the pool, continue?\')">
<img height="16" width="16" border="0" alt="Edit" src="images/delete.gif">
</a>';
}
echo '</td></tr>';
}
echo '</table>';
}
// IP POOL FORM ACTION: Remove IP from Pool
function removeip($id, $pool_id) {
Capsule::table('mod_pvewhmcs_ip_addresses')->where('id', '=', $id)->delete();
header("Location: " . pvewhmcs_BASEURL . "&tab=ippools&action=list_ips&id=" . $pool_id);
$_SESSION['pvewhmcs']['infomsg']['title'] = 'IPv4 Address deleted.';
$_SESSION['pvewhmcs']['infomsg']['message'] = 'Deleted selected item successfully.';
}
function time2format($s) {
$d = intval( $s / 86400 );
if ($d < '10') {
$d = '0' . $d;
}
$s -= $d * 86400;
$h = intval( $s / 3600 );
if ($h < '10') {
$h = '0' . $h;
}
$s -= $h * 3600;
$m = intval( $s / 60 );
if ($m < '10') {
$m = '0' . $m;
}
$s -= $m * 60;
if ($s < '10') {
$s = '0' . $s;
}
if ($d) {
$str = $d . ' days ';
}
if ($h) {
$str .= $h . ':';
}
if ($m) {
$str .= $m . ':';
}
if ($s) {
$str .= $s . '';
}
return $str;
}
?>