23 Commits

Author SHA1 Message Date
Luke S Thompson
4fc839db7e ver bump v1.2.7 2025-01-02 11:26:47 +11:00
Luke S Thompson
1bc82e8b85 (SQL) v1.2.7 op to varchar 2025-01-02 11:22:52 +11:00
Luke S Thompson
c9adad77c3 (SQL) v1.2.7 change to varchar 2025-01-02 11:22:22 +11:00
Luke S Thompson
6becf20c73 Add balloon to Plan add/edit (#87) 2025-01-02 11:21:35 +11:00
Luke S Thompson
e909faf97f (SQL) v1.2.7 database schema 2025-01-02 11:13:08 +11:00
Luke S Thompson
ea26e51f04 Tidy-up, comments, etc re #76 2025-01-02 11:09:53 +11:00
Luke S Thompson
8b3e03490c Merge PR #76 from is7Qin/patch-1 (fixes #85 & #95)
Tweaks: Terminate & Reboot functions; Client Area swap graph logic
2025-01-02 10:58:07 +11:00
Luke S Thompson
89537603e3 (SQL) v1.2.7 database ops 2025-01-02 10:44:15 +11:00
Luke S Thompson
221c4efb25 v1.2.7 & future into C'LOG 2025-01-02 10:41:54 +11:00
Luke S Thompson
08b03266b9 More realistic 2025 C'LOG 2024-12-21 17:35:15 +11:00
Luke S Thompson
28b4d63067 Add req for co-dev/mnt for PVEWHMCS 2024-12-21 17:29:29 +11:00
Luke S Thompson
a824b0bd99 Update CHANGELOG.md 2024-09-22 11:41:31 +10:00
Luke S Thompson
2ed27944b6 Datestamp v1.2.6 2024-09-22 11:36:54 +10:00
Luke S Thompson
efea1bf6cc (v1.2.6) final ver bump 2024-09-22 11:34:20 +10:00
Luke S Thompson
d1ed973761 (v1.2.6) ver. bump addon file 2024-09-22 11:33:39 +10:00
Luke S Thompson
8eab861bc3 Remove 'data' parent key re: #83 2024-09-22 10:31:29 +10:00
Luke S Thompson
f3d4cf02f0 Improve task UPID checking logic re: #83 2024-09-22 10:23:01 +10:00
Luke S Thompson
790fde7216 Capture entire UPID re: #83 2024-09-22 10:12:54 +10:00
Luke S Thompson
408e38ba22 Change UPID task check (colon delimited) re: #83 2024-09-22 10:04:55 +10:00
Luke S Thompson
45655a64e6 Split v1.2.6 and v1.2.7 2024-09-22 09:46:41 +10:00
Luke S Thompson
44be43aec5 PHP max_exec requirement 2024-09-22 09:44:32 +10:00
Luke S Thompson
b5f15514db Task ID Checking for #83 - PVE Guest Creation
Untested implementation of checking the `UPID` for a Guest Creation via PVE API, to avoid 30 second API timeout problem. Values can be set (max retries, and seconds between retries) in the file, which is mentioned in a timeout error back to the GUI.
2024-09-22 09:38:19 +10:00
is7Qin
62b980b217 fix: some logic missing 2024-05-31 20:27:04 +08:00
7 changed files with 207 additions and 89 deletions

View File

@@ -1,11 +1,23 @@
# Changelog
All notable changes to Proxmox VE for WHMCS will be documented in this file.
## [1.2.6] - TBC 2024-??-??
## [1.3.0] - TBC 2025-??-??
### 🚀 Feature
- Old VM: Assign client VM where it exists already. (#75)
- SSH Keys: Deploy your keys into a CT via cloud-init. (#74)
https://github.com/The-Network-Crew/Proxmox-VE-for-WHMCS/milestones
## [1.2.7] - 2025-01-02 - _"Terminate Balloons"_
### 💅 Polish
- RAM/Memory Ballooning - Option to disable (#87)
### 🐛 Bug Fix
- Admin Area: Terminate module command not working (#85)
- Client Area GUI: Swap graph not always accurate (#95)
## [1.2.6] - 2024-09-22 - _"Big Kahunas (TPLs)"_
### 🐛 Bug Fix
- Guest Create: Check UPID to avoid long job time-outs (#83)
## [1.2.5] - 2024-08-22 - _"Updates & Updates"_
@@ -14,8 +26,8 @@ All notable changes to Proxmox VE for WHMCS will be documented in this file.
- TigerVNC: Update from v1.13.1 to v1.14.0 (#81)
### 🐛 Bug Fix
- db.sql: Resolve syntax issues, to ensure table/content creation. (#77/#79)
- db.sql: Options table INSERT to INSERT IGNORE (fix upgrade case). (#78)
- db.sql: Resolve syntax issues, to ensure table/content creation (#77/#79)
- db.sql: Options table INSERT to INSERT IGNORE (fix upgrade case) (#78)
## [1.2.4] - 2024-05-19 - _"Fine tuning"_

View File

@@ -2,6 +2,12 @@
**Salvation, a free and open-source solution for beloved PVE!** If you love it, REVIEW & SHARE IT! ❤️
TNC Dev are looking for a co-developer to assist with finishing the project overhaul.
If you have proven and public git-logged experience, or similar, please say g'day.
Please note: We are only looking for high-quality applicants with spare time.
As it stands, we won't have much spare dev time for PVEWHMCS in early 2025.
<img alt="Logo for the Proxmox VE for WHMCS module" src="zLOGO.png">
- Configure VM/CT plans with custom CPU/RAM/VLAN/On-boot/Bandwidth/etc
@@ -31,7 +37,8 @@ New Biz: Fresh Installations/Businesses using WHMCS need to take note of the Ser
- (WHMCS) v8.x.x stable (HTTPS)
- (WHMCS) **Service ID above 100**
- (PHP) v8.x (stable version)
- (PHP) v8.x.x (latest stable version)
- (PHP) max_execution_time = 300
- (Proxmox) VE v8.x (current)
- (Proxmox) 2 users (API/VNC)

View File

@@ -1,13 +1,19 @@
# SQL Statements for Updates (nav to DB first)
### v1.2.3 to v1.2.4
## v1.2.6 to v1.2.7
```
ALTER TABLE mod_pvewhmcs_plans ADD COLUMN `balloon` varchar(10) DEFAULT '0';
```
## v1.2.3 to v1.2.4
```
ALTER TABLE mod_pvewhmcs_vms ADD COLUMN `v6prefix` varchar(128) DEFAULT NULL;
ALTER TABLE mod_pvewhmcs_plans ADD COLUMN `ipv6` varchar(10) DEFAULT 'auto';
```
### v1.2.1 to v1.2.2
## v1.2.1 to v1.2.2
```
ALTER TABLE mod_pvewhmcs ADD COLUMN `debug_mode` tinyint(1) unsigned DEFAULT 0;

View File

@@ -49,6 +49,7 @@ CREATE TABLE IF NOT EXISTS `mod_pvewhmcs_plans` (
`onboot` tinyint(1) unsigned DEFAULT 0,
`vlanid` varchar(10) DEFAULT NULL,
`ipv6` varchar(10) DEFAULT 'auto',
`balloon` varchar(10) DEFAULT '0',
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `mod_pvewhmcs_vms` (

View File

@@ -35,7 +35,7 @@ function pvewhmcs_config() {
$configarray = array(
"name" => "Proxmox VE for WHMCS",
"description" => "Proxmox VE (Virtual Environment) & WHMCS, integrated & open-source! Provisioning & Management of VMs/CTs.".is_pvewhmcs_outdated(),
"version" => "1.2.5",
"version" => "1.2.7",
"author" => "The Network Crew Pty Ltd",
'language' => 'English'
);
@@ -44,7 +44,7 @@ function pvewhmcs_config() {
// VERSION: also stored in repo/version (for update-available checker)
function pvewhmcs_version(){
return "1.2.5";
return "1.2.7";
}
// WHMCS MODULE: ACTIVATION of the ADDON MODULE
@@ -219,6 +219,9 @@ function pvewhmcs_output($vars) {
<th>
RAM
</th>
<th>
Balloon
</th>
<th>
Swap
</th>
@@ -269,6 +272,7 @@ function pvewhmcs_output($vars) {
echo '<td>'.$vm->cpus . PHP_EOL .'</td>';
echo '<td>'.$vm->cores . PHP_EOL .'</td>';
echo '<td>'.$vm->memory . PHP_EOL .'</td>';
echo '<td>'.$vm->balloon . PHP_EOL .'</td>';
echo '<td>'.$vm->swap . PHP_EOL .'</td>';
echo '<td>'.$vm->disk . PHP_EOL .'</td>';
echo '<td>'.$vm->disktype . PHP_EOL .'</td>';
@@ -302,10 +306,10 @@ function pvewhmcs_output($vars) {
<div id="ippools" class="tab-pane '.($_GET['tab']=="ippools" ? "active" : "").'" >
<div class="btn-group">
<a class="btn btn-default" href="'. pvewhmcs_BASEURL .'&amp;tab=ippools&amp;action=list_ip_pools">
<i class="fa fa-list"></i>&nbsp; List: IP Pools
<i class="fa fa-list"></i>&nbsp; List: IPv4 Pools
</a>
<a class="btn btn-default" href="'. pvewhmcs_BASEURL .'&amp;tab=ippools&amp;action=newip">
<i class="fa fa-plus"></i>&nbsp; Add: IP to Pool
<i class="fa fa-plus"></i>&nbsp; Add: IPv4 to Pool
</a>
</div>
';
@@ -569,6 +573,13 @@ function kvm_plan_add() {
RAM space in Megabyte e.g 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 space in Megabyte e.g 1024 = 1GB (0 = disabled)
</td>
</tr>
<tr>
<td class="fieldlabel">SSD/HDD - Disk</td>
<td class="fieldarea">
@@ -878,6 +889,13 @@ function kvm_plan_edit($id) {
RAM space in Megabytes e.g 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 space in Megabyte e.g 1024 = 1GB (0 = disabled)
</td>
</tr>
<tr>
<td class="fieldlabel">SSD/HDD - Disk</td>
<td class="fieldarea">
@@ -1311,6 +1329,7 @@ function save_kvm_plan() {
'cpulimit' => $_POST['cpulimit'],
'cpuunits' => $_POST['cpuunits'],
'memory' => $_POST['memory'],
'balloon' => $_POST['balloon'],
'disk' => $_POST['disk'],
'diskformat' => $_POST['diskformat'],
'diskcache' => $_POST['diskcache'],
@@ -1354,6 +1373,7 @@ function update_kvm_plan() {
'cpulimit' => $_POST['cpulimit'],
'cpuunits' => $_POST['cpuunits'],
'memory' => $_POST['memory'],
'balloon' => $_POST['balloon'],
'disk' => $_POST['disk'],
'diskformat' => $_POST['diskformat'],
'diskcache' => $_POST['diskcache'],

View File

@@ -25,7 +25,7 @@
if (file_exists('../modules/addons/pvewhmcs/proxmox.php'))
require_once('../modules/addons/pvewhmcs/proxmox.php');
else
require_once(ROOTDIR.'/modules/addons/pvewhmcs/proxmox.php');
require_once(ROOTDIR . '/modules/addons/pvewhmcs/proxmox.php');
// Import SQL Connectivity (WHMCS)
use Illuminate\Database\Capsule\Manager as Capsule;
@@ -71,13 +71,13 @@ function pvewhmcs_ConfigOptions() {
// SQL/Param: configoption1 configoption2
$configarray = array(
"Plan" => array(
"FriendlyName" => "Plan",
"FriendlyName" => "PVE Plan",
"Type" => "dropdown",
'Options' => $plans ,
"Description" => "KVM/LXC : Plan Name"
),
"IPPool" => array(
"FriendlyName" => "IP Pool",
"FriendlyName" => "IPv4 Pool",
"Type" => "dropdown",
'Options'=> $ippools,
"Description" => "IPv4 : Allocation Pool"
@@ -95,13 +95,13 @@ function pvewhmcs_CreateAccount($params) {
throw new Exception("PVEWHMCS Error: Missing Config. Service/Product WHMCS Config not saved (Plan/Pool not assigned to WHMCS Service type). Check Support/Health tab in Module Config for info. Quick and easy fix.");
}
if (empty($params['configoption1'])) {
throw new Exception("PVEWHMCS Error: Missing Config. Service/Product WHMCS Config not saved (Plan/Pool not assigned to WHMCS Service type). Check Support/Health tab in Module Config for info. Quick and easy fix.");
throw new Exception("PVEWHMCS Error: Missing Config. Service/Product WHMCS Config not saved (Plan/Pool not assigned to WHMCS Service type). Check Support/Health tab in Module Config for info. Quick and easy fix.");
}
if (empty($params['configoption2'])) {
throw new Exception("PVEWHMCS Error: Missing Config. Service/Product WHMCS Config not saved (Plan/Pool not assigned to WHMCS Service type). Check Support/Health tab in Module Config for info. Quick and easy fix.");
throw new Exception("PVEWHMCS Error: Missing Config. Service/Product WHMCS Config not saved (Plan/Pool not assigned to WHMCS Service type). Check Support/Health tab in Module Config for info. Quick and easy fix.");
}
// Retrieve Plan from table
// Retrieve Plan from table
$plan = Capsule::table('mod_pvewhmcs_plans')->where('id', '=', $params['configoption1'])->get()[0];
// PVE Host - Connection Info
@@ -112,18 +112,17 @@ function pvewhmcs_CreateAccount($params) {
// Prepare the service config array
$vm_settings = array();
// Select an IP Address from Pool
// Select an IP Address from Pool
$ip = Capsule::select('select ipaddress,mask,gateway from mod_pvewhmcs_ip_addresses i INNER JOIN mod_pvewhmcs_ip_pools p on (i.pool_id=p.id and p.id=' . $params['configoption2'] . ') where i.ipaddress not in(select ipaddress from mod_pvewhmcs_vms) limit 1')[0];
////////////////////////
// CREATE IF QEMU/KVM //
// CREATE IF QEMU/KVM //
////////////////////////
if (!empty($params['customfields']['KVMTemplate'])) {
// file_put_contents('d:\log.txt', $params['customfields']['KVMTemplate']);
// KVM TEMPLATE - CREATION LOGIC
$proxmox = new PVE2_API($serverip, $serverusername, "pam", $serverpassword);
if ($proxmox->login()) {
// Get first node name.
// Get first node name.
$nodes = $proxmox->get_node_list();
$first_node = $nodes[0];
unset($nodes);
@@ -133,6 +132,7 @@ function pvewhmcs_CreateAccount($params) {
// KVM TEMPLATE - Conduct the VM CLONE from Template to Machine
$logrequest = '/nodes/' . $first_node . '/qemu/' . $params['customfields']['KVMTemplate'] . '/clone' . $vm_settings;
$response = $proxmox->post('/nodes/' . $first_node . '/qemu/' . $params['customfields']['KVMTemplate'] . '/clone', $vm_settings);
// DEBUG - Log the request parameters before it's fired
if (Capsule::table('mod_pvewhmcs')->where('id', '1')->value('debug_mode') == 1) {
logModuleCall(
@@ -142,7 +142,43 @@ function pvewhmcs_CreateAccount($params) {
json_decode($response)
);
}
if ($response) {
// Extract UPID from the response (Proxmox returns colon-delimited string)
if (strpos($response, 'UPID:') === 0) {
$upid = trim($response); // Extract the entire UPID including "UPID:"
// Poll for task completion
$max_retries = 10; // Total retries (avoid infinite loop)
$retry_interval = 15; // Delay in seconds between retries
$completed = false; // Starting - not complete until done
for ($i = 0; $i < $max_retries; $i++) {
// Check task status
$task_status = $proxmox->get('/nodes/' . $first_node . '/tasks/' . $upid . '/status');
if (isset($task_status['status']) && $task_status['status'] === 'stopped') {
// Task is completed, now check exit status
if (isset($task_status['exitstatus']) && $task_status['exitstatus'] === 'OK') {
$completed = true;
break;
} else {
// Task stopped, but failed with an exit status
throw new Exception("Proxmox Error: Task failed with exit status: " . $task_status['exitstatus']);
}
} elseif ($task_status['status'] === 'running') {
// Task is still running, wait and retry
sleep($retry_interval);
} else {
// Unexpected task status
throw new Exception("Proxmox Error: Unexpected task status: " . json_encode($task_status));
}
}
if (!$completed) {
throw new Exception("Proxmox Error: Task did not complete in time. Adjust ~/modules/servers/pvewhmcs/pvewhmcs.php >> max_retries option (2 locations).");
}
// Task is completed, now update the database with VM details.
Capsule::table('mod_pvewhmcs_vms')->insert(
[
'id' => $params['serviceid'],
@@ -166,25 +202,20 @@ function pvewhmcs_CreateAccount($params) {
$amendment = $proxmox->post('/nodes/' . $first_node . '/qemu/' . $vm_settings['newid'] . '/config', $cloned_tweaks);
return true;
} else {
if (is_array($response) && isset($response['data']['errors'])) {
$response_message = json_encode($response['data']['errors']);
} elseif (is_string($response) || is_numeric($response)) {
$response_message = (string)$response;
} else {
$response_message = "Unexpected Error/Response. Type: " . gettype($response) . ", Contents: " . print_r($response, true);
}
throw new Exception("Proxmox Error: Failed to create Service. Response: " . $response_message);
throw new Exception("Proxmox Error: Failed to initiate clone. Response: " . json_encode($response));
}
} else {
throw new Exception("Proxmox Error: PVE API login failed. Please check your credentials.");
}
// PREPARE SETTINGS FOR QEMU/LXC EVENTUALITIES
/////////////////////////////////////////////////
// PREPARE SETTINGS FOR QEMU/LXC EVENTUALITIES //
/////////////////////////////////////////////////
} else {
$vm_settings['vmid'] = $params["serviceid"];
if ($plan->vmtype == 'lxc') {
/////////////////////////////
// Process LXC preparation //
/////////////////////////////
///////////////////////////
// LXC: Preparation Work //
///////////////////////////
$vm_settings['ostemplate'] = $plan->storage . ':vztmpl/' . $params['customfields']['Template'];
$vm_settings['swap'] = $plan->swap;
$vm_settings['rootfs'] = $plan->storage . ':' . $plan->disk;
@@ -193,62 +224,54 @@ function pvewhmcs_CreateAccount($params) {
if (!empty($plan->ipv6) && $plan->ipv6 != '0') {
// Standard prep for the 2nd int.
$vm_settings['net1'] = 'name=eth1,bridge=' . $plan->bridge . $plan->vmbr;
// Handling different IPv6 configs
switch ($plan->ipv6) {
case 'auto':
// Passes 'auto' directly, triggering SLAAC
// Pass in auto, triggering SLAAC
$vm_settings['net1'] .= ',ip6=auto';
break;
case 'dhcp':
// Passes 'dhcp' directly
// DHCP for IPv6 option
$vm_settings['net1'] .= ',ip6=dhcp';
break;
case 'prefix':
// Placeholder for future development - currently does nothing
// TODO: Handle 'prefix' case once prefix allocation logic is developed
// Future development
break;
default:
// Handle any unexpected IPv6 settings - logging, etc
break;
}
// VLAN tag, only for v6
if(!empty($plan->vlanid)){
if (!empty($plan->vlanid)) {
$vm_settings['net1'] .= ',trunk=' . $plan->vlanid;
}
}
// VLAN tag, only for v4
if(!empty($plan->vlanid)){
if (!empty($plan->vlanid)) {
$vm_settings['net0'] .= ',trunk=' . $plan->vlanid;
}
$vm_settings['nameserver'] = '76.76.2.0 76.76.10.0';
$vm_settings['onboot'] = $plan->onboot;
$vm_settings['password'] = $params['customfields']['Password'];
} else {
//////////////////////////////
// Process QEMU preparation //
//////////////////////////////
////////////////////////////
// QEMU: Preparation Work //
////////////////////////////
$vm_settings['ostype'] = $plan->ostype;
$vm_settings['sockets'] = $plan->cpus;
$vm_settings['cores'] = $plan->cores;
$vm_settings['cpu'] = $plan->cpuemu;
$vm_settings['ipconfig0'] = 'ip=' . $ip->ipaddress . '/' . mask2cidr($ip->mask) . ',gw=' . $ip->gateway;
if (!empty($plan->ipv6) && $plan->ipv6 != '0') {
// Handling different IPv6 configs
switch ($plan->ipv6) {
case 'auto':
// Passes 'auto' directly, triggering SLAAC
// Pass in auto, triggering SLAAC
$vm_settings['ipconfig1'] = 'ip6=auto';
break;
case 'dhcp':
// Passes 'dhcp' directly
// DHCP for IPv6 option
$vm_settings['ipconfig1'] = 'ip6=dhcp';
break;
case 'prefix':
// Placeholder for future development - currently does nothing
// TODO: Handle 'prefix' case once prefix allocation logic is developed
// Future development
break;
default:
// Handle any unexpected IPv6 settings - logging, etc
break;
}
}
@@ -261,14 +284,13 @@ function pvewhmcs_CreateAccount($params) {
$vm_settings[$plan->disktype . '0'] .= ',cache=' . $plan->diskcache;
}
// Assign ISO File
// ISO: Attach file to the guest
if (isset($params['customfields']['ISO'])) {
$vm_settings['ide2'] = 'local:iso/' . $params['customfields']['ISO'] . ',media=cdrom';
}
/* Network Specifics - Bridge, Rate & Trunk/VLAN */
// NET: Config specifics for guest networking
if ($plan->netmode != 'none') {
// Perform the additions for IPv4 (net0/ipconfig0)
$vm_settings['net0'] = $plan->netmodel;
if ($plan->netmode == 'bridge') {
$vm_settings['net0'] .= ',bridge=' . $plan->bridge . $plan->vmbr;
@@ -280,7 +302,7 @@ function pvewhmcs_CreateAccount($params) {
if (!empty($plan->vlanid)) {
$vm_settings['net0'] .= ',trunk=' . $plan->vlanid;
}
// Check if ipconfig1 exists, and then do the same for net1 if so
// IPv6: Same configs for second interface
if (isset($vm_settings['ipconfig1'])) {
$vm_settings['net1'] = $plan->netmodel;
if ($plan->netmode == 'bridge') {
@@ -295,7 +317,6 @@ function pvewhmcs_CreateAccount($params) {
}
}
}
/* end of network settings */
}
$vm_settings['cpuunits'] = $plan->cpuunits;
@@ -309,7 +330,7 @@ function pvewhmcs_CreateAccount($params) {
$proxmox = new PVE2_API($serverip, $serverusername, "pam", $serverpassword);
if ($proxmox->login()) {
// Get first node name.
// Get first node name.
$nodes = $proxmox->get_node_list();
$first_node = $nodes[0];
unset($nodes);
@@ -323,6 +344,7 @@ function pvewhmcs_CreateAccount($params) {
// ACTION - Fire the attempt to create
$logrequest = '/nodes/' . $first_node . '/' . $v . $vm_settings;
$response = $proxmox->post('/nodes/' . $first_node . '/' . $v, $vm_settings);
// DEBUG - Log the request parameters after it's fired
if (Capsule::table('mod_pvewhmcs')->where('id', '1')->value('debug_mode') == 1) {
logModuleCall(
@@ -332,8 +354,43 @@ function pvewhmcs_CreateAccount($params) {
json_decode($response)
);
}
if ($response) {
unset($vm_settings);
// Extract UPID from the response (Proxmox returns colon-delimited string)
if (strpos($response, 'UPID:') === 0) {
$upid = trim($response); // Extract the entire UPID including "UPID:"
// Poll for task completion
$max_retries = 10; // Total retries (avoid infinite loop)
$retry_interval = 15; // Number of seconds between retries
$completed = false;
for ($i = 0; $i < $max_retries; $i++) {
// Check task status
$task_status = $proxmox->get('/nodes/' . $first_node . '/tasks/' . $upid . '/status');
if (isset($task_status['status']) && $task_status['status'] === 'stopped') {
// Task is completed, now check exit status
if (isset($task_status['exitstatus']) && $task_status['exitstatus'] === 'OK') {
$completed = true;
break;
} else {
// Task stopped, but failed with an exit status
throw new Exception("Proxmox Error: Task failed with exit status: " . $task_status['exitstatus']);
}
} elseif ($task_status['status'] === 'running') {
// Task is still running, wait and retry
sleep($retry_interval);
} else {
// Unexpected task status
throw new Exception("Proxmox Error: Unexpected task status: " . json_encode($task_status));
}
}
if (!$completed) {
throw new Exception("Proxmox Error: Task did not complete in time. Adjust ~/modules/servers/pvewhmcs/pvewhmcs.php >> max_retries option (2 locations).");
}
// Task is completed, now update the database with VM details.
Capsule::table('mod_pvewhmcs_vms')->insert(
[
'id' => $params['serviceid'],
@@ -348,14 +405,7 @@ function pvewhmcs_CreateAccount($params) {
);
return true;
} else {
if (is_array($response) && isset($response['data']['errors'])) {
$response_message = json_encode($response['data']['errors']);
} elseif (is_string($response) || is_numeric($response)) {
$response_message = (string)$response;
} else {
$response_message = "Unexpected Error/Response. Type: " . gettype($response) . ", Contents: " . print_r($response, true);
}
throw new Exception("Proxmox Error: Failed to create Service. Response: " . $response_message);
throw new Exception("Proxmox Error: Failed to initiate creation. Response: " . json_encode($response));
}
} else {
throw new Exception("Proxmox Error: PVE API login failed. Please check your credentials.");
@@ -379,7 +429,7 @@ function pvewhmcs_CreateAccount($params) {
// PVE API FUNCTION, ADMIN: Test Connection with Proxmox node
function pvewhmcs_TestConnection(array $params) {
try {
// Call the service's connection test function.
// Call the service's connection test function
$serverip = $params["serverip"];
$serverusername = $params["serverusername"];
$serverpassword = $params["serverpassword"];
@@ -418,12 +468,13 @@ function pvewhmcs_SuspendAccount(array $params) {
$proxmox = new PVE2_API($serverip, $serverusername, "pam", $serverpassword);
if ($proxmox->login()) {
# Get first node name.
// Get first node name & prepare
$nodes = $proxmox->get_node_list();
$first_node = $nodes[0];
unset($nodes);
$guest=Capsule::table('mod_pvewhmcs_vms')->where('id','=',$params['serviceid'])->get()[0] ;
$pve_cmdparam = array();
// Log and fire request
$logrequest = '/nodes/' . $first_node . '/' . $guest->vtype . '/' . $params['serviceid'] . '/status/stop';
$response = $proxmox->post('/nodes/' . $first_node . '/' . $guest->vtype . '/' . $params['serviceid'] . '/status/stop' , $pve_cmdparam);
}
@@ -454,12 +505,13 @@ function pvewhmcs_UnsuspendAccount(array $params) {
$proxmox = new PVE2_API($serverip, $serverusername, "pam", $serverpassword);
if ($proxmox->login()) {
# Get first node name.
// Get first node name & prepare
$nodes = $proxmox->get_node_list();
$first_node = $nodes[0];
unset($nodes);
$guest=Capsule::table('mod_pvewhmcs_vms')->where('id','=',$params['serviceid'])->get()[0] ;
$pve_cmdparam = array();
// Log and fire request
$logrequest = '/nodes/' . $first_node . '/' . $guest->vtype . '/' . $params['serviceid'] . '/status/start';
$response = $proxmox->post('/nodes/' . $first_node . '/' . $guest->vtype . '/' . $params['serviceid'] . '/status/start');
}
@@ -490,17 +542,22 @@ function pvewhmcs_TerminateAccount(array $params) {
$proxmox = new PVE2_API($serverip, $serverusername, "pam", $serverpassword);
if ($proxmox->login()){
# Get first node name.
// Get first node name
$nodes = $proxmox->get_node_list();
$first_node = $nodes[0];
unset($nodes);
// find virtual machine type
// Find virtual machine type
$guest=Capsule::table('mod_pvewhmcs_vms')->where('id', '=', $params['serviceid'])->get()[0];
// stop the service before terminating
$proxmox->post('/nodes/'.$first_node.'/'.$guest->vtype.'/'.$params['serviceid'].'/status/stop') ;
sleep(30) ;
$pve_cmdparam = array();
// Stop the service if it is not already stopped
$guest_specific = $proxmox->get('/nodes/'.$first_node.'/'.$guest->vtype.'/'.$params['serviceid'].'/status/current');
if ($guest_specific['status'] != 'stopped') {
$proxmox->post('/nodes/' . $first_node . '/' . $guest->vtype . '/' . $params['serviceid'] . '/status/stop' , $pve_cmdparam);
sleep(30);
}
if ($proxmox->delete('/nodes/'.$first_node.'/'.$guest->vtype.'/'.$params['serviceid'],array('skiplock'=>1))) {
// delete entry from module table once service terminated in PVE
// Delete entry from module table once service terminated in PVE
Capsule::table('mod_pvewhmcs_vms')->where('id', '=', $params['serviceid'])->delete();
return "success";
}
@@ -746,10 +803,11 @@ function pvewhmcs_ClientArea($params) {
// Gather access credentials for PVE, as these are no longer passed for Client Area
$pveservice=Capsule::table('tblhosting')->find($params['serviceid']) ;
$pveserver=Capsule::table('tblservers')->where('id','=',$pveservice->server)->get()[0] ;
// Get IP and User for Hypervisor
$serverip = $pveserver->ipaddress;
$serverusername = $pveserver->username;
// Password access is different in Client Area, so retrieve and decrypt
$api_data = array(
'password2' => $pveserver->password,
);
@@ -797,8 +855,14 @@ function pvewhmcs_ClientArea($params) {
$vm_status['memusepercent'] = intval($vm_status['mem'] * 100 / $vm_status['maxmem']);
if ($guest->vtype == 'lxc') {
$ct_specific = $proxmox->get('/nodes/'.$first_node.'/lxc/'.$params['serviceid'] .'/status/current') ;
$vm_status['swapusepercent'] = intval($ct_specific['swap'] * 100 / $ct_specific['maxswap']);
// Check on swap before setting graph value
$ct_specific = $proxmox->get('/nodes/'.$first_node.'/lxc/'.$params['serviceid'].'/status/current');
if ($ct_specific['maxswap'] != 0) {
$vm_status['swapusepercent'] = intval($ct_specific['swap'] * 100 / $ct_specific['maxswap']);
} else {
// Fall back to 0% usage to satisfy chart requirement
$vm_status['swapusepercent'] = 0;
}
}
} else {
// Handle the VM not found in the cluster resources (Optional)
@@ -1106,9 +1170,17 @@ function pvewhmcs_vmReboot($params) {
unset($nodes);
$guest = Capsule::table('mod_pvewhmcs_vms')->where('id','=',$params['serviceid'])->get()[0] ;
$pve_cmdparam = array();
// $pve_cmdparam['timeout'] = '60';
$logrequest = '/nodes/' . $first_node . '/' . $guest->vtype . '/' . $params['serviceid'] . '/status/reboot';
$response = $proxmox->post('/nodes/' . $first_node . '/' . $guest->vtype . '/' . $params['serviceid'] . '/status/reboot' , $pve_cmdparam);
// Check status before doing anything
$guest_specific = $proxmox->get('/nodes/' . $first_node . '/' . $guest->vtype . '/' . $params['serviceid'] . '/status/current');
if ($guest_specific['status'] = 'stopped') {
// START if Stopped
$logrequest = '/nodes/' . $first_node . '/' . $guest->vtype . '/' . $params['serviceid'] . '/status/start';
$response = $proxmox->post('/nodes/' . $first_node . '/' . $guest->vtype . '/' . $params['serviceid'] . '/status/start' , $pve_cmdparam);
} else {
// REBOOT if Started
$logrequest = '/nodes/' . $first_node . '/' . $guest->vtype . '/' . $params['serviceid'] . '/status/reboot';
$response = $proxmox->post('/nodes/' . $first_node . '/' . $guest->vtype . '/' . $params['serviceid'] . '/status/reboot' , $pve_cmdparam);
}
}
// DEBUG - Log the request parameters before it's fired
if (Capsule::table('mod_pvewhmcs')->where('id', '1')->value('debug_mode') == 1) {

View File

@@ -1 +1 @@
1.2.5
1.2.6