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 @@ +[![Create a downloadable preview build](https://github.com/Anuril/Proxmox/actions/workflows/preview.yml/badge.svg)](https://github.com/Anuril/Proxmox/actions/workflows/preview.yml) +[![Build and Test](https://github.com/Anuril/Proxmox/actions/workflows/php-ci.yml/badge.svg)](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 ![image](https://github.com/Anuril/Proxmox/assets/1939311/d81a052e-6c00-429b-81aa-7a3cd8dfad71) @@ -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: ![image](https://github.com/Anuril/Proxmox/assets/1939311/01505103-3e76-4f48-89fb-16775e9b6a91) ## 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 @@ -
- - -
-

- {{ 'General Settings' | trans }} -

-
-
-
- -
-
- - -
-
- - -
-
-
-
- - {% set groups = admin.serviceproxmox_server_groups() %} -
- -
-
-
- -
- -
-
-
- -
-
- - -
-
- - -
-
-
-
-
-

- {{ 'Product Settings' | trans }} -

-
-
-
- -
-
- - -
-
- - -
-
-
- {% set lxc_templates = admin.serviceproxmox_service_get_lxctemplates() %} - {% set vm_templates = admin.serviceproxmox_service_get_vmtemplates() %} - {% set qemu_templates = admin.serviceproxmox_service_get_qemutemplates() %} -
- -
- -
-
-
- -
- -
-
-
- -
- -
-
- - -
-
-{% 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 %} -
-
-
- - -
- -
- -
-
-
- -
- -
-
-
- -
- -
-
- - 1 Core(s) - -
-
-
- -
- -
-
- - 1 GB - -
-
-
- -
-
- - -
-
- - -
-
-
-
- - - -
- -
-
- - 1 GB - -
-
-
- - - {% set os_list = admin.serviceproxmox_os_get_list() %} - -
- -
-
-
- - - {% set bios_list = admin.serviceproxmox_bios_get_list() %} - -
- -
-
-
- -
-
- - -
-
- - -
-
- -
-
- -
-
- - -
-
- - -
-
-
- -
-
-
- -{% 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 + + docs/ + + + + + + + \ 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 @@ +
+ + +
+

+ {{ 'General Settings' | trans }} +

+
+
+
+ +
+
+ + +
+
+ + +
+
+
+
+ + {% set groups = admin.serviceproxmox_server_groups() %} +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ + +
+
+ + +
+
+
+
+
+

+ {{ 'Product Settings' | trans }} +

+
+
+
+ +
+
+ + +
+
+ + +
+
+
+ {% set lxc_templates = admin.serviceproxmox_service_get_lxctemplates() %} + {% set vm_templates = admin.serviceproxmox_service_get_vmtemplates() %} + {% set qemu_templates = admin.serviceproxmox_service_get_qemutemplates() %} +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ + +
+
+{% 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 }}:
- + +
@@ -463,7 +464,50 @@
+
+

Automation Settings

+
+ +
+
+ + +
+
+ + +
+
+
+
+ +
+ +
+ + +
+
+ + +
+
+
+ +
@@ -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 %} + +{% endblock %} {% block content %}
diff --git a/html_admin/mod_serviceproxmox_settings.html.twig b/src/html_admin/mod_serviceproxmox_settings.html.twig similarity index 85% rename from html_admin/mod_serviceproxmox_settings.html.twig rename to src/html_admin/mod_serviceproxmox_settings.html.twig index aa9e4ce..669c7cd 100644 --- a/html_admin/mod_serviceproxmox_settings.html.twig +++ b/src/html_admin/mod_serviceproxmox_settings.html.twig @@ -129,6 +129,22 @@
+
+ + +
+
+ + +
+
+ + +
+
+
@@ -546,8 +539,6 @@ - -
+ + +
+ + +
+ +
+ + +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ + {{vm_config_template.cores}} Core(s) + +
+
+
+ +
+ +
+
+ + {{vm_config_template.memory}} GB + +
+
+
+ +
+
+ + +
+
+ + +
+
+
+
+ +
+ +
+
+ + 1 GB + +
+
+
+ + {% set os_list = admin.serviceproxmox_os_get_list() %} +
+ +
+
+
+ + {% set bios_list = admin.serviceproxmox_bios_get_list() %} +
+ +
+
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+ + +
+
+ + +
+
+
+ +
+ +
+
+ Legend:   + + + + Test Connection   |   + + + + Prepare Server   |   + + + + Edit Server   |   + + + + Delete Server +
+
+
+
+
+ + + + + + + + + + +
+ Storage TypeSize (GB)Controller
+
+
+
+
+
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + 50 GB + +
+
+
+ + {% set controllers = admin.serviceproxmox_storage_controller_get_list() %} +
+ +
+
+
+ + +
+
+
+ +
+
+ + +{% endblock %} +{% block js %} + + + +{% endblock %} \ No newline at end of file diff --git a/html_client/mod_serviceproxmox_manage.html.twig b/src/html_client/mod_serviceproxmox_manage.html.twig similarity index 100% rename from html_client/mod_serviceproxmox_manage.html.twig rename to src/html_client/mod_serviceproxmox_manage.html.twig diff --git a/html_email/mod_serviceproxmox_activated.html.twig b/src/html_email/mod_serviceproxmox_activated.html.twig similarity index 100% rename from html_email/mod_serviceproxmox_activated.html.twig rename to src/html_email/mod_serviceproxmox_activated.html.twig diff --git a/html_email/mod_serviceproxmox_canceled.html.twig b/src/html_email/mod_serviceproxmox_canceled.html.twig similarity index 100% rename from html_email/mod_serviceproxmox_canceled.html.twig rename to src/html_email/mod_serviceproxmox_canceled.html.twig diff --git a/html_email/mod_serviceproxmox_renewed.html.twig b/src/html_email/mod_serviceproxmox_renewed.html.twig similarity index 100% rename from html_email/mod_serviceproxmox_renewed.html.twig rename to src/html_email/mod_serviceproxmox_renewed.html.twig diff --git a/html_email/mod_serviceproxmox_suspended.html.twig b/src/html_email/mod_serviceproxmox_suspended.html.twig similarity index 100% rename from html_email/mod_serviceproxmox_suspended.html.twig rename to src/html_email/mod_serviceproxmox_suspended.html.twig diff --git a/html_email/mod_serviceproxmox_unsuspended.html.twig b/src/html_email/mod_serviceproxmox_unsuspended.html.twig similarity index 100% rename from html_email/mod_serviceproxmox_unsuspended.html.twig rename to src/html_email/mod_serviceproxmox_unsuspended.html.twig diff --git a/icon.svg b/src/icon.svg similarity index 100% rename from icon.svg rename to src/icon.svg diff --git a/manifest.json b/src/manifest.json similarity index 86% rename from manifest.json rename to src/manifest.json index 2cd53ec..8082ef6 100644 --- a/manifest.json +++ b/src/manifest.json @@ -5,8 +5,8 @@ "description": "Provision Proxmox VMs using the Proxmox API", "icon_url": "icon.svg", "homepage_url": "https://github.com/FOSSBilling/Proxmox", - "author": "Scith & the FOSSBilling Team", + "author": "Anuril and the FOSSBilling Team", "author_url": "https://github.com/FOSSBilling/", "license": "GPLv3", "version": "0.1.0" -} +} \ No newline at end of file diff --git a/migrations/0.0.5.sql b/src/migrations/0.1.0.sql similarity index 77% rename from migrations/0.0.5.sql rename to src/migrations/0.1.0.sql index 769062e..09b6e1e 100755 --- a/migrations/0.0.5.sql +++ b/src/migrations/0.1.0.sql @@ -1,7 +1,8 @@ --- Migration: 0.0.5 --- Initial Migration for Proxmox Server Module - +-- Migration: 0.1.0 +-- Initial Migration for Proxmox Server Module v 0.1.0. +-- -------------------------------------------------------- +-- Table for storing proxmox server information -- -------------------------------------------------------- CREATE TABLE IF NOT EXISTS `service_proxmox_server` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, @@ -27,6 +28,8 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_server` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; +-- -------------------------------------------------------- +-- Table for storing proxmox service information -- -------------------------------------------------------- CREATE TABLE IF NOT EXISTS `service_proxmox` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, @@ -51,6 +54,8 @@ CREATE TABLE IF NOT EXISTS `service_proxmox` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; +-- -------------------------------------------------------- +-- Table for storing proxmox user information -- -------------------------------------------------------- CREATE TABLE IF NOT EXISTS `service_proxmox_users` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, @@ -67,14 +72,8 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_users` ( KEY `server_id_idx` (`server_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; - -- -------------------------------------------------------- -CREATE TABLE IF NOT EXISTS `service_proxmox_storageclass` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `storageclass` varchar(35) DEFAULT NULL, - PRIMARY KEY (`id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; - +-- Table for storing proxmox storage information -- -------------------------------------------------------- CREATE TABLE IF NOT EXISTS `service_proxmox_storage` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, @@ -83,15 +82,16 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_storage` ( `type` varchar(255) DEFAULT NULL, `content` varchar(255) DEFAULT NULL, `active` bigint(20) DEFAULT NULL, - `storageclass` varchar(35) DEFAULT NULL, + `storageclass` TEXT DEFAULT NULL, `size` bigint(20) DEFAULT NULL, `used` bigint(20) DEFAULT NULL, `free` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `server_storage_unique` (`server_id`, `storage`) + UNIQUE KEY `server_storage_unique` (`server_id`, `storage`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; - +-- -------------------------------------------------------- +-- Table for storing proxmox lxc appliance information -- -------------------------------------------------------- CREATE TABLE IF NOT EXISTS `service_proxmox_lxc_appliance` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, @@ -99,7 +99,6 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_lxc_appliance` ( `type` varchar(255) DEFAULT NULL, `package` varchar(255) DEFAULT NULL, `section` varchar(255) DEFAULT NULL, - `location` varchar(255) DEFAULT NULL, `headline` varchar(255) DEFAULT NULL, `os` varchar(255) DEFAULT NULL, @@ -114,8 +113,12 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_lxc_appliance` ( UNIQUE KEY `sha512sum_idx` (`sha512sum`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; +-- -------------------------------------------------------- +-- Table for storing vm configuration templates +-- -------------------------------------------------------- CREATE TABLE IF NOT EXISTS `service_proxmox_vm_config_template` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, + `state` VARCHAR(255) NOT NULL, `name` varchar(255) DEFAULT NULL, `description` varchar(255) DEFAULT NULL, `cores` bigint(20) DEFAULT NULL, @@ -132,14 +135,14 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_vm_config_template` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; -- -------------------------------------------------------- --- Create vm storage template table +-- Table for storing vm template storage information -- -------------------------------------------------------- CREATE TABLE IF NOT EXISTS `service_proxmox_vm_storage_template` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `template_id` bigint(20) DEFAULT NULL, `storage_type` varchar(255) DEFAULT NULL, `size` bigint(20) DEFAULT NULL, - `format` varchar(255) DEFAULT NULL, + `controller` VARCHAR(255) DEFAULT NULL, `created_at` varchar(35) DEFAULT NULL, `updated_at` varchar(35) DEFAULT NULL, PRIMARY KEY (`id`), @@ -148,7 +151,7 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_vm_storage_template` ( -- -------------------------------------------------------- --- Create vm network template table +-- Table for storing vm template network information -- -------------------------------------------------------- CREATE TABLE IF NOT EXISTS `service_proxmox_vm_network_template` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, @@ -166,7 +169,11 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_vm_network_template` ( KEY `template_id_idx` (`template_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; - CREATE TABLE IF NOT EXISTS `service_proxmox_lxc_config_template` ( + +-- -------------------------------------------------------- +-- Table for storing lxc template information +-- -------------------------------------------------------- +CREATE TABLE IF NOT EXISTS `service_proxmox_lxc_config_template` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `template_id` bigint(20) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, @@ -183,7 +190,7 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_vm_network_template` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; -- -------------------------------------------------------- --- add lxc storage template table with a foreign key to service_proxmox_lxc_template +-- Table for storing lxc template storage information -- -------------------------------------------------------- CREATE TABLE IF NOT EXISTS `service_proxmox_lxc_storage_template` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, @@ -198,7 +205,7 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_lxc_storage_template` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; -- -------------------------------------------------------- --- add lxc network template table with a foreign key to service_proxmox_lxc_template +-- Table for storing lxc template network information -- -------------------------------------------------------- CREATE TABLE IF NOT EXISTS `service_proxmox_lxc_network_template` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, @@ -215,8 +222,7 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_lxc_network_template` ( PRIMARY KEY (`id`), KEY `template_id_idx` (`template_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; --- -------------------------------------------------------- --- New Table: service_proxmox_client_vlan + -- -------------------------------------------------------- -- Table to store client vlans -- -------------------------------------------------------- @@ -231,8 +237,6 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_client_vlan` ( KEY `client_id_idx` (`client_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; --- -------------------------------------------------------- --- New Table: service_proxmox_ip_range -- -------------------------------------------------------- -- Table to store ip networks -- -------------------------------------------------------- @@ -240,6 +244,7 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_ip_range` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `cidr` varchar(255) DEFAULT NULL, `gateway` varchar(255) DEFAULT NULL, + `network` VARCHAR(255) NOT NULL, `broadcast` varchar(255) DEFAULT NULL, `type` varchar(255) DEFAULT NULL, `created_at` varchar(35) DEFAULT NULL, @@ -247,12 +252,12 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_ip_range` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; - - --- add template vm table for storing information about qemu_template VMs +-- -------------------------------------------------------- +-- Table to qemu template VMs -- -------------------------------------------------------- CREATE TABLE IF NOT EXISTS `service_proxmox_qemu_template` ( - `id` bigint(20) NOT NULL, + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `vmid` INT(11) NOT NULL, `server_id` bigint(20) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, `created_at` varchar(35) DEFAULT NULL, @@ -261,5 +266,43 @@ CREATE TABLE IF NOT EXISTS `service_proxmox_qemu_template` ( KEY `id_idx` (`id`), UNIQUE KEY `vmid_server_id_idx` (`id`, `server_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; + +-- -------------------------------------------------------- +-- Table to store ipam settings +-- -------------------------------------------------------- +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 individual ip adresses +-- -------------------------------------------------------- +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; + +-- -------------------------------------------------------- +-- Add new table for the tagging system +-- -------------------------------------------------------- +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; + -- -------------------------------------------------------- -COMMIT; +COMMIT; \ No newline at end of file diff --git a/tests/Serviceproxmox/Api/AdminTest.php b/tests/Serviceproxmox/Api/AdminTest.php new file mode 100644 index 0000000..ecdbbce --- /dev/null +++ b/tests/Serviceproxmox/Api/AdminTest.php @@ -0,0 +1,28 @@ +api = new \Box\Mod\Serviceproxmox\Api\Admin(); + } + + public function testgetDi() + { + $di = new \Pimple\Container(); + $this->api->setDi($di); + $getDi = $this->api->getDi(); + $this->assertEquals($di, $getDi); + } + +} diff --git a/tests/Serviceproxmox/Api/ClientTest.php b/tests/Serviceproxmox/Api/ClientTest.php new file mode 100644 index 0000000..d1339cc --- /dev/null +++ b/tests/Serviceproxmox/Api/ClientTest.php @@ -0,0 +1,26 @@ +api= new \Box\Mod\Serviceproxmox\Api\Client(); + } + + public function testgetDi() + { + $di = new \Pimple\Container(); + $this->api->setDi($di); + $getDi = $this->api->getDi(); + $this->assertEquals($di, $getDi); + } +} + \ No newline at end of file diff --git a/tests/Serviceproxmox/ServiceTest.php b/tests/Serviceproxmox/ServiceTest.php new file mode 100644 index 0000000..9beb782 --- /dev/null +++ b/tests/Serviceproxmox/ServiceTest.php @@ -0,0 +1,76 @@ +service= new \Box\Mod\Serviceproxmox\Service(); + } + + + public function testgetDi() + { + $di = new \Pimple\Container(); + $this->service->setDi($di); + $getDi = $this->service->getDi(); + $this->assertEquals($di, $getDi); + } + + + public function test_create() + { + // Mocking the order model + $orderModel = new \Model_ClientOrder(); + $orderModel->loadBean(new \DummyBean()); + $orderModel->config = json_encode(['some_config_key' => 'some_config_value']); // Example config + $orderModel->product_id = 123; // Example product ID + + // Mocking the product model + $productModel = new \Model_Product(); + $productModel->loadBean(new \DummyBean()); + + // Mocking the database + $dbMock = $this->getMockBuilder('\Box_Database')->getMock(); + $dbMock->expects($this->atLeastOnce()) + ->method('getExistingModelById') + ->with('Product', $orderModel->product_id, 'Product not found') + ->will($this->returnValue($productModel)); + + $serviceProxmoxModel = new \RedBeanPHP\SimpleModel(); + $serviceProxmoxModel->loadBean(new \DummyBean()); + $dbMock->expects($this->atLeastOnce()) + ->method('dispense') + ->with('service_proxmox') + ->will($this->returnValue($serviceProxmoxModel)); + + $dbMock->expects($this->atLeastOnce()) + ->method('store') + ->with($serviceProxmoxModel); + + $di = new \Pimple\Container(); + $di['db'] = $dbMock; + + // Mocking the find_empty method + $serviceProxmoxMock = $this->getMockBuilder('\Box\Mod\Serviceproxmox\Service') + ->setMethods(['find_empty']) + ->getMock(); + $serviceProxmoxMock->expects($this->once()) + ->method('find_empty') + ->with($productModel) + ->will($this->returnValue(1)); // Example server ID + + $serviceProxmoxMock->setDi($di); + $result = $serviceProxmoxMock->create($orderModel); + + $this->assertInstanceOf('\RedBeanPHP\SimpleModel', $result); + $this->assertEquals($orderModel->client_id, $result->client_id); + $this->assertEquals(1, $result->server_id); // Asserting the server ID is set correctly + } +}