瀏覽代碼

init with original modulsgarden code

root 4 年之前
當前提交
0e6cb182f0
共有 58 個文件被更改,包括 9759 次插入0 次删除
  1. 26 0
      composer.json
  2. 651 0
      v2/AbstractApi.php
  3. 176 0
      v2/Api.php
  4. 139 0
      v2/Factory.php
  5. 32 0
      v2/ProxmoxApiException.php
  6. 34 0
      v2/VmFactory.php
  7. 45 0
      v2/interfaces/KvmInterface.php
  8. 37 0
      v2/interfaces/LxcInterface.php
  9. 33 0
      v2/interfaces/ProductInterface.php
  10. 39 0
      v2/interfaces/VmInterface.php
  11. 120 0
      v2/models/AbstractObject.php
  12. 387 0
      v2/models/AbstractVm.php
  13. 160 0
      v2/models/Agent.php
  14. 262 0
      v2/models/BackupSchedule.php
  15. 147 0
      v2/models/CdRom.php
  16. 80 0
      v2/models/CloudInitDrive.php
  17. 126 0
      v2/models/ClusterResource.php
  18. 46 0
      v2/models/Config.php
  19. 161 0
      v2/models/Features.php
  20. 175 0
      v2/models/File.php
  21. 243 0
      v2/models/FirewallOptions.php
  22. 245 0
      v2/models/FirewallRule.php
  23. 156 0
      v2/models/HaResource.php
  24. 440 0
      v2/models/HardDisk.php
  25. 101 0
      v2/models/IpCidr.php
  26. 177 0
      v2/models/IpConfig.php
  27. 124 0
      v2/models/IpSet.php
  28. 557 0
      v2/models/Kvm.php
  29. 224 0
      v2/models/Lxc.php
  30. 269 0
      v2/models/MountPoint.php
  31. 162 0
      v2/models/Network.php
  32. 266 0
      v2/models/NetworkDeviceKvm.php
  33. 326 0
      v2/models/NetworkDeviceLxc.php
  34. 163 0
      v2/models/Node.php
  35. 178 0
      v2/models/Partition.php
  36. 158 0
      v2/models/Snapshot.php
  37. 158 0
      v2/models/SnapshotKvm.php
  38. 152 0
      v2/models/Storage.php
  39. 229 0
      v2/models/Task.php
  40. 91 0
      v2/models/TemplateKvm.php
  41. 148 0
      v2/models/User.php
  42. 72 0
      v2/repository/AbstractRepository.php
  43. 129 0
      v2/repository/BackupScheduleRepository.php
  44. 133 0
      v2/repository/CdRomRepository.php
  45. 175 0
      v2/repository/ClusterResourcesRepository.php
  46. 357 0
      v2/repository/FileRepository.php
  47. 114 0
      v2/repository/FirewallRulesRepository.php
  48. 82 0
      v2/repository/HaResourceRepository.php
  49. 151 0
      v2/repository/HardDiskRepostiory.php
  50. 82 0
      v2/repository/IpCidrRepository.php
  51. 86 0
      v2/repository/IpConfigRepository.php
  52. 91 0
      v2/repository/IpSetRepository.php
  53. 160 0
      v2/repository/MountPointRepostiory.php
  54. 75 0
      v2/repository/NetworkRepository.php
  55. 244 0
      v2/repository/NodeRepository.php
  56. 140 0
      v2/repository/SnapshotRepository.php
  57. 113 0
      v2/repository/StorageRepository.php
  58. 112 0
      v2/repository/TaskRepository.php

+ 26 - 0
composer.json

@@ -0,0 +1,26 @@
+{
+    "name": "ModulesGarden/Proxmox",
+    "description": "",
+    "version": "2.5.0",
+    "type": "project",
+    "license": "EULA",
+    "homepage": "http://www.modulesgarden.com",
+    "support":
+            {
+                "email": "contact@modulesgarden.com",
+                "issues": "http://www.modulesgarden.com/customers/support",
+                "forum": "http://www.forum.modulesgarden.com/"
+            },
+    "authors": [
+    ],
+    "require":
+            {
+                "php": ">=5.3.7",
+                "phpseclib/phpseclib": "2.*"
+            },
+    "autoload": {
+        "psr-4": {
+            "MGProvision\\Proxmox\\v2\\": "./v2"
+        }
+    }
+}

+ 651 - 0
v2/AbstractApi.php

@@ -0,0 +1,651 @@
+<?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;
+    }
+
+
+}

+ 176 - 0
v2/Api.php

@@ -0,0 +1,176 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2015-07-24)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+/**
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ */
+
+namespace MGProvision\Proxmox\v2;
+
+class Api extends AbstractApi
+{
+    protected $version;
+
+    /**
+     *
+     * @var Api
+     */
+    private static $instance;
+    private $responsePaser;
+
+    public function __construct($pve_hostname, $pve_username, $pve_realm = "pam", $pve_password, $login=true)
+    {
+
+
+        parent::__construct($pve_hostname, $pve_username, $pve_realm, $pve_password);
+
+        $this->validate();
+        if( $pve_realm != "PVEAPIToken" && $login){
+            $this->login();
+        }
+    }
+
+    public function setInstance()
+    {
+        $this->validate();
+        self::$instance = $this;
+    }
+
+    /**
+     * 
+     * @return Api
+     */
+    public static function whmcsFactory($params)
+    {
+
+        $host           = $params['serverip'] ? $params['serverip'] : $params['serverhostname'];
+        return self::$instance = new self($host, $params['serverusername'], $params['serveraccesshash'], $params['serverpassword']);
+    }
+
+    public static function getInstance()
+    {
+
+        if (!empty(self::$instance))
+            return self::$instance;
+
+        throw new ProxmoxApiException("API instance is not defined.");
+    }
+
+    private function validate()
+    {
+
+        if (empty($this->pve_hostname))
+            throw new ProxmoxApiException("Server fields 'Hostname' or 'IP Address' is empty");
+
+        if (empty($this->pve_username))
+            throw new ProxmoxApiException("Server field 'Username' is empty");
+
+        if (empty($this->pve_password))
+            throw new ProxmoxApiException("Server field 'Password' is empty");
+
+        if (empty($this->pve_realm))
+            throw new ProxmoxApiException("Server field 'Authentication' is empty");
+
+        if (gethostbyname($this->pve_hostname) == $this->pve_hostname && !filter_var($this->pve_hostname, FILTER_VALIDATE_IP))
+        {
+            throw new ProxmoxApiException(sprintf("Server Hostname '%s' is invalid", $this->pve_hostname));
+        }
+
+        if (!is_numeric($this->port))
+            throw new ProxmoxApiException(sprintf("Server port '%s' is not valid", $this->port));
+    }
+
+    /**
+     * FUNCTION procesRequest
+     * Catch bug form API
+     * @param array $response
+     * @return string|boolean
+     * @throws Exception
+     */
+    public function processRequest($response)
+    {
+
+        if (is_string($response))
+        {
+            if (is_object($this->responsePaser) && method_exists($this->responsePaser, "parseResponse"))
+                $this->responsePaser->parseResponse($response);
+            return $response;
+        } elseif (isset($response['errors']))
+        {
+
+            $err = null;
+            foreach ($response['errors'] as $param => $msg)
+            {
+                $err .= (is_numeric($param) ? '' : $param . ' - ') . ucfirst(trim($msg)) . ' ';
+            }
+            if ($err)
+            {
+                $err = ucfirst($err);
+                if (is_object($this->responsePaser) && method_exists($this->responsePaser, "parseError"))
+                    $this->responsePaser->parseError($err, $this->httpCode);
+
+                throw new ProxmoxApiException($err, $this->httpCode);
+            }
+            return false;
+        }
+        else if (isset($response['data']['result']['error']['desc']) && $response['data']['result']['error']['desc']){
+            throw new ProxmoxApiException($response['data']['result']['error']['desc'], 0);
+        }
+        else if ($response === null)
+        {
+            return true;
+        }
+        else
+        {
+            return $response;
+        }
+    }
+
+    public function getLoginTicket()
+    {
+        return $this->pve_login_ticket;
+    }
+
+    function getResponsePaser()
+    {
+        return $this->responsePaser;
+    }
+
+    function setResponsePaser($responsePaser)
+    {
+        $this->responsePaser = $responsePaser;
+    }
+
+    public function getVersion()
+    {
+
+        if (empty($this->version))
+        {
+            $info          = $this->get('/version');
+            $this->version = $info ['version'];
+        }
+        return $this->version;
+    }
+
+    public function isProxmox4()
+    {
+
+        return version_compare($this->getVersion(), "4.0", '>=');
+    }
+}

+ 139 - 0
v2/Factory.php

@@ -0,0 +1,139 @@
+<?php
+
+/* * ********************************************************************
+ * proxmoxCloud product developed. (2016-12-30)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2;
+
+use MGProvision\Proxmox\v2 as proxmox;
+
+/**
+ * Description of Factory
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class Factory
+{
+
+    /**
+     * 
+     * @param array $params WHMCS params
+     * @return \MGProvision\Proxmox\v2\Api
+     */
+    static function api($params)
+    {
+        if(is_numeric($params['serverport']) && $params['serverip'] && !preg_match("/\:/",$params['serverip'])){
+            $params['serverip'] .=":".$params['serverport'];
+        }
+        if(is_numeric($params['serverport']) &&  $params['serverhostname'] && !preg_match("/\:/",$params['serverhostname'])){
+            $params['serverhostname'] .=":".$params['serverport'];
+        }
+        $host = $params['serverip'] ? $params['serverip'] : $params['serverhostname'];
+        return new Api($host, $params['serverusername'], $params['serveraccesshash'], $params['serverpassword']);
+    }
+
+    /**
+     * 
+     * @return \MGProvision\Proxmox\v2\Api
+     */
+    static function apiFromServer(\ModulesGarden\ProxmoxAddon\Core\Models\Whmcs\Server $server)
+    {
+        $host = $server->ipaddress ? $server->ipaddress : $server->hostname;
+        return new Api($host, $server->username, $server->accesshash, decrypt($server->password));
+    }
+
+    static function vm($vServer)
+    {
+        if ($vServer instanceof \MGProvision\ProxmoxCloud\models\VServer)
+        {
+            $vmid           = $vServer->getVmid();
+            $node           = $vServer->getNode();
+            $virtualization = $vServer->getVirtualization();
+        }
+        else
+        {
+            $vmid           = $vServer->vmid;
+            $node           = $vServer->node;
+            $virtualization = $vServer->virtualization;
+        }
+        switch (strtolower($virtualization))
+        {
+            case 'kvm':
+            case 'qemu':
+                return new proxmox\models\Kvm($node, $vmid);
+                break;
+            case 'lxc':
+                return new proxmox\models\Lxc($node, $vmid);
+                break;
+            default:
+                throw new \Exception(sprintf("Invalid virtualization type  '%s'", $virtualization));
+        }
+    }
+
+    static function vmVps($product, $params)
+    {
+
+        if (is_string($product))
+        {
+            $virtualization = $product;
+        }
+        else if (is_object($product) && method_exists($product, 'getConfig'))
+        {
+            $virtualization = $product->getConfig('virtualization_type');
+        }else if(is_object($product) && method_exists($product, 'getVirtualization')){
+            $virtualization = $product->getVirtualization();
+        }
+        switch (strtolower($virtualization))
+        {
+            case 'kvm':
+            case 'qemu':
+                $vm = new proxmox\models\Kvm($params['customfields']['node'], $params['customfields']['vmid']);
+                break;
+            case 'lxc':
+                $vm = new proxmox\models\Lxc($params['customfields']['node'], $params['customfields']['vmid']);
+                break;
+            default:
+                throw new \Exception(sprintf("Invalid virtualization type  '%s'", $virtualization));
+        }
+
+        $vm->setName(!empty($params['domain']) ? $params['domain'] : $params['customfields']['proxmoxHostname']);
+        return $vm;
+    }
+
+    /**
+     * 
+     * @param array $params
+     * @return \MGProvision\Proxmox\v2\models\User
+     */
+    static function user(array $params)
+    {
+
+        if (empty($params['customfields']['Authentication']))
+        {
+            throw new \Exception("Custom field ('Authentication') is empty.");
+        }
+        if (empty($params['username']))
+        {
+            throw new \Exception("Servuce field ('Username') is empty.");
+        }
+
+        $userid = $params['username'] . "@" . $params['customfields']['Authentication'];
+        return new proxmox\models\User($userid);
+    }
+}

+ 32 - 0
v2/ProxmoxApiException.php

@@ -0,0 +1,32 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-05)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2;
+
+/**
+ * Description of ProxmoxApiException
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class ProxmoxApiException extends \Exception
+{
+    
+}

+ 34 - 0
v2/VmFactory.php

@@ -0,0 +1,34 @@
+<?php
+
+
+namespace MGProvision\Proxmox\v2;
+
+
+use MGProvision\Proxmox\v2\models\Kvm;
+use MGProvision\Proxmox\v2\models\Lxc;
+use ModulesGarden\ProxmoxAddon\App\Models\Vm;
+use ModulesGarden\ProxmoxAddon\App\Models\VmModel;
+
+class VmFactory
+{
+    /**
+     * @param Vm $vmModel
+     * @return Kvm|Lxc
+     * @throws \Exception
+     */
+    public function fromVmModel(VmModel $vmModel ){
+        switch (strtolower($vmModel->virtualization))
+        {
+            case 'kvm':
+            case 'qemu':
+                return new Kvm($vmModel->node, $vmModel->vmid);
+                break;
+            case 'lxc':
+                return new Lxc($vmModel->node, $vmModel->vmid);
+                break;
+            default:
+                throw new \Exception(sprintf("Invalid virtualization type  '%s'", $vmModel->virtualization));
+        }
+    }
+
+}

+ 45 - 0
v2/interfaces/KvmInterface.php

@@ -0,0 +1,45 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-05)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\interfaces;
+
+/**
+ * Description of KvmInterface
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+interface KvmInterface
+{
+
+    public function status();
+
+    public function resume();
+
+    public function shutdown();
+
+    public function start();
+
+    public function stop();
+
+    public function suspend();
+
+    public function cloneVm($container);
+}

+ 37 - 0
v2/interfaces/LxcInterface.php

@@ -0,0 +1,37 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-05)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\interfaces;
+
+/**
+ * Description of KvmInterface
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+interface LxcInterface
+{
+
+    public function protectionOff();
+
+    public function restore($ostemplate);
+
+    public function findNetworkDevice($ip);
+}

+ 33 - 0
v2/interfaces/ProductInterface.php

@@ -0,0 +1,33 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxCloudVps product developed. (2016-12-30)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\interfaces;
+
+/**
+ * Description of ProxmoxProduct
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+interface ProductInterface
+{
+
+    public function getConfig();
+}

+ 39 - 0
v2/interfaces/VmInterface.php

@@ -0,0 +1,39 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-12-13)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\interfaces;
+
+/**
+ * Description of VmInterface
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+interface VmInterface
+{
+
+    public function getVmid();
+
+    public function getNode();
+
+    public function getPath();
+
+    public function getVirtualization();
+}

+ 120 - 0
v2/models/AbstractObject.php

@@ -0,0 +1,120 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-11)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Description of AbstractObject
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+abstract class AbstractObject
+{
+    protected $api;
+
+    public function setAttributes($attributes)
+    {
+
+        foreach ($attributes as $name => $attValue)
+        {
+            $methodName = 'set' . ucfirst($name);
+            if (method_exists($this, $methodName))
+                $this->{$methodName}($attValue);
+        }
+        return $this;
+    }
+
+    public function setApi($api)
+    {
+        $this->api = $api;
+        return $this;
+    }
+
+    /**
+     * 
+     * @return \MGProvision\Proxmox\v2\Api
+     */
+    protected function api()
+    {
+        if ($this->api !== null)
+            return $this->api;
+        return $this->api = \MGProvision\Proxmox\v2\Api::getInstance();
+    }
+
+    public static function asArray($proxmoxConfig)
+    {
+
+        $result = array();
+        $config = explode(",", $proxmoxConfig);
+        foreach ($config as $c)
+        {
+            list($key, $val) = explode("=", $c);
+            $result[$key] = $val;
+        }
+        return $result;
+    }
+
+    public function toArray()
+    {
+
+        if (method_exists($this, 'getAttributes'))
+        {
+            return $this->getAttributes();
+        }
+
+        $fields = get_class_vars(get_called_class());
+        $data   = array();
+        foreach ($fields as $name => $defult)
+        {
+            $methodName = 'get' . ucfirst($name);
+            if (method_exists($this, $methodName))
+            {
+                $data[$name] = $this->{$methodName}();
+            }
+        }
+
+        return $data;
+    }
+
+    public function toConfig($fields, &$config)
+    {
+        foreach ($fields as $field)
+        {
+            $methodName = 'get' . ucfirst($field);
+            if (method_exists($this, $methodName) && ( $this->{$methodName}() || $this->{$methodName}() ===0  || $this->{$methodName}() ==="0") )
+            {
+                $config[] = "{$field}=" . $this->{$methodName}();
+            }
+        }
+    }
+
+    public function toString()
+    {
+        return print_r($this->getAttributes(), true);
+    }
+
+    public function getHashCode()
+    {
+
+        return hash('ripemd160',$this->toString());
+
+    }
+}

+ 387 - 0
v2/models/AbstractVm.php

@@ -0,0 +1,387 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-05)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use MGProvision\Proxmox\v2\ProxmoxApiException;
+use \MGProvision\Proxmox\v2\repository\IpSetRepository;
+
+/**
+ * Description of AbstractVm
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+abstract class AbstractVm extends AbstractObject implements \MGProvision\Proxmox\v2\interfaces\VmInterface
+{
+    protected $vmid;
+    protected $node;
+    protected $status;
+    private $name;
+    protected $config;
+    public $ipSet;
+
+    function __construct($node, $vmid = null)
+    {
+
+        $this->node = $node;
+        if ($node !== null && $vmid !== null)
+        {
+            $this->vmid = $vmid;
+            $this->validate();
+        }
+    }
+
+    /**
+     * @throws ProxmoxApiException
+     * @todo  remove debug() function
+     */
+    public function validate()
+    {
+        if (empty($this->vmid)){
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Field 'VMID' is empty");
+        }
+
+
+        if (!is_numeric($this->vmid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException(sprintf("Field VMID '%s' is invalid", $this->vmid));
+        if (empty($this->node))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Field 'Node' is empty");
+    }
+
+    abstract function getVirtualization();
+
+    public function status($force = false)
+    {
+
+        if (!empty($this->status) && $force === false)
+            return $this->status;
+
+        return $this->status = $this->api()->get("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/status/current");
+    }
+
+    public function config($force = false)
+    {
+
+        if (!empty($this->config) && $force === false)
+            return $this->config;
+
+        $this->config = $this->api()->get("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/config");
+        ksort($this->config);
+        return $this->config;
+    }
+
+    public function updateConfig($config)
+    {
+
+        $res          = $this->api()->put("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/config", (array) $config);
+        $this->config = null;
+        return $res;
+    }
+
+    public function resume()
+    {
+        return $this->api()->post("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/status/resume");
+    }
+
+    public function shutdown()
+    {
+        return $this->api()->post("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/status/shutdown");
+    }
+
+    public function start()
+    {
+        return $this->api()->post("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/status/start");
+    }
+
+    public function stop()
+    {
+        return $this->api()->post("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/status/stop");
+    }
+
+    public function delete()
+    {
+
+        $status = $this->status(true);
+        if ($status['status'] == "running")
+        {
+            $this->stop();
+            sleep(5);
+        }
+        return $this->api()->delete("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}");
+    }
+
+    public function restart()
+    {
+
+        $status = $this->status(true);
+        if ($status['status'] == "running")
+        {
+            $this->stop();
+            sleep(15);
+        }
+        $end = time() + 250;
+        while (time() < $end)
+        {
+            if ($this->isRunning())
+            {
+                sleep(5);
+                continue;
+            }
+            return $this->start();
+        }
+        return $this->start();
+    }
+
+    public function getVmid()
+    {
+        return $this->vmid;
+    }
+
+    public function getNode()
+    {
+        return $this->node;
+    }
+
+    public function setNode($node){
+        $this->node = $node;
+        return $this;
+    }
+
+    /**
+     * 
+     * @return \MGProvision\Proxmox\v2\models\Node
+     */
+    public function node()
+    {
+        return new Node($this->node);
+    }
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    public function parseResponse(&$response)
+    {
+        //set your own lang here
+    }
+
+    public function parseError(&$error, &$httpCode)
+    {
+        //parse error here
+        $search  = array("CT {$this->getVmid()}");
+        $replace = array($this->getName());
+        $error   = str_replace($search, $replace, $error);
+    }
+
+    public function setApiParser()
+    {
+        $this->api()->setResponsePaser($this);
+    }
+
+    public function rrd($parameter)
+    {
+        return $this->api()->get("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/rrd", $parameter);
+    }
+
+    public function rrdData($parameter)
+    {
+        return $this->api()->get("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/rrddata", $parameter);
+    }
+
+    public function create($container)
+    {
+
+        $result     = $this->api()->post("/nodes/{$this->node}/{$this->getVirtualization()}/", $container);
+        $this->vmid = $container['vmid'];
+        return $result;
+    }
+
+    public function getPath()
+    {
+        return "/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}";
+    }
+
+    public function isRunning()
+    {
+        $status = $this->status(true);
+        return $status['status'] == "running";
+    }
+
+    public function spiceproxy()
+    {
+        return $this->api()->post("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/spiceproxy");
+    }
+
+
+    public function hasSpiceproxy(){
+        if(!$this->isRunning()){
+            return false;
+        }
+        try{
+            return $this->spiceproxy();
+        }catch (ProxmoxApiException $ex){
+            if(preg_match("/Timeout while waiting for port/", $ex->getMessage())){
+                return true;
+            }
+            else if(!preg_match("/No spice port/", $ex->getMessage())){
+                return false;
+            }
+            return false;
+        }
+    }
+
+    public function vncproxy($websocket = 0)
+    {
+
+        $setting              = array();
+        if ($websocket)
+            $setting['websocket'] = 1;
+        return $this->api()->post("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/vncproxy", $setting);
+    }
+
+    public function deleteConfig($id)
+    {
+        $this->config = null;
+        return $this->updateConfig(array("delete" => $id));
+    }
+
+    public function backup($storage = 'local', $routing = "1", $maxFiles = null, $compress = null, $mode = null)
+    {
+
+        $data             = array();
+        $data['all']      = "0";
+        $data['vmid']     = $this->vmid;
+        $data['storage']  = $storage;
+        $data['remove']   = $routing;
+        if(is_numeric($maxFiles)){
+            $data['maxfiles'] = (int) $maxFiles;
+        }
+        if ($compress)
+            $data['compress'] = $compress;
+        if ($mode)
+            $data['mode']     = $mode;
+        return $this->api()->post("/nodes/{$this->node}/vzdump", $data);
+    }
+
+    /**
+     * 
+     * @return \MGProvision\Proxmox\v2\models\FirewallOptions
+     */
+    public function firewallOptions()
+    {
+
+        $obj = new FirewallOptions();
+        return $obj->setPath("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/firewall/options");
+    }
+
+    public function findFreeDeviceId($busType)
+    {
+        $used    = array();
+        $busType = strtolower($busType);
+        foreach ($this->config(true) as $k => $v)
+        {
+            if (strpos($k, $busType) !== false)
+            {
+                $used[] = preg_replace("/[a-z]+/", "", $k);
+            }
+        }
+        /**
+         * ide    0-3
+         * sata   0-5
+         * virtio 0-15
+         * scsi   0-13
+         */
+        switch ($busType)
+        {
+            case 'ide':
+                $to = 3;
+                break;
+            case 'sata':
+                $to = 5;
+                break;
+            case 'virtio':
+                $to = 15;
+                break;
+            case 'scsi':
+                $to = 13;
+                break;
+            default:
+                $to = 3;
+        }
+        for ($n = 0; $n <= $to; $n++)
+        {
+            if (!in_array($n, $used))
+            {
+                return $n;
+            }
+        }
+        return null;
+    }
+
+    public function addIpSet(IpSet $ipSet)
+    {
+        $ipSet->setPath($this->getPath() . "/firewall/ipset")->create();
+    }
+
+    /**
+     * Get IpSet
+     * @return IpSet[]
+     */
+    public function getIpSet()
+    {
+        if ($this->ipSet)
+        {
+            return $this->ipSet;
+        }
+        $repostitory = new IpSetRepository();
+        $repostitory->findByVm($this);
+        $this->ipSet = $repostitory->fetch();
+        unset($repostitory);
+        return $this->ipSet;
+    }
+
+    public function migrate(array $setting)
+    {
+        return $this->api()->post("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/migrate", $setting);
+    }
+
+    public function sshkeysName()
+    {
+        $sshkeys = rawurldecode($this->config()['sshkeys']);
+        if (!$sshkeys)
+        {
+            return null;
+        }
+        $sshkeys = preg_replace('/\s+/', ' ',  $sshkeys);
+        $ex = explode(" ", $sshkeys);
+        return $ex[2];
+    }
+
+    public function isHaManaged(){
+        return $this->status()['ha']['managed']==1;
+    }
+}

+ 160 - 0
v2/models/Agent.php

@@ -0,0 +1,160 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxAddon product developed. (Nov 8, 2018)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Description of Agent
+ *
+ * @author Pawel Kopec <pawelk@modulesgardne.com>
+ */
+class Agent extends AbstractObject
+{
+    protected $path;
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    public function setPath($path)
+    {
+        $this->path = $path;
+        return $this;
+    }
+
+    public function fsfreezeFreeze()
+    {
+        return $this->api()->post($this->getPath() . '/fsfreeze-freeze');
+    }
+
+    public function fsfreezeStatus()
+    {
+        return $this->api()->post($this->getPath() . '/fsfreeze-status');
+    }
+
+    public function fsfreezeThaw()
+    {
+        return $this->api()->post($this->getPath() . '/fsfreeze-thaw');
+    }
+
+    public function fstrim()
+    {
+        return $this->api()->post($this->getPath() . '/fstrim');
+    }
+
+    public function getFsinfo()
+    {
+        return $this->api()->get($this->getPath() . '/get-fsinfo');
+    }
+
+    public function getHostname()
+    {
+        return $this->api()->get($this->getPath() . '/get-host-name');
+    }
+
+    public function getMemoryBlockInfo()
+    {
+        return $this->api()->get($this->getPath() . '/get-memory-block-info');
+    }
+
+    public function getMemoryBlocks()
+    {
+        return $this->api()->get($this->getPath() . '/get-memory-blocks');
+    }
+
+    public function getOsinfo()
+    {
+        return $this->api()->get($this->getPath() . '/get-osinfo');
+    }
+
+    public function getIime()
+    {
+        return $this->api()->get($this->getPath() . '/get-time');
+    }
+
+    public function getIimezone()
+    {
+        return $this->api()->get($this->getPath() . '/get-timezone');
+    }
+
+    public function getUsers()
+    {
+        return $this->api()->get($this->getPath() . '/get-users');
+    }
+
+    public function getVcpus()
+    {
+        return $this->api()->get($this->getPath() . '/get-vcpus');
+    }
+
+    public function info()
+    {
+        return $this->api()->get($this->getPath() . '/info');
+    }
+
+    public function networkGetInterfaces()
+    {
+        return $this->api()->get($this->getPath() . '/network-get-interfaces');
+    }
+
+    public function ping()
+    {
+        return $this->api()->post($this->getPath() . '/ping');
+    }
+
+    public function shutdown()
+    {
+        return $this->api()->post($this->getPath() . '/shutdown');
+    }
+
+    public function suspendDisk()
+    {
+        return $this->api()->post($this->getPath() . '/suspend-disk');
+    }
+
+    public function suspendHybrid()
+    {
+        return $this->api()->post($this->getPath() . '/suspend-hybrid');
+    }
+
+    public function suspendRam()
+    {
+        return $this->api()->post($this->getPath() . '/suspend-ram');
+    }
+
+    /**
+     * @param $command| The command as a list of program + arguments
+     * @return array|bool
+     */
+    public function exec($command){
+        return $this->api()->post($this->getPath() . '/exec', ['command' => $command]);
+    }
+
+
+    public function setUserPassword($username, $password, $crypted=null){
+        $parameters =  ['username' => $username, "password" => $password ];
+        if(is_bool($crypted)){
+            $parameters['crypted'] = $crypted;
+        }
+        return $this->api()->post($this->getPath() . '/set-user-password', $parameters);
+    }
+
+}

+ 262 - 0
v2/models/BackupSchedule.php

@@ -0,0 +1,262 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-12-13)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use MGProvision\Proxmox\v2 as proxmox;
+
+/**
+ * Description of BackupSchedule
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class BackupSchedule extends AbstractObject
+{
+    protected $path;
+    protected $starttime;
+    protected $enabled;
+    protected $mailto;
+    protected $dow;
+    protected $vmid;
+    protected $storage;
+    protected $quiet;
+    protected $id;
+    protected $remove;
+    protected $compress;
+    protected $mode;
+    protected $maxfiles;
+    protected $node;
+
+    public function getStarttime()
+    {
+        return $this->starttime;
+    }
+
+    public function getEnabled()
+    {
+        return $this->enabled;
+    }
+
+    public function getMailto()
+    {
+        return $this->mailto;
+    }
+
+    public function getDow()
+    {
+        return $this->dow;
+    }
+
+    public function getVmid()
+    {
+        return $this->vmid;
+    }
+
+    public function getStorage()
+    {
+        return $this->storage;
+    }
+
+    public function getQuiet()
+    {
+        return $this->quiet;
+    }
+
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    public function getRemove()
+    {
+        return $this->remove;
+    }
+
+    public function getCompress()
+    {
+        return $this->compress;
+    }
+
+    public function getMode()
+    {
+        return $this->mode;
+    }
+
+    public function getMaxfiles()
+    {
+        return $this->maxfiles;
+    }
+
+    public function getNode()
+    {
+        return $this->node;
+    }
+
+    public function setStarttime($starttime)
+    {
+        $this->starttime = $starttime;
+        return $this;
+    }
+
+    public function setEnabled($enabled)
+    {
+        $this->enabled = $enabled;
+        return $this;
+    }
+
+    public function setMailto($mailto)
+    {
+        $this->mailto = $mailto;
+        return $this;
+    }
+
+    public function setDow($dow)
+    {
+        $this->dow = $dow;
+        return $this;
+    }
+
+    public function setVmid($vmid)
+    {
+        $this->vmid = $vmid;
+        return $this;
+    }
+
+    public function setStorage($storage)
+    {
+        $this->storage = $storage;
+        return $this;
+    }
+
+    public function setQuiet($quiet)
+    {
+        $this->quiet = $quiet;
+        return $this;
+    }
+
+    public function setId($id)
+    {
+        $this->id = $id;
+        return $this;
+    }
+
+    public function setRemove($remove)
+    {
+        $this->remove = $remove;
+        return $this;
+    }
+
+    public function setCompress($compress)
+    {
+        $this->compress = $compress;
+        return $this;
+    }
+
+    public function setMode($mode)
+    {
+        $this->mode = $mode;
+        return $this;
+    }
+
+    public function setMaxfiles($maxfiles)
+    {
+        $this->maxfiles = $maxfiles;
+        return $this;
+    }
+
+    public function setNode($node)
+    {
+        $this->node = $node;
+        return $this;
+    }
+
+    public function setPath($path)
+    {
+        $this->path = $path;
+        return $this;
+    }
+
+    public function delete()
+    {
+        if (empty($this->id))
+            throw new proxmox\ProxmoxApiException('Backup Schedule [id] - property is missing and it is not optional');
+        return $this->api()->delete("/cluster/backup/".$this->getId());
+    }
+
+    private function fill()
+    {
+
+        $data = array(
+            "vmid"      => $this->getVmid(),
+            "starttime" => $this->getStarttime(),
+        );
+        if($this->getMaxfiles()){
+            $data['maxfiles'] = (int) $this->getMaxfiles();
+        }
+
+        if ($this->getStorage())
+            $data['storage'] = $this->getStorage();
+
+        if ($this->getRemove())
+            $data['remove'] = $this->getRemove();
+
+        if ($this->getDow())
+            $data['dow'] = $this->getDow();
+
+        if (!is_null($this->mode))
+            $data['mode'] = $this->getMode();
+
+        if (!is_null($this->compress))
+            $data['compress'] = $this->getCompress();
+
+        if ($this->getMailto())
+            $data['mailto'] = $this->getMailto();
+        return $data;
+    }
+
+    public function create()
+    {
+
+        $data = $this->fill();
+        return $this->api()->post("/cluster/backup", $data);
+    }
+
+    public function update()
+    {
+
+        $data = $this->fill();
+        return $this->api()->put("/cluster/backup/" . $this->getId(), $data);
+    }
+
+    public function getAttributes()
+    {
+        return array(
+            'displayId' => preg_replace('/^(.*):/', '', $this->getId()),
+            "starttime" => $this->getStarttime(),
+            "days"      => explode(",", $this->dow),
+            "compress"  => $this->getCompress(),
+            "mode"      => $this->getMode(),
+            "mailto"    => $this->getMailto(),
+            "id"        => $this->getId(),
+            "dow"       => $this->getDow()
+        );
+    }
+}

+ 147 - 0
v2/models/CdRom.php

@@ -0,0 +1,147 @@
+<?php
+
+
+namespace MGProvision\Proxmox\v2\models;
+
+
+class CdRom extends AbstractObject {
+
+    protected $id;
+    protected $storage;
+    protected $isoFile;
+    protected $path;
+    protected $media;
+    protected $location;
+
+    public function __construct($id, $config=null) {
+        $this->id = $id;
+        if ($config) {
+            $config = self::asArray($config);
+            $this->location = key($config);
+            $ex = explode(":", $this->location );
+            $config['storage'] = $ex[0];
+            $config['isoFile'] = $ex[1];
+            $this->setAttributes($config);
+        }
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * @param mixed $path
+     */
+    public function setPath($path)
+    {
+        $this->path = $path;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * @param mixed $id
+     */
+    public function setId($id)
+    {
+        $this->id = $id;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getStorage()
+    {
+        return $this->storage;
+    }
+
+    /**
+     * @param mixed $storage
+     */
+    public function setStorage($storage)
+    {
+        $this->storage = $storage;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getIsoFile()
+    {
+        return $this->isoFile;
+    }
+
+    /**
+     * @param mixed $isoFile
+     */
+    public function setIsoFile($isoFile)
+    {
+        $this->isoFile = $isoFile;
+        return $this;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getMedia()
+    {
+        return $this->media;
+    }
+
+    /**
+     * @param mixed $media
+     */
+    public function setMedia($media)
+    {
+        $this->media = $media;
+    }
+
+    /**
+     * @return int|string|null
+     */
+    public function getLocation()
+    {
+        return $this->location;
+    }
+
+    /**
+     * @param int|string|null $location
+     */
+    public function setLocation($location)
+    {
+        $this->location = $location;
+        if(preg_match("/\:/", $location)){
+            $ex = explode(":", $this->location );
+            $this->setStorage($ex[0]);
+            $this->setIsoFile($ex[1]);
+        }
+        $this->location = $location;
+        return $this;
+    }
+
+
+    public function asConfig(){
+        $filable=['media'];
+        $config = [];
+        $config[]= $this->getLocation();
+        $this->toConfig($filable, $config);
+        return implode(",", $config);
+    }
+
+
+    public function update(){
+
+        return $this->api()->put($this->getPath(), [$this->getId()=>  $this->asConfig()]);
+    }
+
+}

+ 80 - 0
v2/models/CloudInitDrive.php

@@ -0,0 +1,80 @@
+<?php
+
+
+namespace MGProvision\Proxmox\v2\models;
+
+
+class CloudInitDrive extends AbstractObject {
+
+    protected $path;
+    protected $storage;
+    protected $location;
+
+    public function __construct($id, $config=null) {
+        $this->id = $id;
+        if ($config) {
+            $config = self::asArray($config);
+            $this->location = key($config);
+            $ex = explode(":", $this->location );
+            $config['storage'] = $ex[0];
+            $this->setAttributes($config);
+        }
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * @param mixed $path
+     */
+    public function setPath($path)
+    {
+        $this->path = $path;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getStorage()
+    {
+        return $this->storage;
+    }
+
+    /**
+     * @param mixed $storage
+     */
+    public function setStorage($storage)
+    {
+        $this->storage = $storage;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getMedia()
+    {
+        return $this->media;
+    }
+
+    /**
+     * @param mixed $media
+     */
+    public function setMedia($media)
+    {
+        $this->media = $media;
+    }
+
+    public function unmount(){
+        return $this->api()->put($this->getPath(),[$this->id => 'none,media=cdrom' ]);
+    }
+
+    public function mount(){
+        return $this->api()->put($this->getPath(),[$this->id => "{$this->storage}:cloudinit,format=raw" ]);
+    }
+
+}

+ 126 - 0
v2/models/ClusterResource.php

@@ -0,0 +1,126 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2017-06-07)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Description of ClusterResource
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class ClusterResource extends AbstractObject
+{
+    protected $name;
+    protected $vmid;
+    protected $node;
+    protected $template;
+    protected $type;
+    protected $status;
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function isCustom(){
+        return preg_match('/^custom[0-9]*/',$this->getName());
+    }
+
+    public function matchName($serviceid){
+        return preg_match('/^custom'.$serviceid.'-*/',$this->getName());
+    }
+
+    public function getVmid()
+    {
+        return $this->vmid;
+    }
+
+    public function getNode()
+    {
+        return $this->node;
+    }
+
+    public function getTemplate()
+    {
+        return $this->template;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+        return $this;
+    }
+
+    public function setVmid($vmid)
+    {
+        $this->vmid = $vmid;
+        return $this;
+    }
+
+    public function setNode($node)
+    {
+        $this->node = $node;
+        return $this;
+    }
+
+    public function setTemplate($template)
+    {
+        $this->template = $template;
+        return $this;
+    }
+
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    public function setType($type)
+    {
+        $this->type = $type;
+        return $this;
+    }
+
+    public function getStatus()
+    {
+        return $this->status;
+    }
+
+    public function setStatus($status)
+    {
+        $this->status = $status;
+        return $this;
+    }
+
+    public function getVm()
+    {
+        switch ($this->type)
+        {
+            case 'qemu' :
+                return new Kvm($this->node, $this->vmid);
+                break;
+            case 'lxc':
+                return new Lxc($this->node, $this->vmid);
+                break;
+            default :
+                throw new \Exception(sprintf("Unkown virtualization %s type", $this->type));
+        }
+    }
+}

+ 46 - 0
v2/models/Config.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Class Config
+ * @package MGProvision\Proxmox\v2\models
+ */
+class Config
+{
+    /**
+     * @var array
+     */
+    protected $net  = [];
+
+    public function __construct()
+    {
+
+    }
+
+    /**
+     * @param $network
+     * @return $this
+     */
+    public function setNet($network)
+    {
+        $this->net  = $network;
+
+        return $this;
+    }
+
+    /**
+     * @return array
+     */
+    public function toArray()
+    {
+        $output = [];
+
+        foreach($this->net as $key => $net)
+        {
+            $output['net'.$key] = $net->asConfig();
+        }
+
+        return $output;
+    }
+}

+ 161 - 0
v2/models/Features.php

@@ -0,0 +1,161 @@
+<?php
+
+
+namespace MGProvision\Proxmox\v2\models;
+
+
+class Features extends AbstractObject
+{
+    const ID = 'features';
+    private $keyctl;
+    private $nesting;
+    private $mount;
+    private $fuse;
+    private $mknod;
+
+    /**
+     * Features constructor.
+     */
+    public function __construct($config=null)
+    {
+        if ($config) {
+            $config = self::asArray($config);
+            $this->setAttributes($config);
+        }
+    }
+
+    /**
+     * @param mixed $keyctl
+     * @return Features
+     */
+    public function setKeyctl($keyctl)
+    {
+        $this->keyctl = $keyctl;
+        return $this;
+    }
+
+
+    /**
+     * @param mixed $nesting
+     * @return Features
+     */
+    public function setNesting($nesting)
+    {
+        $this->nesting = $nesting;
+        return $this;
+    }
+
+    /**
+     * @param mixed $mount
+     * @return Features
+     */
+    public function setMount($mount)
+    {
+        $this->mount = $mount;
+        return $this;
+    }
+
+    /**
+     * @param mixed $fuse
+     * @return Features
+     */
+    public function setFuse($fuse)
+    {
+        $this->fuse = $fuse;
+        return $this;
+    }
+
+    /**
+     * @param mixed $mknod
+     * @return Features
+     */
+    public function setMknod($mknod)
+    {
+        $this->mknod = $mknod;
+        return $this;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getKeyctl()
+    {
+        return $this->keyctl;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getNesting()
+    {
+        return $this->nesting;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getMount()
+    {
+        return $this->mount;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getFuse()
+    {
+        return $this->fuse;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getMknod()
+    {
+        return $this->mknod;
+    }
+
+    public function addNfs(){
+        if(is_null($this->mount)){
+            $this->mount = 'nfs';
+        }else if(!preg_match("/nfs/", $this->mount)){
+            $this->mount .= ';nfs';
+        }
+        return $this;
+    }
+
+    public function addCifs(){
+        if(is_null($this->mount)){
+            $this->mount = 'cifs';
+        }else if(!preg_match("/cifs/", $this->mount)){
+            $this->mount .= ';cifs';
+        }
+        return $this;
+    }
+
+    public function isEmpty(){
+        return is_null($this->keyctl) && is_null($this->nesting) && is_null($this->mount) &&
+            is_null($this->fuse) && is_null($this->mknod);
+    }
+    public function asConfig(){
+        $filable=[];
+        if($this->keyctl){
+            $filable[] = 'keyctl';
+        }
+        if($this->nesting){
+            $filable[] = 'nesting';
+        }
+        if($this->mount){
+            $filable[] = 'mount';
+        }
+        if($this->fuse){
+            $filable[] = 'fuse';
+        }
+        if($this->mknod){
+            $filable[] = 'mknod';
+        }
+        $config = [];
+        $this->toConfig($filable, $config);
+        return implode(",", $config);
+    }
+}

+ 175 - 0
v2/models/File.php

@@ -0,0 +1,175 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-11)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use MGProvision\Proxmox\v2 as proxmox;
+
+/**
+ * Description of File
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class File extends AbstractObject
+{
+    protected $content;
+    protected $size;
+    protected $format;
+    protected $volid;
+    protected $ctime;
+    private $path;
+
+    public function getContent()
+    {
+        return $this->content;
+    }
+
+    public function getSize()
+    {
+        return $this->size;
+    }
+
+    public function getFormat()
+    {
+        return $this->format;
+    }
+
+    public function getVolid()
+    {
+        return $this->volid;
+    }
+
+    public function setContent($content)
+    {
+        $this->content = $content;
+        return $this;
+    }
+
+    public function setSize($size)
+    {
+        $this->size = $size;
+        return $this;
+    }
+
+    public function setFormat($format)
+    {
+        $this->format = $format;
+        return $this;
+    }
+
+    public function setVolid($volid)
+    {
+        $this->volid = $volid;
+        return $this;
+    }
+
+    public function getFriendlyName()
+    {
+
+        $ex           = explode("/", $this->getVolid());
+        $friendlyName = end($ex);
+        $s            = array("-", "_", ".tar.gz", ".iso", '.tar.xz');
+        $r            = array(" ", " ", "", "", "");
+        $friendlyName = str_replace($s, $r, $friendlyName);
+        $friendlyName = ucwords($friendlyName);
+        return $friendlyName;
+    }
+
+    /**
+     * 
+     * @param string $path i.e. /nodes/proxmox/storage/local/content/local:backup/vzdump-lxc-100-2017_01_18-10_33_18.tar.lzo
+     * @throws proxmox\ProxmoxApiException
+     */
+    public function setPath($path)
+    {
+
+        if (!preg_match('/\/content\//', $path))
+        {
+            throw new proxmox\ProxmoxApiException(sprintf("File Path ('%s') is not valid", $path));
+        }
+        $this->path = $path;
+    }
+
+    public function getDate()
+    {
+        if($this->ctime){
+            return date("Y-m-d", $this->ctime);
+        }
+        preg_match('/[0-9]{4}_[0-9]{2}_[0-9]{2}/', $this->volid, $matches);
+        return str_replace("_", "-", $matches[0]);
+    }
+
+    public function getHour()
+    {
+        if($this->ctime){
+            return date("H:i:s", $this->ctime);
+        }
+        preg_match('/-[0-9]{2}_[0-9]{2}_[0-9]{2}/', $this->volid, $matches);
+        return str_replace(array("-", "_"), array("", ":"), $matches[0]);
+    }
+
+    public function getTimestamp()
+    {
+        return strtotime($this->getDate() . " " . $this->getHour());
+    }
+
+    public function getAttributes()
+    {
+
+        return array(
+            "content" => $this->getContent(),
+            "size"    => $this->getSize(),
+            "format"  => $this->getFormat(),
+            "volid"   => $this->getVolid(),
+            "date"    => $this->getDate(),
+            "hour"    => $this->getHour()
+        );
+    }
+
+    public function delete()
+    {
+
+        if (empty($this->path))
+            throw new proxmox\ProxmoxApiException('File [path] - property is missing and it is not optional');
+
+        return $this->api()->delete($this->path);
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getCtime()
+    {
+        return $this->ctime;
+    }
+
+    /**
+     * @param mixed $ctime
+     * @return File
+     */
+    public function setCtime($ctime)
+    {
+        $this->ctime = $ctime;
+        return $this;
+    }
+
+
+}

+ 243 - 0
v2/models/FirewallOptions.php

@@ -0,0 +1,243 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2017-01-11)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use MGProvision\Proxmox\v2 as proxmox;
+
+/**
+ * Description of FirewallOptions
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class FirewallOptions extends AbstractObject
+{
+    protected $path;
+    protected $enable;
+    protected $ipfilter;
+    protected $policy_in;
+    protected $policy_out;
+    protected $log_level_in;
+    protected $log_level_out;
+    protected $dhcp;
+    protected $ndp;
+    protected $macfilter;
+    protected $radv;
+
+    public function setPath($path)
+    {
+
+        if (!preg_match('/[0-9]\/firewall\/options$/', $path))
+        {
+            throw new proxmox\ProxmoxApiException(sprintf("Firewall Path ('%s') is not valid", $path));
+        }
+        $this->path = $path;
+        return $this;
+    }
+
+    public function read()
+    {
+
+        if (empty($this->path))
+            throw new proxmox\ProxmoxApiException('Path can\'t be blank');
+        $attributes = $this->api()->get($this->path);
+        if(!isset($attributes['dhcp'])){
+            $attributes['dhcp'] = 1;
+        }
+        $this->setAttributes($attributes);
+        return $this;
+    }
+
+    public function update()
+    {
+
+        if (empty($this->path))
+            throw new proxmox\ProxmoxApiException('Path can\'t be blank');
+        $attributes = array();
+        if ($this->enable !== null)
+        {
+            $attributes['enable'] = $this->getEnable();
+        }
+        if ($this->dhcp !== null)
+        {
+            $attributes['dhcp'] = $this->getDhcp();
+        }
+        if ($this->ndp !== null)
+        {
+            $attributes['ndp'] = $this->getNdp();
+        }
+        if ($this->radv !== null)
+        {
+            $attributes['radv'] = $this->getRadv();
+        }
+        if ($this->macfilter !== null)
+        {
+            $attributes['macfilter'] = $this->getMacfilter();
+        }
+        if ($this->ipfilter !== null)
+        {
+            $attributes['ipfilter'] = $this->getIpfilter();
+        }
+        if ($this->log_level_in !== null)
+        {
+            $attributes['log_level_in'] = $this->getLog_level_in();
+        }
+        if ($this->log_level_out !== null)
+        {
+            $attributes['log_level_out'] = $this->getLog_level_out();
+        }
+        if ($this->policy_in !== null)
+        {
+            $attributes['policy_in'] = $this->getPolicy_in();
+        }
+        if ($this->policy_out !== null)
+        {
+            $attributes['policy_out'] = $this->getPolicy_out();
+        }
+        return $this->api()->put($this->path, $attributes);
+    }
+
+    public function getEnable()
+    {
+        return $this->enable;
+    }
+
+    public function getIpfilter()
+    {
+        return $this->ipfilter;
+    }
+
+    public function getPolicy_in()
+    {
+        return $this->policy_in;
+    }
+
+    public function getPolicy_out()
+    {
+        return $this->policy_out;
+    }
+
+    public function getLog_level_in()
+    {
+        return $this->log_level_in;
+    }
+
+    public function getLog_level_out()
+    {
+        return $this->log_level_out;
+    }
+
+    public function getDhcp()
+    {
+        return $this->dhcp;
+    }
+
+    public function getNdp()
+    {
+        return $this->ndp;
+    }
+
+    public function getMacfilter()
+    {
+        return $this->macfilter;
+    }
+
+    public function getRadv()
+    {
+        return $this->radv;
+    }
+
+    public function setEnable($enable)
+    {
+        $this->enable = $enable;
+        return $this;
+    }
+
+    public function setIpfilter($ipfilter)
+    {
+        $this->ipfilter = $ipfilter;
+        return $this;
+    }
+
+    public function setPolicy_in($policy_in)
+    {
+        $this->policy_in = $policy_in;
+        return $this;
+    }
+
+    public function setPolicy_out($policy_out)
+    {
+        $this->policy_out = $policy_out;
+        return $this;
+    }
+
+    public function setLog_level_in($log_level_in)
+    {
+        $this->log_level_in = $log_level_in;
+        return $this;
+    }
+
+    public function setLog_level_out($log_level_out)
+    {
+        $this->log_level_out = $log_level_out;
+        return $this;
+    }
+
+    public function setDhcp($dhcp)
+    {
+        $this->dhcp = $dhcp;
+        return $this;
+    }
+
+    public function setNdp($ndp)
+    {
+        $this->ndp = $ndp;
+        return $this;
+    }
+
+    public function setMacfilter($macfilter)
+    {
+        $this->macfilter = $macfilter;
+        return $this;
+    }
+
+    public function setRadv($radv)
+    {
+        $this->radv = $radv;
+        return $this;
+    }
+
+    public function getAttributes()
+    {
+        return [
+            'enable' => $this->getEnable() ? $this->getEnable() : 0 ,
+            'dhcp' => $this->getDhcp() ?  $this->getDhcp() : 0,
+            'ndp' =>  is_null($this->ndp) || $this->ndp ? 1 :0,
+            'radv' => $this->getRadv() ? $this->getRadv() : 0,
+            'macfilter' => is_null($this->macfilter) || $this->macfilter ? 1 :0,
+            'ipfilter' => $this->getIpfilter() ? $this->getIpfilter() : 0,
+            'log_level_in' => $this->getLog_level_in() ? $this->getLog_level_in() : "nolog",
+            'log_level_out' => $this->getLog_level_out() ? $this->getLog_level_out() : "nolog",
+            'policy_in' => $this->getPolicy_in() ? $this->getPolicy_in() : "DROP",
+            'policy_out' => $this->getPolicy_out() ? $this->getPolicy_out() : "ACCEPT",
+        ];
+    }
+}

+ 245 - 0
v2/models/FirewallRule.php

@@ -0,0 +1,245 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-12-13)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use MGProvision\Proxmox\v2\ProxmoxApiException;
+
+/**
+ * Description of FirewallRule
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class FirewallRule extends AbstractObject
+{
+    protected $proto, $enable, $dport, $comment, $action, $iface, $type, $digest, $pos, $sport, $macro, $source, $dest;
+    protected $path;
+
+    public function __construct($proto=null, $enable=null, $dport=null, $comment=null, $action=null, $iface=null, $type=null, $digest=null, $pos=null, $sport=null, $macro=null, $source=null, $dest=null)
+    {
+        $this->proto   = $proto;
+        $this->enable  = $enable;
+        $this->dport   = $dport;
+        $this->comment = $comment;
+        $this->action  = $action;
+        $this->iface   = $iface;
+        $this->type    = $type;
+        $this->digest  = $digest;
+        $this->pos     = $pos;
+        $this->sport   = $sport;
+        $this->macro   = $macro;
+        $this->source  = $source;
+        $this->dest    = $dest;
+    }
+
+    public function getProto()
+    {
+        return $this->proto;
+    }
+
+    public function getEnable()
+    {
+        return $this->enable;
+    }
+
+    public function getDport()
+    {
+        return $this->dport;
+    }
+
+    public function getComment()
+    {
+        return $this->comment;
+    }
+
+    public function getAction()
+    {
+        return $this->action;
+    }
+
+    public function getIface()
+    {
+        return $this->iface;
+    }
+
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    public function getDigest()
+    {
+        return $this->digest;
+    }
+
+    public function getPos()
+    {
+        return $this->pos;
+    }
+
+    public function getSport()
+    {
+        return $this->sport;
+    }
+
+    public function getMacro()
+    {
+        return $this->macro;
+    }
+
+    public function getSource()
+    {
+        return $this->source;
+    }
+
+    public function getDest()
+    {
+        return $this->dest;
+    }
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    public function setProto($proto)
+    {
+        $this->proto = $proto;
+        return $this;
+    }
+
+    public function setEnable($enable)
+    {
+        $this->enable = $enable;
+        return $this;
+    }
+
+    public function setDport($dport)
+    {
+        $this->dport = $dport;
+        return $this;
+    }
+
+    public function setComment($comment)
+    {
+        $this->comment = $comment;
+        return $this;
+    }
+
+    public function setAction($action)
+    {
+        $this->action = $action;
+        return $this;
+    }
+
+    public function setIface($iface)
+    {
+        $this->iface = $iface;
+        return $this;
+    }
+
+    public function setType($type)
+    {
+        $this->type = $type;
+        return $this;
+    }
+
+    public function setDigest($digest)
+    {
+        $this->digest = $digest;
+        return $this;
+    }
+
+    public function setPos($pos)
+    {
+        $this->pos = $pos;
+        return $this;
+    }
+
+    public function setSport($sport)
+    {
+        $this->sport = $sport;
+        return $this;
+    }
+
+    public function setMacro($macro)
+    {
+        $this->macro = $macro;
+        return $this;
+    }
+
+    public function setSource($source)
+    {
+        $this->source = $source;
+        return $this;
+    }
+
+    public function setDest($dest)
+    {
+        $this->dest = $dest;
+        return $this;
+    }
+
+    public function setPath($path)
+    {
+        if (!preg_match('/\/rules\//', $path))
+        {
+            throw new proxmox\ProxmoxApiException(sprintf("Firewall Rule path('%s') is not valid", $path));
+        }
+        $this->path = $path;
+        return $this;
+    }
+
+    public function delete()
+    {
+        if (empty($this->path))
+            throw new ProxmoxApiException('Firewall Rule [path] - property is missing and it is not optional');
+
+        return $this->api()->delete($this->path);
+    }
+
+    private function fill()
+    {
+        $fillable = ['proto', 'enable', 'dport', 'comment', 'action', 'iface', 'type', 'pos', 'sport', 'macro', 'source', 'dest'];
+        $attrinutes = [];
+        foreach ($fillable as $key) {
+            if (!is_null($this->{$key})) {
+                $attrinutes[$key] = $this->{$key};
+            }
+        }
+        return $attrinutes;
+    }
+
+    public function create()
+    {
+        if (empty($this->path))
+            throw new ProxmoxApiException('Firewall Rule [path] - property is missing and it is not optional');
+        return $this->api()->post($this->path, $this->fill());
+    }
+
+    public function update()
+    {
+        if (empty($this->path))
+            throw new ProxmoxApiException('Firewall Rule [path] - property is missing and it is not optional');
+        return $this->api()->put($this->path, $this->fill());
+    }
+
+}

+ 156 - 0
v2/models/HaResource.php

@@ -0,0 +1,156 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-11-14)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Description of HaResource
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class HaResource extends AbstractObject
+{
+    protected $sid;
+    protected $type;
+    protected $state;
+    protected $group;
+    protected $max_restart;
+    protected $max_relocate;
+
+    public function __construct($sid = null)
+    {
+        if ($sid)
+            $this->setSid($sid);
+    }
+
+    public function getSid()
+    {
+        return $this->sid;
+    }
+
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    public function getState()
+    {
+        return $this->state;
+    }
+
+    public function setSid($sid)
+    {
+        $this->sid = str_replace(array("ct:", "vm:"), "", $sid);
+        return $this;
+    }
+
+    public function setType($type)
+    {
+        $this->type = $type;
+        return $this;
+    }
+
+    public function setState($state)
+    {
+        $this->state = $state;
+        return $this;
+    }
+
+    public function create()
+    {
+
+        if (empty($this->sid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Resource Id is empty");
+        $parameters = array(
+            "sid"          => $this->sid,
+            "state"        => $this->getState(),
+            "type"         => $this->getType(),
+            "max_relocate" => $this->max_relocate,
+            "max_restart"  => $this->max_restart
+        );
+        if ($this->group)
+        {
+            $parameters['group'] = $this->group;
+        }
+        return $this->api()->post("/cluster/ha/resources", $parameters);
+    }
+
+    public function delete()
+    {
+
+        if (empty($this->sid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Resource Id is empty");
+
+        return $this->api()->delete("/cluster/ha/resources/{$this->type}:" . $this->sid);
+    }
+
+    public function get()
+    {
+        return $this->api()->get("/cluster/ha/resources/{$this->type}:" . $this->sid);
+    }
+
+    public function exist()
+    {
+        if (empty($this->sid) || !$this->type)
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Resource Id is empty");
+        try
+        {
+            return is_array($this->get());
+        }
+        catch (\Exception $ex)
+        {// http 401
+        }
+        return false;
+    }
+
+    public function getGroup()
+    {
+        return $this->group;
+    }
+
+    public function getMaxRestart()
+    {
+        return $this->max_restart;
+    }
+
+    public function getMaxRelocate()
+    {
+        return $this->max_relocate;
+    }
+
+    public function setGroup($group)
+    {
+        $this->group = $group;
+        return $this;
+    }
+
+    public function setMaxRestart($maxRestart)
+    {
+        $this->max_restart = $maxRestart;
+        return $this;
+    }
+
+    public function setMaxRelocate($maxRelocate)
+    {
+        $this->max_relocate = $maxRelocate;
+        return $this;
+    }
+}

+ 440 - 0
v2/models/HardDisk.php

@@ -0,0 +1,440 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2017-07-18)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Description of HardDisk
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class HardDisk extends AbstractObject{
+
+    protected $id;
+    protected $location;
+    protected $storage;
+    protected $cache;
+    protected $discard;
+    protected $media;
+    protected $replicate;
+    protected $format;
+    protected $backup;
+    protected $mbps_rd;
+    protected $mbps_rd_max;
+    protected $mbps_wr;
+    protected $mbps_wr_max;
+    protected $iops_rd;
+    protected $iops_rd_max;
+    protected $iops_wr;
+    protected $iops_wr_max;
+    protected $size;
+    protected $iothread;
+    protected $ssd;
+
+    protected $path;
+    private $master;
+
+    public function __construct($id, $config=null) {
+        $this->id = $id;
+        if ($config) {
+            $config = self::asArray($config);
+            $config['location'] = key($config);
+            $ex = explode(":", key($config));
+            $config['storage'] = $ex[0];
+            $this->setAttributes($config);
+        }
+    }
+
+    public function getId(){
+        return $this->id;
+    }
+
+    /**
+     * @param mixed $id
+     */
+    public function setId($id)
+    {
+        $this->id = $id;
+        return $this;
+    }
+
+    public function setMaster($master){
+        $this->master = $master;
+        return $this;
+    }
+
+    public function isMaster(){
+        if(is_bool($this->master)){
+            return $this->master;
+        }
+
+        return stripos($this->getLocation(), 'disk-0') !== false;
+    }
+
+    public function getName(){
+        $matches = array();
+        preg_match('/disk-[0-9]*/', $this->getLocation(), $matches);
+        if ($matches[0]){
+            return $matches[0];
+        }
+    }
+
+    public function getLocation() {
+        return $this->location;
+    }
+
+    public function getStorage(){
+        return $this->storage;
+    }
+
+    public function getFormat() {
+        if(!$this->format && preg_match("/\./", $this->location)){
+            $ex = explode(".", $this->location);
+            return end($ex);
+        }
+        return $this->format;
+    }
+
+    public function getBackup() {
+        return $this->backup;
+    }
+
+    public function isBackup() {
+        return is_null($this->backup);
+    }
+
+    public function getCache() {
+        return $this->cache;
+    }
+
+    public function getMbps_rd() {
+        return $this->mbps_rd;
+    }
+
+    public function getMbps_rd_max() {
+        return $this->mbps_rd_max;
+    }
+
+    public function getMbps_wr() {
+        return $this->mbps_wr;
+    }
+
+    public function getMbps_wr_max() {
+        return $this->mbps_wr_max;
+    }
+
+    public function getIops_rd() {
+        return $this->iops_rd;
+    }
+
+    public function getIops_rd_max() {
+        return $this->iops_rd_max;
+    }
+
+    public function getIops_wr() {
+        return $this->iops_wr;
+    }
+
+    public function getIops_wr_max() {
+        return $this->iops_wr_max;
+    }
+
+    public function getSize() {
+        return $this->size;
+    }
+
+    public function setLocation($location) {
+        $this->location = $location;
+        return $this;
+    }
+
+    /**
+     * @param mixed $storage
+     */
+    public function setStorage($storage)
+    {
+        $this->storage = $storage;
+        return $this;
+    }
+
+    public function setFormat($format) {
+        $this->format = $format;
+        return $this;
+    }
+
+    public function setBackup($backup) {
+        $this->backup = $backup;
+        return $this;
+    }
+
+    public function setCache($cache) {
+        $this->cache = $cache;
+        return $this;
+    }
+
+    public function setMbps_rd($mbps_rd) {
+        $this->mbps_rd = $mbps_rd;
+        return $this;
+    }
+
+    public function setMbps_rd_max($mbps_rd_max) {
+        $this->mbps_rd_max = $mbps_rd_max;
+        return $this;
+    }
+
+    public function setMbps_wr($mbps_wr) {
+        $this->mbps_wr = $mbps_wr;
+        return $this;
+    }
+
+    public function setMbps_wr_max($mbps_wr_max) {
+        $this->mbps_wr_max = $mbps_wr_max;
+        return $this;
+    }
+
+    public function setIops_rd($iops_rd) {
+        $this->iops_rd = $iops_rd;
+        return $this;
+    }
+
+    public function setIops_rd_max($iops_rd_max) {
+        $this->iops_rd_max = $iops_rd_max;
+        return $this;
+    }
+
+    public function setIops_wr($iops_wr) {
+        $this->iops_wr = $iops_wr;
+        return $this;
+    }
+
+    public function setIops_wr_max($iops_wr_max) {
+        $this->iops_wr_max = $iops_wr_max;
+        return $this;
+    }
+
+    public function setSize($size) {
+        $this->size = $size;
+        return $this;
+    }
+    public function getDiscard() {
+        return $this->discard;
+    }
+
+    public function getMedia() {
+        return $this->media;
+    }
+
+    public function getReplicate() {
+        return $this->replicate;
+    }
+
+    public function setDiscard($discard) {
+        $this->discard = $discard;
+        return $this;
+    }
+
+    public function setMedia($media) {
+        $this->media = $media;
+        return $this;
+    }
+
+    public function setReplicate($replicate) {
+        $this->replicate = $replicate;
+        return $this;
+    }
+
+    public function getBytes(){
+        if(preg_match('/K/', $this->getSize())){//KB => Bytes
+            $size = (int) $this->getSize();
+            return $size * 1024 ;
+        }
+        elseif(preg_match('/M/', $this->getSize())){//MB => Bytes
+            $size = (int) $this->getSize();
+            return $size * pow(1024,2);
+        }
+        else if(preg_match('/T/', $this->getSize())){//T => Bytes
+            $size = (int) $this->getSize();
+            return $size * pow(1024,4);
+        }
+        else{//GB => Bytes
+            $size = (int) $this->getSize();
+            return $size * pow(1024,3);
+        }
+    }
+
+    public function getGb(){
+        if(preg_match('/M/', $this->getSize())){//MB => GB
+            $size = (int) $this->getSize();
+            return $size / 1024;
+        }
+        else if(preg_match('/T/', $this->getSize())){//T => GB
+            $size = (int) $this->getSize();
+            return $size * 1024;
+        }
+        else{//GB
+            return  (int) $this->getSize();
+        }
+    }
+
+    public function formatBytes(){//Bytes => GB
+        return $this->getBytes() /  pow(1024,3);
+    }
+
+    public function asConfig(){
+        $fields=['cache','discard','media','replicate','format','backup','mbps_rd','mbps_rd_max',
+            'mbps_wr','mbps_wr_max','iops_rd','iops_rd_max','iops_wr','iops_wr_max','size', 'iothread','ssd'];
+        $config = [];
+        if($this->getLocation()){
+            $config[]= $this->getLocation();
+        }else{
+            $config[]= "{$this->getStorage()}:{$this->getSize()}";
+            unset($fields[array_search("size", $fields)]);
+        }
+        $this->toConfig($fields, $config);
+        return implode(",", $config);
+    }
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    public function setPath($path)
+    {
+        $this->path = $path;
+        return $this;
+    }
+
+
+    public function delete(){
+        return $this->api()->put($this->getPath(), ["delete"=> $this->getId()]);
+    }
+
+    public function create(){
+
+        $config = [];
+        $config[] = $this->getStorage().":".$this->getSize();
+        $fields=['cache','discard','media','replicate','format','backup','mbps_rd','mbps_rd_max',
+            'mbps_wr','mbps_wr_max','iops_rd','iops_rd_max','iops_wr','iops_wr_max', 'iothread'];
+        $this->toConfig($fields, $config);
+        $prameters = implode(",", $config);
+        return $this->api()->put($this->getPath(), [$this->getId()=>  $prameters]);
+    }
+
+    public function getType(){
+        return preg_replace('/[0-9]+/', '', $this->getId());
+    }
+
+    public function getIothread()
+    {
+        return $this->iothread;
+    }
+
+    public function setIothread($iothread)
+    {
+        $this->iothread = $iothread;
+        return $this;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getSsd()
+    {
+        return $this->ssd;
+    }
+
+    /**
+     * @param mixed $ssd
+     * @return HardDisk
+     */
+    public function setSsd($ssd)
+    {
+        $this->ssd = $ssd;
+        return $this;
+    }
+
+    public function getAttributes(){
+        $fields=['cache','discard','media','replicate','format','backup','mbps_rd','mbps_rd_max',
+            'mbps_wr','mbps_wr_max','iops_rd','iops_rd_max','iops_wr','iops_wr_max','size', 'iothread', 'ssd'];
+        $config=[];
+        foreach($fields as $field)
+        {
+            $methodName = 'get'.ucfirst($field);
+            if(method_exists($this,$methodName ) &&  isset( $this->{$field})){
+                $config[$field]=$this->{$methodName}();
+            }
+        }
+        return $config;
+    }
+
+    public function update(){
+        $config = [];
+        $config[]= $this->getLocation();
+        $fields=[ 'cache','discard','media','replicate','format','backup','mbps_rd','mbps_rd_max',
+            'mbps_wr','mbps_wr_max','iops_rd','iops_rd_max','iops_wr','iops_wr_max', 'iothread','ssd'];
+        $this->toConfig($fields, $config);
+        $prameters = implode(",", $config);
+        return $this->api()->put($this->getPath(), [$this->getId() => $prameters]);
+    }
+
+
+    public function resize($size)
+    {
+        $setting = [
+            "disk" => $this->getId(),
+            "size" => $size,
+        ];
+        $path    = str_replace('config', 'resize', $this->getPath());
+        return $this->api()->put($path, $setting);
+    }
+
+    public function getBus(){
+        return preg_replace("/[0-9]+/", "", $this->getId());
+    }
+
+    public static function isConfigValid($id, $config){
+        $bus = [ "ide",'sata',  'virtio',  'scsi'];
+        $invalid = ['hotplug' ,'agent'];
+        if (in_array($id, $invalid))
+        {
+            return false;
+        }
+        if (preg_match('/efidisk/', $config))
+        {
+            return false;
+        }
+        if (preg_match('/unused/', $id))
+        {
+            return false;
+        }
+        if (!preg_match('/disk/', $config))
+        {
+            foreach ($bus as $b){
+                if(preg_match("/{$b}\d/", $id) && !preg_match('/cdrom/', $config) ){
+                    return true;
+                }
+            }
+            return false;
+        }
+        return true;
+    }
+}

+ 101 - 0
v2/models/IpCidr.php

@@ -0,0 +1,101 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxAddon product developed. (Mar 9, 2018)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use \MGProvision\Proxmox\v2\ProxmoxApiException;
+
+/**
+ * Description of IpCidr
+ *
+ * @author Pawel Kopec <pawelk@modulesgardne.com>
+ */
+class IpCidr extends AbstractObject
+{
+    private $cidr;
+    private $comment;
+
+    function __construct($path = null)
+    {
+        if (!is_null($path))
+        {
+            $this->setPath($path);
+        }
+    }
+
+    /**
+     * 
+     * @param string $path /api2/json/nodes/{node}/qemu/{vmid}/firewall/ipset/{$name}
+     * @throws proxmox\ProxmoxApiException
+     */
+    public function setPath($path)
+    {
+
+        if (!preg_match('/ipset/', $path))
+        {
+            throw new ProxmoxApiException(sprintf("IpCidr path ('%s') is not valid", $path));
+        }
+        $this->path = $path;
+        return $this;
+    }
+
+    public function getCidr()
+    {
+        return $this->cidr;
+    }
+
+    public function getComment()
+    {
+        return $this->comment;
+    }
+
+    public function setCidr($cidr)
+    {
+        $this->cidr = $cidr;
+        return $this;
+    }
+
+    public function setComment($comment)
+    {
+        $this->comment = $comment;
+        return $this;
+    }
+
+    public function create()
+    {
+        if (empty($this->path))
+        {
+            throw new ProxmoxApiException("IpCidr path is empty");
+        }
+        $this->api()->post($this->path, ['cidr' => $this->cidr, "comment" => $this->comment]);
+        $this->setPath($this->path . "/{$this->cidr}");
+        return $this;
+    }
+
+    public function delete()
+    {
+        if (empty($this->path))
+        {
+            throw new ProxmoxApiException("IpSet path is empty");
+        }
+        $this->api()->delete($this->path);
+        return $this;
+    }
+}

+ 177 - 0
v2/models/IpConfig.php

@@ -0,0 +1,177 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxAddon product developed. (May 25, 2018)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use \MGProvision\Proxmox\v2\ProxmoxApiException;
+
+/**
+ * Description of IpConfig
+ *
+ * @author Pawel Kopec <pawelk@modulesgardne.com>
+ */
+class IpConfig extends AbstractObject
+{
+    protected $id;
+    protected $path;
+    protected $ip;
+    protected $gw;
+    protected $cidr;
+    protected $ip6;
+    protected $gw6;
+    protected $cidr6;
+
+    public function __construct($id, $config = null)
+    {
+        $this->id = $id;
+        if ($config)
+        {
+            $config = self::asArray($config);
+            $this->setAttributes($config);
+        }
+    }
+
+    public function asConfig()
+    {
+        $config = null;
+        if ($this->getIp())
+        {
+            $config[] = "ip={$this->getIp()}/{$this->getCidr()}";
+        }
+        if ($this->getGw())
+        {
+            $config[] = "gw={$this->getGw()}";
+        }
+        if ($this->getIp6())
+        {
+            $config[] = "ip6={$this->getIp6()}/{$this->getCidr6()}";
+        }
+        if ($this->getGw6())
+        {
+            $config[] = "gw6={$this->getGw6()}";
+        }
+        return implode(",", $config);
+    }
+
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    public function getIp($cird=true)
+    {
+        if(!$cird){
+            $ex = explode("/", $this->ip);
+            return $ex[0];
+        }
+        return $this->ip;
+    }
+
+    public function getGw()
+    {
+        return $this->gw;
+    }
+
+    public function getCidr()
+    {
+        return $this->cidr;
+    }
+
+    public function getIp6()
+    {
+        return $this->ip6;
+    }
+
+    public function getGw6()
+    {
+        return $this->gw6;
+    }
+
+    public function getCidr6()
+    {
+        return $this->cidr6;
+    }
+
+    public function setId($id)
+    {
+        $this->id = $id;
+        return $this;
+    }
+
+    public function setPath($path)
+    {
+        $this->path = $path;
+        return $this;
+    }
+
+    public function setIp($ip)
+    {
+        $ip       = explode("\/", $ip);
+        $this->ip = $ip['0'];
+        $this->setCidr($ip['1']);
+        return $this;
+    }
+
+    public function setGw($gw)
+    {
+        $this->gw = $gw;
+        return $this;
+    }
+
+    public function setCidr($cidr)
+    {
+        $this->cidr = $cidr;
+        return $this;
+    }
+
+    public function setIp6($ip6)
+    {
+        $ip6       = explode("\/", $ip6);
+        $this->ip6 = $ip6['0'];
+        $this->setCidr6($ip6['1']);
+        return $this;
+    }
+
+    public function setGw6($gw6)
+    {
+        $this->gw6 = $gw6;
+        return $this;
+    }
+
+    public function setCidr6($cidr6)
+    {
+        $this->cidr6 = $cidr6;
+        return $this;
+    }
+
+    public function getAttributes(){
+        return [
+            "ip" => $this->getIp(),
+            "ip6" => $this->getIp6(),
+            "gw" => $this->getGw(),
+            "gw6" => $this->getGw6(),
+        ];
+    }
+}

+ 124 - 0
v2/models/IpSet.php

@@ -0,0 +1,124 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxAddon product developed. (Mar 9, 2018)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use \MGProvision\Proxmox\v2\ProxmoxApiException;
+use \MGProvision\Proxmox\v2\repository\IpCidrRepository;
+
+/**
+ * Description of IpSet
+ *
+ * @author Pawel Kopec <pawelk@modulesgardne.com>
+ */
+class IpSet extends AbstractObject
+{
+    const DEFAULT_PATH = 'firewall/ipset';
+    private $path;
+    protected $name;
+    protected $comment;
+    private $ipCidr;
+
+    function __construct($path = null)
+    {
+        if (!is_null($path))
+        {
+            $this->setPath($path);
+        }
+    }
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function getComment()
+    {
+        return $this->comment;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+        return $this;
+    }
+
+    public function setComment($comment)
+    {
+        $this->comment = $comment;
+        return $this;
+    }
+
+    /**
+     * 
+     * @param string $path /api2/json/nodes/{node}/qemu/{vmid}/firewall/ipset
+     * @throws proxmox\ProxmoxApiException
+     */
+    public function setPath($path)
+    {
+
+        if (!preg_match('/ipset/', $path))
+        {
+            throw new ProxmoxApiException(sprintf("IpSet path ('%s') is not valid", $path));
+        }
+        $this->path = $path;
+        return $this;
+    }
+
+    public function create()
+    {
+        if (empty($this->path))
+        {
+            throw new ProxmoxApiException("IpSet path is empty");
+        }
+        $this->api()->post($this->path, ['name' => $this->name, "comment" => $this->comment]);
+        $this->setPath($this->path . "/{$this->name}");
+        return $this;
+    }
+
+    public function delete()
+    {
+        if (empty($this->path))
+        {
+            throw new ProxmoxApiException("IpSet path is empty");
+        }
+        $this->api()->delete($this->path);
+        return $this;
+    }
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * 
+     * @return IpCidr[]
+     */
+    public function getIpCidr()
+    {
+        if ($this->ipCidr)
+        {
+            return $this->ipCidr;
+        }
+        $this->ipCidr = (new IpCidrRepository())->findByPath($this->getPath())->fetch();
+        return $this->ipCidr;
+    }
+}

+ 557 - 0
v2/models/Kvm.php

@@ -0,0 +1,557 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-05)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use MGProvision\Proxmox\v2\models\HardDisk;
+use MGProvision\Proxmox\v2\repository\CdRomRepository;
+use MGProvision\Proxmox\v2\repository\HardDiskRepostiory;
+use \MGProvision\Proxmox\v2\repository\IpConfigRepository;
+
+/**
+ * Description of Kvm
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class Kvm extends AbstractVm implements \MGProvision\Proxmox\v2\interfaces\KvmInterface
+{
+
+    public function getVirtualization()
+    {
+        return 'qemu';
+    }
+
+    public function suspend()
+    {
+        return $this->api()->post("/nodes/{$this->node}/{$this->getVirtualization()}/{$this->vmid}/status/suspend");
+    }
+
+    public function cdrom()
+    {
+        foreach ($this->config() as $k => $c)
+        {
+            if (strpos($c, "media=cdrom") !== false && !preg_match('/cloudinit/', $c))
+            {
+                $c      = explode(",", $c);
+                $isoRaw = $c[0];
+                $c      = explode(":iso/", $isoRaw);
+                $iso    = end($c);
+                $iso    = ucwords(str_replace(array("-", "_", ".iso"), array(" ", " ", ""), $iso));
+                return array(
+                    "iso"    => $iso,
+                    "isoRaw" => $isoRaw,
+                    "bus"    => $k
+                );
+            }
+        }
+    }
+
+    public function getBootOrder()
+    {
+
+        $config = $this->config();
+        //order=scsi0;ide1;scsi2
+        if($config['boot'] && preg_match("/order/",$config['boot'])){
+            list($key, $order) = explode("=", $config['boot']);
+            return  explode(";",$order);
+        }
+        $boot   = array("c", "d", "n");
+        if (isset($config['boot']))
+        {
+            $boot = str_split($config['boot']);
+        }
+        return $boot;
+    }
+
+    public function changeBootOrder($boot)
+    {
+
+        $data         = array();
+        $data['boot'] = $boot;
+        $this->config = null;
+        return $this->api()->put("/nodes/{$this->node}/qemu/{$this->vmid}/config", $data);
+    }
+
+    public function changeIso($iso)
+    {
+
+        $cdrom               = $this->cdrom();
+        if (!$cdrom['bus'])
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("CD-ROM volume not found");
+        $data                = array();
+        $data[$cdrom['bus']] = "{$iso},media=cdrom";
+        $this->config        = null;
+        return $this->api()->put("/nodes/{$this->node}/qemu/{$this->vmid}/config", $data);
+    }
+
+    public function cloneVm($container)
+    {
+
+        return $this->api()->post("/nodes/{$this->node}/qemu/{$this->vmid}/clone", $container);
+    }
+
+    public function getIso()
+    {
+        $result = array();
+        foreach ($this->config() as $k => $c)
+        {
+            if (strpos($c, "media=cdrom") !== false  && !preg_match('/cloudinit/', $c ))
+            {
+                $c      = explode(",", $c);
+                $isoRaw = $c[0];
+                $c      = explode(":iso/", $isoRaw);
+                $iso    = end($c);
+
+                $iso = ucwords(str_replace(array("-", "_", ".iso"), array(" ", " ", ""), $iso));
+                return array(
+                    "iso"    => $iso,
+                    "isoRaw" => $isoRaw,
+                    "bus"    => $k
+                );
+            }
+        }
+    }
+
+    public function updateCdrom($iso)
+    {
+
+        $cdrom               = $this->cdrom();
+        if (!$cdrom['bus'])
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("CD-ROM volume not found");
+        $data                = array();
+        $data[$cdrom['bus']] = "{$iso},media=cdrom";
+        $this->config        = null;
+        return $this->api()->put("/nodes/{$this->node}/qemu/{$this->vmid}/config", $data);
+    }
+
+    public function findFreeNetworDeviceId()
+    {
+        $config = $this->config(true);
+        for($i=0; $i<=50; $i++){
+            if($config['net'.$i]){
+                continue;
+            }
+            return $i;
+        }
+    }
+
+    public function findFreeIde($type)
+    {
+        $i    = 0;
+        $type = strtolower($type);
+        foreach ($this->config(true) as $k => $v)
+        {
+            if (preg_match("/{$type}/", $k))
+                $i++;
+        }
+        return $i;
+    }
+
+    /**
+     * @deprecated since version 2.4.0
+     * @return array
+     */
+    public function getHdds()
+    {
+
+        $disks     = array();
+        $isPrimary = false;
+        $config    = $this->config();
+        foreach ($config as $key => $val)
+        {
+            if (strpos($val, 'disk') !== false)
+            {
+                if (in_array($key, ['hotplug']))
+                {
+                    continue;
+                }
+                $sets        = explode(",", $val);
+                $disks[$key] = array("bus" => $key, "location" => current($sets), "format" => "", "backup" => "", "size" => "", "isPrimary" => false);
+                foreach ($sets as $set)
+                {
+                    list($k, $v) = explode("=", $set);
+                    $disks[$key][$k] = $v;
+                    if ($k == "size")
+                    {
+                        if (preg_match('/M/', $v))
+                        {//MB => GB
+                            $disks[$key]['sizeRaw'] = (int) $v / 1024;
+                        }
+                        else if (preg_match('/T/', $v))
+                        {//T => GB
+                            $disks[$key]['sizeRaw'] = (int) $v * 1024;
+                        }
+                        else
+                        {//GB
+                            $disks[$key]['sizeRaw'] = (int) $v;
+                        }
+                    }
+                }
+                if (empty($disks[$key]['format']) && preg_match('/disk-[0-9]\./', $sets['0']))
+                {
+                    $disks[$key]['format'] = preg_replace('/(.*)\./', '', $sets['0']);
+                }
+                if (isset($config['bootdisk']) && $key == $config['bootdisk'])
+                {
+                    $disks[$key] ['isPrimary'] = true;
+                    $isPrimary                 = true;
+                }
+                else if (!isset($config['bootdisk']) && !$isPrimary && strpos($disks[$key] ['location'], 'disk-0') !== false)
+                {
+                    $disks[$key] ['isPrimary'] = true;
+                    $isPrimary                 = true;
+                }
+                $matches              = array();
+                preg_match('/disk-[0-9]/', $disks[$key] ['location'], $matches);
+                if ($matches[0])
+                    $disks[$key] ['name'] = $matches[0];
+            }
+        }
+
+        ksort($disks);
+        return $disks;
+    }
+
+    /**
+     * @return HardDisk[]
+     */
+    public function getHardDisks()
+    {
+        $disks  = array();
+        $config = $this->config(true);
+        $boot = (array) $this->getBootOrder();
+        $bootDisk = reset($boot );
+        foreach ($config as $id => $value)
+        {
+            if (in_array($id, ['hotplug','agent']))
+            {
+                continue;
+            }
+            if(preg_match("/unused/", $id)){
+                continue;
+            }
+            if (!preg_match('/disk/', $value))
+            {
+                continue;
+            }
+            $hdd = (new HardDisk($id, $value))->setPath("/nodes/{$this->node}/qemu/{$this->vmid}/config");
+            if (isset($config['bootdisk']))
+            {
+                $hdd->setMaster($config['bootdisk'] == $id);
+            }elseif ($bootDisk ){
+                $hdd->setMaster($bootDisk == $id);
+            }
+            $disks[] = $hdd;
+        }
+        return $disks;
+    }
+
+    /**
+     * 
+     * @return HardDisk
+     * @throws \MGProvision\Proxmox
+     */
+    public function getMasterHardDisk()
+    {
+        $hdds   = [];
+        $configs = $this->config();
+        $boot = (array) $this->getBootOrder();
+        $bootDisk = reset($boot );
+        foreach ($configs as $id => $config)
+        {
+            if (!HardDisk::isConfigValid($id, $config))
+            {
+                continue;
+            }
+            $hdd = new HardDisk($id, $config);
+            $hdd->setPath("/nodes/{$this->node}/qemu/{$this->vmid}/config");
+            if (isset($configs['bootdisk']))
+            {
+                $hdd->setMaster($configs['bootdisk'] == $id);
+            }elseif ($bootDisk ){
+                $hdd->setMaster($bootDisk == $id);
+            }
+            if ($hdd->isMaster())
+            {
+                return $hdd;
+            }
+            $hdds[] = $hdd;
+        }
+        foreach ($hdds as $hdd)
+        {
+            if(preg_match('/unused/', $hdd->getId())){
+                continue;
+            }
+            return $hdd;
+        }
+        throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Master Hard Disk not found");
+    }
+
+    public function resizeHdd(array $setting)
+    {
+
+        return $this->api()->put("/nodes/{$this->node}/qemu/{$this->vmid}/resize", $setting);
+    }
+
+    public function getMasterHddSize()
+    {
+
+        $hdds = $this->getHdds();
+        foreach ($hdds as $hdd)
+        {
+            if ($hdd['isPrimary'])
+            {
+                return $hdd['sizeRaw'];
+            }
+        }
+
+        throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Master Disk no found!");
+    }
+
+    /**
+     * @param $id
+     * @return \MGProvision\Proxmox\v2\models\HardDisk
+     * @throws \Exception
+     */
+    public function findHardDiskById($id)
+    {
+
+        foreach($this->getHardDisks() as $disk)
+        {
+            if($disk->getId() == $id)
+            {
+                return $disk;
+            }
+        }
+
+        throw new \Exception('Cannot find disk');
+    }
+
+    public function getSlaveHddsSize($skip = null)
+    {
+        $size = 0;
+        foreach ($this->getHdds() as $hdd)
+        {
+
+            if ($hdd['isPrimary'] || $skip == $hdd['bus'])
+                continue;
+            $size += $hdd['sizeRaw'];
+        }
+        return $size;
+    }
+
+    /**
+     * 
+     * @param type $findBridge
+     * @param type $force
+     * @return \MGProvision\Proxmox\v2\models\NetworkDeviceKvm[]
+     */
+    public function getNetworkDevices($findBridge = null, $force = true)
+    {
+
+        $networks = array();
+        foreach ($this->config($force) as $k => $v)
+        {
+
+            if (!preg_match('/net/', $k))
+                continue;
+
+            $network = new NetworkDeviceKvm($k, $v);
+            if ($findBridge && $network->getBridge() !== $findBridge)
+                continue;
+
+            $networks[] = $network;
+        }
+        return $networks;
+    }
+
+    public function getMacAddresses($findBridge = null, $force = false)
+    {
+
+        $macs = array();
+        foreach ($this->getNetworkDevices($findBridge, $force) as $network)
+        {
+            $macs[] = $network->getMacAddress();
+        }
+        return $macs;
+    }
+
+    /**
+     * 
+     * @param \MGProvision\Proxmox\v2\models\NetworkDeviceKvm[] $networkDevices
+     */
+    public function updateNetworkDevices($networkDevices)
+    {
+
+        $container = array();
+        foreach ($networkDevices as $networkDevice)
+        {
+            $container[$networkDevice->getId()] = $networkDevice->asConfig();
+        }
+        return $this->updateConfig($container);
+    }
+
+    public function getReinstallConfig()
+    {
+
+        $setting = array();
+        foreach ($this->config(true) as $k => $v)
+        {
+            $setting[$k] = $v;
+        }
+        unset($setting['digest']);
+        return $setting;
+    }
+
+    public function restore($archive)
+    {
+
+        try
+        {
+            if ($this->isRunning())
+            {
+                $this->stop();
+                sleep(5);
+            }
+        }
+        catch (\Exception $ex)
+        {//error i.e.: Configuration file 'nodes/proxmox/lxc/117.conf' does not exist 
+            if ($ex->getCode() != 500)
+            {
+                throw $ex;
+            }
+        }
+        $container = array(
+            'vmid'    => $this->vmid,
+            'archive' => $archive,
+            'force'   => 1,
+        );
+
+        return $this->api()->post("/nodes/{$this->node}/qemu", $container);
+    }
+
+    /**
+     * 
+     * @return \MGProvision\Proxmox\v2\models\SnapshotKvm
+     */
+    public function snapshot($name = null)
+    {
+
+        $snap = new SnapshotKvm();
+        $path = "/nodes/{$this->getNode()}/{$this->getVirtualization()}/{$this->getVmid()}/snapshot";
+        if ($name !== null)
+        {
+            $path .= "/{$name}";
+        }
+        $snap->setPath($path);
+        return $snap;
+    }
+
+    public function convertToTemplate()
+    {
+
+        return $this->api()->post("/nodes/{$this->node}/qemu/{$this->vmid}/template");
+    }
+
+    public function findNetworkDevice($macAddress)
+    {
+
+        foreach ($this->getNetworkDevices() as $nd)
+        {
+            if ($nd->getMacAddress() == $macAddress)
+            {
+                return $nd;
+            }
+        }
+        return null;
+    }
+
+    public function deleteNetworkDevice(NetworkDeviceKvm $networkDevice)
+    {
+
+        $this->deleteConfig($networkDevice->getId());
+    }
+
+    /**
+     * 
+     * @param HardDisk $hardDisks
+     * @return array
+     */
+    public function updateHardDisks($hardDisks)
+    {
+        $container = array();
+        foreach ($hardDisks as $hardDisk)
+        {
+            $container[$hardDisk->getId()] = $hardDisk->asConfig();
+        }
+        return $this->updateConfig($container);
+    }
+
+    /**
+     * 
+     * @return IpConfigRepository
+     */
+    public function getIpConfig()
+    {
+        $repostiory = new IpConfigRepository;
+        $repostiory->findByPath("/nodes/{$this->node}/qemu/{$this->vmid}/config");
+        return $repostiory;
+    }
+
+    public function getCdRom()
+    {
+        $repostiory = new CdRomRepository();
+        $repostiory->findByPath("/nodes/{$this->node}/qemu/{$this->vmid}/config");
+
+        return $repostiory;
+    }
+
+    /**
+     * @return Agent
+     */
+    public function agent()
+    {
+        return (new Agent())->setPath("/nodes/{$this->node}/qemu/{$this->vmid}/agent");
+    }
+
+    public function getCdRomRepository(){
+        $repostiory = new CdRomRepository();
+        $repostiory->findByPath("/nodes/{$this->node}/qemu/{$this->vmid}/config");
+        return $repostiory;
+    }
+
+    public function hasCloudInit(){
+        $repostiory = new CdRomRepository();
+        $repostiory->findByPath("/nodes/{$this->node}/qemu/{$this->vmid}/config");
+        return $repostiory->hasCloudInit();
+    }
+
+    /**
+     * @return HardDiskRepostiory
+     * @throws \MGProvision\Proxmox\v2\ProxmoxApiException
+     */
+    public function getHardDiskRepostiory(){
+        $repository = new HardDiskRepostiory();
+        $repository->setApi($this->api());
+        $repository->findByPath($this->getPath() . '/config');
+        return $repository;
+    }
+}

+ 224 - 0
v2/models/Lxc.php

@@ -0,0 +1,224 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-05)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use \MGProvision\Proxmox\v2\repository\MountPointRepostiory;
+
+/**
+ * Description of Lxc
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class Lxc extends AbstractVm implements \MGProvision\Proxmox\v2\interfaces\LxcInterface
+{
+
+    public function getVirtualization()
+    {
+        return 'lxc';
+    }
+
+    public function protectionOff()
+    {
+        return $this->updateConfig(array("protection" => "0"));
+    }
+
+    public function getCreateTask()
+    {
+
+        $tasks = $this->api()->get("/cluster/tasks");
+        foreach ($tasks as $task)
+        {
+            if ($task['saved'] != "0")
+                continue;
+            if ($task['node'] != $this->getNode())
+                continue;
+            if ($task['id'] != $this->getVmid())
+                continue;
+
+            if ($task['type'] != "vzcreate")
+                continue;
+
+            return $task['upid'];
+        }
+    }
+
+    public function getReinstallConfig()
+    {
+
+        $setting = array();
+        foreach ($this->config(true) as $k => $v)
+        {
+            $setting[$k] = $v;
+        }
+        if (isset($setting['rootfs']))
+        {
+            $storage = preg_replace('/:(.*)/', '', $setting['rootfs']);
+            $size    = preg_replace('/(.*)=/', '', $setting['rootfs']);
+            $unit    = preg_replace('/[0-9]/', '', $size);
+            $size    = preg_replace('/\D/', '', $size);
+            if ($unit == "T")
+            {
+                $size = $size * 1024;
+            }
+            $setting['rootfs'] = "{$storage}:{$size}";
+        }
+        unset($setting['digest'], $setting['ostype'], $setting['arch'], $setting['lxc']);
+        return $setting;
+    }
+
+    public function findFreeNetworDeviceId()
+    {
+        $config = $this->config(true);
+        for($i=0; $i<=50; $i++){
+            if($config['net'.$i]){
+                continue;
+            }
+            return $i;
+        }
+    }
+
+    /**
+     * 
+     * @param \MGProvision\Proxmox\v2\models\NetworkDeviceLxc[] $networkDevices
+     */
+    public function updateNetworkDevices($networkDevices)
+    {
+
+        $container = array();
+        foreach ($networkDevices as $networkDevice)
+        {
+            $container[$networkDevice->getId()] = $networkDevice->asConfig();
+        }
+
+        return $this->updateConfig($container);
+    }
+
+    public function getMasterHddSize()
+    {
+
+        $config = $this->config(true);
+        if ($config['rootfs'])
+        {
+            $hdd = self::asArray($config['rootfs']);
+            return (int) $hdd['size'];
+        }
+
+        throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Master Disk no found!");
+    }
+
+    public function resize($config)
+    {
+
+        return $this->api()->put("/nodes/{$this->node}/lxc/{$this->vmid}/resize", $config);
+    }
+
+    /**
+     * 
+     * @param type $findBridge
+     * @param type $force
+     * @return NetworkDeviceLxc[]
+     */
+    public function getNetworkDevices($findBridge = null, $force = true)
+    {
+
+        $networks = array();
+        foreach ($this->config($force) as $k => $v)
+        {
+
+            if (!preg_match('/net/', $k))
+                continue;
+
+            $network = new NetworkDeviceLxc($k, $v);
+            if ($findBridge && $network->getBridge() !== $findBridge)
+                continue;
+
+            $networks[] = $network;
+        }
+        return $networks;
+    }
+
+    public function findNetworkDevice($ipAddress)
+    {
+        foreach ($this->getNetworkDevices() as $nd)
+        {
+            if ($nd->getIp() == $ipAddress || $nd->getIp6() == $ipAddress)
+            {
+                return $nd;
+            }
+        }
+        return null;
+    }
+
+    public function deleteNetworkDevice(NetworkDeviceLxc $networkDevice)
+    {
+
+        $this->deleteConfig($networkDevice->getId());
+    }
+
+    public function restore($ostemplate)
+    {
+
+        try
+        {
+            if ($this->isRunning())
+            {
+                $this->stop();
+                sleep(5);
+            }
+            $container = $this->getReinstallConfig();
+        }
+        catch (\Exception $ex)
+        {//error i.e.: Configuration file 'nodes/proxmox/lxc/117.conf' does not exist 
+            if ($ex->getCode() != 500)
+            {
+                throw $ex;
+            }
+        }
+        $data      = array(
+            'ostemplate' => $ostemplate,
+            'vmid'       => $this->vmid,
+            'restore'    => 1,
+            'force'      => 1
+        );
+        $container = array_merge($data, (array) $container);
+        return $this->api()->post("/nodes/{$this->node}/lxc", $container);
+    }
+
+    /**
+     * 
+     * @return MountPointRepostiory
+     */
+    public function getMounPoints()
+    {
+        $repostiory = new MountPointRepostiory;
+        $repostiory->setApi($this->api);
+        $repostiory->findByPath("/nodes/{$this->node}/lxc/{$this->vmid}/config");
+        return $repostiory;
+    }
+
+    /**
+     * @return MountPointRepostiory
+     */
+    public function getHardDiskRepostiory(){
+        return $this->getMounPoints();
+    }
+}

+ 269 - 0
v2/models/MountPoint.php

@@ -0,0 +1,269 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxAddon product developed. (Apr 12, 2018)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Description of MounPoint
+ *
+ * @author Pawel Kopec <pawelk@modulesgardne.com>
+ */
+class MountPoint extends AbstractObject
+{
+    protected $id;
+    protected $path;
+    protected $mp;
+    protected $size;
+    protected $acl;
+    protected $backup;
+    protected $quota;
+    protected $replicate;
+    protected $ro;
+    protected $location;
+    protected $name;
+    protected $gb;
+
+    public function __construct($id, $config = null)
+    {
+        $this->id = $id;
+        if ($config)
+        {
+            $config             = self::asArray($config);
+            $config['location'] = key($config);
+            $matches['0']       = str_replace(['-'], [" "], $matches['0']);
+            $config['name']     = ucfirst($matches['0']);
+            $this->setAttributes($config);
+            if ($id == 'rootfs')
+            {
+                $this->setBackup(1);
+            }
+        }
+    }
+
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    public function getLocation()
+    {
+        return $this->location;
+    }
+
+    public function getName()
+    {
+        if($this->getId()=="rootfs"){
+            return $this->name   = "Root Disk";
+        }
+        preg_match('/disk-[0-9]/', $this->getLocation(), $matches);
+        $matches['0'] = str_replace(['-'], [" "], $matches['0']);
+        return $this->name   = ucfirst($matches['0']);
+    }
+
+    public function setLocation($location)
+    {
+        $this->location = $location;
+        return $this;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+        return $this;
+    }
+
+    public function setPath($path)
+    {
+        $this->path = $path;
+        return $this;
+    }
+
+    public function getMp()
+    {
+        return $this->mp;
+    }
+
+    public function getSize()
+    {
+        return $this->size;
+    }
+
+    public function getAcl()
+    {
+        return $this->acl;
+    }
+
+    public function getBackup()
+    {
+        return $this->backup;
+    }
+
+    public function isBackup()
+    {
+        return $this->backup==1;
+    }
+
+    public function getQuota()
+    {
+        return $this->quota;
+    }
+
+    public function getReplicate()
+    {
+        return $this->replicate;
+    }
+
+    public function getRo()
+    {
+        return $this->ro;
+    }
+
+    public function setMp($mp)
+    {
+        $this->mp = $mp;
+        return $this;
+    }
+
+    public function setSize($size)
+    {
+        $this->size = $size;
+        return $this;
+    }
+
+    public function setAcl($acl)
+    {
+        $this->acl = $acl;
+        return $this;
+    }
+
+    public function setBackup($backup)
+    {
+        $this->backup = $backup;
+        return $this;
+    }
+
+    public function setQuota($quota)
+    {
+        $this->quota = $quota;
+        return $this;
+    }
+
+    public function setReplicate($replicate)
+    {
+        $this->replicate = $replicate;
+        return $this;
+    }
+
+    public function setRo($ro)
+    {
+        $this->ro = $ro;
+        return $this;
+    }
+
+    public function getBytes()
+    {
+        if(preg_match('/K/', $this->getSize())){//KB => Bytes
+            $size = (int) $this->getSize();
+            return $size * 1024 ;
+        }
+        elseif(preg_match('/M/', $this->getSize())){//MB => Bytes
+            $size = (int) $this->getSize();
+            return $size * pow(1024,2);
+        }
+        else if(preg_match('/T/', $this->getSize())){//T => Bytes
+            $size = (int) $this->getSize();
+            return $size * pow(1024,4);
+        }
+        else{//GB => Bytes
+            $size = (int) $this->getSize();
+            return $size * pow(1024,3);
+        }
+    }
+
+    public function getGb()
+    {
+        if(preg_match('/M/', $this->getSize())){//MB => GB
+            $size = (int) $this->getSize();
+            return $size / 1024;
+        }
+        else if(preg_match('/T/', $this->getSize())){//T => GB
+            $size = (int) $this->getSize();
+            return $size * 1024;
+        }
+        else{//GB
+            return  (int) $this->getSize();
+        }
+    }
+
+    /**
+     * @param mixed $id
+     */
+    public function setId($id)
+    {
+        $this->id = $id;
+        return $this;
+    }
+
+
+    public function resize($size)
+    {
+        $path = str_replace('config', 'resize', $this->path);
+        return $this->api()->put($path, ['disk' => $this->id, 'size' => $size]);
+    }
+
+    public function asConfig()
+    {
+        $config = [$this->location];
+        foreach (['mp', 'acl', 'backup', 'quota', 'replicate', 'ro'] as $k)
+        {
+            if (!is_null($this->{$k}))
+            {
+                $config[] = "{$k}=" . $this->{$k};
+            }
+        }
+        return implode(",", $config);
+    }
+
+    public function update()
+    {
+        return $this->api()->put($this->path, [$this->id => $this->asConfig()]);
+    }
+
+    public function create()
+    {
+        return $this->api()->put($this->path, [$this->id => $this->asConfig()]);
+    }
+
+    public function delete()
+    {
+        return $this->api()->put($this->path, ["delete" => $this->id]);
+    }
+
+    public function isMaster(){
+        return $this->getId() == "rootfs";
+    }
+
+
+}

+ 162 - 0
v2/models/Network.php

@@ -0,0 +1,162 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxAddon product developed. (Apr 12, 2018)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Description of MounPoint
+ *
+ * @author Pawel Kopec <pawelk@modulesgardne.com>
+ */
+class Network extends AbstractObject
+{
+    protected $path;
+    protected $iface;
+    protected $type;
+    protected $active;
+    protected $method;
+    protected $families;
+    protected $exists;
+    protected $method6;
+    protected $priority;
+    protected $address;
+    protected $gateway;
+
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    public function setPath($path)
+    {
+        $this->path = $path;
+        return $this;
+    }
+
+    public function getIface()
+    {
+        return $this->iface;
+    }
+
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    public function getActive()
+    {
+        return $this->active;
+    }
+
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+    public function getFamilies()
+    {
+        return $this->families;
+    }
+
+    public function getExists()
+    {
+        return $this->exists;
+    }
+
+    public function getMethod6()
+    {
+        return $this->method6;
+    }
+
+    public function getPriority()
+    {
+        return $this->priority;
+    }
+
+    public function setIface($iface)
+    {
+        $this->iface = $iface;
+        return $this;
+    }
+
+    public function setType($type)
+    {
+        $this->type = $type;
+        return $this;
+    }
+
+    public function setActive($active)
+    {
+        $this->active = $active;
+        return $this;
+    }
+
+    public function setMethod($method)
+    {
+        $this->method = $method;
+        return $this;
+    }
+
+    public function setFamilies($families)
+    {
+        $this->families = $families;
+        return $this;
+    }
+
+    public function setExists($exists)
+    {
+        $this->exists = $exists;
+        return $this;
+    }
+
+    public function setMethod6($method6)
+    {
+        $this->method6 = $method6;
+        return $this;
+    }
+
+    public function setPriority($priority)
+    {
+        $this->priority = $priority;
+        return $this;
+    }
+
+    public function getAddress()
+    {
+        return $this->address;
+    }
+
+    public function getGateway()
+    {
+        return $this->gateway;
+    }
+
+    public function setAddress($address)
+    {
+        $this->address = $address;
+        return $this;
+    }
+
+    public function setGateway($gateway)
+    {
+        $this->gateway = $gateway;
+        return $this;
+    }
+}

+ 266 - 0
v2/models/NetworkDeviceKvm.php

@@ -0,0 +1,266 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-12-08)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Description of KvmNetworkDevice
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class NetworkDeviceKvm extends AbstractObject
+{
+    protected $id;
+    protected $model;
+    protected $macAddress;
+    protected $bridge;
+    protected $firewall;
+    protected $linkDown;
+    protected $queues;
+    protected $rate;
+    protected $tag;
+    protected $trunks;
+    protected $node;
+    protected $vmid;
+
+    public function __construct($id=null, $config = null)
+    {
+        $this->id = $id;
+
+        if ($config !== null)
+        {
+            $config = self::asArray($config);
+            $model  = array_search(current($config), $config);
+
+            $this->setBridge($config['bridge'])
+                    ->setRate($config['rate'])
+                    ->setMacAddress($config)
+                    ->setModel($model)
+                    ->setTag($config['tag'])
+                    ->setTrunks($config['trunks'])
+                    ->setFirewall($config['firewall'])
+                   ->setQueues($config['queues']);
+        }
+    }
+
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    public function getModel()
+    {
+        return $this->model;
+    }
+
+    public function getMacAddress()
+    {
+        return $this->macAddress;
+    }
+
+    public function getBridge()
+    {
+        return $this->bridge;
+    }
+
+    public function getFirewall()
+    {
+        return $this->firewall;
+    }
+
+    public function getLinkDown()
+    {
+        return $this->linkDown;
+    }
+
+    public function getQueues()
+    {
+        return $this->queues;
+    }
+
+    public function getRate()
+    {
+        return $this->rate;
+    }
+
+    public function getTag()
+    {
+        return $this->tag;
+    }
+
+    public function getNode()
+    {
+        return $this->node;
+    }
+
+    public function getVmid()
+    {
+        return $this->vmid;
+    }
+
+    public function setId($id)
+    {
+        $this->id = $id;
+        return $this;
+    }
+
+    public function setModel($model)
+    {
+        $this->model = $model;
+        return $this;
+    }
+
+    public function setMacAddress($macAddress)
+    {
+
+        if (is_array($macAddress))
+        {
+            $models = array('e1000', 'i82551', 'i82557b', 'i82559er', 'ne2k_isa', 'ne2k_pci', 'pcnet', 'rtl8139', 'virtio', 'vmxnet3');
+            $config = $macAddress;
+            foreach ($config as $k => $v)
+            {
+                foreach ($models as $model)
+                {
+                    if (preg_match("/{$model}/", $k))
+                    {
+                        $macAddress = $v;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if($macAddress =="auto"){
+            $macAddress = null;
+        }
+        if ($macAddress && !preg_match("/^[0-9a-fA-F]{2}(?=([:;.]?))(?:\\1[0-9a-fA-F]{2}){5}$/", $macAddress))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException(sprintf("Invalid MAC Address ('%s') ", $macAddress));
+
+        $this->macAddress = $macAddress;
+        return $this;
+    }
+
+    public function setBridge($bridge)
+    {
+        $this->bridge = $bridge;
+        return $this;
+    }
+
+    public function setFirewall($firewall)
+    {
+        $this->firewall = $firewall;
+        return $this;
+    }
+
+    public function setLinkDown($linkDown)
+    {
+        $this->linkDown = $linkDown;
+        return $this;
+    }
+
+    public function setQueues($queues)
+    {
+        $this->queues = $queues;
+        return $this;
+    }
+
+    public function setRate($rate)
+    {
+        $this->rate = $rate;
+        return $this;
+    }
+
+    public function setTag($tag)
+    {
+        $this->tag = $tag;
+        return $this;
+    }
+
+    public function setNode($node)
+    {
+        $this->node = $node;
+        return $this;
+    }
+
+    public function setVmid($vmid)
+    {
+        $this->vmid = $vmid;
+        return $this;
+    }
+
+    public function getTrunks()
+    {
+        return $this->trunks;
+    }
+
+    public function setTrunks($trunks)
+    {
+        $this->trunks = $trunks;
+        return $this;
+    }
+
+    public function asConfig()
+    {
+
+        $config   = array();
+        if ($this->macAddress)
+            $config[] = $this->model . "=" . $this->macAddress;
+        else if ($this->model)
+            $config[] = $this->model;
+
+        if ($this->bridge)
+            $config[] = "bridge=" . $this->bridge;
+        if ($this->firewall)
+            $config[] = "firewall=1";
+        if (!empty($this->linkDown))
+            $config[] = 'link_down=' . $this->linkDown;
+        if (!empty($this->tag))
+            $config[] = 'tag=' . $this->tag;
+        if (!empty($this->trunks))
+            $config[] = 'trunks=' . $this->trunks;
+        if ($this->rate)
+            $config[] = "rate=" . $this->rate;
+        if(!empty($this->queues)){
+            $config[] = "queues=" . $this->queues;
+        }
+
+        return implode(",", $config);
+    }
+
+    public function getRateMbps()
+    {
+        return $this->rate * 8;
+    }
+
+    public function getAttributes(){
+       return [
+            "id" => $this->getId(),
+            "bridge" => $this->getBridge(),
+            "firewall" => $this->getFirewall(),
+            "tag" => $this->getTag(),
+            "macAddress" => $this->getMacAddress(),
+            "rate" => $this->getRate(),
+            "queues" => $this->getQueues()
+        ];
+    }
+
+
+}

+ 326 - 0
v2/models/NetworkDeviceLxc.php

@@ -0,0 +1,326 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-12-14)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Description of NetworkDeviceLxc
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class NetworkDeviceLxc extends AbstractObject
+{
+    protected $id;
+    protected $name;
+    protected $bridge;
+    protected $firewall;
+    protected $gw;
+    protected $gw6;
+    protected $hwaddr;
+    protected $ip;
+    protected $cidr;
+    protected $ip6;
+    protected $cidr6;
+    protected $tag;
+    protected $trunks;
+    protected $type;
+    protected $rate;
+
+    public function __construct($id=null, $config = null)
+    {
+        $this->id = $id;
+        if ($config !== null)
+        {
+            $config = self::asArray($config);
+            if ($config['ip'] && preg_match("/\\//", $config['ip']))
+            {
+                list($ip, $cidr) = explode("/", $config['ip']);
+                $config['ip']   = $ip;
+                $config['cidr'] = $cidr;
+            }
+            if ($config['ip6'] && preg_match("/\//", $config['ip6']))
+            {
+                list($ip, $cidr) = explode("/", $config['ip6']);
+                $config['ip6']   = $ip;
+                $config['cidr6'] = $cidr;
+            }
+
+            $this->setAttributes($config);
+        }
+    }
+
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function getBridge()
+    {
+        return $this->bridge;
+    }
+
+    public function getFirewall()
+    {
+        return $this->firewall;
+    }
+
+    public function getGw()
+    {
+        return $this->gw;
+    }
+
+    public function getHwaddr()
+    {
+        return $this->hwaddr;
+    }
+
+    public function getIp()
+    {
+        return $this->ip;
+    }
+
+    public function getTag()
+    {
+        return $this->tag;
+    }
+
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    public function setId($id)
+    {
+        $this->id = $id;
+        return $this;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+        return $this;
+    }
+
+    public function setBridge($bridge)
+    {
+        $this->bridge = $bridge;
+        return $this;
+    }
+
+    public function setFirewall($firewall)
+    {
+        $this->firewall = $firewall;
+        return $this;
+    }
+
+    public function setGw($gw)
+    {
+        $this->gw = $gw;
+        return $this;
+    }
+
+    public function setHwaddr($hwaddr)
+    {
+        $this->hwaddr = $hwaddr;
+        return $this;
+    }
+
+    public function setIp($ip)
+    {
+        $this->ip = $ip;
+        return $this;
+    }
+
+    public function setTag($tag)
+    {
+        $this->tag = $tag;
+        return $this;
+    }
+
+    public function setType($type)
+    {
+        $this->type = $type;
+        return $this;
+    }
+
+    public function getGw6()
+    {
+        return $this->gw6;
+    }
+
+    public function getIp6()
+    {
+        return $this->ip6;
+    }
+
+    public function setGw6($gw6)
+    {
+        $this->gw6 = $gw6;
+        return $this;
+    }
+
+    public function setIp6($ip6)
+    {
+        $this->ip6 = $ip6;
+        return $this;
+    }
+
+    public function getTrunks()
+    {
+        return $this->trunks;
+    }
+
+    public function setTrunks($trunks)
+    {
+        $this->trunks = $trunks;
+        return $this;
+    }
+
+    public function asConfig()
+    {
+
+        $config   = array();
+        if ($this->name)
+            $config[] = "name=" . $this->name;
+        if ($this->bridge)
+            $config[] = "bridge=" . $this->bridge;
+        if (!empty($this->hwaddr) && $this->hwaddr != 'auto')
+            $config[] = 'hwaddr=' . $this->hwaddr;
+        if ($this->firewall)
+            $config[] = "firewall=1";
+        if (!empty($this->tag))
+            $config[] = 'tag=' . $this->tag;
+        if (!empty($this->trunks))
+            $config[] = 'trunks=' . $this->trunks;
+        if ($this->ip && $this->ip != "dhcp")
+        {
+            $config[] = "ip=" . $this->ip . '/' . $this->getCidr();
+        }
+        else if ($this->ip && $this->ip == "dhcp")
+        {
+            $config[] = "ip=" . $this->ip;
+        }
+        if ($this->ip6 && $this->ip6 != "dhcp")
+        {
+            $config[] = "ip6=" . $this->ip6 . '/' . $this->getCidr6();
+        }
+        else if ($this->ip6 && $this->ip6 == "dhcp")
+        {
+            $config[] = "ip6=" . $this->ip6;
+        }
+        if ($this->gw && filter_var($this->gw, FILTER_VALIDATE_IP))
+            $config[] = "gw=" . $this->gw;
+        if ($this->gw6 && filter_var($this->gw6, FILTER_VALIDATE_IP))
+            $config[] = "gw6=" . $this->gw6;
+
+        if ($this->rate)
+            $config[] = "rate=" . $this->rate;
+
+        return implode(",", $config);
+    }
+
+    public function getRate()
+    {
+        return $this->rate;
+    }
+
+    public function setRate($rate)
+    {
+        $this->rate = $rate;
+        return $this;
+    }
+
+    public function getCidr()
+    {
+        return $this->cidr;
+    }
+
+    public function getCidr6()
+    {
+        return $this->cidr6;
+    }
+
+    public function setCidr($cidr)
+    {
+        $this->cidr = $cidr;
+        return $this;
+    }
+
+    public function setCidr6($cidr6)
+    {
+        $this->cidr6 = $cidr6;
+        return $this;
+    }
+
+    public function getRateMbps()
+    {
+        return $this->rate * 8;
+    }
+
+    public function remove($ip)
+    {
+
+        if (!preg_match('/\:/', $ip) && $this->getIp() == $ip)
+        {//v4
+            $this->setIp(null)
+                    ->setCidr(null)
+                    ->setGw(null);
+            return true;
+        }
+        else if ($this->getIp6() == $ip)
+        {//v6
+            $this->setIp6(null)
+                    ->setCidr6(null)
+                    ->setGw6(null);
+            return true;
+        }
+    }
+
+    public function getAttributes(){
+        return [
+            "id" => $this->getId(),
+            "name" => $this->getName(),
+            "bridge" => $this->getBridge(),
+            "firewall" => $this->getFirewall(),
+            "hwaddr" => $this->getHwaddr(),
+            "ip" => $this->getIp(),
+            "cidr" => $this->getCidr(),
+            "gw" => $this->getGw(),
+            "ip6" => $this->getIp6(),
+            "cidr6" => $this->getCidr6(),
+            "gw6" => $this->getGw6(),
+            "tag" => $this->getTag(),
+            "rate" => $this->getRate(),
+
+        ];
+    }
+
+    public function isEmpty()
+    {
+
+        return ( ($this->getIp() === null || $this->getIp() === 'dhcp' ) && ( $this->getIp6() === null || $this->getIp6() === 'dhcp' ) );
+    }
+}

+ 163 - 0
v2/models/Node.php

@@ -0,0 +1,163 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-06)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use \MGProvision\Proxmox\v2\ProxmoxApiException;
+
+/**
+ * Description of Node
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class Node extends AbstractObject
+{
+    protected $node;
+
+    function __construct($node)
+    {
+        $this->node = $node;
+    }
+
+    function getNode()
+    {
+        return $this->node;
+    }
+
+    function setNode($node)
+    {
+        $this->node = $node;
+    }
+
+    /**
+     * 
+     * @param string $upid
+     * @return \MGProvision\Proxmox\v2\models\Task
+     * @throws \MGProvision\Proxmox\v2\ProxmoxApiException
+     */
+    public function task($upid)
+    {
+
+        if (empty($upid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Task Id is empty");
+        if (!is_string($upid))
+        {
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException(sprintf("Task Id is invalid '%s'", $upid));
+        }
+        $attributes = $this->api()->get("/nodes/{$this->node}/tasks/{$upid}/status");
+        $task       = new Task();
+        $task->setAttributes($attributes);
+        return $task;
+    }
+
+    public function hasKvm($vmid)
+    {
+
+        if (empty($vmid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Parameter ('vmid') is empty");
+
+        try
+        {
+            $res = $this->api()->get("/nodes/{$this->node}/qemu/{$vmid}");
+            return true;
+        }
+        catch (\MGProvision\Proxmox\v2\ProxmoxApiException $ex)
+        {
+            return false;
+        }
+    }
+
+    public function getFreeSpace($storage = null)
+    {
+        $result = $res    = $this->api()->get("/nodes/{$this->node}/status");
+        $limits = array(
+            "memory" => $result['memory']['free'],
+            "swap"   => $result['swap']['free'],
+        );
+        if ($storage)
+        {
+            $result2           = $this->api()->get("/nodes/{$this->node}/storage/{$storage}/status", array());
+            $limits['storage'] = $result2['avail'];
+        }
+        return $limits;
+    }
+
+    /**
+     * 
+     * @return \MGProvision\Proxmox\v2\models\Kvm
+     */
+    public function kvm()
+    {
+        return new Kvm($this->node);
+    }
+
+    /**
+     * 
+     * @return \MGProvision\Proxmox\v2\models\Lxc
+     */
+    public function lxc()
+    {
+        return new Lxc($this->node);
+    }
+
+    public function getPath()
+    {
+        return "/nodes/{$this->node}";
+    }
+
+    public function getMainIpAddress()
+    {
+        $repository = new \MGProvision\Proxmox\v2\repository\NetworkRepository();
+        $repository->findByPath($this->getPath() . "/network")
+                ->findTypeBridge();
+        foreach ($repository->fetch() as $n)
+        {
+            if ($n->getIface() == 'vmbr0')
+            {
+                if ($n->getAddress())
+                {
+                    return $n->getAddress();
+                }
+            }
+        }
+        throw new ProxmoxApiException(sprintf("IP Addres for node '%s'  not found", $this->getNode()));
+    }
+
+    public function getStatus()
+    {
+        return $this->api()->get("/nodes/{$this->node}/status", array());
+    }
+
+    public function getSubscription()
+    {
+        return $this->api()->get("/nodes/{$this->node}/subscription");
+    }
+
+    public function getDns()
+    {
+        return $this->api()->get("/nodes/{$this->node}/dns");
+    }
+
+    public function rrdData($parameters)
+    {
+        return $this->api()->get("/nodes/{$this->node}/rrddata", $parameters);
+    }
+}

+ 178 - 0
v2/models/Partition.php

@@ -0,0 +1,178 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2017-07-10)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Description of Partition
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class Partition
+{
+    private $device;
+    private $boot;
+    private $start;
+    private $end;
+    private $blocks;
+    private $sectors;
+    private $size;
+    private $id;
+    private $system;
+    private $type;
+    private $number;
+
+    public function setAttributes($attributes)
+    {
+        foreach ($attributes as $name => $attValue)
+        {
+            $methodName = 'set' . ucfirst($name);
+            if (method_exists($this, $methodName))
+                $this->{$methodName}($attValue);
+        }
+    }
+
+    public function getDevice()
+    {
+        return $this->device;
+    }
+
+    public function getBoot()
+    {
+        return $this->boot;
+    }
+
+    public function getStart()
+    {
+        return $this->start;
+    }
+
+    public function getEnd()
+    {
+        return $this->end;
+    }
+
+    public function getBlocks()
+    {
+        return $this->blocks;
+    }
+
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    public function getSystem()
+    {
+        return $this->system;
+    }
+
+    public function getNumber()
+    {
+        return $this->number;
+    }
+
+    public function setDevice($device)
+    {
+        $this->device = $device;
+        return $this;
+    }
+
+    public function setBoot($boot)
+    {
+        $this->boot = $boot;
+        return $this;
+    }
+
+    public function setStart($start)
+    {
+        $this->start = $start;
+        return $this;
+    }
+
+    public function setEnd($end)
+    {
+        $this->end = $end;
+        return $this;
+    }
+
+    public function setBlocks($blocks)
+    {
+        $this->blocks = $blocks;
+        return $this;
+    }
+
+    public function setId($id)
+    {
+        $this->id = $id;
+        return $this;
+    }
+
+    public function setSystem($system)
+    {
+        $this->system = $system;
+        return $this;
+    }
+
+    public function setNumber($number)
+    {
+        $this->number = $number;
+        return $this;
+    }
+
+    public function isBoot()
+    {
+        return preg_match('/\*/', $this->getBoot());
+    }
+
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    public function setType($type)
+    {
+        $this->type = $type;
+        return $this;
+    }
+
+    public function getSectors()
+    {
+        return $this->sectors;
+    }
+
+    public function getSize()
+    {
+        return $this->size;
+    }
+
+    public function setSectors($sectors)
+    {
+        $this->sectors = $sectors;
+        return $this;
+    }
+
+    public function setSize($size)
+    {
+        $this->size = $size;
+        return $this;
+    }
+}

+ 158 - 0
v2/models/Snapshot.php

@@ -0,0 +1,158 @@
+<?php
+
+/* * ********************************************************************
+ * proxmoxCloud product developed. (2017-01-20)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use MGProvision\Proxmox\v2 as proxmox;
+
+/**
+ * Description of Snapshot
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class Snapshot extends AbstractObject
+{
+    private $path;
+    protected $name;
+    protected $description;
+    protected $snaptime;
+    protected $parent;
+    protected $vmstate;
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function getDescription()
+    {
+        return $this->description;
+    }
+
+    public function getSnaptime()
+    {
+        return $this->snaptime;
+    }
+
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+        return $this;
+    }
+
+    public function setDescription($description)
+    {
+        $this->description = $description;
+        return $this;
+    }
+
+    public function setSnaptime($snaptime)
+    {
+        $this->snaptime = $snaptime;
+        return $this;
+    }
+
+    public function setParent($parent)
+    {
+        $this->parent = $parent;
+        return $this;
+    }
+
+    public function getVmstate()
+    {
+        return $this->vmstate;
+    }
+
+    public function setVmstate($vmstate)
+    {
+        $this->vmstate = $vmstate;
+        return $this;
+    }
+
+    public function setPath($path)
+    {
+
+        if (!preg_match('/\/snapshot/', $path))
+        {
+            throw new proxmox\ProxmoxApiException(sprintf("Snapshot Path ('%s') is not valid", $path));
+        }
+
+        $this->path = $path;
+        return $this;
+    }
+
+    public function create()
+    {
+
+        if (empty($this->path))
+            throw new proxmox\ProxmoxApiException('Snapshot [path] - property is missing and it is not optional');
+        $request = array(
+            "snapname"    => $this->getName(),
+            "description" => $this->getDescription(),
+        );
+        if(!is_null($this->vmstate)){
+            $request['vmstate'] =$this->getVmstate();
+        }
+        return $this->api()->post($this->path, $request);
+    }
+
+    public function update()
+    {
+
+        if (empty($this->path))
+            throw new proxmox\ProxmoxApiException('Snapshot [path] - property is missing and it is not optional');
+        return $this->api()->put($this->path . "/config", array("description" => $this->getDescription()));
+    }
+
+    public function rollback()
+    {
+
+        if (empty($this->path))
+            throw new proxmox\ProxmoxApiException('Snapshot [path] - property is missing and it is not optional');
+        return $this->api()->post($this->path . '/rollback');
+    }
+
+    public function delete()
+    {
+
+        if (empty($this->path))
+            throw new proxmox\ProxmoxApiException('Snapshot [path] - property is missing and it is not optional');
+        return $this->api()->delete($this->path);
+    }
+
+    public function getAttributes()
+    {
+
+        return array(
+            "name"        => $this->getName(),
+            "date"        => date("Y-m-d H:i:s", $this->snaptime),
+            "description" => $this->getDescription(),
+            "snaptime"    => $this->getSnaptime(),
+            "parent"      => $this->getParent(),
+            "vmstate"     => $this->getVmstate(),
+        );
+    }
+}

+ 158 - 0
v2/models/SnapshotKvm.php

@@ -0,0 +1,158 @@
+<?php
+
+/* * ********************************************************************
+ * proxmoxCloud product developed. (2017-01-20)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use MGProvision\Proxmox\v2 as proxmox;
+
+/**
+ * Description of Snapshot
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ * @deprecated 2.7.0
+ */
+class SnapshotKvm extends AbstractObject
+{
+    private $path;
+    protected $name;
+    protected $description;
+    protected $snaptime;
+    protected $parent;
+    protected $vmstate;
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function getDescription()
+    {
+        return $this->description;
+    }
+
+    public function getSnaptime()
+    {
+        return $this->snaptime;
+    }
+
+    public function getParent()
+    {
+        return $this->parent;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+        return $this;
+    }
+
+    public function setDescription($description)
+    {
+        $this->description = $description;
+        return $this;
+    }
+
+    public function setSnaptime($snaptime)
+    {
+        $this->snaptime = $snaptime;
+        return $this;
+    }
+
+    public function setParent($parent)
+    {
+        $this->parent = $parent;
+        return $this;
+    }
+
+    public function getVmstate()
+    {
+        return $this->vmstate;
+    }
+
+    public function setVmstate($vmstate)
+    {
+        $this->vmstate = $vmstate;
+        return $this;
+    }
+
+    public function setPath($path)
+    {
+
+        if (!preg_match('/\/snapshot/', $path))
+        {
+            throw new proxmox\ProxmoxApiException(sprintf("Snapshot Path ('%s') is not valid", $path));
+        }
+
+        $this->path = $path;
+        return $this;
+    }
+
+    public function create()
+    {
+
+        if (empty($this->path))
+            throw new proxmox\ProxmoxApiException('Snapshot [path] - property is missing and it is not optional');
+        $request = array(
+            "snapname"    => $this->getName(),
+            "description" => $this->getDescription(),
+            "vmstate"     => $this->getVmstate()
+        );
+
+        return $this->api()->post($this->path, $request);
+    }
+
+    public function update()
+    {
+
+        if (empty($this->path))
+            throw new proxmox\ProxmoxApiException('Snapshot [path] - property is missing and it is not optional');
+        return $this->api()->put($this->path . "/config", array("description" => $this->getDescription()));
+    }
+
+    public function rollback()
+    {
+
+        if (empty($this->path))
+            throw new proxmox\ProxmoxApiException('Snapshot [path] - property is missing and it is not optional');
+        return $this->api()->post($this->path . '/rollback');
+    }
+
+    public function delete()
+    {
+
+        if (empty($this->path))
+            throw new proxmox\ProxmoxApiException('Snapshot [path] - property is missing and it is not optional');
+        return $this->api()->delete($this->path);
+    }
+
+    public function getAttributes()
+    {
+
+        return array(
+            "name"        => $this->getName(),
+            "date"        => date("Y:m:d H:i:s", $this->snaptime),
+            "description" => $this->getDescription(),
+            "snaptime"    => $this->getSnaptime(),
+            "parent"      => $this->getParent(),
+            "vmstate"     => $this->getVmstate(),
+        );
+    }
+}

+ 152 - 0
v2/models/Storage.php

@@ -0,0 +1,152 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-11)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Description of Storage
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class Storage extends AbstractObject
+{
+    protected $used;
+    protected $content;
+    protected $shared;
+    protected $storage;
+    protected $active;
+    protected $type;
+    protected $total;
+    protected $avail;
+    protected $path;
+
+    public function getUsed()
+    {
+        return $this->used;
+    }
+
+    public function getContent()
+    {
+        return $this->content;
+    }
+
+    public function getContentAsArray(){
+        return explode(",", $this->content);
+    }
+
+    public function getShared()
+    {
+        return $this->shared;
+    }
+
+    public function getStorage()
+    {
+        return $this->storage;
+    }
+
+    public function getActive()
+    {
+        return $this->active;
+    }
+
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    public function getTotal()
+    {
+        return $this->total;
+    }
+
+    public function getAvail()
+    {
+        return $this->avail;
+    }
+
+    public function setUsed($used)
+    {
+        $this->used = $used;
+        return $this;
+    }
+
+    public function setContent($content)
+    {
+        $this->content = $content;
+        return $this;
+    }
+
+    public function setShared($shared)
+    {
+        $this->shared = $shared;
+        return $this;
+    }
+
+    public function setStorage($storage)
+    {
+        $this->storage = $storage;
+        return $this;
+    }
+
+    public function setActive($active)
+    {
+        $this->active = $active;
+        return $this;
+    }
+
+    public function setType($type)
+    {
+        $this->type = $type;
+        return $this;
+    }
+
+    public function setTotal($total)
+    {
+        $this->total = $total;
+        return $this;
+    }
+
+    public function setAvail($avail)
+    {
+        $this->avail = $avail;
+        return $this;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getPath()
+    {
+        return $this->path;
+    }
+
+    /**
+     * @param mixed $path
+     * @return Storage
+     */
+    public function setPath($path)
+    {
+        $this->path = $path;
+        return $this;
+    }
+
+
+}

+ 229 - 0
v2/models/Task.php

@@ -0,0 +1,229 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-11-15)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Description of Task
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class Task extends AbstractObject
+{
+    protected $starttime;
+    protected $pstart;
+    protected $id;
+    protected $exitstatus;
+    protected $pid;
+    protected $user;
+    protected $node;
+    protected $upid;
+    protected $type;
+    protected $status;
+    protected $endtime;
+    private $path;
+
+    public function getStarttime()
+    {
+        return $this->starttime;
+    }
+
+    public function getPstart()
+    {
+        return $this->pstart;
+    }
+
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    public function getExitstatus()
+    {
+        return $this->exitstatus;
+    }
+
+    public function getPid()
+    {
+        return $this->pid;
+    }
+
+    public function getUser()
+    {
+        return $this->user;
+    }
+
+    public function getNode()
+    {
+        return $this->node;
+    }
+
+    public function getUpid()
+    {
+        return $this->upid;
+    }
+
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    public function getStatus()
+    {
+        return $this->status;
+    }
+
+    public function isRunning()
+    {
+        return $this->status === "running";
+    }
+
+    public function isFalied()
+    {
+        return $this->exitstatus && $this->exitstatus !== "OK";
+    }
+
+    public function isDone()
+    {
+        return $this->exitstatus === "OK";
+    }
+
+    public function setStarttime($starttime)
+    {
+        $this->starttime = $starttime;
+        return $this;
+    }
+
+    public function setPstart($pstart)
+    {
+        $this->pstart = $pstart;
+        return $this;
+    }
+
+    public function setId($id)
+    {
+        $this->id = $id;
+        return $this;
+    }
+
+    public function setExitstatus($exitstatus)
+    {
+        $this->exitstatus = $exitstatus;
+        return $this;
+    }
+
+    public function setPid($pid)
+    {
+        $this->pid = $pid;
+        return $this;
+    }
+
+    public function setUser($user)
+    {
+        $this->user = $user;
+        return $this;
+    }
+
+    public function setNode($node)
+    {
+        $this->node = $node;
+        return $this;
+    }
+
+    public function setUpid($upid)
+    {
+        $this->upid = $upid;
+        return $this;
+    }
+
+    public function setType($type)
+    {
+        $this->type = $type;
+        return $this;
+    }
+
+    public function setStatus($status)
+    {
+        $this->status = $status;
+        return $this;
+    }
+
+    public function getEndtime()
+    {
+        return $this->endtime;
+    }
+
+    public function setEndtime($endtime)
+    {
+        $this->endtime = $endtime;
+        return $this;
+    }
+
+    public function getStartDate()
+    {
+        return date('Y-m-d H:i:s', $this->starttime);
+    }
+
+    public function getEndDate()
+    {
+        return date('Y-m-d H:i:s', $this->endtime);
+    }
+
+    public function setPath($path)
+    {
+
+        if (!preg_match('/\/tasks\//', $path))
+        {
+            throw new proxmox\ProxmoxApiException(sprintf("Task Path ('%s') is not valid", $path));
+        }
+        $this->path = $path;
+    }
+
+    public function delete()
+    {
+
+        if (empty($this->path))
+            throw new proxmox\ProxmoxApiException('Task [path] - property is missing and it is not optional');
+
+        return $this->api()->delete($this->path);
+    }
+
+    public function description()
+    {
+
+        return ucwords(str_replace(array("vz", "qm"), array("VM {$this->getId()} - ", "CT {$this->getId()} - "), $this->getType()));
+    }
+
+    public function getAttributes(){
+        return [
+            "id" => $this->getId(),
+            "starttime" => $this->getStarttime(),
+            "endtime" => $this->getEndtime(),
+            "pstart" => $this->getPstart(),
+            "exitstatus" => $this->getExitstatus(),
+            "pid" => $this->getPid(),
+            "node"  => $this->getNode(),
+            "upid"=> $this->getUpid(),
+            "type" => $this->getType(),
+            "status" => $this->getStatus(),
+        ];
+    }
+}

+ 91 - 0
v2/models/TemplateKvm.php

@@ -0,0 +1,91 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-12-16)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+/**
+ * Description of TemplateKvm
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class TemplateKvm extends File
+{
+    protected $vmid;
+    protected $node;
+    protected $format;
+    protected $name;
+    protected $description;
+
+    public function getVmid()
+    {
+        return $this->vmid;
+    }
+
+    public function getNode()
+    {
+        return $this->node;
+    }
+
+    public function getFormat()
+    {
+        return $this->format;
+    }
+
+    public function getName()
+    {
+        return $this->name;
+    }
+
+    public function getDescription()
+    {
+        return $this->description;
+    }
+
+    public function setVmid($vmid)
+    {
+        $this->vmid = $vmid;
+        return $this;
+    }
+
+    public function setNode($node)
+    {
+        $this->node = $node;
+        return $this;
+    }
+
+    public function setFormat($format)
+    {
+        $this->format = $format;
+        return $this;
+    }
+
+    public function setName($name)
+    {
+        $this->name = $name;
+        return $this;
+    }
+
+    public function setDescription($description)
+    {
+        $this->description = $description;
+        return $this;
+    }
+}

+ 148 - 0
v2/models/User.php

@@ -0,0 +1,148 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-12)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\models;
+
+use MGProvision\Proxmox\v2\ProxmoxApiException;
+
+/**
+ * Description of User
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class User extends AbstractObject
+{
+    protected $userid;
+
+    public function __construct($userid = null)
+    {
+        $this->userid = $userid;
+    }
+
+    public function setUserid($userid)
+    {
+        if (empty($userid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("UserId is empty");
+        $this->userid = $userid;
+        return $this;
+    }
+
+    public function getUserid()
+    {
+        return $this->userid;
+    }
+
+    public function updatePermission($vmid, $roles, $delete = 0)
+    {
+
+        if (empty($this->userid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("UserId is empty");
+
+        if (empty($vmid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("VMID is empty");
+
+        $parameter = array(
+            "path"   => "/vms/" . $vmid,
+            "delete" => $delete,
+            "roles"  => $roles,
+            "users"  => $this->userid,
+        );
+        return $this->api()->put("/access/acl", $parameter);
+    }
+
+    public function permissions()
+    {
+
+        return $this->api()->get("/access/acl");
+    }
+
+    public function create($parameters)
+    {
+
+        $res          = $this->api()->post("/access/users", $parameters);
+        $this->userid = $parameters['userid'];
+        return $res;
+    }
+
+    public function update($parameters)
+    {
+
+        if (empty($this->userid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("UserId is empty");
+        return $this->api()->put("/access/users/{$this->userid}", $parameters);
+    }
+
+    public function disable()
+    {
+
+        if (empty($this->userid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("UserId is empty");
+
+        return $this->api()->put("/access/users/{$this->userid}", array("enable" => "0"));
+    }
+
+    public function enable()
+    {
+
+        if (empty($this->userid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("UserId is empty");
+
+        return $this->api()->put("/access/users/{$this->userid}", array("enable" => "1"));
+    }
+
+    public function changePassword($password)
+    {
+
+        if (empty($this->userid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("UserId is empty");
+        $data = array(
+            "userid"   => $this->userid,
+            "password" => $password
+        );
+        return $this->api()->put("/access/password", $data);
+    }
+
+    public function delete()
+    {
+
+        if (empty($this->userid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("UserId is empty");
+        return $this->api()->delete("/access/users/" . $this->userid);
+    }
+
+    public function configuration()
+    {
+
+        if (empty($this->userid))
+            throw new \MGProvision\Proxmox\v2\ProxmoxApiException("UserId is empty");
+
+        return $this->api()->get("/access/users/" . $this->userid);
+    }
+
+    public function exist(){
+        try {
+            $this->configuration();
+            return true;
+        }catch (ProxmoxApiException $exception){
+            return false;
+        }
+    }
+}

+ 72 - 0
v2/repository/AbstractRepository.php

@@ -0,0 +1,72 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-06)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+/**
+ * Description of AbstractRepository
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+Abstract class AbstractRepository
+{
+    protected $api;
+    protected $fetch;
+
+    public function setApi($api)
+    {
+        $this->api = $api;
+    }
+
+    /**
+     * 
+     * @return \MGProvision\Proxmox\v2\Api
+     */
+    protected function api()
+    {
+        if ($this->api !== null)
+            return $this->api;
+        return $this->api = \MGProvision\Proxmox\v2\Api::getInstance();
+    }
+
+    abstract public function fetch();
+
+    public function fetchArray()
+    {
+        $data = array();
+        foreach ($this->fetch() as $entity)
+        {
+            $data[] = $entity->toArray();
+        }
+        return $data;
+    }
+
+    public function reset()
+    {
+        $this->fetch = null;
+        return $this;
+    }
+
+    public function count()
+    {
+        return count($this->fetch());
+    }
+}

+ 129 - 0
v2/repository/BackupScheduleRepository.php

@@ -0,0 +1,129 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-12-13)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use MGProvision\Proxmox\v2\interfaces\VmInterface;
+use MGProvision\Proxmox\v2\models\BackupSchedule;
+use ModulesGarden\ProxmoxAddon\App\Models\VmModel;
+
+/**
+ * Description of BackupSchedule
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class BackupScheduleRepository extends AbstractRepository
+{
+    protected $vmids = array();
+    protected $nodes = array();
+
+    public function findByVm(VmInterface $vm)
+    {
+
+        $this->findByVmids(array($vm->getVmid()));
+        return $this;
+    }
+
+    public function findByVmids(array $vmids)
+    {
+        $this->vmids = $vmids;
+        return $this;
+    }
+
+    public function findByNodes(array $nodes)
+    {
+        $this->nodes = $nodes;
+        return $this;
+    }
+
+    /**
+     * 
+     * @return BackupSchedule[]
+     */
+    public function fetch()
+    {
+
+
+        if (!empty($this->fetch))
+        {
+            return $this->fetch;
+        }
+        $path        = "/cluster/backup";
+        $res         = $this->api()->get($path);
+        $this->fetch = array();
+        foreach ($res as $job)
+        {
+            if ((!$job['vmid'] && $this->vmids) || ($job['vmid'] && $this->vmids && !in_array($job['vmid'], $this->vmids)))
+            {
+                continue;
+            }
+            if ((!$job['node'] && $this->nodes) || ( $job['node'] && $this->nodes && !in_array($job['node'], $this->nodes)))
+            {
+                continue;
+            }
+            $backupShedule = new BackupSchedule();
+            $backupShedule->setAttributes($job);
+            $backupShedule->setPath(sprintf("{$path}/%s", $job['id']));
+            $this->fetch[] = $backupShedule;
+        }
+        return $this->fetch;
+    }
+
+    /**
+     * 
+     * @param VmModel[] $vservers
+     */
+    public function findByVmModel($vservers)
+    {
+
+        foreach ($vservers as $vserver)
+        {
+            if (!$vserver instanceof VmModel)
+            {
+                throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Ukown  object: " . get_class($vserver));
+            }
+            $this->vmids[] = $vserver->vmid;
+        }
+        return $this;
+    }
+
+    public function count()
+    {
+        return count($this->fetch());
+    }
+
+    public function delete()
+    {
+
+        for ($i = 0; $i <= 100; $i++)
+        {
+            $this->reset();
+            $backups = $this->fetch();
+            if (empty($backups))
+                break;
+            foreach ($backups as $b)
+            {
+                $b->delete();
+                break;
+            }
+        }
+    }
+}

+ 133 - 0
v2/repository/CdRomRepository.php

@@ -0,0 +1,133 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxAddon product developed. (May 28, 2018)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use MGProvision\Proxmox\v2\models\CdRom;
+use MGProvision\Proxmox\v2\models\CloudInitDrive;
+use \MGProvision\Proxmox\v2\ProxmoxApiException;
+use \MGProvision\Proxmox\v2\models\IpConfig;
+
+/**
+ * Description of IpConfigRepository
+ *
+ * @author Pawel Kopec <pawelk@modulesgardne.com>
+ */
+class CdRomRepository extends AbstractRepository
+{
+    protected $path;
+
+    public function findByPath($path)
+    {
+        if (!preg_match('/config/', $path))
+        {
+            throw new ProxmoxApiException(sprintf("CdRom path ('%s') is not valid", $path));
+        }
+        $this->path = $path;
+        return $this;
+    }
+
+    /**
+     * 
+     * @return CdRom[]
+     * @throws ProxmoxApiException
+     */
+    public function fetch()
+    {
+        if ($this->fetch)
+        {
+            return $this->fetch;
+        }
+        if (empty($this->path))
+        {
+            throw new ProxmoxApiException("CdRom path is empty");
+        }
+        $this->fetch = [];
+        $get         = $this->api()->get($this->path);
+        foreach ($get as $id => $config)
+        {
+            if (preg_match('/media\=cdrom/', $config) && !preg_match('/cloudinit/', $config))
+            {
+                $entity = new CdRom($id, $config);
+                $entity->setPath($this->path);
+                $entity->setApi($this->api);
+                $this->fetch[] =  $entity;
+            }
+
+        }
+        return $this->fetch;
+    }
+
+    /**
+     * @return CdRom
+     * @throws ProxmoxApiException
+     */
+    public function first(){
+        return $this->fetch()[0];
+    }
+
+
+    public function hasCloudInit()
+    {
+        if (empty($this->path))
+        {
+            throw new ProxmoxApiException("CdRom path is empty");
+        }
+        if (!$this->fetch)
+        {
+            $this->fetch =  $this->api()->get($this->path);
+        }
+        foreach ($this->fetch as $id => $config)
+        {
+            if (preg_match('/cloudinit/', $config))
+            {
+                return true;
+            }
+
+        }
+        return false;
+    }
+
+    /**
+     * @return CloudInitDrive
+     * @throws ProxmoxApiException
+     */
+    public function getCloudInitDrive(){
+        if (empty($this->path))
+        {
+            throw new ProxmoxApiException("CdRom path is empty");
+        }
+        if (!$this->fetch)
+        {
+            $this->fetch =  $this->api()->get($this->path);
+        }
+        foreach ($this->fetch as $id => $config)
+        {
+            if (preg_match('/cloudinit/', $config))
+            {
+                $entity = new CloudInitDrive($id, $config);
+                $entity->setPath($this->path);
+                $entity->setApi($this->api);
+                return $entity;
+            }
+        }
+        throw new ProxmoxApiException("Cloud Init Drive not found");
+    }
+}

+ 175 - 0
v2/repository/ClusterResourcesRepository.php

@@ -0,0 +1,175 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2017-06-07)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use MGProvision\Proxmox\v2\models\ClusterResource;
+
+/**
+ * Description of ClusterResourcesRepository
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class ClusterResourcesRepository extends AbstractRepository
+{
+    private $type;
+    private $filters = [];
+
+    /**
+     * 
+     * @param string $type enum vm | storage | node
+     * @return $this
+     */
+    public function findType($type)
+    {
+        $this->type = $type;
+        return $this;
+    }
+
+    public function findByNodes($nodes)
+    {
+        $this->addfilter(['node' => $nodes]);
+        return $this;
+    }
+
+    public function addfilter(array $filter)
+    {
+        $this->filters = array_merge($this->filters, $filter);
+        return $this;
+    }
+
+    public function findVm()
+    {
+        $this->findType('vm');
+        return $this;
+    }
+
+    public function findKvmTemplate()
+    {
+        $this->findType('vm')
+                ->addfilter(['template' => '1']);
+        return $this;
+    }
+
+    public function findQemu(){
+       $this ->addfilter(['type' => 'qemu']);
+        return $this;
+    }
+
+    public function findLxc(){
+        $this ->addfilter(['type' => 'lxc']);
+        return $this;
+    }
+
+    public function findVmid($vmid){
+        $this ->addfilter(['vmid' => $vmid]);
+        return $this;
+    }
+
+    /**
+     * 
+     * @return ClusterResource[]
+     */
+    public function fetch()
+    {
+        if($this->fetch){
+            return $this->fetch;
+        }
+        $parameters = [];
+        if ($this->type)
+        {
+            $parameters['type'] = $this->type;
+        }
+        $data = $this->api()->get('/cluster/resources', $parameters);
+        foreach ($data as $k => $resurces)
+        {
+
+            foreach ($resurces as $keyRes => $resurce)
+            {
+                if (!isset($this->filters[$keyRes]))
+                {
+                    continue;
+                }
+                if (is_array($this->filters[$keyRes]) && !in_array($resurce, $this->filters[$keyRes]))
+                {
+                    unset($data[$k], $resurces);
+                    break;
+                }
+                else if (is_string($this->filters[$keyRes]) && $resurce != $this->filters[$keyRes])
+                {
+                    unset($data[$k], $resurces);
+                    break;
+                }
+            }
+            foreach ($this->filters as $fk => $f)
+            {
+                if (!isset($resurces[$fk]))
+                {
+                    unset($data[$k], $resurces);
+                    break;
+                }
+            }
+            if (empty($resurces))
+            {
+                continue;
+            }
+            $obj           = new ClusterResource();
+            $obj->setAttributes($resurces);
+            $this->fetch[] = $obj;
+        }
+
+        return $this->fetch;
+    }
+
+    public function fetchWithUniqueNames($defaultNode){
+
+        if(is_null($defaultNode)){
+            throw new \InvalidArgumentException("The default node parameter must be defined");
+        }
+        if($this->filters['node']){
+            return $this->fetch();
+        }
+        $currents =[];
+        foreach ($this->fetch() as $entity){
+            if($entity->getNode() != $defaultNode){
+                continue;
+            }
+            $currents [$entity->getVmid()] = $entity->getName();
+        }
+        $currents = array_unique(  $currents);
+        $fetch =[];
+        foreach ($this->fetch() as $entity){
+
+            if( in_array($entity->getName(), $currents)  && $entity->getNode() != $defaultNode ){
+                continue;
+            }
+            $fetch[] = $entity;
+        }
+        return $fetch;
+    }
+
+    public function firstOrFail(){
+        foreach ($this->fetch() as $clusterResource){
+            return $clusterResource;
+        }
+        throw new \InvalidArgumentException("Cannot find: ".ClusterResource::class);
+    }
+}

+ 357 - 0
v2/repository/FileRepository.php

@@ -0,0 +1,357 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-11)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use MGProvision\Proxmox\v2\interfaces\VmInterface;
+Use MGProvision\Proxmox\v2\models\File;
+use MGProvision\Proxmox\v2\models\TemplateKvm;
+use ModulesGarden\ProxmoxAddon\App\Models\VmModel;
+use ModulesGarden\ProxmoxAddon\App\Services\Utility;
+
+/**
+ * Description of FileRepository
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class FileRepository extends AbstractRepository
+{
+    protected $content        = null;
+    protected $storages       = array();
+    protected $nodes          = array();
+    protected $vmid;
+    private $templateKvm;
+    private $vmids            = array();
+    protected $filterNotMatch = [];
+    protected $forceFetch = true;
+    protected $filterPattern;
+
+    public function findByVmid($vmid)
+    {
+        $this->vmid = $vmid;
+        return $this;
+    }
+
+    /**
+     * 
+     * @param VmInterface $vm
+     * @return \MGProvision\Proxmox\v2\repository\FileRepository
+     */
+    public function findBackup(VmInterface $vm)
+    {
+
+        $this->findByVmid($vm->getVmid())
+                ->findByNodes(array($vm->getNode()));
+
+        $this->findByContent('backup');
+        return $this;
+    }
+
+    /**
+     * 
+     * @param VmModel[] $vservers
+     */
+    public function findBackupByVmModel($vservers)
+    {
+
+        $nodes = array();
+        foreach ($vservers as $vserver)
+        {
+            if (!$vserver instanceof VmModel)
+            {
+                throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Ukown  object: " . get_class($vserver));
+            }
+            $nodes[]       = $vserver->node;
+            $this->vmids[] = $vserver->vmid;
+        }
+        $this->findByNodes($nodes)
+                ->findByContent('backup');
+        return $this;
+    }
+
+    /**
+     * 
+     * @return File[]|TemplateKvm[]
+     */
+    public function fetch()
+    {
+
+        if (!empty($this->fetch) || !$this->forceFetch)
+        {
+            return $this->fetch;
+        }
+        $request            = array();
+        if ($this->content)
+            $request["content"] = $this->content;
+        if ($this->vmid)
+            $request["vmid"]    = $this->vmid;
+
+        $i           = 0;
+        $this->fetch = array();
+        foreach ($this->nodes as $node)
+        {
+            if (isset($this->vmids[$i]) && $this->vmids[$i])
+            {
+                $request["vmid"] = $this->vmids[$i];
+            }
+            foreach ($this->storages as $storage)
+            {
+                $vmid = null;
+                if($request["vmid"] &&  version_compare($this->api()->getVersion(), "5.4", '>=')){
+                    $vmid = $request["vmid"];
+                    unset($request["vmid"]);
+                }
+                $path  = "/nodes/{$node}/storage/{$storage}/content";
+                $files = $this->api()->get($path, $request);
+                foreach ($files as $file)
+                {
+                    if(($vmid && $file['vmid'] && $file['vmid'] != $vmid ) || ($vmid && !$file['vmid'] && !preg_match("/\-{$vmid}\-/", $file['volid']))){
+                        continue;
+                    }
+                    if ($this->filterNotMatch && preg_match("/{$this->filterNotMatch['volid']}/", $file['volid']))
+                    {
+                        continue;
+                    }
+                    if ($this->filterPattern && !preg_match($this->filterPattern, $file['volid']))
+                    {
+                        continue;
+                    }
+
+                    if ($this->templateKvm)
+                    {
+                        $entityObj = $this->formatTemplateKvm($node, $file);
+                        if (!$entityObj instanceof TemplateKvm)
+                            continue;
+                    }else
+                    {
+                        $entityObj = new File();
+                        $entityObj->setAttributes($file);
+                    }
+                    $entityObj->setPath(sprintf("{$path}/%s", $file['volid']));
+                    $this->fetch[] = $entityObj;
+                }
+            }
+            $i++;
+        }
+        $this->forceFetch = false;
+        return $this->fetch;
+    }
+
+    public function count()
+    {
+        return count($this->fetch());
+    }
+
+    public function size()
+    {
+        $size = (float) 0;
+        foreach ($this->fetch() as $entityObj)
+        {
+            $size += $entityObj->getSize();
+        }
+        return $size;
+    }
+
+    /**
+     * @return float
+     * @throws \Exception
+     */
+    public function getSizeInGb()
+    {
+        $size =  $this->size();
+        Utility::unitFormat($size, "bytes", "gb");
+        return $size;
+    }
+
+
+
+    private function formatTemplateKvm($node, array $file)
+    {
+
+        if (empty($file['vmid']))
+        {
+            return;
+        }
+        try
+        {
+
+            $vmid   = $file['vmid'];
+            $status = $this->api()->get("/nodes/{$node}/qemu/{$vmid}/status/current", array());
+            if ($status['template'] != '1')
+            {
+                return;
+            }
+            $config = $this->api()->get("/nodes/{$node}/qemu/{$vmid}/config", array());
+            if (empty($config))
+            {
+                return;
+            }
+            $attributes         = array();
+            $attributes['node'] = $node;
+            $attributes         = array_merge($attributes, $file);
+            $attributes         = array_merge($attributes, $config);
+            $template           = new TemplateKvm();
+            $template->setAttributes($attributes);
+            return $template;
+        }
+        catch (\Exception $ex)
+        {
+            
+        }
+    }
+
+    public function fetchAsArray()
+    {
+        $data = array();
+        foreach ($this->fetch() as $entity)
+        {
+            $data[$entity->getVolid()] = $entity->getFriendlyName();
+        }
+        return $data;
+    }
+
+    public function findByContent($content)
+    {
+        $this->content = $content;
+        return $this;
+    }
+
+    public function findLxcTemplates()
+    {
+        $this->content = 'vztmpl';
+        return $this;
+    }
+
+    public function findKvmTemplates()
+    {
+        $this->content     = 'images';
+        $this->templateKvm = true;
+        return $this;
+    }
+
+    public function findIso()
+    {
+        $this->content = 'iso';
+        return $this;
+    }
+
+    public function findByStorages($storages)
+    {
+        $this->storages = $storages;
+        return $this;
+    }
+
+    public function findByNodes(array  $nodes)
+    {
+        $this->nodes = $nodes;
+        return $this;
+    }
+
+    public function findByNode(\MGProvision\Proxmox\v2\models\Node $node)
+    {
+        $this->nodes = array($node->getNode());
+        return $this;
+    }
+
+    public function delete()
+    {
+        $this->reset();
+        $backups = $this->fetch();
+        foreach ($backups as $b)
+        {
+            $b->delete();
+        }
+    }
+
+    /**
+     * 
+     * @return File|TemplateKvm|null
+     */
+    public function fetchLast()
+    {
+
+        $file = null;
+        foreach ($this->fetch() as $t)
+        {
+            if ($file === null)
+            {
+                $file = $t;
+                continue;
+            }
+            else if ($t->getTimestamp() > $file->getTimestamp())
+            {
+                $file = $t;
+            }
+        }
+        return $file;
+    }
+
+    public function findVolidNotIn(array $volids){
+        $this->filterNotMatch['volid'] = $volids;
+        return $this;
+    }
+
+    public function sortByTime(){
+        $keys = [];
+        $orginal = $this->fetch();
+        foreach ($orginal as $key => $entity){
+            $keys[$key] = $entity->getTimestamp();
+        }
+        uasort($keys, function($a, $b){
+            if ($a == $b) {
+                return 0;
+            }
+            return ($a < $b) ? -1 : 1;
+        });
+        $this->fetch=[];
+        foreach ($keys as $id => $entity){
+            $this->fetch[$id] = $orginal[$id];
+        }
+        return $this;
+    }
+
+    public function remove($id){
+        unset($this->fetch[$id]);
+        return $this;
+    }
+
+    public function findSnippets()
+    {
+        $this->content = 'snippets';
+        return $this;
+    }
+
+    public function findSnippetsByServiceId($serviceId){
+        $this->findSnippets();
+        $this->filterPattern = "/userconfig\-{$serviceId}\.yaml/";
+        return $this;
+    }
+
+    public function findSnippetsByVmModel(VmModel $vmModel){
+        $this->findSnippets();
+        $this->filterPattern = "/userconfig\-{$vmModel->hosting_id}\-{$vmModel->id}\.yaml/";
+        return $this;
+    }
+
+    public function first(){
+        return $this->fetch()[0];
+    }
+}

+ 114 - 0
v2/repository/FirewallRulesRepository.php

@@ -0,0 +1,114 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-12-13)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use MGProvision\Proxmox\v2\ProxmoxApiException;
+use MGProvision\Proxmox\v2\models\FirewallRule;
+use MGProvision\Proxmox\v2\interfaces\VmInterface;
+use ModulesGarden\ProxmoxAddon\App\Models\VmModel;
+
+/**
+ * Description of FirewallRulesRepository
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class FirewallRulesRepository extends AbstractRepository
+{
+    protected $path;
+
+    public function findByPath($path)
+    {
+
+        if (empty($path))
+            throw new ProxmoxApiException('[path] - property is missing and it is not optional');
+
+        $this->path = [$path];
+        return $this;
+    }
+
+    /**
+     *
+     * @param VmModel[] $vservers
+     */
+    public function findByVmModel($vservers)
+    {
+        $nodes = array();
+        $this->path = [];
+        foreach ($vservers as $vserver)
+        {
+            if (!$vserver instanceof VmModel)
+            {
+                throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Ukown  object: " . get_class($vserver));
+            }
+            $this->path[] = "/nodes/{$vserver->node}/{$vserver->virtualization}/{$vserver->vmid}/firewall/rules";
+        }
+        return $this;
+    }
+
+    public function findByVm(VmInterface $vm)
+    {
+        $this->findByPath($vm->getPath() . '/firewall/rules');
+        return $this;
+    }
+
+    /**
+     * 
+     * @return FirewallRule[]
+     * @throws ProxmoxApiException
+     */
+    public function fetch()
+    {
+        $errors   = array();
+        if (empty($this->path))
+            $errors[] = '[path] - property is missing and it is not optional';
+        if (!empty($errors))
+            throw new ProxmoxApiException(implode(", ", $errors));
+        $results = array();
+        foreach ($this->path as $path){
+            $res     = $this->api()->get($path);
+            foreach ($res as $r)
+            {
+                $rule = new FirewallRule($r['proto'], $r['enable'], $r['dport'], $r['comment'], $r['action'], $r['iface'], $r['type'], $r['digest'], $r['pos'], $r['sport'], $r['macro'], $r['source'], $r['dest']);
+                $rule->setPath(sprintf("{$path}/%s", $r['pos']));
+                $results[] = $rule;
+            }
+        }
+        return $results;
+    }
+
+    public function delete()
+    {
+
+        for ($i = 0; $i <= 1000; $i++)
+        {
+            $rules = $this->fetch();
+            if (empty($rules))
+                break;
+            foreach ($rules as $rule)
+            {
+                $rule->delete();
+                break;
+            }
+            unset($rules);
+        }
+    }
+}

+ 82 - 0
v2/repository/HaResourceRepository.php

@@ -0,0 +1,82 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-11-14)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+/**
+ * Description of HaResource
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class HaResourceRepository extends AbstractRepository
+{
+    private $type;
+
+    public function findVm()
+    {
+        $this->type = "vm";
+    }
+
+    public function findCt()
+    {
+        $this->type = "ct";
+    }
+
+    public function findVirtualization($virtualization)
+    {
+        switch (strtolower($virtualization))
+        {
+            case "lxc":
+                $this->type = "ct";
+                break;
+            case "qemu":
+            case "kvm":
+                $this->type = "vm";
+                break;
+            default:
+                throw new \Exception('Unknown virtualization type: ' . $virtualization);
+        }
+        return $this;
+    }
+
+    /**
+     * 
+     * @return \MGProvision\Proxmox\v2\models\HaResource[]
+     */
+    public function fetch()
+    {
+
+        $parameters         = array();
+        if ($this->type)
+            $parameters['type'] = $this->type;
+        $resources          = $this->api()->get("/cluster/ha/resources", $parameters);
+        $data               = array();
+        foreach ($resources as $resource)
+        {
+            $entity = new \MGProvision\Proxmox\v2\models\HaResource($resource['sid']);
+            $entity->setAttributes($resource);
+            $data[] = $entity;
+        }
+
+        unset($resources, $entity);
+        return $data;
+    }
+}

+ 151 - 0
v2/repository/HardDiskRepostiory.php

@@ -0,0 +1,151 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxAddon product developed. (Apr 12, 2018)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use MGProvision\Proxmox\v2\models\HardDisk;
+use \MGProvision\Proxmox\v2\ProxmoxApiException;
+use \MGProvision\Proxmox\v2\models\MountPoint;
+
+/**
+ * Description of MounPointRepostiory
+ *
+ * @author Pawel Kopec <pawelk@modulesgardne.com>
+ */
+class HardDiskRepostiory extends AbstractRepository
+{
+    protected $path;
+    private $excludeIds=[];
+
+    public function findByPath($path)
+    {
+
+        if (!preg_match('/config/', $path))
+        {
+            throw new ProxmoxApiException(sprintf("Hard Disk path ('%s') is not valid", $path));
+        }
+        $this->path = $path;
+        return $this;
+    }
+
+    /**
+     * 
+     * @return HardDisk[]
+     * @throws ProxmoxApiException
+     */
+    public function fetch()
+    {
+        if ($this->fetch)
+        {
+            return $this->fetch;
+        }
+        if (empty($this->path))
+        {
+            throw new ProxmoxApiException("Hard Disk path is empty");
+        }
+        $this->fetch = [];
+        $config = $this->api()->get($this->path);
+        $boot = (array) $this->getBootOrder();
+        $bootDisk = reset($boot );
+        foreach ($config as $id => $value)
+        {
+            if (!HardDisk::isConfigValid($id, $value))
+            {
+                continue;
+            }
+            $hdd = (new HardDisk($id, $value))->setPath($this->path);
+            if (isset($config['bootdisk']))
+            {
+                $hdd->setMaster($config ['bootdisk'] == $id);
+            }elseif ($bootDisk ){
+                $hdd->setMaster($bootDisk == $id);
+            }
+            $this->fetch[$id] = $hdd;
+        }
+        krsort($this->fetch);
+        return $this->fetch;
+    }
+
+    public function size()
+    {
+        $size = 0;
+        foreach ($this->fetch() as $hdd)
+        {
+            $size += (int) $hdd->getSize();
+        }
+        return $size;
+    }
+
+
+    public function additionalSize()
+    {
+        $size = 0;
+        foreach ($this->fetch() as $hd)
+        {
+            if ($hd->isMaster())
+            {
+                continue;
+            }
+            else if($this->excludeIds && in_array($hd->getId(), $this->excludeIds)){
+                continue;
+            }
+            $size += (int) $hd->getGb();
+        }
+        return $size;
+    }
+
+    public function findById($id){
+        foreach($this->fetch() as $entity){
+            if($entity->getId()==$id){
+                return $entity;
+            }
+        }
+        throw new ProxmoxApiException("Hard Disk {$id} not found");
+    }
+
+    public function whereNotIn(array $excludeIds){
+        $this->excludeIds = $excludeIds;
+        return $this;
+    }
+
+    public function  deleteUnused(){
+        $this->reset();
+        foreach ($this->fetch() as $disk){
+            if(preg_match('/unused/', $disk->getId())){
+                $disk->delete();
+            }
+        }
+    }
+
+    public function getBootOrder()
+    {
+        $config = $this->api()->get($this->path);
+        //order=scsi0;ide1;scsi2
+        if ($config['boot'] && preg_match("/order/", $config['boot'])) {
+            list($key, $order) = explode("=", $config['boot']);
+            return explode(";", $order);
+        }
+        $boot = array("c", "d", "n");
+        if (isset($config['boot'])) {
+            $boot = str_split($config['boot']);
+        }
+        return $boot;
+    }
+}

+ 82 - 0
v2/repository/IpCidrRepository.php

@@ -0,0 +1,82 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxAddon product developed. (Mar 9, 2018)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use \MGProvision\Proxmox\v2\ProxmoxApiException;
+use \MGProvision\Proxmox\v2\models\IpSet;
+use \MGProvision\Proxmox\v2\models\IpCidr;
+
+/**
+ * Description of IpSetRepository
+ *
+ * @author Pawel Kopec <pawelk@modulesgardne.com>
+ */
+class IpCidrRepository extends AbstractRepository
+{
+    protected $path;
+
+    public function findByPath($path)
+    {
+
+        if (!preg_match('/ipset/', $path))
+        {
+            throw new ProxmoxApiException(sprintf("IpCidr path ('%s') is not valid", $path));
+        }
+
+        $this->path = $path;
+        return $this;
+    }
+
+    /**
+     * 
+     * @return IpCidr[]
+     * @throws ProxmoxApiException
+     */
+    public function fetch()
+    {
+        if ($this->fetch)
+        {
+            return $this->fetch;
+        }
+        if (empty($this->path))
+        {
+            throw new ProxmoxApiException("IpSet path is empty");
+        }
+        $this->fetch = [];
+        foreach ($this->api()->get($this->path) as $entity)
+        {
+            $this->fetch[] = (new IpCidr($this->path . "/" . $entity['cidr']))->setAttributes($entity);
+        }
+        return $this->fetch;
+    }
+
+    public function find(IpCidr $ipCidr)
+    {
+        foreach ($this->fetch() as $cIpCidr)
+        {
+            if ($cIpCidr->getCidr() == $ipCidr->getCidr())
+            {
+                return $cIpCidr;
+            }
+        }
+        return null;
+    }
+}

+ 86 - 0
v2/repository/IpConfigRepository.php

@@ -0,0 +1,86 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxAddon product developed. (May 28, 2018)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use \MGProvision\Proxmox\v2\ProxmoxApiException;
+use \MGProvision\Proxmox\v2\models\IpConfig;
+
+/**
+ * Description of IpConfigRepository
+ *
+ * @author Pawel Kopec <pawelk@modulesgardne.com>
+ */
+class IpConfigRepository extends AbstractRepository
+{
+    protected $path;
+
+    protected $filters=[];
+
+    public function findByPath($path)
+    {
+
+        if (!preg_match('/config/', $path))
+        {
+            throw new ProxmoxApiException(sprintf("Ipconfigt path ('%s') is not valid", $path));
+        }
+
+        $this->path = $path;
+        return $this;
+    }
+
+    /**
+     * 
+     * @return IpConfig[]
+     * @throws ProxmoxApiException
+     */
+    public function fetch()
+    {
+        if ($this->fetch)
+        {
+            return $this->fetch;
+        }
+        if (empty($this->path))
+        {
+            throw new ProxmoxApiException("IpConfig path is empty");
+        }
+        $this->fetch = [];
+        $get         = $this->api()->get($this->path);
+        krsort($get);
+        foreach ($get as $id => $config)
+        {
+            if (!preg_match('/ipconfig/', $id))
+            {
+                continue;
+            }
+            $key = preg_replace("/ipconfig/","", $id);
+            if(isset($this->filters['networkId']) && $key  != $this->filters['networkId']){
+                continue;
+            }
+            $this->fetch[] = new IpConfig($id, $config);
+        }
+        return $this->fetch;
+    }
+
+    public function findByNetworkId($networkId){
+        $this->filters['networkId'] = preg_replace("/net/","", $networkId);
+        return $this;
+    }
+}

+ 91 - 0
v2/repository/IpSetRepository.php

@@ -0,0 +1,91 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxAddon product developed. (Mar 9, 2018)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use \MGProvision\Proxmox\v2\ProxmoxApiException;
+use \MGProvision\Proxmox\v2\models\IpSet;
+use \MGProvision\Proxmox\v2\interfaces\VmInterface;
+
+/**
+ * Description of IpSetRepository
+ *
+ * @author Pawel Kopec <pawelk@modulesgardne.com>
+ */
+class IpSetRepository extends AbstractRepository
+{
+    protected $path;
+
+    public function findByPath($path)
+    {
+
+        if (!preg_match('/ipset/', $path))
+        {
+            throw new ProxmoxApiException(sprintf("IpSet path ('%s') is not valid", $path));
+        }
+
+        $this->path = $path;
+        return $this;
+    }
+
+    public function findByVm(VmInterface $vm)
+    {
+
+        $this->findByPath($vm->getPath() . '/firewall/ipset');
+        return $this;
+    }
+
+    /**
+     * 
+     * @return IpSet[]
+     * @throws ProxmoxApiException
+     */
+    public function fetch()
+    {
+        if ($this->fetch)
+        {
+            return $this->fetch;
+        }
+        if (empty($this->path))
+        {
+            throw new ProxmoxApiException("IpSet path is empty");
+        }
+        $this->fetch = [];
+        $ipSet       = $this->api()->get($this->path);
+        krsort($ipSet);
+        foreach ($ipSet as $entity)
+        {
+            $this->fetch[] = (new IpSet("{$this->path}/{$entity['name']}"))->setAttributes($entity);
+        }
+        return $this->fetch;
+    }
+
+    public function find(IpSet $ipSet)
+    {
+        foreach ($this->fetch() as $cIpSet)
+        {
+            if ($cIpSet->getName() == $ipSet->getName())
+            {
+                return $cIpSet;
+            }
+        }
+        return null;
+    }
+}

+ 160 - 0
v2/repository/MountPointRepostiory.php

@@ -0,0 +1,160 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxAddon product developed. (Apr 12, 2018)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use \MGProvision\Proxmox\v2\ProxmoxApiException;
+use \MGProvision\Proxmox\v2\models\MountPoint;
+
+/**
+ * Description of MounPointRepostiory
+ *
+ * @author Pawel Kopec <pawelk@modulesgardne.com>
+ */
+class MountPointRepostiory extends AbstractRepository
+{
+    protected $path;
+    private $excludeIds=[];
+
+    public function findByPath($path)
+    {
+
+        if (!preg_match('/config/', $path))
+        {
+            throw new ProxmoxApiException(sprintf("MounPoint path ('%s') is not valid", $path));
+        }
+
+        $this->path = $path;
+        return $this;
+    }
+
+    /**
+     * 
+     * @return MountPoint[]
+     * @throws ProxmoxApiException
+     */
+    public function fetch()
+    {
+        if ($this->fetch)
+        {
+            return $this->fetch;
+        }
+        if (empty($this->path))
+        {
+            throw new ProxmoxApiException("MounPoint path is empty");
+        }
+        $this->fetch = [];
+        foreach ($this->api()->get($this->path) as $id => $config)
+        {
+            if (!preg_match('/mp/', $id) && $id != 'rootfs' && !preg_match('/disk/', $config))
+            {
+                continue;
+            }
+
+            $this->fetch[$id] = (new MountPoint($id, $config))->setPath($this->path);
+        }
+        krsort($this->fetch);
+        return $this->fetch;
+    }
+
+    public function size()
+    {
+        $size = 0;
+        foreach ($this->fetch() as $mp)
+        {
+            $size += (int) $mp->getSize();
+        }
+        return $size;
+    }
+
+    /**
+     * Return sieze in GB
+     * @return int
+     * @throws ProxmoxApiException
+     */
+    public function additionalSize()
+    {
+        $size = 0;
+        foreach ($this->fetch() as $mp)
+        {
+            if ($mp->getId() == "rootfs")
+            {
+                continue;
+            }
+            else if($this->excludeIds && in_array($mp->getId(), $this->excludeIds)){
+                continue;
+            }
+
+            $size += (int) $mp->getSize();
+        }
+        return $size;
+    }
+
+    public function additionalSizeToBytes(){
+        //GB => Bytes
+        return $this->additionalSize() * pow(1024, 3);
+    }
+
+
+    public function nextId()
+    {
+        $used    = [];
+        foreach ($this->fetch() as $mp)
+        {
+            $used[] = preg_replace("/[a-z]+/", "", $mp->getId());
+        }
+        for ($n = 0; $n <= 7; $n++)
+        {
+            if (!in_array($n, $used))
+            {
+                return "mp" . $n;
+            }
+        }
+        throw new ProxmoxApiException("Mount Point limit exceeded");
+    }
+
+
+    public function findMountPointById($id){
+        foreach($this->fetch() as $entity){
+            if($entity->getId()==$id){
+                return $entity;
+            }
+        }
+        throw new ProxmoxApiException("Mount Point {$id} not found");
+    }
+
+    public function findById($id){
+        return $this->findMountPointById($id);
+    }
+
+    public function whereNotIn(array $excludeIds){
+        $this->excludeIds = $excludeIds;
+        return $this;
+    }
+
+    public function  deleteUnused(){
+        $this->reset();
+        foreach ($this->fetch() as $disk){
+            if(preg_match('/unused/', $disk->getId())){
+                $disk->delete();
+            }
+        }
+    }
+}

+ 75 - 0
v2/repository/NetworkRepository.php

@@ -0,0 +1,75 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxAddon product developed. (May 18, 2018)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use \MGProvision\Proxmox\v2\ProxmoxApiException;
+Use \MGProvision\Proxmox\v2\models\Network;
+
+/**
+ * Description of NetworkRepository
+ *
+ * @author Pawel Kopec <pawelk@modulesgardne.com>
+ */
+class NetworkRepository extends AbstractRepository
+{
+    protected $path;
+    protected $filters = [];
+
+    public function findByPath($path)
+    {
+
+        if (!preg_match('/network/', $path))
+        {
+            throw new ProxmoxApiException(sprintf(__CLASS__ . " path ('%s') is not valid", $path));
+        }
+        $this->path = $path;
+        return $this;
+    }
+
+    public function findTypeBridge()
+    {
+        $this->filters['type'] = 'bridge';
+        return $this;
+    }
+
+    /**
+     * 
+     * @return Network[]
+     * @throws ProxmoxApiException
+     */
+    public function fetch()
+    {
+        if ($this->fetch)
+        {
+            return $this->fetch;
+        }
+        if (empty($this->path))
+        {
+            throw new ProxmoxApiException(__CLASS__ . " path is empty");
+        }
+        $this->fetch = [];
+        foreach ($this->api()->get($this->path, $this->filters) as $data)
+        {
+            $this->fetch[] = (new Network())->setAttributes($data)->setPath($this->path);
+        }
+        return $this->fetch;
+    }
+}

+ 244 - 0
v2/repository/NodeRepository.php

@@ -0,0 +1,244 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-06)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use MGProvision\Proxmox\v2\models\Node;
+use MGProvision\Proxmox\v2\ProxmoxApiException;
+
+/**
+ * Description of NodeRepository
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class NodeRepository extends AbstractRepository
+{
+    private $online = true;
+
+    /**
+     *
+     * @param string $hostname
+     * @param string $ipAddress
+     * @return string
+     * @throws ProxmoxApiException
+     */
+    public function findWithHostOrIp($hostname, $ipAddress)
+    {
+
+        if (empty($hostname) && empty($ipAddress))
+        {
+            throw new ProxmoxApiException('Server\'s Hostname is empty');
+        }
+        //Hostname
+        if (preg_match('/\:/', $hostname))
+        {
+            list($host, $port) = explode(":", $hostname);
+            $port = (int) $port;
+            if (is_string($host))
+            {
+                $hostname = $host;
+            }
+        }
+        //IpAddress
+        if (preg_match('/\:/', $ipAddress))
+        {
+            list($host, $port) = explode(":", $ipAddress);
+            $port = (int) $port;
+            if (is_string($host))
+            {
+                $ipAddress = $host;
+            }
+        }
+        //cluster nodes
+        $clusterConfig = $this->api()->get('/cluster/config/nodes', array());
+        foreach ($clusterConfig as $config){
+            if ($config['node'] && $config['ring0_addr'] &&  $config['ring0_addr'] == $ipAddress || ( $hostname && $config['ring0_addr'] == $hostname)){
+                return new Node($config['node']);
+            }
+        }
+        //All nodes to array
+        $nodes  = array();
+        $result = $this->api()->get("/nodes");
+        foreach ($result as $ret)
+        {
+            if ($this->online && $ret['status'] && $ret['status'] != 'online' )
+            {
+                continue;
+            }
+            $nodes[] = $ret['node'];
+        }
+
+        $nodes  = array_filter($nodes);
+
+        //Check if only one node
+        if(count($nodes) === 1)
+        {
+            return new Node($nodes[0]);
+        }
+
+        //Find by hostname
+        if ($hostname && !filter_var($hostname, FILTER_VALIDATE_IP))
+        {
+            $temp     = explode(".", $hostname);
+            $hostNode = current($temp);
+            if ($hostNode)
+            {
+                foreach ($nodes as $node)
+                {
+                    if (strtolower($hostNode) == strtolower($node))
+                    {
+                        return new Node($node);
+                    }
+                }
+            }
+        }
+        //Find by Ip Address
+        foreach ($nodes as $node) {
+            $networks = $this->api()->get('/nodes/' . $node . '/network', array());
+            foreach ($networks as $n) {
+                try {
+                    if (!$n['iface']) {
+                        continue;
+                    }
+                    $res = $this->api()->get('/nodes/' . $node . '/network/' . $n['iface'], array());
+                    if(!$res['address']){
+                        continue;
+                    }
+                    if ($res['address'] && preg_match('/\//', $res['address'])) {
+                        $res['address'] = preg_replace('/\/(.*)*/', '', $res['address']);
+                    }
+                    if ($res['address'] && $res['address'] == $ipAddress || ( $hostname && $res['address'] == $hostname))
+                        return new Node($node);
+                } catch (\Exception $ex) {//interface does not exist
+                    if(!preg_match('/interface does not exist/',$ex->getMessage())){
+                        throw $ex;
+                    }
+                }
+            }
+        }
+
+        if (empty($ipAddress))
+        {
+            throw new ProxmoxApiException('Server\'s IP Address is empty');
+        }
+        throw new ProxmoxApiException('Node for IP Address "' . $ipAddress . '" not found');
+    }
+
+    /**
+     *
+     * @param float $bytes
+     * @return Node
+     * @throws ProxmoxApiException
+     */
+    public function findByDiskSize($bytes)
+    {
+
+        $nodes_arr = array();
+        $nodes     = $this->api()->get('/nodes');
+        foreach ($nodes as $nodeValues)
+        {
+            if ($this->online && $nodeValues['status'] && $nodeValues['status'] != 'online' )
+            {
+                continue;
+            }
+            $node         = $nodeValues['node'];
+            $nodeStatus   = $this->api()->get("/nodes/{$node}/status");
+            $nodeStorages = $this->api()->get("/nodes/{$node}/storage");
+            $storages     = array();
+            foreach ($nodeStorages as $ns)
+            {
+                $storages[] = array(
+                    "storage" => $ns['storage'],
+                    "avail"   => $ns['avail'],
+                    "content" => $ns['content']
+                );
+            }
+            if (empty($storages))
+                continue;
+            $nodes_arr[] = array(
+                'node'     => $node,
+                "memory"   => $nodeStatus['memory']['free'], //order by "memory":"free"
+                "storages" => $storages// re-order by "avail
+            );
+        }
+
+        foreach ($nodes_arr as $nk => $row)
+        {
+            $memory[$nk] = $row['memory'];
+            foreach ($row['storages'] as $sk => $storage)
+            {
+                $storages[$sk] = $storage['avail'];
+            }
+            $r                          = array_multisort($storages, SORT_DESC, $row['storages']);
+            $nodes_arr[$nk]['storages'] = $row['storages'];
+        }
+
+        $r              = array_multisort($memory, SORT_DESC, $nodes_arr);
+        $storage        = null;
+        $backup_storage = null;
+        foreach ($nodes_arr[0]["storages"] as $s)
+        {
+
+            if ($storage == null && ( strpos($s['content'], 'rootdir') !== false || strpos($s['content'], 'images') !== false ))
+                $storage = $s['storage'];
+
+            if ($backup_storage == null && strpos($s['content'], 'backup') !== false)
+                $backup_storage = $s['storage'];
+        }
+
+        $selectedNode = array();
+        foreach ($nodes_arr as $n)
+        {
+            if ($n["storages"]["0"]["avail"] > $bytes)
+            {
+                return new Node($n['node']);
+                break;
+            }
+        }
+
+        throw new ProxmoxApiException(sprintf("Storage with free space \"%s\" bytes not found", $bytes));
+    }
+
+    /**
+     *
+     * @return Node[]
+     */
+    public function fetch()
+    {
+        $data   = array();
+        $result = $this->api()->get("/nodes");
+        foreach ($result as $ret)
+        {
+            if ($this->online && $ret['status'] && $ret['status'] != 'online' )
+            {
+                continue;
+            }
+            $data[] = new Node($ret['node']);
+        }
+        return $data;
+    }
+
+    public function findOnline($online)
+    {
+        $this->online = (boolean) $online;
+        return $this;
+    }
+}

+ 140 - 0
v2/repository/SnapshotRepository.php

@@ -0,0 +1,140 @@
+<?php
+
+/* * ********************************************************************
+ * proxmoxCloudVps product developed. (2017-01-20)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use MGProvision\Proxmox\v2 as proxmox;
+use MGProvision\Proxmox\v2\interfaces\VmInterface;
+use MGProvision\Proxmox\v2\models\Snapshot;
+use ModulesGarden\ProxmoxAddon\App\Models\VmModel;
+
+/**
+ * Description of SnapshotRepository
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class SnapshotRepository extends AbstractRepository
+{
+    private $paths         = array();
+    private $ignoreCurrent = false;
+    private $current;
+
+    public function findByVm(VmInterface $vm)
+    {
+
+        $this->paths[] = "/nodes/{$vm->getNode()}/{$vm->getVirtualization()}/{$vm->getVmid()}/snapshot";
+        return $this;
+    }
+
+    /**
+     * 
+     * @param VmModel[] $vservers
+     */
+    public function findByVmModel($vservers)
+    {
+
+        $nodes = array();
+        foreach ($vservers as $vserver)
+        {
+            if (!$vserver instanceof VmModel)
+            {
+                throw new \MGProvision\Proxmox\v2\ProxmoxApiException("Ukown  object: " . get_class($vserver));
+            }
+            $this->paths[] = "/nodes/{$vserver->node}/{$vserver->virtualization}/{$vserver->vmid}/snapshot";
+        }
+
+        return $this;
+    }
+
+    public function reset()
+    {
+        parent::reset();
+        $this->ignoreCurrent = false;
+        $this->paths         = array();
+        return $this;
+    }
+
+    public function ignoreCurrent($ignoreCurrent)
+    {
+        $this->ignoreCurrent = (boolean) $ignoreCurrent;
+        return $this;
+    }
+
+    /**
+     * @return proxmox\models\SnapshotKvm
+     */
+    public function getCurrent()
+    {
+        return $this->current;
+    }
+
+    /**
+     * @return proxmox\models\SnapshotKvm[]
+     * @throws proxmox\ProxmoxApiException
+     */
+    public function fetch()
+    {
+
+        if (!empty($this->fetch))
+        {
+            return $this->fetch;
+        }
+
+        foreach ($this->paths as $path)
+        {
+
+            $temp = $this->api()->get($path);
+
+            foreach ($temp as $snap)
+            {
+                $entityObj     = new Snapshot();
+                $entityObj->setAttributes($snap);
+                $entityObj->setPath(sprintf("{$path}/%s", $snap['name']));
+                if ($this->ignoreCurrent && $snap['name'] == "current")
+                {
+                    $this->current = $entityObj;
+                    continue;
+                }
+                $this->fetch[] = $entityObj;
+            }
+        }
+        return $this->fetch;
+    }
+
+    public function sortBySnaptime(){
+        $keys = [];
+        $orginal = $this->fetch();
+        foreach ($orginal as $key => $entity){
+            $keys[$key] = $entity->getSnaptime();
+        }
+        uasort($keys, function($a, $b){
+            if ($a == $b) {
+                return 0;
+            }
+            return ($a < $b) ? -1 : 1;
+        });
+        $this->fetch=[];
+        foreach ($keys as $id => $snaptime){
+            $this->fetch[$id] = $orginal[$id];
+        }
+        return $this;
+    }
+}

+ 113 - 0
v2/repository/StorageRepository.php

@@ -0,0 +1,113 @@
+<?php
+
+/* * ********************************************************************
+ * ProxmoxVPS product developed. (2016-10-11)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+/**
+ * Description of StorageRepository
+ *
+ * @author Pawel Kopec <pawelk@modulesgarden.com>
+ * @version 1.0.0
+ */
+class StorageRepository extends AbstractRepository
+{
+    protected $nodes = array();
+    private $enabled;
+    protected $filterPattern;
+
+    public function findEnabed()
+    {
+        $this->enabled = 1;
+        return $this;
+    }
+
+    public function findContent($content)
+    {
+        $this->content = $content;
+        return $this;
+    }
+
+    public function findIso()
+    {
+        $this->content = 'iso';
+        return $this;
+    }
+
+    public function findSnippets(){
+        $this->filterPattern = "/snippets/";
+    }
+
+    /**
+     * @return  \MGProvision\Proxmox\v2\models\Storage[]
+     */
+    public function fetch()
+    {
+
+        $request = $this->content ? array("content" => $this->content) : array();
+        if (!is_null($this->enabled))
+        {
+            $request['enabled'] = $this->enabled;
+        }
+        $data = array();
+        foreach ($this->nodes as $node)
+        {
+            $storages = $this->api()->get("/nodes/{$node}/storage", $request);
+            foreach ($storages as $storage)
+            {
+                if ($this->enabled && (!$storage['enabled'] && version_compare($this->api()->getVersion(), "5.0", '>') ))
+                {
+                    continue;
+                }
+                $storageObj = new \MGProvision\Proxmox\v2\models\Storage();
+                $storageObj->setAttributes($storage);
+                $data[]     = $storageObj;
+            }
+        }
+        if(empty($this->nodes)){
+            $storages = $this->api()->get("/storage", $request);
+            foreach ($storages as $storage)
+            {
+                if($this->filterPattern && !preg_match($this->filterPattern, $storage['content'])){
+                    continue;
+                }
+                $storageObj = new \MGProvision\Proxmox\v2\models\Storage();
+                $storageObj->setAttributes($storage);
+                $data[]     = $storageObj;
+            }
+        }
+        return $data;
+    }
+
+    public function fetchAsArray()
+    {
+        $data = array();
+        foreach ($this->fetch() as $storage)
+        {
+            $data[] = $storage->getStorage();
+        }
+        return $data;
+    }
+
+    public function findByNodes(array $nodes)
+    {
+        $this->nodes = $nodes;
+        return $this;
+    }
+}

+ 112 - 0
v2/repository/TaskRepository.php

@@ -0,0 +1,112 @@
+<?php
+
+/* * ********************************************************************
+ * proxmoxCloud product developed. (2017-01-18)
+ * *
+ *
+ *  CREATED BY MODULESGARDEN       ->       http://modulesgarden.com
+ *  CONTACT                        ->       contact@modulesgarden.com
+ *
+ *
+ * This software is furnished under a license and may be used and copied
+ * only  in  accordance  with  the  terms  of such  license and with the
+ * inclusion of the above copyright notice.  This software  or any other
+ * copies thereof may not be provided or otherwise made available to any
+ * other person.  No title to and  ownership of the  software is  hereby
+ * transferred.
+ *
+ *
+ * ******************************************************************** */
+
+namespace MGProvision\Proxmox\v2\repository;
+
+use MGProvision\Proxmox\v2 as proxmox;
+use MGProvision\Proxmox\v2\interfaces\VmInterface;
+
+class TaskRepository extends AbstractRepository
+{
+    private $nodes;
+    private $vmids;
+    private $limit;
+    private $fromStartime;
+
+    public function findByVm(VmInterface $vm)
+    {
+        $this->nodes = array($vm->getNode());
+        $this->vmids = array($vm->getVmid());
+        return $this;
+    }
+
+    public function findByNodes($nodes)
+    {
+        $this->nodes = $nodes;
+        return $this;
+    }
+
+    public function findByVmids($vmids)
+    {
+        $this->vmids = $vmids;
+        return $this;
+    }
+
+    public function limit($limit)
+    {
+        $this->limit = $limit;
+        return $this;
+    }
+
+    public function fromStartTime($fromStartime)
+    {
+        $this->fromStartime = $fromStartime;
+        return $this;
+
+    }
+    /**
+     * 
+     * @return proxmox\models\Task[]
+     */
+    public function fetch()
+    {
+
+        if (!empty($this->fetch))
+        {
+            return $this->fetch;
+        }
+        $data = array();
+        $i    = 0;
+        foreach ($this->nodes as $node)
+        {
+            $parameters = array();
+            if (isset($this->vmids[$i]))
+            {
+                $parameters["vmid"] = $this->vmids[$i];
+            }
+            if (isset($this->limit))
+            {
+                $parameters["limit"] = $this->limit;
+            }
+            $collection = $this->api()->get("/nodes/{$node}/tasks", $parameters);
+            foreach ($collection as $task)
+            {
+                if($this->fromStartime && $task['starttime'] < $this->fromStartime ){
+                    continue;
+                }
+                $entityObj = new proxmox\models\Task();
+                $entityObj->setAttributes($task);
+                $entityObj->setPath("/nodes/{$node}/tasks/" . $task['upid']);
+                $data[]    = $entityObj;
+            }
+            $i++;
+        }
+        return $data;
+    }
+
+    public function delete()
+    {
+
+        foreach ($this->fetch() as $task)
+        {
+            $task->delete();
+        }
+    }
+}