From 2cc79953a93dc24704704ac7711df229cb53e714 Mon Sep 17 00:00:00 2001
From: Christoph Schlaepfer <1939311+Anuril@users.noreply.github.com>
Date: Fri, 13 Oct 2023 19:24:06 +0200
Subject: [PATCH] Updating the current preview for 0.1.0
This is substantial work towards #26 and an eventual release of 0.1.0
* Fixed Divide by 0 if a Storage is not reachable or has 0 in any value
* Fixed small things, started storage class edit
* Removed broken is_configured
* Fixed API Wrapper ";
- $server = null;
- $server_ratio = 0;
- // use values from database to calculate ratio and store the server id, cpu and ram usage ratio if it's better than the previous one
- foreach ($servers as $s) {
- $cpu_usage = $s['cpu_cores_allocated'];
- $ram_usage = $s['ram_allocated'];
- $cpu_cores = $s['cpu_cores'];
- $ram = $s['ram'];
-
- $cpu_ratio = $cpu_usage / $cpu_cores;
- $ram_ratio = $ram_usage / $ram;
-
- // if avoid_overprovision is set to true, servers with a ratio of >1 are ignored
- if ($avoid_overprovision && ($cpu_ratio > 1 || $ram_ratio > 1)) {
- continue;
- }
- // calculate ratio with overprovisioning
- $cpu_ratio = $cpu_ratio * (1 + $cpu_overprovion_percent / 100);
- $ram_ratio = $ram_ratio * (1 + $ram_overprovion_percent / 100);
- // check current best ratio
- if ($cpu_ratio + $ram_ratio > $server_ratio) {
- $server_ratio = $cpu_ratio + $ram_ratio;
- $server = $s['id'];
- }
- }
- // if no server is found, return null
- if ($server == null) {
- return null;
- }
- // if a server is found, return the id
- return $server;
- }
-
- /*
- Find access to server (hostname, ipv4, ipv6)
- */
- public function find_access($server)
- {
- if (!empty($server->hostname)) {
- return $server->hostname;
- } else if (!empty($server->ipv4)) {
- return $server->ipv4;
- } else if (!empty($server->ipv6)) {
- return $server->ipv6;
- } else {
- throw new \Box_Exception('No IPv6, IPv4 or Hostname found for server ' . $server->id);
- }
- }
-
- /*
- Find server hardware usage information "getHardwareData"
- */
- public function getHardwareData($server)
- {
- // Retrieve associated server
- $serveraccess = $this->find_access($server);
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $server->tokenname, tokensecret: $server->tokenvalue);
-
- if ($proxmox->login()) {
- $hardware = $proxmox->get("/nodes/" . $server->name . "/status");
- return $hardware;
- } else {
- throw new \Box_Exception("Failed to connect to the server. hw Token Access Failed");
- }
- }
-
- public function getStorageData($server)
- {
- // Retrieve associated server
- $serveraccess = $this->find_access($server);
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $server->tokenname, tokensecret: $server->tokenvalue);
- if ($proxmox->login()) {
- $storage = $proxmox->get("/nodes/" . $server->name . "/storage");
- return $storage;
- } else {
- throw new \Box_Exception("Failed to connect to the server. st");
- }
- }
-
- // function to get all assigned cpu_cores and ram on a server (used to find free resources)
- public function getAssignedResources($server)
- {
- // Retrieve associated server
- $serveraccess = $this->find_access($server);
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $server->tokenname, tokensecret: $server->tokenvalue);
- if ($proxmox->login()) {
- $assigned_resources = $proxmox->get("/nodes/" . $server->name . "/qemu");
- return $assigned_resources;
- } else {
- throw new \Box_Exception("Failed to connect to the server. st");
- }
- }
-
- // function to get available appliances from a server
- public function getAvailableAppliances()
- {
- $server = $this->di['db']->getExistingModelById('service_proxmox_server', 1, 'Server not found');
-
- $serveraccess = $this->find_access($server);
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $server->tokenname, tokensecret: $server->tokenvalue);
- if ($proxmox->login()) {
- $appliances = $proxmox->get("/nodes/" . $server->name . "/aplinfo");
- return $appliances;
- } else {
- throw new \Box_Exception("Failed to connect to the server. st");
- }
- }
-
- // function to get available template vms from a server
- public function getQemuTemplates($server)
- {
- $serveraccess = $this->find_access($server);
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $server->tokenname, tokensecret: $server->tokenvalue);
- if ($proxmox->login()) {
- $templates = $proxmox->get("/nodes/" . $server->name . "/qemu");
- return $templates;
- } else {
- throw new \Box_Exception("Failed to connect to the server. st");
- }
- }
-}
diff --git a/ProxmoxTemplates.php b/ProxmoxTemplates.php
deleted file mode 100644
index 23a8e26..0000000
--- a/ProxmoxTemplates.php
+++ /dev/null
@@ -1,75 +0,0 @@
-di['db']->findAll('service_proxmox_vm_config_template');
- return $templates;
- }
-
-
- // Function that gets all the LXC templates and returns them as an array
- public function get_lxctemplates()
- {
- // get all the LXC templates from the service_proxmox_lxc_config_template table
- $templates = $this->di['db']->findAll('service_proxmox_lxc_config_template');
- return $templates;
- }
-
- // Function that gets all qemu templates and returns them as an array
- public function get_qemutemplates()
- {
- // get all the qemu templates from the service_proxmox_qemu_template table
- $qemu_templates = $this->di['db']->findAll('service_proxmox_qemu_template');
- // Get server name for each template
- foreach ($qemu_templates as $qemu_template) {
- $server = $this->di['db']->getExistingModelById('service_proxmox_server', $qemu_template->server_id);
- $qemu_template->server_name = $server->name;
- }
- return $qemu_templates;
- }
-
- // Function that gets a vm config template by id
- public function get_vmconfig($id)
- {
- // get the vm config template from the service_proxmox_vm_config_template table
- $template = $this->di['db']->getExistingModelById('service_proxmox_vm_config_template', $id);
- return $template;
- }
-
- // Function that gets a lxc config template by id
- public function get_lxc_conftempl($id)
- {
- // get the lxc config template from the service_proxmox_lxc_config_template table
- $template = $this->di['db']->getExistingModelById('service_proxmox_lxc_config_template', $id);
- return $template;
- }
-}
diff --git a/README.md b/README.md
index 398a551..219e117 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,11 @@
+[](https://github.com/Anuril/Proxmox/actions/workflows/preview.yml)
+[](https://github.com/Anuril/Proxmox/actions/workflows/php-ci.yml)
+
# Proxmox module for FOSSBilling
-Initial Proxmox support for FOSSBilling.
+Proxmox support for FOSSBilling.
**This module is still in development and not ready for production use.**
-Based on [previous work](https://github.com/scith/BoxBilling_Proxmox) by [Scith](https://github.com/scith).
-
-
## Server List

@@ -42,12 +42,43 @@ You need to run Fossbilling 0.5.5, otherwise this preview will not work.
```mv /var/www/pmxconfig /var/www/pmxold```
- Make sure you don't have any tables beginning with service_promxox in your database anymore:
```
-mysql -u root
-MariaDB [(none)]>use fossbilling;
-MariaDB [fossbilling]> show tables;```
-and then as Example:
-```
-MariaDB [fossbilling]> drop table service_proxmox_tag;
+-- Create a stored procedure to drop tables starting with "service_proxmox" in the "client" database --
+
+USE client; -- Set this to the database where FOSSBilling is installed
+
+DELIMITER //
+CREATE PROCEDURE DropTables()
+BEGIN
+ DECLARE done INT DEFAULT FALSE;
+ DECLARE tableName VARCHAR(255);
+ DECLARE cur CURSOR FOR
+ SELECT table_name
+ FROM information_schema.tables
+ WHERE table_schema = 'client' -- replace client with your database name
+ AND table_name LIKE 'service_proxmox%';
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
+
+ OPEN cur;
+ read_loop: LOOP
+ FETCH cur INTO tableName;
+ IF done THEN
+ LEAVE read_loop;
+ END IF;
+ SET @sql = CONCAT('DROP TABLE IF EXISTS `', tableName, '`;');
+ PREPARE stmt FROM @sql;
+ EXECUTE stmt;
+ DEALLOCATE PREPARE stmt;
+ END LOOP;
+ CLOSE cur;
+END;
+//
+DELIMITER ;
+
+-- Call the stored procedure to drop the tables
+CALL DropTables();
+
+-- Drop the stored procedure
+DROP PROCEDURE IF EXISTS DropTables;
```
### Installation for 0.1.0 Preview
@@ -96,4 +127,9 @@ The Proxmox Addon now has its own Menu Entry:

## Licensing
-This module is licensed under the GNU General Public License v3.0. See the LICENSE file for more information.
\ No newline at end of file
+This module is licensed under the GNU General Public License v3.0. See the LICENSE file for more information.
+
+## Copyright
+Copyright: Christoph Schläpfer & the FOSSBilling Team.
+
+Based on [previous work](https://github.com/scith/BoxBilling_Proxmox) by [Scith](https://github.com/scith).
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..42873b7
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,38 @@
+{
+ "name": "fossbilling/proxmox",
+ "description": "Proxmox Module for FOSSBilling",
+ "type": "project",
+ "require": {
+ "php": "^8.0|8.1|^8.2",
+ "pve2-api-client/pve2-api-client": "^1.0.2",
+ "symfony/finder": "^5.3"
+ },
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fossbilling"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/FOSSBilling"
+ }
+ ],
+ "require-dev": {
+ "phpstan/extension-installer": "^1.2",
+ "phpstan/phpstan-deprecation-rules": "^1.1",
+ "phpunit/phpunit": "^9.6"
+ },
+ "license": "GPL 3.0",
+ "authors": [
+ {
+ "name": "Christoph Schläpfer",
+ "email": "chris+github@cleverly.ch"
+ }
+ ],
+ "config": {
+ "vendor-dir": "./src/vendor",
+ "allow-plugins": {
+ "phpstan/extension-installer": true
+ }
+ }
+}
diff --git a/html_admin/mod_serviceproxmox_config.html.twig b/html_admin/mod_serviceproxmox_config.html.twig
deleted file mode 100644
index 29600fd..0000000
--- a/html_admin/mod_serviceproxmox_config.html.twig
+++ /dev/null
@@ -1,248 +0,0 @@
-
-{% block js %}
-
-{% endblock %}
-
\ No newline at end of file
diff --git a/html_admin/mod_serviceproxmox_storage.html.twig b/html_admin/mod_serviceproxmox_storage.html.twig
deleted file mode 100644
index 0e4b40d..0000000
--- a/html_admin/mod_serviceproxmox_storage.html.twig
+++ /dev/null
@@ -1,43 +0,0 @@
-{% import 'macro_functions.html.twig' as mf %}
-{% extends "layout_default.html.twig" %}
-{% block meta_title %}
- {{ 'Manage Proxmox storage' | trans }}
-{% endblock %}
-{% set active_menu = 'system' %}
-
-{% block content %}
-
-
-
- Manage
-
- {{ storage.storage }}
-
-
-
-
-
-
- {% endblock %}
- {% block js %}{% endblock %}
-
\ No newline at end of file
diff --git a/html_admin/mod_serviceproxmox_templates_qemu.html.twig b/html_admin/mod_serviceproxmox_templates_qemu.html.twig
deleted file mode 100644
index d83688f..0000000
--- a/html_admin/mod_serviceproxmox_templates_qemu.html.twig
+++ /dev/null
@@ -1,164 +0,0 @@
-{% import 'macro_functions.html.twig' as mf %}
-{% extends 'layout_default.html.twig' %}
-{% block meta_title %}
- {{ 'Proxmox Configuration Templates' | trans }}
-{% endblock %}
-{% set active_menu = 'proxmox' %}
-
-{% block content %}
-
-
-
-
-
-
-{% endblock %}
\ No newline at end of file
diff --git a/migrations/0.0.6.sql b/migrations/0.0.6.sql
deleted file mode 100755
index 5beae37..0000000
--- a/migrations/0.0.6.sql
+++ /dev/null
@@ -1,19 +0,0 @@
--- --------------------------------------------------------
--- increment all tables to 0.0.6
--- --------------------------------------------------------
-ALTER TABLE `service_proxmox_server` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox_users` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox_storageclass` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox_storage` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox_lxc_appliance` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox_vm_config_template` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox_vm_storage_template` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox_vm_network_template` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox_lxc_config_template` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox_lxc_storage_template` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox_lxc_network_template` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox_qemu_template` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox_client_vlan` COMMENT = '0.0.6';
-ALTER TABLE `service_proxmox_ip_range` COMMENT = '0.0.6';
--- --------------------------------------------------------
\ No newline at end of file
diff --git a/migrations/0.0.7.sql b/migrations/0.0.7.sql
deleted file mode 100644
index 6b3d8d8..0000000
--- a/migrations/0.0.7.sql
+++ /dev/null
@@ -1,84 +0,0 @@
--- Migration: 0.0.7
--- --------------------------------------------------------
--- Add new table for ipam settings
--- --------------------------------------------------------
--- Fields: Minimum Network size (default 24), Maximum Network size (default 23), DNS Server 1, DNS Server 2
--- --------------------------------------------------------
-CREATE TABLE IF NOT EXISTS `service_proxmox_ipam_settings` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `min_network_size` int(11) NOT NULL DEFAULT '24',
- `max_network_size` int(11) NOT NULL DEFAULT '23',
- `dns_server_1` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- `dns_server_2` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- `use_proxmox_sdn` tinyint(1) NOT NULL DEFAULT '0',
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-
--- --------------------------------------------------------
--- Add new table for ip adresses
--- --------------------------------------------------------
--- Fields: IP address, ip range, dedicated, gateway, vlan
-
-CREATE TABLE IF NOT EXISTS `service_proxmox_ipadress` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `ip` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `ip_range_id` int(11) NOT NULL,
- `dedicated` tinyint(1) NOT NULL DEFAULT '0',
- `gateway` tinyint(1) NOT NULL DEFAULT '0',
- `vlan` int(11) NOT NULL DEFAULT '0',
- PRIMARY KEY (`id`),
- KEY `ip_range_id` (`ip_range_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-
--- --------------------------------------------------------
--- alter service_proxmox_qemu_template to add auto increment to id, and add vmid field after id
--- --------------------------------------------------------
-ALTER TABLE `service_proxmox_qemu_template` CHANGE `id` `id` INT(11) NOT NULL AUTO_INCREMENT;
-ALTER TABLE `service_proxmox_qemu_template` ADD `vmid` INT(11) NOT NULL AFTER `id`;
-
--- --------------------------------------------------------
--- alter service_proxmox_ip_range to add network field after gateway
--- --------------------------------------------------------
-ALTER TABLE `service_proxmox_ip_range` ADD `network` VARCHAR(255) NOT NULL AFTER `gateway`;
-
--- --------------------------------------------------------
--- alter service_proxmox_vm_config_template to add state field after id
--- --------------------------------------------------------
-ALTER TABLE `service_proxmox_vm_config_template` ADD `state` VARCHAR(255) NOT NULL AFTER `id`;
--- --------------------------------------------------------
--- insert state "draft" for all existing templates
-
-UPDATE `service_proxmox_vm_config_template` SET `state` = 'draft';
-
--- --------------------------------------------------------
--- add new table for tags and tag relations
--- --------------------------------------------------------
-CREATE TABLE IF NOT EXISTS `service_proxmox_tag` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-
--- --------------------------------------------------------
--- increment all tables to 0.0.7
--- --------------------------------------------------------
-ALTER TABLE `service_proxmox_server` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_users` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_storageclass` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_storage` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_lxc_appliance` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_vm_config_template` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_vm_storage_template` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_vm_network_template` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_lxc_config_template` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_lxc_storage_template` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_lxc_network_template` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_qemu_template` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_client_vlan` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_ip_range` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_ipam_settings` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_ipadress` COMMENT = '0.0.7';
-ALTER TABLE `service_proxmox_tag` COMMENT = '0.0.7';
--- --------------------------------------------------------
\ No newline at end of file
diff --git a/phpdoc.dist.xml b/phpdoc.dist.xml
new file mode 100644
index 0000000..c3fbaf3
--- /dev/null
+++ b/phpdoc.dist.xml
@@ -0,0 +1,19 @@
+
+
+ phpDocumentor
+
+
+
+
+
+
+ tests/**/*
+ src/vendor/**/*
+
+
+
+
\ No newline at end of file
diff --git a/phpstan.dist.neon b/phpstan.dist.neon
new file mode 100644
index 0000000..e1d950f
--- /dev/null
+++ b/phpstan.dist.neon
@@ -0,0 +1,15 @@
+parameters:
+ level: 1
+ bootstrapFiles:
+ - ../../load.php
+ paths:
+ - ./src
+ excludePaths:
+ analyse:
+ - ./src/vendor
+ scanDirectories:
+ - ../../
+ - ./src/vendor
+ ignoreErrors:
+ - '#^Function __trans not found\.$#'
+ - '#^Variable \$proxmox might not be defined\.#'
\ No newline at end of file
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..73ea2b5
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,14 @@
+parameters:
+ level: 1
+ bootstrapFiles:
+ - FOSSBilling/src/load.php
+ paths:
+ - src
+ - FOSSBilling
+ excludePaths:
+ analyse:
+ - src/vendor
+ - FOSSBilling
+ ignoreErrors:
+ - '#^Function __trans not found\.$#'
+ - '#^Variable \$proxmox might not be defined\.#'
\ No newline at end of file
diff --git a/pve2_api.class.php b/pve2_api.class.php
deleted file mode 100755
index 71fafaa..0000000
--- a/pve2_api.class.php
+++ /dev/null
@@ -1,663 +0,0 @@
- ['min_range' => 1, 'max_range' => 65535]])) {
- throw new PVE2_Exception("Port must be an integer between 1 and 65535.", 6);
- }
- // Check that verify_ssl is boolean.
- if (!is_bool($verify_ssl)) {
- throw new PVE2_Exception("verify_ssl must be boolean.", 7);
- }
-
- $this->hostname = $hostname;
- $this->username = $username;
- $this->realm = $realm;
- $this->tokenid = $tokenid;
- $this->tokensecret = $tokensecret;
- $this->password = $password;
- $this->port = $port;
- $this->verify_ssl = $verify_ssl;
- // Check if we have a tokenid and tokensecret, if so, we can use API token access.
- if (!empty($tokenid) && !empty($tokensecret)) {
- $this->api_token_access = true;
- }else {
- $this->api_token_access = false;
- }
- }
-
- /*
- * bool login ()
- * Performs login to PVE Server using JSON API, and obtains Access Ticket.
- */
- public function login () {
- // Prepare login variables.
- $login_postfields = array();
-
- // Prepare cURL handle.
- $prox_ch = curl_init();
-
- // Base URL for later use.
- $api_url_base = "https://{$this->hostname}:{$this->port}/api2/json";
-
- // If we can use API token access, do so.
- if ($this->api_token_access) {
-
- $put_post_http_headers[] = "Authorization: PVEAPIToken={$this->tokenid}={$this->tokensecret}";
- curl_setopt($prox_ch, CURLOPT_URL, $api_url_base."/version");
- curl_setopt($prox_ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($prox_ch, CURLOPT_HTTPHEADER, $put_post_http_headers);
- curl_setopt($prox_ch, CURLOPT_SSL_VERIFYPEER, $this->verify_ssl);
- curl_setopt($prox_ch, CURLOPT_SSL_VERIFYHOST, $this->verify_ssl);
- $version_information = curl_exec($prox_ch);
- $version_information_info = curl_getinfo($prox_ch);
- $version_information_data = json_decode($version_information, true);
- curl_close($prox_ch);
- unset($prox_ch);
-
- if (!$version_information) {
- // SSL negotiation failed or connection timed out
- return false;
- }
-
- if ($version_information_data == null || $version_information_data['data'] == null) {
- // Login failed.
- if ($version_information_info['ssl_verify_result'] == 1) {
- throw new PVE2_Exception("Invalid SSL cert on {$this->hostname} - check that the hostname is correct, and that it appears in the server certificate's SAN list. Alternatively set the verify_ssl flag to false if you are using internal self-signed certs (ensure you are aware of the security risks before doing so).", 4);
- }
- return false;
- } else {
- // Login success.
- $this->reload_node_list();
- return true;
- }
- } else {
-
-
- $login_postfields['username'] = $this->username;
- $login_postfields['password'] = $this->password;
- $login_postfields['realm'] = $this->realm;
-
- $login_postfields_string = http_build_query($login_postfields);
- unset($login_postfields);
-
- // Perform login request.
-
- curl_setopt($prox_ch, CURLOPT_URL, $api_url_base."/access/ticket");
- curl_setopt($prox_ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($prox_ch, CURLOPT_POSTFIELDS, $login_postfields_string);
- curl_setopt($prox_ch, CURLOPT_SSL_VERIFYPEER, $this->verify_ssl);
- curl_setopt($prox_ch, CURLOPT_SSL_VERIFYHOST, $this->verify_ssl);
-
- $login_ticket_request = curl_exec($prox_ch);
- $login_request_info = curl_getinfo($prox_ch);
-
- curl_close($prox_ch);
- unset($prox_ch);
- unset($login_postfields_string);
-
- if (!$login_ticket_request) {
- throw new PVE2_Exception("SSL negotiation failed or connection timed out.", 7);
- // SSL negotiation failed or connection timed out
- $this->login_ticket_timestamp = null;
- return false;
- }
-
- $login_ticket_data = json_decode($login_ticket_request, true);
- if ($login_ticket_data == null || $login_ticket_data['data'] == null) {
- // Login failed.
- // Just to be safe, set this to null again.
- $this->login_ticket_timestamp = null;
- if ($login_request_info['ssl_verify_result'] == 1) {
- throw new PVE2_Exception("Invalid SSL cert on {$this->hostname} - check that the hostname is correct, and that it appears in the server certificate's SAN list. Alternatively set the verify_ssl flag to false if you are using internal self-signed certs (ensure you are aware of the security risks before doing so).", 4);
- }
- return false;
- } else {
- // Login success.
- $this->login_ticket = $login_ticket_data['data'];
- // We store a UNIX timestamp of when the ticket was generated here,
- // so we can identify when we need a new one expiration-wise later
- // on...
- $this->login_ticket_timestamp = time();
- $this->reload_node_list();
- return true;
- }
- }
- }
-
-
- # Sets the PVEAuthCookie
- # Attetion, after using this the user is logged into the web interface aswell!
- # Use with care, and DO NOT use with root, it may harm your system
- public function setCookie() {
- // exit successfully if api_token_access is true
- if ($this->api_token_access) {
- return true;
- }
- if (!$this->check_login_ticket()) {
- throw new PVE2_Exception("Not logged into Proxmox host. No Login access ticket found or ticket expired.", 3);
- }
-
- setrawcookie("PVEAuthCookie", $this->login_ticket['ticket'], 0, "/");
- }
-
- /*
- * bool check_login_ticket ()
- * Checks if the login ticket is valid still, returns false if not.
- * Method of checking is purely by age of ticket right now...
- */
- protected function check_login_ticket () {
- // if api_token_access is true, return true
- if ($this->api_token_access) {
- return true;
- }
- if ( $this->login_ticket == null) {
- // Just to be safe, set this to null again.
- $this->login_ticket_timestamp = null;
- return false;
- }
-
- if ($this->login_ticket_timestamp >= (time() + 7200)) {
- // Reset login ticket object values.
- $this->login_ticket = null;
- $this->login_ticket_timestamp = null;
- return false;
- } else {
- return true;
- }
- }
-
- /*
- * object action (string action_path, string http_method[, array put_post_parameters])
- * This method is responsible for the general cURL requests to the JSON API,
- * and sits behind the abstraction layer methods get/put/post/delete etc.
- */
- private function action ($action_path, $http_method, $put_post_parameters = null) {
- // Check if we have a prefixed / on the path, if not add one.
- if (substr($action_path, 0, 1) != "/") {
- $action_path = "/".$action_path;
- }
-
- if (!$this->check_login_ticket()) {
- throw new PVE2_Exception("No valid connection to Proxmox host. No Login access ticket found, ticket expired or no API Token set up.", 3);
- }
-
- // Prepare cURL resource.
- $prox_ch = curl_init();
- curl_setopt($prox_ch, CURLOPT_URL, "https://{$this->hostname}:{$this->port}/api2/json{$action_path}");
-
- $put_post_http_headers = array();
- if (!$this->api_token_access) {
- $put_post_http_headers[] = "CSRFPreventionToken: {$this->login_ticket['CSRFPreventionToken']}";
- } else {
- $put_post_http_headers[] = "Authorization: PVEAPIToken={$this->tokenid}={$this->tokensecret}";
- }
- // Lets decide what type of action we are taking...
- switch ($http_method) {
- case "GET":
- // add headers for GET requests if api_token_access is true
- if ($this->api_token_access) {
-
-
- // Add required HTTP headers.
- curl_setopt($prox_ch, CURLOPT_HTTPHEADER, $put_post_http_headers);
- }
-
- break;
- case "PUT":
- curl_setopt($prox_ch, CURLOPT_CUSTOMREQUEST, "PUT");
-
- // Set "POST" data.
- $action_postfields_string = http_build_query($put_post_parameters);
- curl_setopt($prox_ch, CURLOPT_POSTFIELDS, $action_postfields_string);
- unset($action_postfields_string);
-
- // Add required HTTP headers.
- curl_setopt($prox_ch, CURLOPT_HTTPHEADER, $put_post_http_headers);
- break;
- case "POST":
- curl_setopt($prox_ch, CURLOPT_POST, true);
-
- // Set POST data.
- $action_postfields_string = http_build_query($put_post_parameters);
- curl_setopt($prox_ch, CURLOPT_POSTFIELDS, $action_postfields_string);
- unset($action_postfields_string);
-
- // Add required HTTP headers.
- curl_setopt($prox_ch, CURLOPT_HTTPHEADER, $put_post_http_headers);
- break;
- case "DELETE":
- curl_setopt($prox_ch, CURLOPT_CUSTOMREQUEST, "DELETE");
- // No "POST" data required, the delete destination is specified in the URL.
-
- // Add required HTTP headers.
- curl_setopt($prox_ch, CURLOPT_HTTPHEADER, $put_post_http_headers);
- break;
- default:
- throw new PVE2_Exception("Error - Invalid HTTP Method specified.", 5);
- return false;
- }
-
- curl_setopt($prox_ch, CURLOPT_HEADER, true);
- curl_setopt($prox_ch, CURLOPT_RETURNTRANSFER, true);
-
- // only set this cookie if api_token_access is false
- if (!$this->api_token_access) {
- curl_setopt($prox_ch, CURLOPT_COOKIE, "PVEAuthCookie=".$this->login_ticket['ticket']);
- }
- curl_setopt($prox_ch, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($prox_ch, CURLOPT_SSL_VERIFYHOST, false);
-
- $action_response = curl_exec($prox_ch);
-
- curl_close($prox_ch);
- unset($prox_ch);
-
- $split_action_response = explode("\r\n\r\n", $action_response, 2);
- $header_response = $split_action_response[0];
- $body_response = $split_action_response[1];
- $action_response_array = json_decode($body_response, true);
-
- $action_response_export = var_export($action_response_array, true);
- error_log("----------------------------------------------\n" .
- "FULL RESPONSE:\n\n{$action_response}\n\nEND FULL RESPONSE\n\n" .
- "Headers:\n\n{$header_response}\n\nEnd Headers\n\n" .
- "Data:\n\n{$body_response}\n\nEnd Data\n\n" .
- "RESPONSE ARRAY:\n\n{$action_response_export}\n\nEND RESPONSE ARRAY\n" .
- "----------------------------------------------");
-
- unset($action_response);
- unset($action_response_export);
-
- // Parse response, confirm HTTP response code etc.
- $split_headers = explode("\r\n", $header_response);
- if (substr($split_headers[0], 0, 9) == "HTTP/1.1 ") {
- $split_http_response_line = explode(" ", $split_headers[0]);
- if ($split_http_response_line[1] == "200") {
- if ($http_method == "PUT") {
- return true;
- } else {
- return $action_response_array['data'];
- }
- } else {
- error_log("This API Request Failed.\n" .
- "HTTP Response - {$split_http_response_line[1]}\n" .
- "HTTP Error - {$split_headers[0]}");
- return false;
- }
- } else {
- error_log("Error - Invalid HTTP Response.\n" . var_export($split_headers, true));
- return false;
- }
-
- if (!empty($action_response_array['data'])) {
- return $action_response_array['data'];
- } else {
- error_log("\$action_response_array['data'] is empty. Returning false.\n" .
- var_export($action_response_array['data'], true));
- return false;
- }
- }
-
- /*
- * array reload_node_list ()
- * Returns the list of node names as provided by /api2/json/nodes.
- * We need this for future get/post/put/delete calls.
- * ie. $this->get("nodes/XXX/status"); where XXX is one of the values from this return array.
- */
- public function reload_node_list () {
- $node_list = $this->get("/nodes");
- if (count($node_list) > 0) {
- $nodes_array = array();
- foreach ($node_list as $node) {
- $nodes_array[] = $node['node'];
- }
- $this->cluster_node_list = $nodes_array;
- return true;
- } else {
- error_log(" Empty list of nodes returned in this cluster.");
- return false;
- }
- }
-
- /*
- * array get_node_list ()
- *
- */
- public function get_node_list () {
- // We run this if we haven't queried for cluster nodes as yet, and cache it in the object.
- if ($this->cluster_node_list == null) {
- if ($this->reload_node_list() === false) {
- return false;
- }
- }
-
- return $this->cluster_node_list;
- }
-
- /*
- * bool|int get_next_vmid ()
- * Get Last VMID from a Cluster or a Node
- * returns a VMID, or false if not found.
- */
- public function get_next_vmid () {
- $vmid = $this->get("/cluster/nextid");
- if ($vmid == null) {
- return false;
- } else {
- return $vmid;
- }
- }
-
- /*
- * array get_vms ()
- * Get List of all vms
- */
- public function get_vms () {
- $node_list = $this->get_node_list();
- $result=[];
- if (count($node_list) > 0) {
- foreach ($node_list as $node_name) {
- $vms_list = $this->get("nodes/" . $node_name . "/qemu/");
- if (count($vms_list) > 0) {
- $key_values = array_column($vms_list, 'vmid');
- array_multisort($key_values, SORT_ASC, $vms_list);
- foreach($vms_list as &$row) {
- $row[node] = $node_name;
- }
- $result = array_merge($result, $vms_list);
- }
- if (count($result) > 0) {
- $this->$cluster_vms_list = $result;
- return $this->$cluster_vms_list;
- } else {
- error_log(" Empty list of vms returned in this cluster.");
- return false;
- }
- }
- } else {
- error_log(" Empty list of nodes returned in this cluster.");
- return false;
- }
- }
-
- /*
- * bool|int start_vm ($node,$vmid)
- * Start specific vm
- */
- public function start_vm ($node,$vmid) {
- if(isset($vmid) && isset($node)){
- $parameters = array(
- "vmid" => $vmid,
- "node" => $node,
- );
- $url = "/nodes/" . $node . "/qemu/" . $vmid . "/status/start";
- $post = $this->post($url,$parameters);
- if ($post) {
- error_log("Started vm " . $vmid . "");
- return true;
- } else {
- error_log("Error starting vm " . $vmid . "");
- return false;
- }
- } else {
- error_log("no vm or node valid");
- return false;
- }
- }
-
- /*
- * bool|int shutdown_vm ($node,$vmid)
- * Gracefully shutdown specific vm
- */
- public function shutdown_vm ($node,$vmid) {
- if(isset($vmid) && isset($node)){
- $parameters = array(
- "vmid" => $vmid,
- "node" => $node,
- "timeout" => 60,
- );
- $url = "/nodes/" . $node . "/qemu/" . $vmid . "/status/shutdown";
- $post = $this->post($url,$parameters);
- if ($post) {
- error_log("Shutdown vm " . $vmid . "");
- return true;
- } else {
- error_log("Error shutting down vm " . $vmid . "");
- return false;
- }
- } else {
- error_log("no vm or node valid");
- return false;
- }
- }
-
- /*
- * bool|int stop_vm ($node,$vmid)
- * Force stop specific vm
- */
- public function stop_vm ($node,$vmid) {
- if(isset($vmid) && isset($node)){
- $parameters = array(
- "vmid" => $vmid,
- "node" => $node,
- "timeout" => 60,
- );
- $url = "/nodes/" . $node . "/qemu/" . $vmid . "/status/stop";
- $post = $this->post($url,$parameters);
- if ($post) {
- error_log("Stopped vm " . $vmid . "");
- return true;
- } else {
- error_log("Error stopping vm " . $vmid . "");
- return false;
- }
- } else {
- error_log("no vm or node valid");
- return false;
- }
- }
-
- /*
- * bool|int resume_vm ($node,$vmid)
- * Resume from suspend specific vm
- */
- public function resume_vm ($node,$vmid) {
- if(isset($vmid) && isset($node)){
- $parameters = array(
- "vmid" => $vmid,
- "node" => $node,
- );
- $url = "/nodes/" . $node . "/qemu/" . $vmid . "/status/resume";
- $post = $this->post($url,$parameters);
- if ($post) {
- error_log("Resumed vm " . $vmid . "");
- return true;
- } else {
- error_log("Error resuming vm " . $vmid . "");
- return false;
- }
- } else {
- error_log("no vm or node valid");
- return false;
- }
- }
-
- /*
- * bool|int suspend_vm ($node,$vmid)
- * Suspend specific vm
- */
- public function suspend_vm ($node,$vmid) {
- if(isset($vmid) && isset($node)){
- $parameters = array(
- "vmid" => $vmid,
- "node" => $node,
- );
- $url = "/nodes/" . $node . "/qemu/" . $vmid . "/status/suspend";
- $post = $this->post($url,$parameters);
- if ($post) {
- error_log("Suspended vm " . $vmid . "");
- return true;
- } else {
- error_log("Error suspending vm " . $vmid . "");
- return false;
- }
- } else {
- error_log("no vm or node valid");
- return false;
- }
- }
-
- /*
- * bool|int clone_vm ($node,$vmid)
- * Create fullclone of vm
- */
- public function clone_vm ($node,$vmid) {
- if(isset($vmid) && isset($node)){
- $lastid = $this->get_next_vmid();
- $parameters = array(
- "vmid" => $vmid,
- "node" => $node,
- "newid" => $lastid,
- "full" => true,
- );
- $url = "/nodes/" . $node . "/qemu/" . $vmid . "/clone";
- $post = $this->post($url,$parameters);
- if ($post) {
- error_log("Cloned vm " . $vmid . " to " . $lastid . "");
- return true;
- } else {
- error_log("Error cloning vm " . $vmid . " to " . $lastid . "");
- return false;
- }
- } else {
- error_log("no vm or node valid");
- return false;
- }
- }
-
- /*
- * bool|int snapshot_vm ($node,$vmid,$snapname = NULL)
- * Create snapshot of vm
- */
- public function snapshot_vm ($node,$vmid,$snapname = NULL) {
- if(isset($vmid) && isset($node)){
- $lastid = $this->get_next_vmid();
- if (is_null($snapname)){
- $parameters = array(
- "vmid" => $vmid,
- "node" => $node,
- "vmstate" => true,
- );
- } else {
- $parameters = array(
- "vmid" => $vmid,
- "node" => $node,
- "vmstate" => true,
- "snapname" => $snapname,
- );
- }
- $url = "/nodes/" . $node . "/qemu/" . $vmid . "/snapshot";
- $post = $this->post($url,$parameters);
- if ($post) {
- error_log("Cloned vm " . $vmid . " to " . $lastid . "");
- return true;
- } else {
- error_log("Error cloning vm " . $vmid . " to " . $lastid . "");
- return false;
- }
- } else {
- error_log("no vm or node valid");
- return false;
- }
- }
-
- /*
- * bool|string get_version ()
- * Return the version and minor revision of Proxmox Server
- */
- public function get_version () {
- $version = $this->get("/version");
- if ($version == null) {
- return false;
- } else {
- return $version['version'];
- }
- }
-
- /*
- * object/array? get (string action_path)
- */
- public function get ($action_path) {
- return $this->action($action_path, "GET");
- }
-
- /*
- * bool put (string action_path, array parameters)
- */
- public function put ($action_path, $parameters) {
- return $this->action($action_path, "PUT", $parameters);
- }
-
- /*
- * bool post (string action_path, array parameters)
- */
- public function post ($action_path, $parameters) {
- return $this->action($action_path, "POST", $parameters);
- }
-
- /*
- * bool delete (string action_path)
- */
- public function delete ($action_path) {
- return $this->action($action_path, "DELETE");
- }
-
- // Logout not required, PVEAuthCookie tokens have a 2 hour lifetime.
-}
diff --git a/Api/Admin.php b/src/Api/Admin.php
similarity index 84%
rename from Api/Admin.php
rename to src/Api/Admin.php
index 476adec..e3eec93 100644
--- a/Api/Admin.php
+++ b/src/Api/Admin.php
@@ -29,9 +29,9 @@ class Admin extends \Api_Abstract
/**
* Get list of servers
*
- * @return array
+ * @return array
*/
- public function server_get_list($data)
+ public function server_get_list()
{
// Retrieve all servers from the database
$servers = $this->di['db']->find('service_proxmox_server');
@@ -98,11 +98,11 @@ public function server_get_list($data)
}
/**
- * Get list of storage
+ * Get list of storages
*
- * @return array
+ * @return array
*/
- public function storage_get_list($data)
+ public function storage_get_list()
{
$storages = $this->di['db']->find('service_proxmox_storage');
$storages_grouped = array();
@@ -174,7 +174,7 @@ public function storage_get_list($data)
'size' => $storage->size,
'used' => $storage->used,
'free' => $storage->free,
- 'percent_used' => round($storage->used / $storage->size * 100, 2),
+ 'percent_used' => ($storage->size == 0 || $storage->used == 0 || $storage->free == 0) ? 0 : round($storage->used / $storage->size * 100, 2),
);
}
return $storages_grouped;
@@ -185,8 +185,7 @@ public function storage_get_list($data)
*
* @return array
*/
-
- public function storageclass_get_list($data)
+ public function storageclass_get_list()
{
$storageclasses = $this->di['db']->find('service_proxmox_storageclass');
return $storageclasses;
@@ -197,7 +196,7 @@ public function storageclass_get_list($data)
*
* @return array
*/
- public function storage_controller_get_list($data)
+ public function storage_controller_get_list()
{
// Return Array of storage controllers:
// lsi | lsi53c810 | virtio-scsi-pci | virtio-scsi-single | megasas | pvscsi
@@ -217,9 +216,9 @@ public function storage_controller_get_list($data)
/** *
* Get list of Active Services
*
- * @return array
+ * @return array
*/
- public function service_proxmox_get_list($data)
+ public function service_proxmox_get_list()
{
$services = $this->di['db']->find('service_proxmox');
return $services;
@@ -228,7 +227,7 @@ public function service_proxmox_get_list($data)
/**
* Create a new storageclass
*
- * @return array
+ * @return array
*/
public function storageclass_create($data)
{
@@ -241,7 +240,7 @@ public function storageclass_create($data)
/**
* Retrieve a single storageclass
*
- * @return array
+ * @return array
*/
public function storageclass_get($data)
{
@@ -295,7 +294,7 @@ public function qemu_templates_on_server($data)
/**
* Get list of OS types
*
- * @return array
+ * @return array
*/
public function os_get_list()
@@ -321,7 +320,7 @@ public function os_get_list()
/**
* Get list of BIOS types
*
- * @return array
+ * @return array
*/
public function bios_get_list()
{
@@ -335,7 +334,7 @@ public function bios_get_list()
/**
* Get list of VNC types
*
- * @return array
+ * @return array
*/
public function lxc_appliance_get_list()
{
@@ -355,6 +354,23 @@ public function get_lxc_config_template()
return $lxc_tmpl;
}
+ // Function to enable qemu template
+ public function vm_config_template_enable($data)
+ {
+ $vm_config_template = $this->di['db']->getExistingModelById('service_proxmox_vm_config_template', $data['id'], 'VM Config Template not found');
+ $vm_config_template->state = 'active';
+ $this->di['db']->store($vm_config_template);
+ return $vm_config_template;
+ }
+
+ // Function to disable qemu template
+ public function vm_config_template_disable($data)
+ {
+ $vm_config_template = $this->di['db']->getExistingModelById('service_proxmox_vm_config_template', $data['id'], 'VM Config Template not found');
+ $vm_config_template->state = 'inactive';
+ $this->di['db']->store($vm_config_template);
+ return $vm_config_template;
+ }
/* ################################################################################################### */
/* ########################################## Servers ############################################## */
/* ################################################################################################### */
@@ -382,12 +398,17 @@ public function server_create($data)
'ipv4' => 'Server ipv4 is missing',
'hostname' => 'Server hostname is missing',
'port' => 'Server port is missing',
- 'root_user' => 'Root user is missing',
- 'root_password' => 'Root password is missing',
+ 'auth_type' => 'Authentication type is missing',
'realm' => 'Proxmox user realm is missing',
);
$this->di['validator']->checkRequiredParamsForArray($required, $data);
+ // check if server already exists based on name, ipv4 or hostname
+ $server = $this->di['db']->findOne('service_proxmox_server', 'name=:name OR ipv4=:ipv4 OR hostname=:hostname', array(':name' => $data['name'], ':ipv4' => $data['ipv4'], ':hostname' => $data['hostname']));
+ if ($server) {
+ throw new \Box_Exception('Server already exists');
+ }
+
$server = $this->di['db']->dispense('service_proxmox_server');
$server->name = $data['name'];
$server->group = $data['group'];
@@ -396,19 +417,28 @@ public function server_create($data)
$server->hostname = $data['hostname'];
$server->port = $data['port'];
$server->realm = $data['realm'];
- $server->root_user = $data['root_user'];
- $server->root_password = $data['root_password'];
- $server->config = $data['config'];
$server->active = $data['active'];
$server->created_at = date('Y-m-d H:i:s');
$server->updated_at = date('Y-m-d H:i:s');
$this->di['db']->store($server);
-
$this->di['logger']->info('Created Proxmox server %s', $server->id);
- // Validate server by testing connection
- $this->getService()->test_connection;
+ // check if auth_type is username or token
+ if ($data['auth_type'] == 'username') {
+ $server->root_user = $data['root_user'];
+ $server->root_password = $data['root_password'];
+ $server->tokenname = '';
+ $server->tokenvalue = '';
+ } else {
+ $server->root_user = '';
+ $server->root_password = '';
+ $server->tokenname = $data['tokenname'];
+ $server->tokenvalue = $data['tokenvalue'];
+ }
+ // Validate server by testing connection
+ $this->di['db']->store($server);
+ $this->getService()->test_connection($server);
return true;
}
@@ -416,7 +446,7 @@ public function server_create($data)
* Get server details
*
* @param int $id - server id
- * @return array
+ * @return array
*
* @throws \Box_Exception
*/
@@ -485,7 +515,7 @@ public function server_update($data)
$server->cpu_cores = $data['cpu_cores'];
$server->ram = $data['ram'];
$server->root_user = $data['root_user'];
- $server->root_password = $data['root_password'];
+ $server->root_password = $data['root_password'];
$server->tokenname = $data['tokenname'];
$server->config = $data['config'];
$server->active = $data['active'];
@@ -529,6 +559,7 @@ public function server_delete($data)
// delete server
$server = $this->di['db']->getExistingModelById('service_proxmox_server', $data['id'], 'Server not found');
$this->di['db']->trash($server);
+ return true;
}
}
@@ -568,7 +599,7 @@ public function server_get_from_order($data)
* Receive Hardware Data from proxmox server
*
* @param int $server_id
- * @return array
+ * @return array
*/
public function get_hardware_data($server_id)
{
@@ -613,26 +644,28 @@ public function get_hardware_data($server_id)
$server->ram_allocated += $value['maxmem'];
}
$this->di['db']->store($server);
- $qemu_templates = $service->getQemuTemplates($server);
- foreach ($qemu_templates as $key => $value) {
- if ($value['template'] == 1) {
- $sql = "SELECT * FROM `service_proxmox_qemu_template` WHERE server_id = " . $server_id . " AND vmid = " . $value['vmid'];
- $template = $this->di['db']->getAll($sql);
-
- // if the template exists, update it, otherwise create it
- if (!empty($template)) {
- $template = $this->di['db']->findOne('service_proxmox_qemu_template', 'server_id=:server_id AND vmid=:vmid', array(':server_id' => $server_id, ':vmid' => $value['vmid']));
- } else {
- $template = $this->di['db']->dispense('service_proxmox_qemu_template');
+ $qemu_vms = $service->getQemuVMs($server);
+ foreach ($qemu_vms as $key => $value) {
+ // check if $value['template'] exists, and if it's content is 1
+ if (!empty($value['template'])) {
+ if ($value['template'] == 1) {
+ $sql = "SELECT * FROM `service_proxmox_qemu_template` WHERE server_id = " . $server_id . " AND vmid = " . $value['vmid'];
+ $template = $this->di['db']->getAll($sql);
+
+ // if the template exists, update it, otherwise create it
+ if (!empty($template)) {
+ $template = $this->di['db']->findOne('service_proxmox_qemu_template', 'server_id=:server_id AND vmid=:vmid', array(':server_id' => $server_id, ':vmid' => $value['vmid']));
+ } else {
+ $template = $this->di['db']->dispense('service_proxmox_qemu_template');
+ }
+ $template->vmid = $value['vmid'];
+ $template->server_id = $server_id;
+ $template->name = $value['name'];
+ $template->created_at = date('Y-m-d H:i:s');
+ $template->updated_at = date('Y-m-d H:i:s');
+
+ $stored = $this->di['db']->store($template);
}
- $template->vmid = $value['vmid'];
- $template->server_id = $server_id;
- $template->name = $value['name'];
- $template->created_at = date('Y-m-d H:i:s', $value['ctime']);
- $template->updated_at = date('Y-m-d H:i:s', $value['ctime']);
-
- $stored = $this->di['db']->store($template);
- error_log('template saved: ' . print_r($stored, true));
}
}
@@ -719,7 +752,7 @@ public function test_access($data)
/**
* Get all available templates from any proxmox server
*
- * @return array
+ * @return array
* @throws \Box_Exception
*/
public function pull_lxc_appliances()
@@ -766,14 +799,14 @@ public function pull_lxc_appliances()
*
* @param int $id - storage id
*
- * @return array
+ * @return array
* @throws \Box_Exception
*/
public function storage_get($data)
{
// Retrieve associated storage
$storage = $this->di['db']->findOne('service_proxmox_storage', 'id=:id', array(':id' => $data['storage_id']));
-
+ $server = $this->di['db']->getExistingModelById('service_proxmox_server', $storage->server_id, 'Server not found');
$output = array(
'id' => $storage->id,
'name' => $storage->name,
@@ -786,9 +819,10 @@ public function storage_get($data)
'size' => $storage->size,
'used' => $storage->used,
'free' => $storage->free,
- 'percent_used' => round($storage->used / $storage->size * 100, 2),
+ 'percent_used' => ($storage->size == 0 || $storage->used == 0 || $storage->free == 0) ? 0 : round($storage->used / $storage->size * 100, 2),
// add list of storage classes
- 'storageclasses' => $this->storageclass_get_list($data),
+ 'storageclasses' => $this->storageclass_get_list(),
+ 'server_name' => $server->name,
);
return $output;
@@ -800,22 +834,48 @@ public function storage_get($data)
* TODO: Implement & Fix functionality
* @param int $id - server id
*
- * @return array
+ * @return array
* @throws \Box_Exception
*/
public function storage_update($data)
{
$required = array(
- 'id' => 'Storage id is missing',
- 'storageclass' => 'Storage class is missing',
+ 'storageid' => 'Storage id is missing',
+ 'storageTypeTags' => 'Storage tags are missing',
);
$this->di['validator']->checkRequiredParamsForArray($required, $data);
// Retrieve associated storage
- $storage = $this->di['db']->findOne('service_proxmox_storage', 'id=:id', array(':id' => $data['id']));
- $storage->storageclass = $data['storageclass'];
- $this->di['db']->store($storage);
+ $storage = $this->di['db']->findOne('service_proxmox_storage', 'id=:id', array(':id' => $data['storageid']));
+
+ $storageType = $data['storagetype'];
+ $tagArray = [];
+ foreach ($data['storageTypeTags'] as $tag) {
+ $splitTags = explode(',', $tag);
+ $tagArray = array_merge($tagArray, $splitTags);
+ }
+
+ $jsonArray = [];
+ foreach ($tagArray as $tagId) {
+ // Fetch the tag details from DB
+ $tag_from_db = $this->di['db']->findOne('service_proxmox_tag', 'id=:id AND type=:type', [
+ ':id' => $tagId,
+ ':type' => $storageType
+ ]);
+
+ if ($tag_from_db) {
+ $jsonArray[] = [
+ 'id' => $tag_from_db->id,
+ 'name' => $tag_from_db->name
+ ];
+ } else {
+ error_log("No DB entry found for tagId: $tagId and type: $storageType");
+ }
+ }
+ $jsonString = json_encode($jsonArray);
+ $storage->storageclass = $jsonString;
+ $this->di['db']->store($storage);
return true;
}
@@ -878,7 +938,7 @@ public function product_update($data)
/**
* Get list of vm templates
*
- * @return array
+ * @return array
*/
public function service_get_vmtemplates()
{
@@ -889,7 +949,7 @@ public function service_get_vmtemplates()
/**
* Get list of qemu templates
*
- * @return array
+ * @return array
*/
public function service_get_qemutemplates()
{
@@ -900,7 +960,7 @@ public function service_get_qemutemplates()
/**
* Get list of lxc templates
*
- * @return array
+ * @return array
*/
public function service_get_lxctemplates()
{
@@ -911,7 +971,7 @@ public function service_get_lxctemplates()
/**
* Get list of ip ranges
*
- * @return array
+ * @return array
*/
public function service_get_ip_ranges()
{
@@ -932,7 +992,7 @@ public function service_get_ip_adresses()
/**
* Get list of vlans
*
- * @return array
+ * @return array
*/
public function service_get_vlans()
{
@@ -941,9 +1001,9 @@ public function service_get_vlans()
}
/**
- * Get list of tags by input
+ * Get list of tags by type
*
- * @return array
+ * @return array
*/
public function service_get_tags($data)
{
@@ -951,15 +1011,30 @@ public function service_get_tags($data)
return $output;
}
+ public function service_add_tag($data)
+ {
+ $added_tag = $this->getService()->save_tag($data);
+ return $added_tag;
+ }
+
+ /**
+ * Get list of tags by storage id
+ *
+ * @return array
+ */
+ public function service_get_tags_by_storage($data)
+ {
+ $output = $this->getService()->get_tags_by_storage($data);
+ return $output;
+ }
/**
* Get a vm configuration templates
*
- * @return array
+ * @return array
*/
public function vm_config_template_get($data)
{
- error_log("vm_config_template_get");
$vm_config_template = $this->di['db']->findOne('service_proxmox_vm_config_template', 'id=:id', array(':id' => $data['id']));
if (!$vm_config_template) {
throw new \Box_Exception('VM template not found');
@@ -986,20 +1061,51 @@ public function vm_config_template_get($data)
/**
* Function to get storages for vm config template
*
- * @return array
+ * @return array
*/
public function vm_config_template_get_storages($data)
{
$vm_config_template = $this->di['db']->find('service_proxmox_vm_storage_template', 'template_id=:id', array(':id' => $data['id']));
+ // Replace the storage_type with the name of the tag
+ foreach ($vm_config_template as $key => $value) {
+ $storage_tag_ids = explode(',', json_decode($value->storage_type)); // Split the IDs
+ $tagNames = [];
+
+ foreach ($storage_tag_ids as $tagId) {
+ $tag = $this->di['db']->findOne('service_proxmox_tag', 'id=:id', array(':id' => $tagId));
+ if ($tag) {
+ $tagNames[] = $tag->name;
+ } else {
+ error_log("No DB entry found for tagId: $tagId");
+ }
+ }
+
+ // Combine all the tag names into a single string
+ $vm_config_template[$key]->storage_type = implode(', ', $tagNames);
+ }
+
return $vm_config_template;
}
+ /**
+ * Function to delete vm config template storage
+ *
+ * @return bool
+ */
+ public function vm_config_template_delete_storage($data)
+ {
+ $vm_config_template = $this->di['db']->findOne('service_proxmox_vm_storage_template', 'id=:id', array(':id' => $data['id']));
+ $this->di['db']->trash($vm_config_template);
+ return true;
+ }
+
+
/**
* Get list of lxc configuration templates
*
- * @return array
+ * @return array
*/
public function lxc_config_template_get($data)
{
@@ -1029,7 +1135,7 @@ public function lxc_config_template_get($data)
/**
* Get ip range
*
- * @return array
+ * @return array
*/
public function ip_range_get($data)
{
@@ -1101,24 +1207,7 @@ public function vm_config_template_create($data)
// Fill vm_config_template
$vm_config_template->name = $data['name'];
$vm_config_template->state = "draft";
- /* $vm_config_template->description = $data['description'];
- $vm_config_template->cores = $data['cpu_cores'];
- $vm_config_template->memory = $data['vmmemory'];
- $vm_config_template->balloon = $data['balloon'];
- // if $data['ballon'] is true, check if $data['ballon_size'] is set, otherwise use $data['memory']
- if ($data['balloon'] == true) {
- if (isset($data['balloon_size'])) {
- $vm_config_template->balloon_size = $data['balloon_size'];
- } else {
- $vm_config_template->balloon_size = $data['memory'];
- }
- }
- $vm_config_template->os = $data['os'];
- $vm_config_template->bios = $data['bios'];
- $vm_config_template->onboot = $data['onboot'];
- $vm_config_template->agent = $data['agent'];
- $vm_config_template->created_at = date('Y-m-d H:i:s');
- $vm_config_template->updated_at = date('Y-m-d H:i:s'); */
+
$this->di['db']->store($vm_config_template);
@@ -1146,7 +1235,6 @@ public function vm_template_update($data)
);
$this->di['validator']->checkRequiredParamsForArray($required, $data);
-
// Retrieve associated vm_config_template
$vm_config_template = $this->di['db']->findOne('service_proxmox_vm_config_template', 'id=:id', array(':id' => $data['id']));
@@ -1287,6 +1375,38 @@ public function lxc_template_delete($id)
return true;
}
+ /**
+ * Create VM Config Template Storage
+ *
+ * @return bool
+ */
+ public function vm_config_template_storage_create($data)
+ {
+ $required = array(
+ 'template_id' => 'Template ID is missing',
+ 'storage_size' => 'Storage size is missing',
+ 'storage_type_tags' => 'Storage type is missing',
+ 'storage_controller' => 'Storage controller is missing',
+ );
+
+ $this->di['validator']->checkRequiredParamsForArray($required, $data);
+
+
+ // dispense new vm_config_template_storage
+ $vm_config_template_storage = $this->di['db']->dispense('service_proxmox_vm_storage_template');
+ // Fill vm_config_template_storage
+ $vm_config_template_storage->template_id = $data['template_id'];
+ $vm_config_template_storage->size = $data['storage_size'];
+ $vm_config_template_storage->storage_type = json_encode($data['storage_type_tags']);
+ $vm_config_template_storage->controller = $data['storage_controller'];
+ $vm_config_template_storage->created_at = date('Y-m-d H:i:s');
+ $vm_config_template_storage->updated_at = date('Y-m-d H:i:s');
+ $this->di['db']->store($vm_config_template_storage);
+ return true;
+ }
+
+
+
/**
* Create ip range
*
@@ -1337,7 +1457,7 @@ public function ip_range_update($data)
$this->di['validator']->checkRequiredParamsForArray($required, $data);
// Retrieve associated ip_network
- $ip_range = $this->di['db']->findOne('service_proxmox_ip_range', 'id=:id', array(':id' => $id));
+ $ip_range = $this->di['db']->findOne('service_proxmox_ip_range', 'id=:id', array(':id' => $data['id']));
// Fill ip_network
$ip_range->cidr = $data['cidr'];
@@ -1355,7 +1475,7 @@ public function ip_range_update($data)
* Delete ip range
*
* @param int $id
- * @return array
+ * @return array
*/
public function ip_range_delete($id)
{
@@ -1435,9 +1555,15 @@ public function client_vlan_update($data)
*/
public function client_vlan_delete($data)
{
+ $required = array(
+ 'id' => 'ID is missing',
+ );
+
+ $this->di['validator']->checkRequiredParamsForArray($required, $data);
+
$client_network = $this->di['db']->findOne('service_proxmox_client_vlan', 'id = ?', [$data['id']]);
$this->di['db']->trash($client_network);
- $this->di['logger']->info('Delete Client Network %s', $id);
+ $this->di['logger']->info('Delete Client Network %s', $data['id']);
return true;
}
@@ -1468,7 +1594,7 @@ public function proxmox_backup_config($data)
/**
* List existing Backups
*
- * @return array
+ * @return array
*/
public function proxmox_list_backups()
{
@@ -1502,4 +1628,14 @@ public function get_module_version()
$config = $this->di['mod_config']('Serviceproxmox');
return $config['version'];
}
+
+ /**
+ * Returns the salt value from the configuration.
+ *
+ * @return string The salt value.
+ */
+ private function _getSalt()
+ {
+ return $this->di['config']['salt'];
+ }
} // EOF
\ No newline at end of file
diff --git a/Api/Client.php b/src/Api/Client.php
similarity index 100%
rename from Api/Client.php
rename to src/Api/Client.php
diff --git a/Controller/Admin.php b/src/Controller/Admin.php
similarity index 70%
rename from Controller/Admin.php
rename to src/Controller/Admin.php
index d1cdfe7..471e412 100644
--- a/Controller/Admin.php
+++ b/src/Controller/Admin.php
@@ -37,7 +37,7 @@ public function getDi(): ?\Pimple\Container
*
* @return array
*/
- public function fetchNavigation()
+ public function fetchNavigation(): array
{
return array(
'group' => array(
@@ -76,8 +76,10 @@ public function fetchNavigation()
/**
* Registers the admin area routes
*
+ * @param \Box_App $app
+ * @return void
*/
- public function register(\Box_App &$app)
+ public function register(\Box_App &$app): void
{
$app->get('/serviceproxmox', 'get_index', null, get_class($this));
$app->get('/serviceproxmox/templates', 'get_templates', null, get_class($this));
@@ -97,12 +99,17 @@ public function register(\Box_App &$app)
$app->get('/serviceproxmox/templates/lxc_config/:id', 'get_lxc_config_template', array('id' => '[0-9]+'), get_class($this));
$app->get('/serviceproxmox/templates/vm_config', 'get_vm_config_template', null, get_class($this));
$app->get('/serviceproxmox/templates/vm_config/:id', 'get_vm_config_template', array('id' => '[0-9]+'), get_class($this));
+ $app->get('/serviceproxmox/templates/enable/:id', 'enable_template', array('id' => '[0-9]+'), get_class($this));
+ $app->get('/serviceproxmox/templates/disable/:id', 'disable_template', array('id' => '[0-9]+'), get_class($this));
}
/**
* Renders the admin area index page
+ *
+ * @param \Box_App $app
+ * @return string
*/
- public function get_index(\Box_App $app)
+ public function get_index(\Box_App $app): string
{
$this->di['is_admin_logged'];
return $app->render('mod_serviceproxmox_index');
@@ -110,32 +117,70 @@ public function get_index(\Box_App $app)
/**
* Renders the admin area templates page
+ *
+ * @param \Box_App $app
+ * @return string
*/
- public function get_templates(\Box_App $app)
+ public function get_templates(\Box_App $app): string
{
return $app->render('mod_serviceproxmox_templates');
}
+ /**
+ * Enables the QEMU Template
+ *
+ * @param \Box_App $app
+ */
+ public function enable_template(\Box_App $app, $id)
+ {
+ $api = $this->di['api_admin'];
+ $api->Serviceproxmox_vm_config_template_enable(array('id' => $id));
+ return $app->redirect('serviceproxmox/templates');
+ }
+ /**
+ * Disables the QEMU Template
+ *
+ * @param \Box_App $app
+ */
+ public function disable_template(\Box_App $app, $id)
+ {
+ $api = $this->di['api_admin'];
+ $api->Serviceproxmox_vm_config_template_disable(array('id' => $id));
+ return $app->redirect('serviceproxmox/templates');
+ }
/**
* Renders the admin area ipam page
+ *
+ * @param \Box_App $app
+ * @return string
*/
- public function get_ipam(\Box_App $app)
+ public function get_ipam(\Box_App $app): string
{
return $app->render('mod_serviceproxmox_ipam');
}
/**
- * Handles CRUD for Proxmox Servers
+ * Handles Updates for Proxmox Servers
+ *
+ * @param \Box_App $app
+ * @param int $id
*/
- public function get_server(\Box_App $app, $id)
+ public function get_server(\Box_App $app, $id): string
{
$api = $this->di['api_admin'];
$server = $api->Serviceproxmox_server_get(array('server_id' => $id));
return $app->render('mod_serviceproxmox_server', array('server' => $server));
}
- public function get_server_by_group(\Box_App $app, $id)
+ /**
+ * Retrieves the server by group ID.
+ *
+ * @param \Box_App $app The Box application instance.
+ * @param int $id The ID of the group.
+ * @return string The server information.
+ */
+ public function get_server_by_group(\Box_App $app, $id): string
{
$api = $this->di['api_admin'];
$server = $api->Serviceproxmox_servers_in_group(array('group' => $id));
@@ -143,9 +188,14 @@ public function get_server_by_group(\Box_App $app, $id)
}
/**
- * Handles CRUD for Proxmox Storage
+ * Handles Updates for Proxmox Storage
+ *
+ * @param \Box_App $app
+ * @param int $id
+ *
+ * @return string
*/
- public function get_storage(\Box_App $app, $id)
+ public function get_storage(\Box_App $app, $id): string
{
$api = $this->di['api_admin'];
$storage = $api->Serviceproxmox_storage_get(array('storage_id' => $id));
@@ -153,9 +203,14 @@ public function get_storage(\Box_App $app, $id)
}
/**
- * Handles CRUD for IP Range
+ * Handles Updates for IP Range
+ *
+ * @param \Box_App $app
+ * @param int $id
+ *
+ * @return string
*/
- public function get_ip_range(\Box_App $app, $id)
+ public function get_ip_range(\Box_App $app, $id): string
{
$api = $this->di['api_admin'];
$ip_range = $api->Serviceproxmox_ip_range_get(array('id' => $id));
@@ -163,9 +218,12 @@ public function get_ip_range(\Box_App $app, $id)
}
/**
- * Handles CRUD for Client VLAN
+ * Handles Updates for Client VLAN
+ *
+ * @param \Box_App $app
+ * @param int $id
*/
- public function client_vlan(\Box_App $app, $id)
+ public function client_vlan(\Box_App $app, $id): string
{
$api = $this->di['api_admin'];
$client_vlan = $api->Serviceproxmox_vlan_get(array('id' => $id));
@@ -173,9 +231,12 @@ public function client_vlan(\Box_App $app, $id)
}
/**
- * Handles CRUD for LXC Config Templates
+ * Handles Updates for LXC Config Templates
+ *
+ * @param \Box_App $app
+ * @param int $id
*/
- public function get_lxc_config_template(\Box_App $app, $id)
+ public function get_lxc_config_template(\Box_App $app, $id): string
{
$api = $this->di['api_admin'];
$lxc_config_template = $api->Serviceproxmox_lxc_config_template_get(array('id' => $id));
@@ -183,11 +244,15 @@ public function get_lxc_config_template(\Box_App $app, $id)
}
/**
- * Handles CRUD for VM Config Templates
+ * Handles Updates for VM Config Templates
+ *
+ * @param \Box_App $app
+ * @param int $id
+ *
+ * @return string
*/
- public function get_vm_config_template(\Box_App $app, $id)
+ public function get_vm_config_template(\Box_App $app, $id): string
{
- error_log("Controller get_vm_config_template");
$api = $this->di['api_admin'];
$vm_config_template = $api->Serviceproxmox_vm_config_template_get(array('id' => $id));
return $app->render('mod_serviceproxmox_templates_qemu', array('vm_config_template' => $vm_config_template));
@@ -195,12 +260,14 @@ public function get_vm_config_template(\Box_App $app, $id)
/**
* Renders the admin area settings page
+ *
+ * @param \Box_App $app
*/
public function start_backup(\Box_App $app)
{
$api = $this->di['api_admin'];
- $backup = $api->Serviceproxmox_proxmox_backup_config('backup');
+ $api->Serviceproxmox_proxmox_backup_config('backup');
return $app->redirect('extension/settings/serviceproxmox');
}
}
diff --git a/Controller/Client.php b/src/Controller/Client.php
similarity index 65%
rename from Controller/Client.php
rename to src/Controller/Client.php
index b7fcdf0..ad3712c 100644
--- a/Controller/Client.php
+++ b/src/Controller/Client.php
@@ -22,7 +22,7 @@ class Client implements \FOSSBilling\InjectionAwareInterface
{
protected ?\Pimple\Container $di;
- public function setDi(\Pimple\Container $di): void
+ public function setDi(\Pimple\Container|null $di): void
{
$this->di = $di;
}
@@ -32,6 +32,11 @@ public function getDi(): ?\Pimple\Container
return $this->di;
}
+ /**
+ * Registers a new client route.
+ *
+ * @param \Box_App &$app The Box_App instance.
+ */
public function register(\Box_App &$app)
{
// register all routers to load novnc app.js & dependencies from proxmox
@@ -40,20 +45,46 @@ public function register(\Box_App &$app)
$app->get('/serviceproxmox/novnc/:folder/:subfolder/:filename.:fileending', 'get_novnc_appjs_folder_subfolder_filename', [], static::class);
}
- // create functions to call get_novnc_appjs with paths
- // create function for only filename
+
+ /**
+ * Returns the filename of the NoVNC app.js file.
+ *
+ * @param \Box_App $app The Box_App object.
+ * @param string $filename The name of the file.
+ * @param string $fileending The file extension.
+ * @return string The filename of the NoVNC app.js file.
+ */
public function get_novnc_appjs_filename(\Box_App $app, $filename, $fileending)
{
$file_path = $filename . '.' . $fileending;
return $this->get_novnc_appjs($app, $file_path);
}
- // create function for filename and folder
+
+ /**
+ * Returns the filename of the NoVNC app.js file in a folder.
+ *
+ * @param \Box_App $app The Box_App object.
+ * @param string $folder The name of the folder.
+ * @param string $filename The name of the file.
+ * @param string $fileending The file extension.
+ * @return string The filename of the NoVNC app.js file in a folder.
+ */
public function get_novnc_appjs_folder_filename(\Box_App $app, $folder, $filename, $fileending)
{
$file_path = $folder . '/' . $filename . '.' . $fileending;
return $this->get_novnc_appjs($app, $file_path);
}
- // create function for filename, folder and subfolder
+
+ /**
+ * Returns the filename of the NoVNC app.js file in a subfolder.
+ *
+ * @param \Box_App $app The Box_App object.
+ * @param string $folder The name of the folder.
+ * @param string $subfolder The name of the subfolder.
+ * @param string $filename The name of the file.
+ * @param string $fileending The file extension.
+ * @return string The filename of the NoVNC app.js file in a subfolder.
+ */
public function get_novnc_appjs_folder_subfolder_filename(\Box_App $app, $folder, $subfolder, $filename, $fileending)
{
$file_path = $folder . '/' . $subfolder . '/' . $filename . '.' . $fileending;
@@ -61,7 +92,13 @@ public function get_novnc_appjs_folder_subfolder_filename(\Box_App $app, $folder
}
- // create get_novnc_appjs function
+ /**
+ * Returns the contents of the specified JavaScript file for the noVNC app.
+ *
+ * @param \Box_App $app The Box application instance.
+ * @param string $file_path The path to the JavaScript file.
+ * @return string The contents of the JavaScript file.
+ */
public function get_novnc_appjs(\Box_App $app, $file_path)
{
$api = $this->di['api_client'];
diff --git a/ProxmoxAuthentication.php b/src/ProxmoxAuthentication.php
similarity index 53%
rename from ProxmoxAuthentication.php
rename to src/ProxmoxAuthentication.php
index 375d75c..aa6e0a6 100644
--- a/ProxmoxAuthentication.php
+++ b/src/ProxmoxAuthentication.php
@@ -19,6 +19,12 @@
namespace Box\Mod\Serviceproxmox;
+/**
+ * Authentication Class Trait for FOSSSBilling Proxmox Module
+ *
+ * This class trait contains all the functions that are used to manage the authentication inside the Proxmox Module.
+ *
+ */
trait ProxmoxAuthentication
{
/* ################################################################################################### */
@@ -28,22 +34,18 @@ trait ProxmoxAuthentication
/**
* Function to set up proxmox server for fossbilling
* @param $server - server object
- * @return array - array of permission information
+ * @return bool - array of permission information
*/
public function prepare_pve_setup($server)
{
- // Retrieve the server access information
- $serveraccess = $this->find_access($server);
-
- // Create a new instance of the PVE2_API class with the server access details
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $server->tokenname, tokensecret: $server->tokenvalue);
-
+ $proxmox = $this->getProxmoxInstance($server);
// Attempt to log in to the server using the API
if (!$proxmox->login()) {
- throw new \Box_Exception("Failed to connect to the server.");
+ throw new \Box_Exception("Failed to log in to the proxmox server. Check username & password and try again.");
}
- // Create an API token for the Admin user if not logged in via API token
+ // If this code runs, login already worked, so either username + password or token login worked.
+ // Create an API token for the Admin user if there is both tokenname and tokenvalue already present
if (empty($server->tokenname) || empty($server->tokenvalue)) {
// Check if the connecting user has the 'Realm.AllocateUser' permission
$permissions = $proxmox->get("/access/permissions");
@@ -74,7 +76,7 @@ public function prepare_pve_setup($server)
case 0:
// Create a new group
$groupid = 'fossbilling_' . rand(1000, 9999);
- $newgroup = $proxmox->post("/access/groups", array('groupid' => $groupid, 'comment' => 'fossbilling group'));
+ $proxmox->post("/access/groups", array('groupid' => $groupid, 'comment' => 'fossbilling group'));
break;
case 1:
// Use the existing group
@@ -83,85 +85,93 @@ public function prepare_pve_setup($server)
throw new \Box_Exception("More than one group found");
break;
}
- }
-
-
-
- // Validate if there already is a user and token for fossbilling
- $users = $proxmox->get("/access/users");
- $found = 0;
- // Iterate through the users and check for a user beginning with 'fb'
- foreach ($users as $user) {
- if (strpos($user['userid'], 'fb') === 0) {
- $found += 1;
- $userid = $user['userid'];
- }
- // Handle the cases where there are no users, one user, or multiple users
- switch ($found) {
- case 0:
- // Create a new user
- $userid = 'fb_' . rand(1000, 9999) . '@pve'; // TODO: Make realm configurable in the module settings
- $newuser = $proxmox->post("/access/users", array('userid' => $userid, 'password' => $this->di['tools'], 'enable' => 1, 'comment' => 'fossbilling user', 'groups' => $groupid));
- // Create a token for the new user
- $token = $proxmox->post("/access/users/" . $userid . "/token/fb_access", array());
-
- // Check if the token was created successfully
- if ($token) {
- $server->tokenname = $token['full-tokenid'];
- $server->tokenvalue = $token['value'];
- } else {
- throw new \Box_Exception("Failed to create token for fossbilling user");
+ // Validate if there already is a user and token for fossbilling
+ $users = $proxmox->get("/access/users");
+ $found = 0;
+ // Iterate through the users and check for a user beginning with 'fb'
+ foreach ($users as $user) {
+ if (strpos($user['userid'], 'fb') === 0) {
+ $found += 1;
+ $userid = $user['userid'];
+ }
+ // Handle the cases where there are no users, one user, or multiple users
+ switch ($found) {
+ case 0:
+ // Create a new user
+ $userid = 'fb_' . rand(1000, 9999) . '@pve'; // TODO: Make realm configurable in the module settings
+ // $groupid has to be defined because it is set in the switch statement above, otherwise it would throw an exception. $proxmox also, because otherwise, login would fail and break.
+ $proxmox->post("/access/users", array('userid' => $userid, 'password' => $this->di['tools'], 'enable' => 1, 'comment' => 'fossbilling user', 'groups' => $groupid)); /* @phpstan-ignore-line */
+
+ // Create a token for the new user
+ $token = $proxmox->post("/access/users/" . $userid . "/token/fb_access", array()); /* @phpstan-ignore-line Proxmox is set, otherwise code errors out */
+
+ // Check if the token was created successfully
+ if ($token) {
+ $server->tokenname = $token['full-tokenid'];
+ $server->tokenvalue = $token['value'];
+ } else {
+ throw new \Box_Exception("Failed to create token for fossbilling user");
+ break;
+ }
break;
- }
- break;
- case 1:
- // Create a token for the existing user
- $token = $proxmox->post("/access/users/" . $userid . "/token/fb_access", array());
- if ($token) {
- $server->tokenname = $token['full-tokenid'];
- $server->tokenvalue = $token['value'];
- } else {
- throw new \Box_Exception("Failed to create token for fossbilling user");
+ case 1:
+ // Create a token for the existing user
+ $token = $proxmox->post("/access/users/" . $userid . "/token/fb_access", array());/* @phpstan-ignore-line Proxmox is set, otherwise code errors out */
+ if ($token) {
+ $server->tokenname = $token['full-tokenid'];
+ $server->tokenvalue = $token['value'];
+ } else {
+ throw new \Box_Exception("Failed to create token for fossbilling user");
+ break;
+ }
+ break;
+ default:
+ throw new \Box_Exception("There are more than one fossbilling users on the server. Please delete all but one.");
break;
+ }
+ // Create permissions for the newly created token
+ // Set up permissions for the token (Admin user) to manage users, groups, and other administrative tasks
+ $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEUserAdmin', 'propagate' => 1, 'users' => $userid)); /* @phpstan-ignore-line */
+ $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEAuditor', 'propagate' => 1, 'users' => $userid)); /* @phpstan-ignore-line */
+ $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVESysAdmin', 'propagate' => 1, 'users' => $userid)); /* @phpstan-ignore-line */
+ $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEPoolAdmin', 'propagate' => 1, 'users' => $userid)); /* @phpstan-ignore-line */
+ $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEDatastoreAdmin', 'propagate' => 1, 'users' => $userid)); /* @phpstan-ignore-line */
+ $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEUserAdmin', 'propagate' => 1, 'tokens' => $server->tokenname));
+ $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEAuditor', 'propagate' => 1, 'tokens' => $server->tokenname));
+ $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVESysAdmin', 'propagate' => 1, 'tokens' => $server->tokenname));
+ $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEPoolAdmin', 'propagate' => 1, 'tokens' => $server->tokenname));
+ $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEDatastoreAdmin', 'propagate' => 1, 'tokens' => $server->tokenname));
+
+ // Sleep for 5 seconds
+ sleep(5);
+
+ // Delete the root password and unset the PVE2_API instance
+ $server->root_password = null;
+ unset($proxmox);
+
+ // Return the test_access result for the server
+ return $this->test_access($server);
+ }
+ } elseif (!empty($server->tokenname) && !empty($server->tokenvalue)) {
+ // Validate Permissions for the token
+ $permissions = $proxmox->get("/access/acl/");
+ // Check for 'PVEUserAdmin', 'PVEAuditor', 'PVESysAdmin', 'PVEPoolAdmin', and 'PVEDatastoreAdmin' permissions, and if they don't exist, try to create them.
+ $required_permissions = array('PVEUserAdmin', 'PVEAuditor', 'PVESysAdmin', 'PVEPoolAdmin', 'PVEDatastoreAdmin');
+ foreach ($required_permissions as $permission) {
+ $found_permission = 0;
+ foreach ($permissions as $acl) {
+ if ($acl['roleid'] == $permission) {
+ $found_permission += 1;
}
- break;
- default:
- throw new \Box_Exception("There are more than one fossbilling users on the server. Please delete all but one.");
- break;
+ }
+ if (!$found_permission) {
+ $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => $permission, 'propagate' => 1, 'tokens' => $server->tokenname));
+ }
}
- // Create permissions for the newly created token
- // Set up permissions for the token (Admin user) to manage users, groups, and other administrative tasks
- $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEUserAdmin', 'propagate' => 1, 'users' => $userid));
- $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEAuditor', 'propagate' => 1, 'users' => $userid));
- $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVESysAdmin', 'propagate' => 1, 'users' => $userid));
- $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEPoolAdmin', 'propagate' => 1, 'users' => $userid));
- $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEDatastoreAdmin', 'propagate' => 1, 'users' => $userid));
- $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEUserAdmin', 'propagate' => 1, 'tokens' => $server->tokenname));
- $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEAuditor', 'propagate' => 1, 'tokens' => $server->tokenname));
- $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVESysAdmin', 'propagate' => 1, 'tokens' => $server->tokenname));
- $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEPoolAdmin', 'propagate' => 1, 'tokens' => $server->tokenname));
- $permissions = $proxmox->put("/access/acl/", array('path' => '/', 'roles' => 'PVEDatastoreAdmin', 'propagate' => 1, 'tokens' => $server->tokenname));
-
- // Sleep for 5 seconds
- sleep(5);
-
- // Check if the permissions were created correctly by logging in and creating another user
- /*
- echo "";
- echo "";
- echo "";
- echo "";
- echo "
";
- */
-
- // Delete the root password and unset the PVE2_API instance
- $server->root_password = null;
- unset($proxmox);
-
- // Return the test_access result for the server
return $this->test_access($server);
}
+ return false;
}
@@ -174,12 +184,7 @@ public function prepare_pve_setup($server)
*/
public function test_access($server)
{
- // Retrieve the server access information
- $serveraccess = $this->find_access($server);
-
- // Create a new instance of the PVE2_API class with the server access details
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $server->tokenname, tokensecret: $server->tokenvalue);
-
+ $proxmox = $this->getProxmoxInstance($server);
// Attempt to log in to the server using the API
if (!$proxmox->login()) {
throw new \Box_Exception("Failed to connect to the server. testpmx");
@@ -189,7 +194,7 @@ public function test_access($server)
$userid = 'tfb_' . rand(1000, 9999) . '@pve'; // TODO: Make realm configurable in the module settings
// Create a new user for testing purposes
- $newuser = $proxmox->post("/access/users", array('userid' => $userid, 'password' => $this->di['tools']->generatePassword(16, 4), 'enable' => '1', 'comment' => 'fossbilling user 2'));
+ $proxmox->post("/access/users", array('userid' => $userid, 'password' => $this->di['tools']->generatePassword(16, 4), 'enable' => '1', 'comment' => 'FOSSBilling test user ' . $userid));
// Retrieve the newly created user
$newuser = $proxmox->get("/access/users/" . $userid);
@@ -226,8 +231,7 @@ public function create_client_user($server, $client)
$clientuser = $this->di['db']->dispense('service_proxmox_users');
$clientuser->client_id = $client->id;
$this->di['db']->store($clientuser);
- $serveraccess = $this->find_access($server);
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $server->tokenname, tokensecret: $server->tokenvalue);
+ $proxmox = $this->getProxmoxInstance($server);
if (!$proxmox->login()) {
throw new \Box_Exception("Failed to connect to the server. create_client_user");
}
diff --git a/ProxmoxIPAM.php b/src/ProxmoxIPAM.php
similarity index 59%
rename from ProxmoxIPAM.php
rename to src/ProxmoxIPAM.php
index 5c1aa6a..93f0d89 100644
--- a/ProxmoxIPAM.php
+++ b/src/ProxmoxIPAM.php
@@ -19,15 +19,18 @@
namespace Box\Mod\Serviceproxmox;
/**
- * Proxmox module for FOSSBilling
+ * IPAM Class Trait for FOSSSBilling Proxmox Module
+ *
+ * This class trait contains all the functions that are used to manage the IPAM inside the Proxmox Module.
+ *
*/
trait ProxmoxIPAM
{
- /* ################################################################################################### */
- /* #################################### IPAM Management ########################################### */
- /* ################################################################################################### */
-
- // Function that gets all the VM templates and returns them as an array
+ /**
+ * Retrieves the IP ranges from the Proxmox IPAM.
+ *
+ * @return array An array of IP ranges.
+ */
public function get_ip_ranges()
{
// get all the VM templates from the service_proxmox_vm_config_template table
@@ -35,7 +38,12 @@ public function get_ip_ranges()
return $ip_ranges;
}
- // Function that gets all IP Adresses
+
+ /**
+ * Retrieves a list of IP addresses from the Proxmox IPAM.
+ *
+ * @return array An array of IP addresses.
+ */
public function get_ip_adresses()
{
// get all the VM templates from the service_proxmox_vm_config_template table
@@ -43,12 +51,14 @@ public function get_ip_adresses()
return $ip_addresses;
}
- // Function that gets all the LXC templates and returns them as an array
+ /**
+ * Retrieves a list of VLANs from the Proxmox IPAM service.
+ *
+ * @return array An array of VLAN objects, each containing the VLAN ID and name.
+ */
public function get_vlans()
{
- // get all the LXC templates from the service_proxmox_lxc_config_template table
$vlans = $this->di['db']->find('service_proxmox_client_vlan');
- // Fill in the client name
foreach ($vlans as $vlan) {
$client = $this->di['db']->getExistingModelById('client', $vlan->client_id);
$vlan->client_name = $client->first_name . " " . $client->last_name;
@@ -56,8 +66,19 @@ public function get_vlans()
return $vlans;
}
+}
+
- /* ################################################################################################### */
- /* ################################### Manage PVE Network ######################################### */
- /* ################################################################################################### */
+/* ################################################################################################### */
+/* ################################### Manage PVE Network ######################################### */
+/* ################################################################################################### */
+
+
+/**
+ * Trait ProxmoxNetwork
+ *
+ * This class trait contains all the functions that are used to manage the SDN on the PVE Hosts.
+ */
+trait ProxmoxNetwork
+{
}
diff --git a/src/ProxmoxServer.php b/src/ProxmoxServer.php
new file mode 100644
index 0000000..e2f7975
--- /dev/null
+++ b/src/ProxmoxServer.php
@@ -0,0 +1,293 @@
+getProxmoxInstance($server);
+ // check if tokenname and tokenvalue contain values by checking their content
+ if (empty($server->tokenname) || empty($server->tokenvalue)) {
+ if (!empty($server->root_user) && !empty($server->root_password)) {
+
+ if ($proxmox->login()) {
+ error_log("Serviceproxmox: Login with username and password successful");
+ return true;
+ } else {
+ throw new \Box_Exception("Login to Proxmox Host failed");
+ }
+ } else {
+ throw new \Box_Exception("No login information provided");
+ }
+ } else if ($proxmox->getVersion()) {
+ error_log("Serviceproxmox: Login with token successful!");
+ return true;
+ } else {
+ throw new \Box_Exception("Failed to connect to the server.");
+ }
+ }
+
+
+ /**
+ * This function tests the token connection to the Proxmox server.
+ * It checks if the tokenname and tokenvalue contain values by checking their content.
+ * If they are empty, it throws an exception.
+ * If the login with token is successful, it retrieves the permissions and checks for 'Realm.AllocateUser' permission.
+ * If the permission is not found, it throws an exception.
+ * It also validates if there already is a group for fossbilling and checks if the groupid is the same as the id of the token.
+ *
+ * @param object $server The server object containing the tokenname and tokenvalue.
+ * @return bool Returns true if the token connection is successful.
+ * @throws \Box_Exception Throws an exception if the token access fails, the connection to the server fails, or the token does not have 'Realm.AllocateUser' permission.
+ */
+ public function test_token_connection($server)
+ {
+ $proxmox = $this->getProxmoxInstance($server);
+ // check if tokenname and tokenvalue contain values by checking their content
+ if (empty($server->tokenname) || empty($server->tokenvalue)) {
+ throw new \Box_Exception("Token Access Failed: No tokenname or tokenvalue provided");
+ } else if ($proxmox->get_version()) {
+ error_log("Serviceproxmox: Login with token successful!");
+ $permissions = $proxmox->get("/access/permissions");
+ $found_permission = 0;
+ // Iterate through the permissions and check for 'Realm.AllocateUser' permission
+ foreach ($permissions as $permission) {
+ if ($permission['Realm.AllocateUser'] == 1) {
+ $found_permission += 1;
+ }
+ }
+ // Throw an exception if the 'Realm.AllocateUser' permission is not found
+ if (!$found_permission) {
+ throw new \Box_Exception("Token does not have 'Realm.AllocateUser' permission");
+ }
+
+ // Validate if there already is a group for fossbilling
+ $groups = $proxmox->get("/access/groups");
+ $foundgroups = 0;
+ // Iterate through the groups and check for a group beginning with 'fossbilling'
+ foreach ($groups as $group) {
+ if (strpos($group['groupid'], 'fossbilling') === 0) {
+ $foundgroups += 1;
+ $groupid = $group['groupid'];
+ }
+ // check if groupid is the same as the id of the token (fb_1234@pve!fb_access)
+ $fb_token_instanceid = explode('@', $server->tokenname)[1];
+
+
+ if ($group['groupid'] == $server->tokenname) {
+ $foundgroups += 1;
+ $groupid = $group['groupid'];
+ }
+ }
+ return true;
+ } else {
+ throw new \Box_Exception("Failed to connect to the server.");
+ }
+ }
+
+
+
+
+ /**
+ * Finds an empty server with the best CPU and RAM usage ratio based on the given product's group and filling.
+ *
+ * @param object $product The product to find an empty server for.
+ * @return int|null The ID of the server with the best CPU and RAM usage ratio, or null if no server is found.
+ */
+ public function find_empty($product)
+ {
+ $productconfig = json_decode($product->config, 1);
+ $group = $productconfig['group'];
+ $filling = $productconfig['filling'];
+
+ // retrieve overprovisioning information from extension settings
+ $config = $this->di['mod_config']('Serviceproxmox');
+ $cpu_overprovion_percent = $config['cpu_overprovisioning'];
+ $ram_overprovion_percent = $config['ram_overprovisioning'];
+ $avoid_overprovision = $config['avoid_overprovision'];
+
+
+ $servers = $this->di['db']->find('service_proxmox_server', ['status' => 'active']);
+
+ $server = null;
+ $server_ratio = 0;
+ // use values from database to calculate ratio and store the server id, cpu and ram usage ratio if it's better than the previous one
+ foreach ($servers as $s) {
+ $cpu_usage = $s['cpu_cores_allocated'];
+ $ram_usage = $s['ram_allocated'];
+ $cpu_cores = $s['cpu_cores'];
+ $ram = $s['ram'];
+
+ $cpu_ratio = $cpu_usage / $cpu_cores;
+ $ram_ratio = $ram_usage / $ram;
+
+ // if avoid_overprovision is set to true, servers with a ratio of >1 are ignored
+ if ($avoid_overprovision && ($cpu_ratio > 1 || $ram_ratio > 1)) {
+ continue;
+ }
+ // calculate ratio with overprovisioning
+ $cpu_ratio = $cpu_ratio * (1 + $cpu_overprovion_percent / 100);
+ $ram_ratio = $ram_ratio * (1 + $ram_overprovion_percent / 100);
+ // check current best ratio
+ if ($cpu_ratio + $ram_ratio > $server_ratio) {
+ $server_ratio = $cpu_ratio + $ram_ratio;
+ $server = $s['id'];
+ }
+ }
+ // if no server is found, return null
+ if ($server == null) {
+ return null;
+ }
+ // if a server is found, return the id
+ return $server;
+ }
+
+ /**
+ * Find the access of the server based on its hostname, IPv4 or IPv6 address.
+ *
+ * @param object $server The server object containing hostname, IPv4 and/or IPv6 address.
+ *
+ * @return string The hostname, IPv4 or IPv6 address of the server.
+ *
+ * @throws \Box_Exception If no hostname, IPv4 or IPv6 address is found for the server.
+ */
+ public function find_access($server)
+ {
+ if (!empty($server->hostname)) {
+ return $server->hostname;
+ } else if (!empty($server->ipv4)) {
+ return $server->ipv4;
+ } else if (!empty($server->ipv6)) {
+ return $server->ipv6;
+ } else {
+ throw new \Box_Exception('No IPv6, IPv4 or Hostname found for server ' . $server->id);
+ }
+ }
+
+ /**
+ * Returns hardware data for a given server.
+ *
+ * @param object $server The server object.
+ * @return array The hardware data.
+ * @throws \Box_Exception If failed to connect to the server.
+ */
+ public function getHardwareData($server)
+ {
+ $proxmox = $this->getProxmoxInstance($server);
+ if ($proxmox->login()) {
+ error_log("ProxmoxServer.php: getHardwareData: Login successful");
+ $hardware = $proxmox->get("/nodes/" . $server->name . "/status");
+ return $hardware;
+ } else {
+ throw new \Box_Exception("Failed to connect to the server. hw Token Access Failed");
+ }
+ }
+
+ /**
+ * Returns storage data for a given server.
+ *
+ * @param object $server The server object.
+ * @return array The storage data.
+ * @throws \Box_Exception If failed to connect to the server.
+ */
+ public function getStorageData($server)
+ {
+ $proxmox = $this->getProxmoxInstance($server);
+ if ($proxmox->login()) {
+ $storage = $proxmox->get("/nodes/" . $server->name . "/storage");
+ return $storage;
+ } else {
+ throw new \Box_Exception("Failed to connect to the server. st");
+ }
+ }
+
+ /**
+ * Returns assigned resources for a given server.
+ *
+ * @param object $server The server object.
+ * @return array The assigned resources.
+ * @throws \Box_Exception If failed to connect to the server.
+ */
+ public function getAssignedResources($server)
+ {
+ $proxmox = $this->getProxmoxInstance($server);
+ if ($proxmox->login()) {
+ $assigned_resources = $proxmox->get("/nodes/" . $server->name . "/qemu");
+ return $assigned_resources;
+ } else {
+ throw new \Box_Exception("Failed to connect to the server. st");
+ }
+ }
+
+ /**
+ * Returns available appliances.
+ *
+ * @return array The available appliances.
+ * @throws \Box_Exception If failed to connect to the server.
+ */
+ public function getAvailableAppliances()
+ {
+ $server = $this->di['db']->getExistingModelById('service_proxmox_server', 1, 'Server not found');
+
+ $proxmox = $this->getProxmoxInstance($server);
+ if ($proxmox->login()) {
+ $appliances = $proxmox->get("/nodes/" . $server->name . "/aplinfo");
+ return $appliances;
+ } else {
+ throw new \Box_Exception("Failed to connect to the server. st");
+ }
+ }
+
+ /**
+ * Returns an array of QEMU virtual machines for the given Proxmox server.
+ *
+ * @param object $server The server object containing the server name.
+ * @return array An array of QEMU virtual machines.
+ * @throws \Box_Exception If failed to connect to the server.
+ */
+ public function getQemuVMs($server)
+ {
+ $proxmox = $this->getProxmoxInstance($server);
+ if ($proxmox->login()) {
+ $templates = $proxmox->get("/nodes/" . $server->name . "/qemu");
+ return $templates;
+ } else {
+ throw new \Box_Exception("Failed to connect to the server. st");
+ }
+ }
+}
diff --git a/src/ProxmoxTemplates.php b/src/ProxmoxTemplates.php
new file mode 100644
index 0000000..723f21e
--- /dev/null
+++ b/src/ProxmoxTemplates.php
@@ -0,0 +1,146 @@
+di['db']->findAll('service_proxmox_vm_config_template');
+ return $templates;
+ }
+
+ /**
+ * Returns an array of all LXC templates.
+ *
+ * @return array
+ */
+ public function get_lxctemplates()
+ {
+ $templates = $this->di['db']->findAll('service_proxmox_lxc_config_template');
+ return $templates;
+ }
+
+ /**
+ * Returns an array of all QEMU templates, with the server name for each template.
+ *
+ * @return array
+ */
+ public function get_qemutemplates()
+ {
+ $qemu_templates = $this->di['db']->findAll('service_proxmox_qemu_template');
+ // Get server name for each template
+ foreach ($qemu_templates as $qemu_template) {
+ $server = $this->di['db']->getExistingModelById('service_proxmox_server', $qemu_template->server_id);
+ $qemu_template->server_name = $server->name;
+ }
+ return $qemu_templates;
+ }
+
+ /**
+ * Get the virtual machine configuration template by ID.
+ *
+ * @param int $id The ID of the template to retrieve.
+ * @return Model The virtual machine configuration template.
+ */
+ public function get_vmconfig($id)
+ {
+ $template = $this->di['db']->getExistingModelById('service_proxmox_vm_config_template', $id);
+ return $template;
+ }
+
+ /**
+ * Get the Linux container configuration template by ID.
+ *
+ * @param int $id The ID of the template to retrieve.
+ * @return Model The Linux container configuration template.
+ */
+ public function get_lxc_conftempl($id)
+ {
+ $template = $this->di['db']->getExistingModelById('service_proxmox_lxc_config_template', $id);
+ return $template;
+ }
+
+ /**
+ * Get all tags of a certain type.
+ *
+ * @param array $data An array containing the type of tags to retrieve.
+ * @return array An array of tags of the specified type.
+ */
+ public function get_tags($data)
+ {
+ $tags = $this->di['db']->find('service_proxmox_tag', 'type=:type', array(':type' => $data['type']));
+ return $tags;
+ }
+
+ /**
+ * Saves a tag to the database, creating it if it doesn't already exist.
+ *
+ * @param array $data An array containing the tag type and name.
+ * @return object The tag that was just created or the tag that already exists.
+ */
+ public function save_tag($data)
+ {
+ // search if the tag already exists
+ $tag_exists = $this->di['db']->findOne('service_proxmox_tag', 'type=:type AND name=:name', array(':type' => $data['type'], ':name' => $data['tag']));
+ // and if not create it
+ if (!$tag_exists) {
+ $model = $this->di['db']->dispense('service_proxmox_tag');
+ $model->type = $data['type'];
+ $model->name = $data['tag'];
+ $this->di['db']->store($model);
+ // return the tag that was just created.
+ return $model;
+ }
+ // return the tag that already exists
+ return $tag_exists;
+ }
+
+ /**
+ * Gets the tags associated with a given storage ID.
+ *
+ * @param array $data An array containing the storage ID.
+ * @return mixed An array of tags or an empty string if the storage has no tags.
+ */
+ public function get_tags_by_storage($data)
+ {
+ $storage = $this->di['db']->findOne('service_proxmox_storage', 'id=:id', array(':id' => $data['storageid']));
+
+ if (empty($storage->storageclass)) {
+ // if empty return empty string
+ $tags = "";
+ } else {
+ $tags = json_decode($storage->storageclass, true);
+ }
+ return $tags;
+ }
+
+}
diff --git a/ProxmoxVM.php b/src/ProxmoxVM.php
similarity index 64%
rename from ProxmoxVM.php
rename to src/ProxmoxVM.php
index 898bb30..0d9c01b 100644
--- a/ProxmoxVM.php
+++ b/src/ProxmoxVM.php
@@ -89,14 +89,9 @@ public function delete($order, $model)
$product = $this->di['db']->load('product', $order->product_id);
$product_config = json_decode($product->config, 1);
-
- // Retrieve associated server
$server = $this->di['db']->findOne('service_proxmox_server', 'id=:id', array(':id' => $model->server_id));
- // Connect to YNH API
- $serveraccess = $this->find_access($server);
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $server->tokenname, tokensecret: $server->tokenvalue);
-
+ $proxmox = $this->getProxmoxInstance($server);
if ($proxmox->login()) {
$proxmox->post("/nodes/" . $model->node . "/" . $product_config['virt'] . "/" . $model->vmid . "/status/shutdown", array());
$status = $proxmox->get("/nodes/" . $model->node . "/" . $product_config['virt'] . "/" . $model->vmid . "/status/current");
@@ -121,6 +116,7 @@ public function delete($order, $model)
throw new \Box_Exception("Login to Proxmox Host failed");
}
}
+ return false;
}
/*
@@ -133,14 +129,10 @@ public function vm_info($order, $service)
$product = $this->di['db']->load('product', $order->product_id);
$product_config = json_decode($product->config, 1);
$client = $this->di['db']->load('client', $order->client_id);
-
- // Retrieve associated server
$server = $this->di['db']->findOne('service_proxmox_server', 'id=:id', array(':id' => $service->server_id));
$clientuser = $this->di['db']->findOne('service_proxmox_users', 'server_id = ? and client_id = ?', array($server->id, $client->id));
- // Test if login
- $serveraccess = $this->find_access($server);
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $clientuser->admin_tokenname, tokensecret: $clientuser->admin_tokenvalue);
+ $proxmox = $this->getProxmoxInstance($server);
if ($proxmox->get_version()) {
$status = $proxmox->get("/nodes/" . $server->name . "/" . $product_config['virt'] . "/" . $service->vmid . "/status/current");
// VM monitoring?
@@ -163,13 +155,10 @@ public function vm_reboot($order, $service)
$product_config = json_decode($product->config, 1);
$client = $this->di['db']->load('client', $order->client_id);
- // Retrieve associated server
$server = $this->di['db']->findOne('service_proxmox_server', 'id=:id', array(':id' => $service->server_id));
$clientuser = $this->di['db']->findOne('service_proxmox_users', 'server_id = ? and client_id = ?', array($server->id, $client->id));
- // Test if login
- $serveraccess = $this->find_access($server);
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $clientuser->admin_tokenname, tokensecret: $clientuser->admin_tokenvalue);
+ $proxmox = $this->getProxmoxInstance($server);
if ($proxmox->login()) {
$proxmox->post("/nodes/" . $server->name . "/" . $product_config['virt'] . "/" . $service->vmid . "/status/shutdown", array());
$status = $proxmox->get("/nodes/" . $server->name . "/" . $product_config['virt'] . "/" . $service->vmid . "/status/current");
@@ -209,14 +198,10 @@ public function vm_start($order, $service)
$product = $this->di['db']->load('product', $order->product_id);
$product_config = json_decode($product->config, 1);
$client = $this->di['db']->load('client', $order->client_id);
- // Retrieve associated server
- // Retrieve associated server
$server = $this->di['db']->findOne('service_proxmox_server', 'id=:id', array(':id' => $service->server_id));
$clientuser = $this->di['db']->findOne('service_proxmox_users', 'server_id = ? and client_id = ?', array($server->id, $client->id));
- // Test if login
- $serveraccess = $this->find_access($server);
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $clientuser->admin_tokenname, tokensecret: $clientuser->admin_tokenvalue);
+ $proxmox = $this->getProxmoxInstance($server);
if ($proxmox->login()) {
$proxmox->post("/nodes/" . $server->name . "/" . $product_config['virt'] . "/" . $service->vmid . "/status/start", array());
return true;
@@ -233,15 +218,10 @@ public function vm_shutdown($order, $service)
$product = $this->di['db']->load('product', $order->product_id);
$product_config = json_decode($product->config, 1);
$client = $this->di['db']->load('client', $order->client_id);
- // Retrieve associated server
$server = $this->di['db']->findOne('service_proxmox_server', 'id=:id', array(':id' => $service->server_id));
- // Test if login
- // find service access for server
$clientuser = $this->di['db']->findOne('service_proxmox_users', 'server_id = ? and client_id = ?', array($server->id, $client->id));
- //echo "D: ".var_dump($order);
- $serveraccess = $this->find_access($server);
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $clientuser->admin_tokenname, tokensecret: $clientuser->admin_tokenvalue);
+ $proxmox = $this->getProxmoxInstance($server);
if ($proxmox->login()) {
$settings = array(
'forceStop' => true
@@ -253,61 +233,4 @@ public function vm_shutdown($order, $service)
throw new \Box_Exception("Login to Proxmox Host failed.");
}
}
-
-
- /*
- VM iframe for Web CLI
- */
- public function vm_cli($order, $service)
- {
-
- $product = $this->di['db']->load('product', $order->product_id);
- $product_config = json_decode($product->config, 1);
- $client = $this->di['db']->load('client', $order->client_id);
-
- // Retrieve associated server
- $server = $this->di['db']->findOne('service_proxmox_server', 'id=:id', array(':id' => $service->server_id));
- $clientuser = $this->di['db']->findOne('service_proxmox_users', 'server_id = ? and client_id = ?', array($server->id, $client->id));
-
- // Find access route
- $serveraccess = $this->find_access($server);
-
- // Setup console type
- $console = ($product_config['virt'] == 'qemu') ? 'kvm' : 'shell';
-
- // The user enters the password to see the iframe: TBD
- //$password = 'test';
- //$proxmox = new PVE2_API($serveraccess, $client->id, $server->name, $password);
-
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $clientuser->view_tokenname, tokensecret: $clientuser->view_tokenvalue);
-
- if ($proxmox->login()) {
- // Get VNC Web proxy ticket by calling /nodes/{node}/{type}/{vmid}/vncproxy
- $vncproxy_response = $proxmox->post("/nodes/" . $server->name . "/" . $product_config['virt'] . "/" . $service->vmid . "/vncproxy", array('node' => $server->name, 'vmid' => $service->vmid));
- $vncproxy_port = $vncproxy_response['data']['port'];
- $vncproxy_ticket = $vncproxy_response['data']['ticket'];
- // open a vnc web socket
- // we'll do it ourselves until the upstream API supports it
- $put_post_http_headers[] = "Authorization: PVEAPIToken={$clientuser->view_tokenname}={$clientuser->view_tokenvalue}";
- // add the vncticket to the post_http_headers
- $put_post_http_headers[] = "vncticket: {$vncproxy_ticket}";
- // add the port to the post_http_headers
- $put_post_http_headers[] = "port: {$vncproxy_port}";
- // add the host to the post_http_headers
- $put_post_http_headers[] = "host: {$serveraccess}";
- // add the vmid to the post_http_headers
- $put_post_http_headers[] = "vmid: {$service->vmid}";
- // open websocket connection and display the console
- /* curl_setopt($prox_ch, CURLOPT_URL, "https://{$serveraccess}:8006/api2/json/nodes/{$server->name}/{$product_config['virt']}/{$service->vmid}/vncwebsocket");
- curl_setopt($prox_ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($prox_ch, CURLOPT_POSTFIELDS, $login_postfields_string);
- curl_setopt($prox_ch, CURLOPT_SSL_VERIFYPEER, $this->verify_ssl);
- curl_setopt($prox_ch, CURLOPT_SSL_VERIFYHOST, $this->verify_ssl);
- */
- // return array of vncport & ticket
- return array('port' => $vncproxy_port, 'ticket' => $vncproxy_ticket);
- } else {
- throw new \Box_Exception("Login to Proxmox VM failed.");
- }
- }
}
diff --git a/Service.php b/src/Service.php
similarity index 70%
rename from Service.php
rename to src/Service.php
index 0c0ca6a..14f4e66 100644
--- a/Service.php
+++ b/src/Service.php
@@ -18,20 +18,22 @@
namespace Box\Mod\Serviceproxmox;
-require_once 'pve2_api.class.php';
+require __DIR__ . '/vendor/autoload.php';
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
use PDO;
+use PDOException;
+
/**
- * Proxmox module for FOSSBilling
+ * Provides the Proxmox module for FOSSBilling.
*/
class Service implements \FOSSBilling\InjectionAwareInterface
{
protected $di;
-
+ private $pdo;
public function setDi(\Pimple\Container|null $di): void
{
$this->di = $di;
@@ -48,30 +50,39 @@ public function getDi(): ?\Pimple\Container
use ProxmoxIPAM;
- public function validateCustomForm(array &$data, array $product)
+ /**
+ * Returns a PDO instance for the database connection.
+ *
+ * @return PDO The PDO instance.
+ */
+ private function getPdo(): PDO
{
- if ($product['form_id']) {
- $formbuilderService = $this->di['mod_service']('formbuilder');
- $form = $formbuilderService->getForm($product['form_id']);
-
- foreach ($form['fields'] as $field) {
- if ($field['required'] == 1) {
- $field_name = $field['name'];
- if ((!isset($data[$field_name]) || empty($data[$field_name]))) {
- throw new \Box_Exception("You must fill in all required fields. " . $field['label'] . " is missing", null, 9684);
- }
- }
-
- if ($field['readonly'] == 1) {
- $field_name = $field['name'];
- if ($data[$field_name] != $field['default_value']) {
- throw new \Box_Exception("Field " . $field['label'] . " is read only. You can not change its value", null, 5468);
- }
- }
- }
+ if (!$this->pdo) {
+ // Get db config
+ $db_user = $this->di['config']['db']['user'];
+ $db_password = $this->di['config']['db']['password'];
+ $db_name = $this->di['config']['db']['name'];
+
+ // Create PDO instance
+ $this->pdo = new PDO('mysql:host=localhost;dbname=' . $db_name, $db_user, $db_password);
+ $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
+
+ return $this->pdo;
}
+
+ /**
+ * Fetches all tables in the database that start with 'service_proxmox'.
+ *
+ * @return array An array of table names.
+ */
+ private function fetchServiceProxmoxTables(): array
+ {
+ $pdo = $this->getPdo();
+ $stmt = $pdo->query("SHOW TABLES LIKE 'service_proxmox%'");
+ return $stmt->fetchAll(PDO::FETCH_COLUMN);
+ }
/**
* Method to install module. In most cases you will provide your own
* database table or tables to store extension related data.
@@ -80,8 +91,10 @@ public function validateCustomForm(array &$data, array $product)
* database table might be enough.
*
* @return bool
+ *
+ * @throws \Box_Exception
*/
- public function install()
+ public function install(): bool
{
// read manifest.json to get current version number
$manifest = json_decode(file_get_contents(__DIR__ . '/manifest.json'), true);
@@ -89,10 +102,7 @@ public function install()
// check if there is a sqldump backup with "uninstall" in it's name in the pmxconfig folder, if so, restore it
$filesystem = new Filesystem();
- // list content of pmxconfig folder using symfony finder
$finder = new Finder();
-
- // check if pmxconfig folder exists
if (!$filesystem->exists(PATH_ROOT . '/pmxconfig')) {
$filesystem->mkdir(PATH_ROOT . '/pmxconfig');
}
@@ -123,16 +133,10 @@ public function install()
$dump_version = str_replace('-- Proxmox module version: ', '', $version_line);
$dump = str_replace($version_line . "\n", '', $dump);
- // Get db config
- $db_user = $this->di['config']['db']['user'];
- $db_password = $this->di['config']['db']['password'];
- $db_name = $this->di['config']['db']['name'];
try {
- // Create PDO instance
- $pdo = new PDO('mysql:host=localhost;dbname=' . $db_name, $db_user, $db_password);
- $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
-
+ // Retrieve PDO instance
+ $pdo = $this->getPdo();
// If version number in dump is smaller than current version number, restore dump and run upgrade function
if ($dump_version < $version) {
// Split the dump into an array by each sql command
@@ -159,7 +163,7 @@ public function install()
} else {
throw new \Box_Exception("The version number of the sql dump is bigger than the current version number of the module. Please check the installed Module version.", null, 9684);
}
- } catch (Exception $e) {
+ } catch (\Box_Exception $e) {
throw new \Box_Exception('Error during restoration process: ' . $e->getMessage());
}
}
@@ -210,15 +214,10 @@ public function install()
error_log('PDO Exception: ' . $e->getMessage());
exit(1);
}
-
-
- // Create table for vm
}
- // add default values to module config table:
- // cpu_overprovisioning, ram_overprovisioning, storage_overprovisioning, avoid_overprovision, no_overprovision, use_auth_tokens
- // example: $extensionService->setConfig(['ext' => 'mod_massmailer', 'limit' => '2', 'interval' => '10', 'test_client_id' => 1]);
+
$extensionService = $this->di['mod_service']('extension');
- $extensionService->setConfig(['ext' => 'mod_serviceproxmox', 'cpu_overprovisioning' => '1', 'ram_overprovisioning' => '1', 'storage_overprovisioning' => '1', 'avoid_overprovision' => '0', 'no_overprovision' => '1', 'use_auth_tokens' => '1']);
+ $extensionService->setConfig(['ext' => 'mod_serviceproxmox', 'cpu_overprovisioning' => '1', 'ram_overprovisioning' => '1', 'storage_overprovisioning' => '1', 'avoid_overprovision' => '0', 'no_overprovision' => '1', 'use_auth_tokens' => '1', 'pmx_debug_logging' =>'0']);
return true;
@@ -231,31 +230,15 @@ public function install()
*
* @return bool
*/
- public function uninstall()
+ public function uninstall(): bool
{
- $this->pmxdbbackup('uninstall');
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_server`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_users`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_storage`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_templates`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_vms`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_vm_config_template`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_vm_storage_template`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_vm_network_template`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_lxc_appliance`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_storageclass`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_client_network`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_ip_networks`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_ip_range`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_client_vlan`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_lxc_config_template`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_lxc_network_template`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_lxc_storage_template`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_qemu_template`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_ipam_settings`");
- $this->di['db']->exec("DROP TABLE IF EXISTS `service_proxmox_ipadress`");
+ // Retrieve PDO instance
+ $pdo = $this->getPdo();
+ $tables = $this->fetchServiceProxmoxTables();
+ foreach ($tables as $table) {
+ $pdo->exec("DROP TABLE IF EXISTS `$table`");
+ }
return true;
}
@@ -265,7 +248,7 @@ public function uninstall()
* @param string $previous_version
* @return bool
*/
- public function upgrade($previous_version)
+ public function upgrade($previous_version): bool
{
// read current module version from manifest.json
$manifest = json_decode(file_get_contents(__DIR__ . '/manifest.json'), true);
@@ -279,40 +262,49 @@ public function upgrade($previous_version)
return version_compare(basename($a, '.sql'), basename($b, '.sql'));
});
- // Get db config
- $db_user = $this->di['config']['db']['user'];
- $db_password = $this->di['config']['db']['password'];
- $db_name = $this->di['config']['db']['name'];
-
- // Create PDO instance
- $pdo = new PDO('mysql:host=localhost;dbname=' . $db_name, $db_user, $db_password);
- $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ // Retrieve PDO instance
+ $pdo = $this->getPdo();
foreach ($migrations as $migration) {
// get version from filename
- // log to debug.log
- error_log('found migration: ' . $migration);
+ error_log('Found migration: ' . $migration);
$filename = basename($migration, '.sql');
$version = str_replace('_', '.', $filename);
- // check if version is between previous and current version
- error_log('version: ' . $version . ' previous_version: ' . $previous_version . ' current_version: ' . $current_version);
-
- // Apply migration if version is larger than previous version and smaller or equal to current version
- error_log('version_compare: ' . version_compare($version, $previous_version, '>') . ' version_compare2: ' . version_compare($version, $current_version, '<='));
-
if (version_compare($version, $previous_version, '>') && version_compare($version, $current_version, '<=')) {
- error_log('applying migration: ' . $migration);
+ error_log('Applying migration: ' . $migration);
// run migration
$migration_sql = file_get_contents($migration);
$pdo->exec($migration_sql);
+ } else {
+ error_log('Skipping migration: ' . $migration);
}
}
return true;
}
+ /**
+ * Method to update module. When you release new version to
+ * extensions.fossbilling.org then this method will be called
+ * after the new files are placed.
+ *
+ * @param array $manifest - information about the new module version
+ *
+ * @return bool
+ *
+ * @throws \Box_Exception
+ */
+ public function update(array $manifest): bool
+ {
+ // throw new \Box_Exception("Throw exception to terminate module update process with a message", array(), 125);
+ return true;
+ }
+
+
+
+
/**
* Method to check if all tables have been migrated to current Module Version.
* Not yet used, but will be in the admin settings page for the module
@@ -325,32 +317,14 @@ public function check_db_migration()
// read current module version from manifest.json
$manifest = json_decode(file_get_contents(__DIR__ . '/manifest.json'), true);
$current_version = $manifest['version'];
- $tables = array(
- 'service_proxmox_server',
- 'service_proxmox',
- 'service_proxmox_users',
- 'service_proxmox_storageclass',
- 'service_proxmox_storage',
- 'service_proxmox_lxc_appliance',
- 'service_proxmox_vm_config_template',
- 'service_proxmox_vm_storage_template',
- 'service_proxmox_vm_network_template',
- 'service_proxmox_lxc_config_template',
- 'service_proxmox_lxc_storage_template',
- 'service_proxmox_lxc_network_template',
- 'service_proxmox_qemu_template',
- 'service_proxmox_client_vlan',
- 'service_proxmox_ip_range'
-
- );
+ $tables = $this->fetchServiceProxmoxTables();
foreach ($tables as $table) {
- $sql = "SELECT table_comment FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='" . DB_NAME . "' AND table_name='" . $table . "'";
+ $sql = "SELECT table_comment FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='" . DB_NAME . "' AND table_name='" . $table . "'"; /* @phpstan-ignore-line */
$result = $this->di['db']->query($sql);
$row = $result->fetch();
// check if version is the same as current version
if ($row['table_comment'] != $current_version) {
- // if not, throw error to inform user about inconsistent database status
throw new \Box_Exception('Database migration is not up to date. Please run the database migration script.');
}
}
@@ -370,48 +344,20 @@ public function pmxdbbackup($data)
$filesystem = new Filesystem();
$filesystem->mkdir([PATH_ROOT . '/pmxconfig'], 0750);
} catch (IOException $e) {
- error_log($e->getMessage());
+ error_log('An error occurred while creating backup directory at ' . $e->getMessage());
throw new \Box_Exception('Unable to create directory pmxconfig');
}
- // create filename with timestamp
- // check if $data is 'uninstall' or 'backup'
+
if ($data == 'uninstall') {
$filename = '/pmxconfig/proxmox_uninstall_' . date('Y-m-d_H-i-s') . '.sql';
} else {
$filename = '/pmxconfig/proxmox_backup_' . date('Y-m-d_H-i-s') . '.sql';
}
- // get db config
- $db_user = $this->di['config']['db']['user'];
- $db_password = $this->di['config']['db']['password'];
- $db_name = $this->di['config']['db']['name'];
try {
- // create PDO instance
- $pdo = new PDO('mysql:host=localhost;dbname=' . $db_name, $db_user, $db_password);
-
- // List of tables to backup
- $tables = array(
- 'service_proxmox_server',
- 'service_proxmox',
- 'service_proxmox_users',
- 'service_proxmox_storageclass',
- 'service_proxmox_storage',
- 'service_proxmox_lxc_appliance',
- 'service_proxmox_vm_config_template',
- 'service_proxmox_vm_storage_template',
- 'service_proxmox_vm_network_template',
- 'service_proxmox_lxc_config_template',
- 'service_proxmox_lxc_storage_template',
- 'service_proxmox_lxc_network_template',
- 'service_proxmox_qemu_template',
- 'service_proxmox_client_vlan',
- 'service_proxmox_ip_range',
- 'service_proxmox_ipadress',/*
- 'service_proxmox_ipam_settings' */
- );
-
- // Initialize backup variable
+ $pdo = $this->getPdo();
+ $tables = $tables = $this->fetchServiceProxmoxTables();
$backup = '';
// Loop through tables and create SQL statement
@@ -446,64 +392,8 @@ public function pmxdbbackup($data)
$handle = fopen(PATH_ROOT . $filename, 'w+');
fwrite($handle, $backup);
fclose($handle);
- // create PDO instance
- $pdo = new PDO('mysql:host=localhost;dbname=' . $db_name, $db_user, $db_password);
-
- // List of tables to backup
- $tables = array(
- 'service_proxmox_server',
- 'service_proxmox',
- 'service_proxmox_users',
- 'service_proxmox_storageclass',
- 'service_proxmox_storage',
- 'service_proxmox_lxc_appliance',
- 'service_proxmox_vm_config_template',
- 'service_proxmox_vm_storage_template',
- 'service_proxmox_vm_network_template',
- 'service_proxmox_lxc_config_template',
- 'service_proxmox_lxc_storage_template',
- 'service_proxmox_lxc_network_template',
- 'service_proxmox_qemu_template',
- 'service_proxmox_client_vlan',
- 'service_proxmox_ip_range'
- );
-
- // Initialize backup variable
- $backup = '';
-
- // Loop through tables and create SQL statement
- foreach ($tables as $table) {
- $result = $pdo->query('SELECT * FROM ' . $table);
- $num_fields = $result->columnCount();
- $backup .= 'DROP TABLE IF EXISTS ' . $table . ';';
- $row2 = $pdo->query('SHOW CREATE TABLE ' . $table)->fetch(PDO::FETCH_NUM);
- $backup .= "\n\n" . $row2[1] . ";\n\n";
-
- while ($row = $result->fetch(PDO::FETCH_NUM)) {
- $backup .= 'INSERT INTO ' . $table . ' VALUES(';
- for ($j = 0; $j < $num_fields; $j++) {
- $row[$j] = addslashes($row[$j]);
- $row[$j] = preg_replace("/\n/", "\\n", $row[$j]);
- if (isset($row[$j])) {
- $backup .= '"' . $row[$j] . '"';
- } else {
- $backup .= '""';
- }
- if ($j < ($num_fields - 1)) {
- $backup .= ',';
- }
- }
- $backup .= ");\n";
- }
- $backup .= "\n\n\n";
- }
-
- // Save to file
- $handle = fopen(PATH_ROOT . $filename, 'w+');
- fwrite($handle, $backup);
- fclose($handle);
- } catch (Exception $e) {
+ } catch (\Box_Exception $e) {
throw new \Box_Exception('Error during backup process: ' . $e->getMessage());
}
@@ -549,40 +439,29 @@ public function pmxbackuplist()
/**
* Method to restore Proxmox tables from backup
- * It's a bit destructive, as it will drop & overwrite all existing tables
+ * It's a destructive operation, as it will drop & overwrite all existing tables
*
* @param string $data - filename of backup
* @return bool
*/
public function pmxbackuprestore($data)
{
- // get filename from $data and see if it exists using finder
$manifest = json_decode(file_get_contents(__DIR__ . '/manifest.json'), true);
$version = $manifest['version'];
- //if the file exists, restore it
$dump = file_get_contents(PATH_ROOT . '/pmxconfig/' . $data['backup']);
- // check if dump is not empty
if (!empty($dump)) {
- // check version number in first line of dump format:
- // -- Proxmox module version: 0.0.5
- // get first line of dump
$version_line = strtok($dump, "\n");
- // get version number from line
$dump_version = str_replace('-- Proxmox module version: ', '', $version_line);
$dump = str_replace($version_line . "\n", '', $dump);
- // if version number in dump is smaller than current version number, restore dump and run upgrade function
if ($dump_version == $version) {
- // get db config
$db_user = $this->di['config']['db']['user'];
$db_password = $this->di['config']['db']['password'];
$db_name = $this->di['config']['db']['name'];
try {
// create PDO instance
- $pdo = new PDO('mysql:host=localhost;dbname=' . $db_name, $db_user, $db_password);
- $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
-
+ $pdo = $this->getPdo();
// split the dump into an array by each sql command
$query_array = explode(";", $dump);
@@ -594,7 +473,7 @@ public function pmxbackuprestore($data)
}
return true;
- } catch (Exception $e) {
+ } catch (\Box_Exception $e) {
throw new \Box_Exception('Error during restoration process: ' . $e->getMessage());
}
} else {
@@ -648,10 +527,7 @@ public function create($order)
$model->created_at = date('Y-m-d H:i:s');
$model->updated_at = date('Y-m-d H:i:s');
- // Find suitable server and save it to service_proxmox
$model->server_id = $this->find_empty($product);
- // Retrieve server info
-
$this->di['db']->store($model);
return $model;
@@ -688,7 +564,8 @@ public function activate($order, $model)
$serveraccess = $this->find_access($server);
// find client permissions for server
$clientuser = $this->di['db']->findOne('service_proxmox_users', 'server_id = ? and client_id = ?', array($server->id, $client->id));
- $proxmox = new PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $clientuser->admin_tokenname, tokensecret: $clientuser->admin_tokenvalue);
+ $config = $this->di['mod_config']('Serviceproxmox');
+ $proxmox = new \PVE2APIClient\PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $clientuser->admin_tokenname, tokensecret: $clientuser->admin_tokenvalue, debug: $config['pmx_debug_logging']);
// Create Proxmox VM
if ($proxmox->login()) {
@@ -709,7 +586,7 @@ public function activate($order, $model)
$description = 'Service package ' . $model->id . ' belonging to client id: ' . $client->id;
if ($product_config['clone'] == true) {
- $clone = '/' . $product_config['cloneid'] . '/clone'; // Define the route for cloning
+ $clone = '/' . $product_config['cloneid'] . '/clone';
$container_settings = array(
'newid' => $vmid,
'name' => $model->username,
@@ -720,8 +597,8 @@ public function activate($order, $model)
if ($product_config['virt'] == 'qemu') {
$container_settings = array(
'vmid' => $vmid,
- 'name' => 'vm' . $vmid, // Hostname to define
- 'node' => $server->name, // Node to create the VM on
+ 'name' => 'vm' . $vmid,
+ 'node' => $server->name,
'description' => $description,
'storage' => $product_config['storage'],
'memory' => $product_config['memory'],
@@ -738,7 +615,7 @@ public function activate($order, $model)
} else {
$container_settings = array(
'vmid' => $vmid,
- 'hostname' => 'vm' . $vmid, // Hostname to define
+ 'hostname' => 'vm' . $vmid,
'description' => $description,
'storage' => $product_config['storage'],
'memory' => $product_config['memory'],
@@ -746,7 +623,7 @@ public function activate($order, $model)
'password' => $proxmoxuser_password,
'net0' => $product_config['network']
);
- // Storage to do for LXC
+ // TODO: Storage for LXC
}
}
@@ -754,7 +631,6 @@ public function activate($order, $model)
$vmurl = "/nodes/" . $server->name . "/" . $product_config['virt'] . $clone;
$vmcreate = $proxmox->post($vmurl, $container_settings);
- //echo "Debug:\n " . var_dump($vmcreate) . "\n \n";
if ($vmcreate) {
// Start the vm
@@ -820,7 +696,12 @@ public function toApiArray($model)
);
}
- // function to get novnc_appjs file
+ /**
+ * Retrieves the novnc appjs file from a Proxmox server.
+ *
+ * @param array $data An array containing the version of the appjs file to retrieve.
+ * @return object The contents of the appjs file.
+ */
public function get_novnc_appjs($data)
{
// get list of servers
@@ -833,7 +714,6 @@ public function get_novnc_appjs($data)
// build url
$url = "https://$hostname:8006/novnc/" . $data; //$data['ver'];
- // get file using symphony http client
// set options
$client = $this->getHttpClient()->withOptions([
'verify_peer' => false,
@@ -841,22 +721,66 @@ public function get_novnc_appjs($data)
'timeout' => 60,
]);
$result = $client->request('GET', $url);
- //echo "";
// return file
return $result;
}
+ /**
+ * Returns an instance of the Symfony HttpClient.
+ *
+ * @return \Symfony\Component\HttpClient\HttpClient
+ */
public function getHttpClient()
{
return \Symfony\Component\HttpClient\HttpClient::create();
}
- // function to get tags for type
- public function get_tags($data)
+
+
+ /**
+ * Validates custom form data against a product's form fields.
+ * TODO: This needs to be fixes / changed
+ * @param array &$data The form data to validate.
+ * @param array $product The product containing the form fields to validate against.
+ * @throws \Box_Exception If a required field is missing or a read-only field is modified.
+ */
+ public function validateCustomForm(array &$data, array $product)
+ {
+ if ($product['form_id']) {
+ $formbuilderService = $this->di['mod_service']('formbuilder');
+ $form = $formbuilderService->getForm($product['form_id']);
+
+ foreach ($form['fields'] as $field) {
+ if ($field['required'] == 1) {
+ $field_name = $field['name'];
+ if ((!isset($data[$field_name]) || empty($data[$field_name]))) {
+ throw new \Box_Exception("You must fill in all required fields. " . $field['label'] . " is missing", null, 9684);
+ }
+ }
+
+ if ($field['readonly'] == 1) {
+ $field_name = $field['name'];
+ if ($data[$field_name] != $field['default_value']) {
+ throw new \Box_Exception("Field " . $field['label'] . " is read only. You can not change its value", null, 5468);
+ }
+ }
+ }
+ }
+ }
+ /**
+ * Returns the salt value from the configuration.
+ *
+ * @return string The salt value.
+ */
+ private function _getSalt()
{
- // get list of tags for input type
- $tags = $this->di['db']->find('service_proxmox_tag', 'type=:type', array(':type' => $data['type']));
- // return tags
- return $tags;
+ return $this->di['config']['salt'];
+ }
+
+ private function getProxmoxInstance($server)
+ {
+ $serveraccess = $this->find_access($server);
+ $config = $this->di['mod_config']('Serviceproxmox');
+ return new \PVE2APIClient\PVE2_API($serveraccess, $server->root_user, $server->realm, $server->root_password, port: $server->port, tokenid: $server->tokenname, tokensecret: $server->tokenvalue, debug: $config['pmx_debug_logging']);
}
}
diff --git a/src/assets/css/proxmox.css b/src/assets/css/proxmox.css
new file mode 100644
index 0000000..5345e4c
--- /dev/null
+++ b/src/assets/css/proxmox.css
@@ -0,0 +1,63 @@
+/* The switch - the box around the slider */
+.switch {
+ position: relative;
+ display: inline-block;
+ width: 60px;
+ height: 34px;
+ }
+
+ /* Hide default HTML checkbox */
+ .switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+ }
+
+ /* The slider */
+ .slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #ccc;
+ -webkit-transition: .4s;
+ transition: .4s;
+ }
+
+ .slider:before {
+ position: absolute;
+ content: "";
+ height: 26px;
+ width: 26px;
+ left: 4px;
+ bottom: 4px;
+ background-color: white;
+ -webkit-transition: .4s;
+ transition: .4s;
+ }
+
+ input:checked + .slider {
+ background-color: #2196F3;
+ }
+
+ input:focus + .slider {
+ box-shadow: 0 0 1px #2196F3;
+ }
+
+ input:checked + .slider:before {
+ -webkit-transform: translateX(26px);
+ -ms-transform: translateX(26px);
+ transform: translateX(26px);
+ }
+
+ /* Rounded sliders */
+ .slider.round {
+ border-radius: 34px;
+ }
+
+ .slider.round:before {
+ border-radius: 50%;
+ }
+
\ No newline at end of file
diff --git a/assets/vnc.html b/src/assets/vnc.html
similarity index 100%
rename from assets/vnc.html
rename to src/assets/vnc.html
diff --git a/src/html_admin/mod_serviceproxmox_config.html.twig b/src/html_admin/mod_serviceproxmox_config.html.twig
new file mode 100644
index 0000000..f49c652
--- /dev/null
+++ b/src/html_admin/mod_serviceproxmox_config.html.twig
@@ -0,0 +1,232 @@
+
+{% block js %}
+
+{% endblock %}
diff --git a/html_admin/mod_serviceproxmox_index.html.twig b/src/html_admin/mod_serviceproxmox_index.html.twig
similarity index 73%
rename from html_admin/mod_serviceproxmox_index.html.twig
rename to src/html_admin/mod_serviceproxmox_index.html.twig
index fdcb879..01accd0 100644
--- a/html_admin/mod_serviceproxmox_index.html.twig
+++ b/src/html_admin/mod_serviceproxmox_index.html.twig
@@ -34,6 +34,16 @@
+
+
+
+ Show help
+
+
+
@@ -43,13 +53,34 @@
{{ 'Proxmox Servers' | trans }}
-
+
+
+
+ Legend:
+
+ Test Connection |
+
+ Prepare Server |
+
+ Edit Server |
+
+ Delete Server
+
+
{% set servers_grouped = admin.serviceproxmox_server_get_list() %}
{% for group in servers_grouped %}
{{ group.group }} group
-
+
@@ -159,7 +190,10 @@
{% endfor %}
+
+
+
@@ -224,25 +258,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
+
+ This password will only be used to create the access tokens on the pve server.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This token will be used to authenticate with the pve server.
+
@@ -434,4 +509,110 @@
-{% endblock %}
\ No newline at end of file
+{% endblock %}
+{% block js%}
+
+
+{% endblock %}
diff --git a/html_admin/mod_serviceproxmox_ipam.html.twig b/src/html_admin/mod_serviceproxmox_ipam.html.twig
similarity index 80%
rename from html_admin/mod_serviceproxmox_ipam.html.twig
rename to src/html_admin/mod_serviceproxmox_ipam.html.twig
index ba67697..a72055a 100644
--- a/html_admin/mod_serviceproxmox_ipam.html.twig
+++ b/src/html_admin/mod_serviceproxmox_ipam.html.twig
@@ -131,7 +131,8 @@
{{ 'CIDR' | trans }}:
-
+
+ Please enter a valid IP in CIDR notation.
@@ -463,7 +464,50 @@
+
@@ -478,51 +522,68 @@
{% block js %}
{% endblock %}
diff --git a/html_admin/mod_serviceproxmox_ipam_client_vlan.html.twig b/src/html_admin/mod_serviceproxmox_ipam_client_vlan.html.twig
similarity index 98%
rename from html_admin/mod_serviceproxmox_ipam_client_vlan.html.twig
rename to src/html_admin/mod_serviceproxmox_ipam_client_vlan.html.twig
index 2e193d0..d88cfa3 100644
--- a/html_admin/mod_serviceproxmox_ipam_client_vlan.html.twig
+++ b/src/html_admin/mod_serviceproxmox_ipam_client_vlan.html.twig
@@ -55,8 +55,8 @@
- {% endblock %}
- {% block js %}
+{% endblock %}
+{% block js %}
- {% endblock %}
+{% endblock %}
\ No newline at end of file
diff --git a/html_admin/mod_serviceproxmox_ipam_iprange.html.twig b/src/html_admin/mod_serviceproxmox_ipam_iprange.html.twig
similarity index 95%
rename from html_admin/mod_serviceproxmox_ipam_iprange.html.twig
rename to src/html_admin/mod_serviceproxmox_ipam_iprange.html.twig
index 62d3ad9..f05543e 100644
--- a/html_admin/mod_serviceproxmox_ipam_iprange.html.twig
+++ b/src/html_admin/mod_serviceproxmox_ipam_iprange.html.twig
@@ -76,8 +76,8 @@
- {% endblock %}
- {% block js %}
+{% endblock %}
+{% block js %}
- {% endblock %}
-
-
\ No newline at end of file
+{% endblock %}
\ No newline at end of file
diff --git a/html_admin/mod_serviceproxmox_manage.html.twig b/src/html_admin/mod_serviceproxmox_manage.html.twig
similarity index 100%
rename from html_admin/mod_serviceproxmox_manage.html.twig
rename to src/html_admin/mod_serviceproxmox_manage.html.twig
diff --git a/html_admin/mod_serviceproxmox_server.html.twig b/src/html_admin/mod_serviceproxmox_server.html.twig
similarity index 91%
rename from html_admin/mod_serviceproxmox_server.html.twig
rename to src/html_admin/mod_serviceproxmox_server.html.twig
index ea64974..640702e 100644
--- a/html_admin/mod_serviceproxmox_server.html.twig
+++ b/src/html_admin/mod_serviceproxmox_server.html.twig
@@ -4,7 +4,21 @@
{{ 'Manage Proxmox server' | trans }}
{% endblock %}
{% set active_menu = 'system' %}
-
+{% block breadcrumbs %}
+
+