mirror of
https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS.git
synced 2026-04-02 02:28:18 +00:00
* 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>
2399 lines
99 KiB
PHP
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 > Products/Services > 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 .'&tab=vmplans&action=planlist">
|
|
<i class="fa fa-list"></i> List: Guest Plans
|
|
</a>
|
|
<a class="btn btn-default" href="'. pvewhmcs_BASEURL .'&tab=vmplans&action=add_qemu_plan">
|
|
<i class="fa fa-plus-square"></i> Add: QEMU Plan
|
|
</a>
|
|
<a class="btn btn-default" href="'. pvewhmcs_BASEURL .'&tab=vmplans&action=add_lxc_plan">
|
|
<i class="fa fa-plus-square"></i> Add: LXC Plan
|
|
</a>
|
|
<a class="btn btn-default" href="'. pvewhmcs_BASEURL .'&tab=vmplans&action=import_guest">
|
|
<i class="fa fa-upload"></i> 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 . '&tab=vmplans&action=editplan&id=' . $vm->id . '&vmtype=' . $vm->vmtype . '"><img height="16" width="16" border="0" alt="Edit" src="images/edit.gif"></a>
|
|
<a href="' . pvewhmcs_BASEURL . '&tab=vmplans&action=removeplan&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 .'&tab=ippools&action=list_ip_pools">
|
|
<i class="fa fa-list"></i> List: IPv4 Pools
|
|
</a>
|
|
<a class="btn btn-default" href="'. pvewhmcs_BASEURL .'&tab=ippools&action=newip">
|
|
<i class="fa fa-plus"></i> 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;">⚙</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;">♥</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 & improve on!</p>
|
|
<p style="margin:0;">
|
|
<a href="https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/" target="_blank" style="color:#5c3d7a;">➔ 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;">★</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;">★★★★★ 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;">⚙</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;">➔ View Documentation</a>
|
|
</p>
|
|
<p style="margin:0 0 12px 0;font-size:14px;line-height:1.6;">Only raise a GitHub Issue — including logs — 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;">➔ 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;">⚠ 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;">⚙</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 & 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 & 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 . '&tab=ippools&action=new_ip_pool"><i class="fa fa-plus-square"></i> 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 . '&tab=ippools&action=list_ips&id=' . $pool->id . '"><img height="16" width="16" border="0" alt="Info" src="images/edit.gif"></a>
|
|
<a href="' . pvewhmcs_BASEURL . '&tab=ippools&action=removeippool&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 . '&tab=ippools&action=removeip&pool_id=' . $ip->pool_id . '&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;
|
|
}
|
|
?>
|