| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670 |
- <?php
- /*
- Copyright (c) 2012 Nathan Sullivan
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- https://github.com/CpuID/pve2-api-php-client
- *
- */
- /* * ***********************************
- * EDITED BY MODULESGARDEN (2015-07-24)
- * *********************************** */
- namespace MGProvision\Proxmox\v2;
- abstract class AbstractApi
- {
- private $constructor_success;
- protected $pve_hostname;
- protected $pve_username;
- protected $pve_realm;
- protected $pve_password;
- private $print_debug;
- protected $pve_login_ticket;
- protected $pve_login_ticket_timestamp;
- protected $pve_cluster_node_list;
- protected $whmcsDebugMode = false;
- public $raw_message;
- public $port = '8006';
- protected $httpCode = '0';
- private static $moduleLogs = [];
- /**
- * FUNCTION __construct
- * Construct API object
- * @param string $pve_hostname
- * @param string $pve_username
- * @param string $pve_realm
- * @param string $pve_password
- */
- public function __construct($pve_hostname, $pve_username, $pve_realm, $pve_password)
- {
- if ($pve_realm == "")
- $pve_realm = "pam";
- if (strpos($pve_hostname, ':') !== false)
- {
- $ex = explode(":", $pve_hostname);
- $port = $ex['1'];
- $host = $ex['0'];
- if ($port && is_string($host))
- {
- $this->port = $port;
- $pve_hostname = $host;
- }
- }
- $this->pve_hostname = $pve_hostname;
- $this->pve_username = $pve_username;
- $this->pve_realm = $pve_realm;
- $this->pve_password = $pve_password;
- $this->print_debug = false;
- # Default this to null, so we can check later on if were logged in or not.
- $this->pve_login_ticket = null;
- $this->pve_login_ticket_timestamp = null;
- $this->pve_cluster_node_list = null;
- $this->constructor_success = true;
- }
- /**
- * FUNCTION constructor_success
- * Verify construct object
- * @return boolean $this->constructor_success
- */
- public function constructor_success()
- {
- return $this->constructor_success;
- }
- /**
- * FUNCTION convert_postfields_array_to_string
- * Convert postfields to string
- * @param array $postfields_array
- * @return string $postfields_string
- */
- private function convert_postfields_array_to_string($postfields_array)
- {
- $postfields_key_values = array();
- foreach ($postfields_array as $field_key => $field_value)
- {
- $postfields_key_values[] = urlencode($field_key) . "=" . urlencode($field_value);
- }
- $postfields_string = implode("&", $postfields_key_values);
- return $postfields_string;
- }
- /**
- * FUNCTION set_debug
- * Sets if we should print() debug information throughout the process,
- * to assist in troubleshooting...
- * @param boolean $on_off
- * @return boolean
- */
- public function set_debug($on_off)
- {
- if (is_bool($on_off))
- {
- $this->print_debug = $on_off;
- return true;
- }
- else
- {
- return false;
- }
- }
- /**
- * FUNCTION login
- * Performs login to PVE Server using JSON API, and obtains Access Ticket.
- * @return boolean
- */
- public function login()
- {
- if (!$this->constructor_success)
- {
- return false;
- }
- # Prepare login variables.
- $login_postfields = array();
- $login_postfields['username'] = $this->pve_username;
- $login_postfields['password'] = $this->pve_password;
- $login_postfields['realm'] = $this->pve_realm;
- $login_postfields_string = $this->convert_postfields_array_to_string($login_postfields);
- # Perform login request.
- $prox_ch = curl_init();
- curl_setopt($prox_ch, CURLOPT_URL, "https://{$this->pve_hostname}:{$this->port}/api2/json/access/ticket");
- curl_setopt($prox_ch, CURLOPT_POST, true);
- curl_setopt($prox_ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($prox_ch, CURLOPT_POSTFIELDS, $login_postfields_string);
- curl_setopt($prox_ch, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($prox_ch, CURLOPT_SSL_VERIFYHOST, false);
- curl_setopt($prox_ch, CURLOPT_CONNECTTIMEOUT, 30);
- $login_ticket = curl_exec($prox_ch);
- $err = curl_error($prox_ch);
- if ($login_ticket === false)
- {
- $err = $err ? $err : "Unable connect to Proxmox";
- throw new ProxmoxApiException($err);
- }
- curl_close($prox_ch);
- unset($prox_ch);
- unset($login_postfields_string);
- $login_ticket_data = json_decode($login_ticket, true);
- if ($this->whmcsDebugMode)
- {
- if (function_exists('logModuleCall'))
- {
- logModuleCall(
- "Proxmox", "https://{$this->pve_hostname}:{$this->port}/api2/json/access/ticket", print_r($login_postfields, true), '', print_r($login_ticket_data, true) . "\n {$err}", array($this->pve_username, $this->pve_password)
- );
- self::$moduleLogs[] = [
- "proxmoxVPS",
- "https://{$this->pve_hostname}:{$this->port}/api2/json/access/ticket",
- print_r($login_postfields, true),
- '',
- print_r($login_ticket_data, true),
- [$this->pve_username, $this->pve_password]
- ];
- }
- }
- unset($login_postfields);
- if ($login_ticket_data == null || $login_ticket_data['data'] == null)
- {
- # Login failed.
- # Just to be safe, set this to null again.
- $this->pve_login_ticket_timestamp = null;
- throw new ProxmoxApiException(sprintf("Login to Proxmox host '%s' port '%s' failed", $this->pve_hostname, $this->port),401);
- return false;
- }
- else
- {
- # Login success.
- $this->pve_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->pve_login_ticket_timestamp = time();
- return true;
- }
- }
- /**
- * FUNCTION pve_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...
- * @return boolean
- */
- protected function pve_check_login_ticket()
- {
- if($this->pve_realm=="PVEAPIToken"){
- return true;
- }
- if ($this->pve_login_ticket == null)
- {
- # Just to be safe, set this to null again.
- $this->pve_login_ticket_timestamp = null;
- return false;
- }
- if ($this->pve_login_ticket_timestamp >= (time() + 7200))
- {
- # Reset login ticket object values.
- $this->pve_login_ticket = null;
- $this->pve_login_ticket_timestamp = null;
- return false;
- }
- else
- {
- return true;
- }
- }
- /**
- * FUNCTION pve_action
- * 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.
- * @param string $action_path
- * @param string $http_method
- * @param array $put_post_parameters
- * @return array
- */
- private function pve_action($action_path, $http_method, $put_post_parameters = null)
- {
- if (!$this->constructor_success)
- {
- return false;
- }
- # 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->pve_check_login_ticket())
- {
- if ($this->print_debug === true)
- {
- print("Error - Not logged into Proxmox Host. No Login Access Ticket found or Ticket Expired.\n");
- }
- return false;
- }
- # Prepare cURL resource.
- $prox_ch = curl_init();
- if ($this->print_debug === true)
- {
- print("\nURL - https://{$this->pve_hostname}:{$this->port}/api2/json" . $action_path . "\n");
- }
- #GET parameters
- $getparameters = null;
- if (!empty($put_post_parameters) && $http_method == "GET")
- {
- $getparameters = "?";
- foreach ($put_post_parameters as $k => $v)
- {
- $getparameters .= urlencode($k) . "=" . urlencode($v) . "&";
- }
- $getparameters = substr($getparameters, 0, -1);
- }
- $comand = explode("/", $action_path);
- $comand = end($comand);
- if ($comand == "rrd")
- {
- curl_setopt($prox_ch, CURLOPT_URL, "https://{$this->pve_hostname}:{$this->port}/api2/png" . $action_path . $getparameters);
- }
- else
- {
- curl_setopt($prox_ch, CURLOPT_URL, "https://{$this->pve_hostname}:{$this->port}/api2/json" . $action_path . $getparameters);
- }
- $put_post_http_headers = array();
- if($this->pve_realm=="PVEAPIToken"){
- $put_post_http_headers[] = sprintf("Authorization: PVEAPIToken=%s=%s", $this->pve_username, $this->pve_password);
- }else{
- $put_post_http_headers[] = "CSRFPreventionToken: " . $this->pve_login_ticket['CSRFPreventionToken'];
- }
- curl_setopt($prox_ch, CURLOPT_HEADER, true);
- # Lets decide what type of action we are taking...
- switch ($http_method)
- {
- case "GET":
- if($this->pve_realm=="PVEAPIToken"){
- curl_setopt($prox_ch, CURLOPT_HTTPHEADER, $put_post_http_headers);
- }
- # Nothing extra to do.
- break;
- case "PUT":
- curl_setopt($prox_ch, CURLOPT_CUSTOMREQUEST, "PUT");
- # Set "POST" data.
- $action_postfields_string = $this->convert_postfields_array_to_string($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 = $this->convert_postfields_array_to_string($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:
- if ($this->print_debug === true)
- {
- print("Error - Invalid HTTP Method specified.\n");
- }
- return false;
- }
- curl_setopt($prox_ch, CURLOPT_CONNECTTIMEOUT, 30);
- curl_setopt($prox_ch, CURLOPT_RETURNTRANSFER, true);
- if($this->pve_realm!="PVEAPIToken"){
- curl_setopt($prox_ch, CURLOPT_COOKIE, "PVEAuthCookie=" . $this->pve_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);
- if(!$action_response ){
- $error = curl_error($prox_ch);
- $errorCode = curl_errno($prox_ch);
- }
- $this->httpCode = curl_getinfo($prox_ch, CURLINFO_HTTP_CODE);
- $this->raw_message = $action_response;
- curl_close($prox_ch);
- unset($prox_ch);
- // ModulesGarden
- if ($this->whmcsDebugMode)
- {
- if (function_exists('logModuleCall'))
- {
- logModuleCall(
- "proxmoxVPS", $action_path, $http_method . " https://{$this->pve_hostname}:{$this->port}/api2/json" . $action_path . "\n" . print_r($put_post_parameters, true), '', sprintf("HTTP %s %s",$this->httpCode, $action_response), array($this->pve_username, $this->pve_password)
- );
- self::$moduleLogs[] = [
- "proxmoxVPS",
- $action_path,
- $http_method . " https://{$this->pve_hostname}:{$this->port}/api2/json" . $action_path . "\n" . print_r($put_post_parameters, true),
- '',
- sprintf("HTTP %s %s",$this->httpCode, $action_response),
- [$this->pve_username, $this->pve_password]
- ];
- }
- }
- if($error){
- throw new ProxmoxApiException($error, $errorCode);
- }
- $response = explode("\r\n\r\n", $action_response, 2);
- $header_response = explode("\r\n", $response[0], 2);
- $body_response = $response[1];
- $action_response_array = json_decode( $body_response , true);
- if ($comand == "rrd")
- {
- return $action_response;
- }
- # Parse response, confirm HTTP response code etc.
- if ($this->httpCode && in_array($this->httpCode,[403,500]) || $this->httpCode > 500 && empty( $action_response_array['data'])){
- $header_response[0] = str_replace("HTTP/1.1 {$this->httpCode}", "",$header_response[0] );
- $message = $header_response[0] ? ucfirst(trim($header_response[0])) : 'Wrong response from server';
- throw new ProxmoxApiException($message, $this->httpCode);
- }
- if ($this->httpCode)
- {
- if ($this->httpCode < 400 )
- {
- return $action_response_array['data'];
- }
- else
- {
- $errors = null;
- if (!empty($action_response_array['errors']))
- {
- $errors = "(";
- foreach ($action_response_array['errors'] as $k => $v)
- {
- $errors .= " [$k] - $v";
- }
- $errors .= " )";
- }
- return array('errors' => array( $errors));
- }
- } else
- {
- return array('errors' => array('Invalid HTTP Response - ' . print_r($action_response, true)));
- }
- if (!empty($action_response_array['data']))
- {
- return $action_response_array['data'];
- }
- }
- /**
- * FUNCTION whmcsDebugMode
- * Turn WHMCS DEBUG on
- * @author Grzegorz Draganik - ModulesGarden
- * @param bool $turnon
- */
- public function debug($turnon = true)
- {
- $this->whmcsDebugMode = $turnon;
- }
- /**
- * FUNCTION 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.
- * @return boolean
- */
- public function reload_node_list()
- {
- if (!$this->constructor_success)
- {
- return false;
- }
- $node_list = $this->pve_action("/nodes", "GET");
- if (count($node_list) > 0)
- {
- $nodes_array = array();
- foreach ($node_list as $node)
- {
- $nodes_array[] = $node['node'];
- }
- $this->pve_cluster_node_list = $nodes_array;
- return true;
- }
- else
- {
- if ($this->print_debug === true)
- {
- print("Error - Empty list of nodes returned in this cluster.\n");
- }
- return false;
- }
- }
- /**
- * FUNCTION get_node_list
- * Geting node from proxmox server
- * @return array | boolean
- */
- 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->pve_cluster_node_list == null)
- {
- if ($this->reload_node_list() === false)
- {
- return false;
- }
- }
- return $this->pve_cluster_node_list;
- }
- /**
- * FUNCTION get
- * GET request
- * @param string $action_path
- * @param array $parameters
- * @return array | boolean
- */
- public function get($action_path, $parameters = false)
- {
- if (!$this->constructor_success)
- {
- return false;
- }
- # We run this if we haven't queried for cluster nodes as yet, and cache it in the object.
- if ($this->pve_cluster_node_list == null)
- {
- if ($this->reload_node_list() === false)
- {
- return false;
- }
- }
- return $this->processRequest($this->pve_action($action_path, "GET", $parameters));
- }
- /**
- * FUNCTION put
- * PUT request
- * @param string $action_path
- * @param array $parameters
- * @return boolean | array
- */
- public function put($action_path, $parameters)
- {
- if (!$this->constructor_success)
- {
- return false;
- }
- # We run this if we haven't queried for cluster nodes as yet, and cache it in the object.
- if ($this->pve_cluster_node_list == null)
- {
- if ($this->reload_node_list() === false)
- {
- return false;
- }
- }
- return $this->processRequest($this->pve_action($action_path, "PUT", $parameters));
- }
- /**
- * FUNCTION post
- * POST request
- * @param string $action_path
- * @param string $parameters
- * @return boolean | array
- */
- public function post($action_path, $parameters = array())
- {
- if (!$this->constructor_success)
- {
- return false;
- }
- # We run this if we haven't queried for cluster nodes as yet, and cache it in the object.
- if ($this->pve_cluster_node_list == null)
- {
- if ($this->reload_node_list() === false)
- {
- return false;
- }
- }
- return $this->processRequest($this->pve_action($action_path, "POST", $parameters));
- }
- /**
- * FUNCTION delete
- * Delete request
- * @param string $action_path
- * @return boolean | array
- */
- public function delete($action_path)
- {
- if (!$this->constructor_success)
- {
- return false;
- }
- # We run this if we haven't queried for cluster nodes as yet, and cache it in the object.
- if ($this->pve_cluster_node_list == null)
- {
- if ($this->reload_node_list() === false)
- {
- return false;
- }
- }
- return $this->processRequest($this->pve_action($action_path, "DELETE"));
- }
- public function isProxmox4()
- {
- if (empty($this->_version))
- {
- $info = $this->get('/version');
- $this->_version = $info ['version'];
- }
- return version_compare($this->_version, "4.0", '>=');
- }
- abstract function processRequest($response);
- # Logout not required, PVEAuthCookie tokens have a 2 hour lifetime.
- public static function beginTransaction(){
- self::$moduleLogs=[];
- }
- public static function commit(){
- foreach (self::$moduleLogs as &$log){
- logModuleCall(
- $log[0], $log[1], $log[2], $log[3], $log[4], $log[5]
- );
- unset($log);
- }
- }
- public function getPveHostname(){
- return $this->pve_hostname;
- }
- /**
- * @return mixed
- */
- public function getTokenId()
- {
- return $this->tokenId;
- }
- /**
- * @param mixed $tokenId
- * @return AbstractApi
- */
- public function setTokenId($tokenId)
- {
- $this->tokenId = $tokenId;
- return $this;
- }
- /**
- * @return mixed|string
- */
- public function getPort()
- {
- return $this->port;
- }
- /**
- * @param mixed|string $port
- * @return AbstractApi
- */
- public function setPort($port)
- {
- $this->port = $port;
- return $this;
- }
- }
|