proxmoxer-1.0.3/0000755000175000017500000000000013345547757014106 5ustar swayfswayf00000000000000proxmoxer-1.0.3/LICENSE.txt0000644000175000017500000000206113344210403015676 0ustar swayfswayf00000000000000The MIT License Copyright (c) 2013 Oleg Butovich 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.proxmoxer-1.0.3/MANIFEST.in0000644000175000017500000000014213344210403015607 0ustar swayfswayf00000000000000include LICENSE.txt include README.txt include README.rst global-exclude *.orig *.pyc *.log *.swp proxmoxer-1.0.3/PKG-INFO0000644000175000017500000002673213345547757015215 0ustar swayfswayf00000000000000Metadata-Version: 1.1 Name: proxmoxer Version: 1.0.3 Summary: Python Wrapper for the Proxmox 2.x API (HTTP and SSH) Home-page: https://github.com/swayf/proxmoxer Author: Oleg Butovich Author-email: obutovich@gmail.com License: MIT Download-URL: http://pypi.python.org/pypi/proxmoxer Description: ========================================= Proxmoxer: A wrapper for Proxmox REST API ========================================= master branch: |master_build_status| |master_coverage_status| |pypi_version| |pypi_downloads| develop branch: |develop_build_status| |develop_coverage_status| What does it do and what's different? ------------------------------------- Proxmoxer is a wrapper around the `Proxmox REST API v2 `_. It was inspired by slumber, but it dedicated only to Proxmox. It allows to use not only REST API over HTTPS, but the same api over ssh and pvesh utility. Like `Proxmoxia `_ it dynamically creates attributes which responds to the attributes you've attempted to reach. Installation ------------ :: pip install proxmoxer For 'https' backend install requests :: pip install requests For 'ssh_paramiko' backend install paramiko :: pip install paramiko Short usage information ----------------------- The first thing to do is import the proxmoxer library and create ProxmoxAPI instance. :: from proxmoxer import ProxmoxAPI proxmox = ProxmoxAPI('proxmox_host', user='admin@pam', password='secret_word', verify_ssl=False) This will connect by default through the 'https' backend. It is possible to use already prepared public/private key authentication. It is possible to use ssh-agent also. :: from proxmoxer import ProxmoxAPI proxmox = ProxmoxAPI('proxmox_host', user='proxmox_admin', backend='ssh_paramiko') **Please note, https-backend needs 'requests' library, ssh_paramiko-backend needs 'paramiko' library, openssh-backend needs 'openssh_wrapper' library installed.** Queries are exposed via the access methods **get**, **post**, **put** and **delete**. For convenience added two synonyms: **create** for **post**, and **set** for **put**. :: for node in proxmox.nodes.get(): for vm in proxmox.nodes(node['node']).openvz.get(): print "{0}. {1} => {2}" .format(vm['vmid'], vm['name'], vm['status']) >>> 141. puppet-2.london.baseblack.com => running 101. munki.london.baseblack.com => running 102. redmine.london.baseblack.com => running 140. dns-1.london.baseblack.com => running 126. ns-3.london.baseblack.com => running 113. rabbitmq.london.baseblack.com => running same code can be rewritten in the next way:: for node in proxmox.get('nodes'): for vm in proxmox.get('nodes/%s/openvz' % node['node']): print "%s. %s => %s" % (vm['vmid'], vm['name'], vm['status']) for example next lines do the same job:: proxmox.nodes(node['node']).openvz.get() proxmox.nodes(node['node']).get('openvz') proxmox.get('nodes/%s/openvz' % node['node']) proxmox.get('nodes', node['node'], 'openvz') Some more examples:: for vm in proxmox.cluster.resources.get(type='vm'): print("{0}. {1} => {2}" .format(vm['vmid'], vm['name'], vm['status'])) :: node = proxmox.nodes('proxmox_node') pprint(node.storage('local').content.get()) or the with same results :: node = proxmox.nodes.proxmox_node() pprint(node.storage.local.content.get()) Example of creation of lxc container:: node = proxmox.nodes('proxmox_node') node.lxc.create(vmid=202, ostemplate='local:vztmpl/debian-9.0-standard_20170530_amd64.tar.gz', hostname='debian-stretch', storage='local', memory=512, swap=512, cores=1, password='secret', net0='name=eth0,bridge=vmbr0,ip=192.168.22.1/20,gw=192.168.16.1') Example of creating the same lxc container with parameters in a dictonary. This approach allows to add ``ssh-public-keys`` without getting syntax errors. :: newcontainer = { 'vmid': 202, 'ostemplate': 'local:vztmpl/debian-9.0-standard_20170530_amd64.tar.gz', 'hostname': 'debian-stretch', 'storage': 'local', 'memory': 512, 'swap': 512, 'cores': 1, 'password': 'secret', 'net0': 'name=eth0,bridge=vmbr0,ip=192.168.22.1/20,gw=192.168.16.1' } node = proxmox.nodes('proxmox_node') node.lxc.create(**newcontainer) Example of template upload:: local_storage = proxmox.nodes('proxmox_node').storage('local') local_storage.upload.create(content='vztmpl', filename=open(os.path.expanduser('~/templates/debian-6-my-core_1.0-1_i386.tar.gz')))) Example of rrd download:: response = proxmox.nodes('proxmox').rrd.get(ds='cpu', timeframe='hour') with open('cpu.png', 'wb') as f: f.write(response['image'].encode('raw_unicode_escape')) Example of usage of logging:: # now logging debug info will be written to stdout logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s:%(name)s: %(message)s') Roadmap ------- * write tests * support other actual python versions * add optional validation of requests * add some shortcuts for convenience History ------- 1.0.3 (1018-09-10) .................. * Improvement: Added option to specify port in hostname parameter (``_) * Improvement: Added stderr to the Response content (`Jérôme Schneider `_) * Bugfix: Paramiko python3: stdout and stderr must be a str not bytes (`Jérôme Schneider `_) * New lxc example in docu (`Geert Stappers `_) 1.0.2 (2017-12-02) .................. * Tarball repackaged with tests 1.0.1 (2017-12-02) .................. * LICENSE file now included in tarball * Added verify_ssl parameter to ProxmoxHTTPAuth (`Walter Doekes `_) 1.0.0 (2017-11-12) .................. * Update Proxmoxer readme (`Emmanuel Kasper `_) * Display the reason of API calls errors (`Emmanuel Kasper `_, `kantsdog `_) * Filter for ssh response code (`Chris Plock `_) 0.2.5 (2017-02-12) .................. * Adding sudo to execute CLI with paramiko ssh backend (`Jason Meridth `_) * Proxmoxer/backends/ssh_paramiko: improve file upload (`Jérôme Schneider `_) 0.2.4 (2016-05-02) .................. * Removed newline in tmp_filename string (`Jérôme Schneider `_) * Fix to avoid module reloading (`jklang `_) 0.2.3 (2016-01-20) .................. * Minor typo fix (`Srinivas Sakhamuri `_) 0.2.2 (2016-01-19) .................. * Adding sudo to execute pvesh CLI in openssh backend (`Wei Tie `_, `Srinivas Sakhamuri `_) * Add support to specify an identity file for ssh connections (`Srinivas Sakhamuri `_) 0.2.1 (2015-05-02) .................. * fix for python 3.4 (`kokuev `_) 0.2.0 (2015-03-21) .................. * Https will now raise AuthenticationError when appropriate. (`scap1784 `_) * Preliminary python 3 compatibility. (`wdoekes `_) * Additional example. (`wdoekes `_) 0.1.7 (2014-11-16) .................. * Added ignore of "InsecureRequestWarning: Unverified HTTPS request is being made..." warning while using https (requests) backend. 0.1.4 (2013-06-01) .................. * Added logging * Added openssh backend * Tests are reorganized 0.1.3 (2013-05-30) .................. * Added next tests * Bugfixes 0.1.2 (2013-05-27) .................. * Added first tests * Added support for travis and coveralls * Bugfixes 0.1.1 (2013-05-13) .................. * Initial try. .. |master_build_status| image:: https://travis-ci.org/swayf/proxmoxer.png?branch=master :target: https://travis-ci.org/swayf/proxmoxer .. |master_coverage_status| image:: https://coveralls.io/repos/swayf/proxmoxer/badge.png?branch=master :target: https://coveralls.io/r/swayf/proxmoxer .. |develop_build_status| image:: https://travis-ci.org/swayf/proxmoxer.png?branch=develop :target: https://travis-ci.org/swayf/proxmoxer .. |develop_coverage_status| image:: https://coveralls.io/repos/swayf/proxmoxer/badge.png?branch=develop :target: https://coveralls.io/r/swayf/proxmoxer .. |pypi_version| image:: https://img.shields.io/pypi/v/proxmoxer.svg :target: https://pypi.python.org/pypi/proxmoxer .. |pypi_downloads| image:: https://img.shields.io/pypi/dm/proxmoxer.svg :target: https://pypi.python.org/pypi/proxmoxer Keywords: proxmox,api Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development :: Libraries :: Python Modules proxmoxer-1.0.3/README.rst0000644000175000017500000002154613345546241015570 0ustar swayfswayf00000000000000========================================= Proxmoxer: A wrapper for Proxmox REST API ========================================= master branch: |master_build_status| |master_coverage_status| |pypi_version| |pypi_downloads| develop branch: |develop_build_status| |develop_coverage_status| What does it do and what's different? ------------------------------------- Proxmoxer is a wrapper around the `Proxmox REST API v2 `_. It was inspired by slumber, but it dedicated only to Proxmox. It allows to use not only REST API over HTTPS, but the same api over ssh and pvesh utility. Like `Proxmoxia `_ it dynamically creates attributes which responds to the attributes you've attempted to reach. Installation ------------ .. code-block:: bash pip install proxmoxer For 'https' backend install requests .. code-block:: bash pip install requests For 'ssh_paramiko' backend install paramiko .. code-block:: bash pip install paramiko Short usage information ----------------------- The first thing to do is import the proxmoxer library and create ProxmoxAPI instance. .. code-block:: python from proxmoxer import ProxmoxAPI proxmox = ProxmoxAPI('proxmox_host', user='admin@pam', password='secret_word', verify_ssl=False) This will connect by default through the 'https' backend. It is possible to use already prepared public/private key authentication. It is possible to use ssh-agent also. .. code-block:: python from proxmoxer import ProxmoxAPI proxmox = ProxmoxAPI('proxmox_host', user='proxmox_admin', backend='ssh_paramiko') **Please note, https-backend needs 'requests' library, ssh_paramiko-backend needs 'paramiko' library, openssh-backend needs 'openssh_wrapper' library installed.** Queries are exposed via the access methods **get**, **post**, **put** and **delete**. For convenience added two synonyms: **create** for **post**, and **set** for **put**. .. code-block:: python for node in proxmox.nodes.get(): for vm in proxmox.nodes(node['node']).openvz.get(): print "{0}. {1} => {2}" .format(vm['vmid'], vm['name'], vm['status']) >>> 141. puppet-2.london.baseblack.com => running 101. munki.london.baseblack.com => running 102. redmine.london.baseblack.com => running 140. dns-1.london.baseblack.com => running 126. ns-3.london.baseblack.com => running 113. rabbitmq.london.baseblack.com => running same code can be rewritten in the next way: .. code-block:: python for node in proxmox.get('nodes'): for vm in proxmox.get('nodes/%s/openvz' % node['node']): print "%s. %s => %s" % (vm['vmid'], vm['name'], vm['status']) for example next lines do the same job: .. code-block:: python proxmox.nodes(node['node']).openvz.get() proxmox.nodes(node['node']).get('openvz') proxmox.get('nodes/%s/openvz' % node['node']) proxmox.get('nodes', node['node'], 'openvz') Some more examples: .. code-block:: python for vm in proxmox.cluster.resources.get(type='vm'): print("{0}. {1} => {2}" .format(vm['vmid'], vm['name'], vm['status'])) .. code-block:: python node = proxmox.nodes('proxmox_node') pprint(node.storage('local').content.get()) or the with same results .. code-block:: python node = proxmox.nodes.proxmox_node() pprint(node.storage.local.content.get()) Example of creation of lxc container: .. code-block:: python node = proxmox.nodes('proxmox_node') node.lxc.create(vmid=202, ostemplate='local:vztmpl/debian-9.0-standard_20170530_amd64.tar.gz', hostname='debian-stretch', storage='local', memory=512, swap=512, cores=1, password='secret', net0='name=eth0,bridge=vmbr0,ip=192.168.22.1/20,gw=192.168.16.1') Example of creating the same lxc container with parameters in a dictonary. This approach allows to add ``ssh-public-keys`` without getting syntax errors. .. code-block:: python newcontainer = { 'vmid': 202, 'ostemplate': 'local:vztmpl/debian-9.0-standard_20170530_amd64.tar.gz', 'hostname': 'debian-stretch', 'storage': 'local', 'memory': 512, 'swap': 512, 'cores': 1, 'password': 'secret', 'net0': 'name=eth0,bridge=vmbr0,ip=192.168.22.1/20,gw=192.168.16.1' } node = proxmox.nodes('proxmox_node') node.lxc.create(**newcontainer) Example of template upload: .. code-block:: python local_storage = proxmox.nodes('proxmox_node').storage('local') local_storage.upload.create(content='vztmpl', filename=open(os.path.expanduser('~/templates/debian-6-my-core_1.0-1_i386.tar.gz')))) Example of rrd download: .. code-block:: python response = proxmox.nodes('proxmox').rrd.get(ds='cpu', timeframe='hour') with open('cpu.png', 'wb') as f: f.write(response['image'].encode('raw_unicode_escape')) Example of usage of logging: .. code-block:: python # now logging debug info will be written to stdout logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s:%(name)s: %(message)s') Roadmap ------- * write tests * support other actual python versions * add optional validation of requests * add some shortcuts for convenience History ------- 1.0.3 (1018-09-10) .................. * Improvement: Added option to specify port in hostname parameter (`pvanagtmaal `_) * Improvement: Added stderr to the Response content (`Jérôme Schneider `_) * Bugfix: Paramiko python3: stdout and stderr must be a str not bytes (`Jérôme Schneider `_) * New lxc example in docu (`Geert Stappers `_) 1.0.2 (2017-12-02) .................. * Tarball repackaged with tests 1.0.1 (2017-12-02) .................. * LICENSE file now included in tarball * Added verify_ssl parameter to ProxmoxHTTPAuth (`Walter Doekes `_) 1.0.0 (2017-11-12) .................. * Update Proxmoxer readme (`Emmanuel Kasper `_) * Display the reason of API calls errors (`Emmanuel Kasper `_, `kantsdog `_) * Filter for ssh response code (`Chris Plock `_) 0.2.5 (2017-02-12) .................. * Adding sudo to execute CLI with paramiko ssh backend (`Jason Meridth `_) * Proxmoxer/backends/ssh_paramiko: improve file upload (`Jérôme Schneider `_) 0.2.4 (2016-05-02) .................. * Removed newline in tmp_filename string (`Jérôme Schneider `_) * Fix to avoid module reloading (`jklang `_) 0.2.3 (2016-01-20) .................. * Minor typo fix (`Srinivas Sakhamuri `_) 0.2.2 (2016-01-19) .................. * Adding sudo to execute pvesh CLI in openssh backend (`Wei Tie `_, `Srinivas Sakhamuri `_) * Add support to specify an identity file for ssh connections (`Srinivas Sakhamuri `_) 0.2.1 (2015-05-02) .................. * fix for python 3.4 (`kokuev `_) 0.2.0 (2015-03-21) .................. * Https will now raise AuthenticationError when appropriate. (`scap1784 `_) * Preliminary python 3 compatibility. (`wdoekes `_) * Additional example. (`wdoekes `_) 0.1.7 (2014-11-16) .................. * Added ignore of "InsecureRequestWarning: Unverified HTTPS request is being made..." warning while using https (requests) backend. 0.1.4 (2013-06-01) .................. * Added logging * Added openssh backend * Tests are reorganized 0.1.3 (2013-05-30) .................. * Added next tests * Bugfixes 0.1.2 (2013-05-27) .................. * Added first tests * Added support for travis and coveralls * Bugfixes 0.1.1 (2013-05-13) .................. * Initial try. .. |master_build_status| image:: https://travis-ci.org/swayf/proxmoxer.png?branch=master :target: https://travis-ci.org/swayf/proxmoxer .. |master_coverage_status| image:: https://coveralls.io/repos/swayf/proxmoxer/badge.png?branch=master :target: https://coveralls.io/r/swayf/proxmoxer .. |develop_build_status| image:: https://travis-ci.org/swayf/proxmoxer.png?branch=develop :target: https://travis-ci.org/swayf/proxmoxer .. |develop_coverage_status| image:: https://coveralls.io/repos/swayf/proxmoxer/badge.png?branch=develop :target: https://coveralls.io/r/swayf/proxmoxer .. |pypi_version| image:: https://img.shields.io/pypi/v/proxmoxer.svg :target: https://pypi.python.org/pypi/proxmoxer .. |pypi_downloads| image:: https://img.shields.io/pypi/dm/proxmoxer.svg :target: https://pypi.python.org/pypi/proxmoxer proxmoxer-1.0.3/README.txt0000644000175000017500000002100113345545473015567 0ustar swayfswayf00000000000000========================================= Proxmoxer: A wrapper for Proxmox REST API ========================================= master branch: |master_build_status| |master_coverage_status| |pypi_version| |pypi_downloads| develop branch: |develop_build_status| |develop_coverage_status| What does it do and what's different? ------------------------------------- Proxmoxer is a wrapper around the `Proxmox REST API v2 `_. It was inspired by slumber, but it dedicated only to Proxmox. It allows to use not only REST API over HTTPS, but the same api over ssh and pvesh utility. Like `Proxmoxia `_ it dynamically creates attributes which responds to the attributes you've attempted to reach. Installation ------------ :: pip install proxmoxer For 'https' backend install requests :: pip install requests For 'ssh_paramiko' backend install paramiko :: pip install paramiko Short usage information ----------------------- The first thing to do is import the proxmoxer library and create ProxmoxAPI instance. :: from proxmoxer import ProxmoxAPI proxmox = ProxmoxAPI('proxmox_host', user='admin@pam', password='secret_word', verify_ssl=False) This will connect by default through the 'https' backend. It is possible to use already prepared public/private key authentication. It is possible to use ssh-agent also. :: from proxmoxer import ProxmoxAPI proxmox = ProxmoxAPI('proxmox_host', user='proxmox_admin', backend='ssh_paramiko') **Please note, https-backend needs 'requests' library, ssh_paramiko-backend needs 'paramiko' library, openssh-backend needs 'openssh_wrapper' library installed.** Queries are exposed via the access methods **get**, **post**, **put** and **delete**. For convenience added two synonyms: **create** for **post**, and **set** for **put**. :: for node in proxmox.nodes.get(): for vm in proxmox.nodes(node['node']).openvz.get(): print "{0}. {1} => {2}" .format(vm['vmid'], vm['name'], vm['status']) >>> 141. puppet-2.london.baseblack.com => running 101. munki.london.baseblack.com => running 102. redmine.london.baseblack.com => running 140. dns-1.london.baseblack.com => running 126. ns-3.london.baseblack.com => running 113. rabbitmq.london.baseblack.com => running same code can be rewritten in the next way:: for node in proxmox.get('nodes'): for vm in proxmox.get('nodes/%s/openvz' % node['node']): print "%s. %s => %s" % (vm['vmid'], vm['name'], vm['status']) for example next lines do the same job:: proxmox.nodes(node['node']).openvz.get() proxmox.nodes(node['node']).get('openvz') proxmox.get('nodes/%s/openvz' % node['node']) proxmox.get('nodes', node['node'], 'openvz') Some more examples:: for vm in proxmox.cluster.resources.get(type='vm'): print("{0}. {1} => {2}" .format(vm['vmid'], vm['name'], vm['status'])) :: node = proxmox.nodes('proxmox_node') pprint(node.storage('local').content.get()) or the with same results :: node = proxmox.nodes.proxmox_node() pprint(node.storage.local.content.get()) Example of creation of lxc container:: node = proxmox.nodes('proxmox_node') node.lxc.create(vmid=202, ostemplate='local:vztmpl/debian-9.0-standard_20170530_amd64.tar.gz', hostname='debian-stretch', storage='local', memory=512, swap=512, cores=1, password='secret', net0='name=eth0,bridge=vmbr0,ip=192.168.22.1/20,gw=192.168.16.1') Example of creating the same lxc container with parameters in a dictonary. This approach allows to add ``ssh-public-keys`` without getting syntax errors. :: newcontainer = { 'vmid': 202, 'ostemplate': 'local:vztmpl/debian-9.0-standard_20170530_amd64.tar.gz', 'hostname': 'debian-stretch', 'storage': 'local', 'memory': 512, 'swap': 512, 'cores': 1, 'password': 'secret', 'net0': 'name=eth0,bridge=vmbr0,ip=192.168.22.1/20,gw=192.168.16.1' } node = proxmox.nodes('proxmox_node') node.lxc.create(**newcontainer) Example of template upload:: local_storage = proxmox.nodes('proxmox_node').storage('local') local_storage.upload.create(content='vztmpl', filename=open(os.path.expanduser('~/templates/debian-6-my-core_1.0-1_i386.tar.gz')))) Example of rrd download:: response = proxmox.nodes('proxmox').rrd.get(ds='cpu', timeframe='hour') with open('cpu.png', 'wb') as f: f.write(response['image'].encode('raw_unicode_escape')) Example of usage of logging:: # now logging debug info will be written to stdout logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s:%(name)s: %(message)s') Roadmap ------- * write tests * support other actual python versions * add optional validation of requests * add some shortcuts for convenience History ------- 1.0.3 (1018-09-10) .................. * Improvement: Added option to specify port in hostname parameter (``_) * Improvement: Added stderr to the Response content (`Jérôme Schneider `_) * Bugfix: Paramiko python3: stdout and stderr must be a str not bytes (`Jérôme Schneider `_) * New lxc example in docu (`Geert Stappers `_) 1.0.2 (2017-12-02) .................. * Tarball repackaged with tests 1.0.1 (2017-12-02) .................. * LICENSE file now included in tarball * Added verify_ssl parameter to ProxmoxHTTPAuth (`Walter Doekes `_) 1.0.0 (2017-11-12) .................. * Update Proxmoxer readme (`Emmanuel Kasper `_) * Display the reason of API calls errors (`Emmanuel Kasper `_, `kantsdog `_) * Filter for ssh response code (`Chris Plock `_) 0.2.5 (2017-02-12) .................. * Adding sudo to execute CLI with paramiko ssh backend (`Jason Meridth `_) * Proxmoxer/backends/ssh_paramiko: improve file upload (`Jérôme Schneider `_) 0.2.4 (2016-05-02) .................. * Removed newline in tmp_filename string (`Jérôme Schneider `_) * Fix to avoid module reloading (`jklang `_) 0.2.3 (2016-01-20) .................. * Minor typo fix (`Srinivas Sakhamuri `_) 0.2.2 (2016-01-19) .................. * Adding sudo to execute pvesh CLI in openssh backend (`Wei Tie `_, `Srinivas Sakhamuri `_) * Add support to specify an identity file for ssh connections (`Srinivas Sakhamuri `_) 0.2.1 (2015-05-02) .................. * fix for python 3.4 (`kokuev `_) 0.2.0 (2015-03-21) .................. * Https will now raise AuthenticationError when appropriate. (`scap1784 `_) * Preliminary python 3 compatibility. (`wdoekes `_) * Additional example. (`wdoekes `_) 0.1.7 (2014-11-16) .................. * Added ignore of "InsecureRequestWarning: Unverified HTTPS request is being made..." warning while using https (requests) backend. 0.1.4 (2013-06-01) .................. * Added logging * Added openssh backend * Tests are reorganized 0.1.3 (2013-05-30) .................. * Added next tests * Bugfixes 0.1.2 (2013-05-27) .................. * Added first tests * Added support for travis and coveralls * Bugfixes 0.1.1 (2013-05-13) .................. * Initial try. .. |master_build_status| image:: https://travis-ci.org/swayf/proxmoxer.png?branch=master :target: https://travis-ci.org/swayf/proxmoxer .. |master_coverage_status| image:: https://coveralls.io/repos/swayf/proxmoxer/badge.png?branch=master :target: https://coveralls.io/r/swayf/proxmoxer .. |develop_build_status| image:: https://travis-ci.org/swayf/proxmoxer.png?branch=develop :target: https://travis-ci.org/swayf/proxmoxer .. |develop_coverage_status| image:: https://coveralls.io/repos/swayf/proxmoxer/badge.png?branch=develop :target: https://coveralls.io/r/swayf/proxmoxer .. |pypi_version| image:: https://img.shields.io/pypi/v/proxmoxer.svg :target: https://pypi.python.org/pypi/proxmoxer .. |pypi_downloads| image:: https://img.shields.io/pypi/dm/proxmoxer.svg :target: https://pypi.python.org/pypi/proxmoxer proxmoxer-1.0.3/proxmoxer/0000755000175000017500000000000013345547757016151 5ustar swayfswayf00000000000000proxmoxer-1.0.3/proxmoxer/__init__.py0000644000175000017500000000021213345546241020240 0ustar swayfswayf00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __version__ = '1.0.3' __licence__ = 'MIT' from .core import * proxmoxer-1.0.3/proxmoxer/backends/0000755000175000017500000000000013345547757017723 5ustar swayfswayf00000000000000proxmoxer-1.0.3/proxmoxer/backends/__init__.py0000644000175000017500000000013713344210403022003 0ustar swayfswayf00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' proxmoxer-1.0.3/proxmoxer/backends/base_ssh.py0000644000175000017500000000450013345546241022046 0ustar swayfswayf00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' from itertools import chain import json import re class Response(object): def __init__(self, content, status_code): self.status_code = status_code self.content = content self.headers = {"content-type": "application/json"} class ProxmoxBaseSSHSession(object): def _exec(self, cmd): raise NotImplementedError() # noinspection PyUnusedLocal def request(self, method, url, data=None, params=None, headers=None): method = method.lower() data = data or {} params = params or {} url = url.strip() cmd = {'post': 'create', 'put': 'set'}.get(method, method) #for 'upload' call some workaround tmp_filename = '' if url.endswith('upload'): #copy file to temporary location on proxmox host tmp_filename, _ = self._exec( "python -c 'import tempfile; import sys; tf = tempfile.NamedTemporaryFile(); sys.stdout.write(tf.name)'") self.upload_file_obj(data['filename'], tmp_filename) data['filename'] = data['filename'].name data['tmpfilename'] = tmp_filename translated_data = ' '.join(["-{0} {1}".format(k, v) for k, v in chain(data.items(), params.items())]) full_cmd = 'pvesh {0}'.format(' '.join(filter(None, (cmd, url, translated_data)))) stdout, stderr = self._exec(full_cmd) match = lambda s: re.match('\d\d\d [a-zA-Z]', s) # sometimes contains extra text like 'trying to acquire lock...OK' status_code = next( (int(s.split()[0]) for s in stderr.splitlines() if match(s)), 500) if stdout: return Response(stdout, status_code) else: return Response(stderr, status_code) def upload_file_obj(self, file_obj, remote_path): raise NotImplementedError() class JsonSimpleSerializer(object): def loads(self, response): try: return json.loads(response.content) except ValueError: return response.content class BaseBackend(object): def get_session(self): return self.session def get_base_url(self): return '' def get_serializer(self): return JsonSimpleSerializer() proxmoxer-1.0.3/proxmoxer/backends/https.py0000644000175000017500000001143713345546241021430 0ustar swayfswayf00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' import json import sys try: import requests urllib3 = requests.packages.urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) from requests.auth import AuthBase from requests.cookies import cookiejar_from_dict except ImportError: import sys sys.stderr.write("Chosen backend requires 'requests' module\n") sys.exit(1) if sys.version_info[0] >= 3: import io def is_file(obj): return isinstance(obj, io.IOBase) else: def is_file(obj): return isinstance(obj, file) class AuthenticationError(Exception): def __init__(self, msg): super(AuthenticationError, self).__init__(msg) self.msg = msg def __str__(self): return self.msg def __repr__(self): return self.__str__() class ProxmoxHTTPAuth(AuthBase): def __init__(self, base_url, username, password, verify_ssl=False): response_data = requests.post(base_url + "/access/ticket", verify=verify_ssl, data={"username": username, "password": password}).json()["data"] if response_data is None: raise AuthenticationError("Couldn't authenticate user: {0} to {1}".format(username, base_url + "/access/ticket")) self.pve_auth_cookie = response_data["ticket"] self.csrf_prevention_token = response_data["CSRFPreventionToken"] def __call__(self, r): r.headers["CSRFPreventionToken"] = self.csrf_prevention_token return r class ProxmoxHTTPTokenAuth(ProxmoxHTTPAuth): """Use existing ticket/token to create a session. Overrides ProxmoxHTTPAuth so that an existing auth cookie and csrf token may be used instead of passing username/password. """ def __init__(self, auth_token, csrf_token): self.pve_auth_cookie = auth_token self.csrf_prevention_token = csrf_token class JsonSerializer(object): content_types = [ "application/json", "application/x-javascript", "text/javascript", "text/x-javascript", "text/x-json" ] def get_accept_types(self): return ", ".join(self.content_types) def loads(self, response): try: return json.loads(response.content.decode('utf-8'))['data'] except (UnicodeDecodeError, ValueError): return response.content class ProxmoxHttpSession(requests.Session): def request(self, method, url, params=None, data=None, headers=None, cookies=None, files=None, auth=None, timeout=None, allow_redirects=True, proxies=None, hooks=None, stream=None, verify=None, cert=None, serializer=None): # take set verify flag from session request does not have this parameter explicitly if verify is None: verify = self.verify #filter out streams files = files or {} data = data or {} for k, v in data.copy().items(): if is_file(v): files[k] = v del data[k] headers = None if not files and serializer: headers = {"content-type": 'application/x-www-form-urlencoded'} return super(ProxmoxHttpSession, self).request(method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert) class Backend(object): def __init__(self, host, user, password, port=8006, verify_ssl=True, mode='json', timeout=5, auth_token=None, csrf_token=None): if ':' in host: host, host_port = host.split(':') port = host_port if host_port.isdigit() else port self.base_url = "https://{0}:{1}/api2/{2}".format(host, port, mode) if auth_token is not None: self.auth = ProxmoxHTTPTokenAuth(auth_token, csrf_token) else: self.auth = ProxmoxHTTPAuth(self.base_url, user, password, verify_ssl) self.verify_ssl = verify_ssl self.mode = mode self.timeout = timeout def get_session(self): session = ProxmoxHttpSession() session.verify = self.verify_ssl session.auth = self.auth session.cookies = cookiejar_from_dict({"PVEAuthCookie": self.auth.pve_auth_cookie}) session.headers['Connection'] = 'keep-alive' session.headers["accept"] = self.get_serializer().get_accept_types() return session def get_base_url(self): return self.base_url def get_serializer(self): assert self.mode == 'json' return JsonSerializer() def get_tokens(self): """Return the in-use auth and csrf tokens.""" return self.auth.pve_auth_cookie, self.auth.csrf_prevention_token proxmoxer-1.0.3/proxmoxer/backends/openssh.py0000644000175000017500000000425713344210403021732 0ustar swayfswayf00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' from proxmoxer.backends.base_ssh import ProxmoxBaseSSHSession, BaseBackend try: import openssh_wrapper except ImportError: import sys sys.stderr.write("Chosen backend requires 'openssh_wrapper' module\n") sys.exit(1) class ProxmoxOpenSSHSession(ProxmoxBaseSSHSession): def __init__(self, host, username, configfile=None, port=22, timeout=5, forward_ssh_agent=False, sudo=False, identity_file=None): self.host = host self.username = username self.configfile = configfile self.port = port self.timeout = timeout self.forward_ssh_agent = forward_ssh_agent self.sudo = sudo self.identity_file = identity_file self.ssh_client = openssh_wrapper.SSHConnection(self.host, login=self.username, port=self.port, timeout=self.timeout, identity_file=self.identity_file) def _exec(self, cmd): if self.sudo: cmd = "sudo " + cmd ret = self.ssh_client.run(cmd, forward_ssh_agent=self.forward_ssh_agent) return ret.stdout, ret.stderr def upload_file_obj(self, file_obj, remote_path): self.ssh_client.scp((file_obj,), target=remote_path) class Backend(BaseBackend): def __init__(self, host, user, configfile=None, port=22, timeout=5, forward_ssh_agent=False, sudo=False, identity_file=None): self.session = ProxmoxOpenSSHSession(host, user, configfile=configfile, port=port, timeout=timeout, forward_ssh_agent=forward_ssh_agent, sudo=sudo, identity_file=identity_file) proxmoxer-1.0.3/proxmoxer/backends/ssh_paramiko.py0000644000175000017500000000503113345546241022737 0ustar swayfswayf00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' import os from proxmoxer.backends.base_ssh import ProxmoxBaseSSHSession, BaseBackend try: import paramiko except ImportError: import sys sys.stderr.write("Chosen backend requires 'paramiko' module\n") sys.exit(1) class ProxmoxParamikoSession(ProxmoxBaseSSHSession): def __init__(self, host, username, password=None, private_key_file=None, port=22, timeout=5, sudo=False): self.host = host self.username = username self.password = password self.private_key_file = private_key_file self.port = port self.timeout = timeout self.sudo = sudo self.ssh_client = self._connect() def _connect(self): ssh_client = paramiko.SSHClient() ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) if self.private_key_file: key_filename = os.path.expanduser(self.private_key_file) else: key_filename = None ssh_client.connect(self.host, username=self.username, allow_agent=(not self.password), look_for_keys=True, key_filename=key_filename, password=self.password, timeout=self.timeout, port=self.port) return ssh_client def _exec(self, cmd): if self.sudo: cmd = 'sudo ' + cmd session = self.ssh_client.get_transport().open_session() session.exec_command(cmd) stdout = session.makefile('rb', -1).read().decode() stderr = session.makefile_stderr('rb', -1).read().decode() return stdout, stderr def upload_file_obj(self, file_obj, remote_path): sftp = self.ssh_client.open_sftp() sftp.putfo(file_obj, remote_path) sftp.close() class Backend(BaseBackend): def __init__(self, host, user, password=None, private_key_file=None, port=22, timeout=5, sudo=False): self.session = ProxmoxParamikoSession(host, user, password=password, private_key_file=private_key_file, port=port, timeout=timeout, sudo=sudo) proxmoxer-1.0.3/proxmoxer/core.py0000644000175000017500000000715613344251117017441 0ustar swayfswayf00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' import importlib import posixpath import logging # Python 3 compatibility: try: import httplib except ImportError: # py3 from http import client as httplib try: import urlparse except ImportError: # py3 from urllib import parse as urlparse try: basestring except NameError: # py3 basestring = (bytes, str) logger = logging.getLogger(__name__) class ProxmoxResourceBase(object): def __getattr__(self, item): if item.startswith("_"): raise AttributeError(item) kwargs = self._store.copy() kwargs['base_url'] = self.url_join(self._store["base_url"], item) return ProxmoxResource(**kwargs) def url_join(self, base, *args): scheme, netloc, path, query, fragment = urlparse.urlsplit(base) path = path if len(path) else "/" path = posixpath.join(path, *[('%s' % x) for x in args]) return urlparse.urlunsplit([scheme, netloc, path, query, fragment]) class ResourceException(Exception): pass class ProxmoxResource(ProxmoxResourceBase): def __init__(self, **kwargs): self._store = kwargs def __call__(self, resource_id=None): if not resource_id: return self if isinstance(resource_id, basestring): resource_id = resource_id.split("/") elif not isinstance(resource_id, (tuple, list)): resource_id = [str(resource_id)] kwargs = self._store.copy() if resource_id is not None: kwargs["base_url"] = self.url_join(self._store["base_url"], *resource_id) return self.__class__(**kwargs) def _request(self, method, data=None, params=None): url = self._store["base_url"] if data: logger.info('%s %s %r', method, url, data) else: logger.info('%s %s', method, url) resp = self._store["session"].request(method, url, data=data or None, params=params) logger.debug('Status code: %s, output: %s', resp.status_code, resp.content) if resp.status_code >= 400: raise ResourceException("{0} {1}: {2}".format(resp.status_code, httplib.responses[resp.status_code], resp.content)) elif 200 <= resp.status_code <= 299: return self._store["serializer"].loads(resp) def get(self, *args, **params): return self(args)._request("GET", params=params) def post(self, *args, **data): return self(args)._request("POST", data=data) def put(self, *args, **data): return self(args)._request("PUT", data=data) def delete(self, *args, **params): return self(args)._request("DELETE", params=params) def create(self, *args, **data): return self.post(*args, **data) def set(self, *args, **data): return self.put(*args, **data) class ProxmoxAPI(ProxmoxResourceBase): def __init__(self, host, backend='https', **kwargs): #load backend module self._backend = importlib.import_module('.backends.%s' % backend, 'proxmoxer').Backend(host, **kwargs) self._backend_name = backend self._store = { "base_url": self._backend.get_base_url(), "session": self._backend.get_session(), "serializer": self._backend.get_serializer(), } def get_tokens(self): """Return the auth and csrf tokens. Returns (None, None) if the backend is not https. """ if self._backend_name != 'https': return None, None return self._backend.get_tokens() proxmoxer-1.0.3/proxmoxer.egg-info/0000755000175000017500000000000013345547757017643 5ustar swayfswayf00000000000000proxmoxer-1.0.3/proxmoxer.egg-info/PKG-INFO0000644000175000017500000002673213345547756020751 0ustar swayfswayf00000000000000Metadata-Version: 1.1 Name: proxmoxer Version: 1.0.3 Summary: Python Wrapper for the Proxmox 2.x API (HTTP and SSH) Home-page: https://github.com/swayf/proxmoxer Author: Oleg Butovich Author-email: obutovich@gmail.com License: MIT Download-URL: http://pypi.python.org/pypi/proxmoxer Description: ========================================= Proxmoxer: A wrapper for Proxmox REST API ========================================= master branch: |master_build_status| |master_coverage_status| |pypi_version| |pypi_downloads| develop branch: |develop_build_status| |develop_coverage_status| What does it do and what's different? ------------------------------------- Proxmoxer is a wrapper around the `Proxmox REST API v2 `_. It was inspired by slumber, but it dedicated only to Proxmox. It allows to use not only REST API over HTTPS, but the same api over ssh and pvesh utility. Like `Proxmoxia `_ it dynamically creates attributes which responds to the attributes you've attempted to reach. Installation ------------ :: pip install proxmoxer For 'https' backend install requests :: pip install requests For 'ssh_paramiko' backend install paramiko :: pip install paramiko Short usage information ----------------------- The first thing to do is import the proxmoxer library and create ProxmoxAPI instance. :: from proxmoxer import ProxmoxAPI proxmox = ProxmoxAPI('proxmox_host', user='admin@pam', password='secret_word', verify_ssl=False) This will connect by default through the 'https' backend. It is possible to use already prepared public/private key authentication. It is possible to use ssh-agent also. :: from proxmoxer import ProxmoxAPI proxmox = ProxmoxAPI('proxmox_host', user='proxmox_admin', backend='ssh_paramiko') **Please note, https-backend needs 'requests' library, ssh_paramiko-backend needs 'paramiko' library, openssh-backend needs 'openssh_wrapper' library installed.** Queries are exposed via the access methods **get**, **post**, **put** and **delete**. For convenience added two synonyms: **create** for **post**, and **set** for **put**. :: for node in proxmox.nodes.get(): for vm in proxmox.nodes(node['node']).openvz.get(): print "{0}. {1} => {2}" .format(vm['vmid'], vm['name'], vm['status']) >>> 141. puppet-2.london.baseblack.com => running 101. munki.london.baseblack.com => running 102. redmine.london.baseblack.com => running 140. dns-1.london.baseblack.com => running 126. ns-3.london.baseblack.com => running 113. rabbitmq.london.baseblack.com => running same code can be rewritten in the next way:: for node in proxmox.get('nodes'): for vm in proxmox.get('nodes/%s/openvz' % node['node']): print "%s. %s => %s" % (vm['vmid'], vm['name'], vm['status']) for example next lines do the same job:: proxmox.nodes(node['node']).openvz.get() proxmox.nodes(node['node']).get('openvz') proxmox.get('nodes/%s/openvz' % node['node']) proxmox.get('nodes', node['node'], 'openvz') Some more examples:: for vm in proxmox.cluster.resources.get(type='vm'): print("{0}. {1} => {2}" .format(vm['vmid'], vm['name'], vm['status'])) :: node = proxmox.nodes('proxmox_node') pprint(node.storage('local').content.get()) or the with same results :: node = proxmox.nodes.proxmox_node() pprint(node.storage.local.content.get()) Example of creation of lxc container:: node = proxmox.nodes('proxmox_node') node.lxc.create(vmid=202, ostemplate='local:vztmpl/debian-9.0-standard_20170530_amd64.tar.gz', hostname='debian-stretch', storage='local', memory=512, swap=512, cores=1, password='secret', net0='name=eth0,bridge=vmbr0,ip=192.168.22.1/20,gw=192.168.16.1') Example of creating the same lxc container with parameters in a dictonary. This approach allows to add ``ssh-public-keys`` without getting syntax errors. :: newcontainer = { 'vmid': 202, 'ostemplate': 'local:vztmpl/debian-9.0-standard_20170530_amd64.tar.gz', 'hostname': 'debian-stretch', 'storage': 'local', 'memory': 512, 'swap': 512, 'cores': 1, 'password': 'secret', 'net0': 'name=eth0,bridge=vmbr0,ip=192.168.22.1/20,gw=192.168.16.1' } node = proxmox.nodes('proxmox_node') node.lxc.create(**newcontainer) Example of template upload:: local_storage = proxmox.nodes('proxmox_node').storage('local') local_storage.upload.create(content='vztmpl', filename=open(os.path.expanduser('~/templates/debian-6-my-core_1.0-1_i386.tar.gz')))) Example of rrd download:: response = proxmox.nodes('proxmox').rrd.get(ds='cpu', timeframe='hour') with open('cpu.png', 'wb') as f: f.write(response['image'].encode('raw_unicode_escape')) Example of usage of logging:: # now logging debug info will be written to stdout logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s:%(name)s: %(message)s') Roadmap ------- * write tests * support other actual python versions * add optional validation of requests * add some shortcuts for convenience History ------- 1.0.3 (1018-09-10) .................. * Improvement: Added option to specify port in hostname parameter (``_) * Improvement: Added stderr to the Response content (`Jérôme Schneider `_) * Bugfix: Paramiko python3: stdout and stderr must be a str not bytes (`Jérôme Schneider `_) * New lxc example in docu (`Geert Stappers `_) 1.0.2 (2017-12-02) .................. * Tarball repackaged with tests 1.0.1 (2017-12-02) .................. * LICENSE file now included in tarball * Added verify_ssl parameter to ProxmoxHTTPAuth (`Walter Doekes `_) 1.0.0 (2017-11-12) .................. * Update Proxmoxer readme (`Emmanuel Kasper `_) * Display the reason of API calls errors (`Emmanuel Kasper `_, `kantsdog `_) * Filter for ssh response code (`Chris Plock `_) 0.2.5 (2017-02-12) .................. * Adding sudo to execute CLI with paramiko ssh backend (`Jason Meridth `_) * Proxmoxer/backends/ssh_paramiko: improve file upload (`Jérôme Schneider `_) 0.2.4 (2016-05-02) .................. * Removed newline in tmp_filename string (`Jérôme Schneider `_) * Fix to avoid module reloading (`jklang `_) 0.2.3 (2016-01-20) .................. * Minor typo fix (`Srinivas Sakhamuri `_) 0.2.2 (2016-01-19) .................. * Adding sudo to execute pvesh CLI in openssh backend (`Wei Tie `_, `Srinivas Sakhamuri `_) * Add support to specify an identity file for ssh connections (`Srinivas Sakhamuri `_) 0.2.1 (2015-05-02) .................. * fix for python 3.4 (`kokuev `_) 0.2.0 (2015-03-21) .................. * Https will now raise AuthenticationError when appropriate. (`scap1784 `_) * Preliminary python 3 compatibility. (`wdoekes `_) * Additional example. (`wdoekes `_) 0.1.7 (2014-11-16) .................. * Added ignore of "InsecureRequestWarning: Unverified HTTPS request is being made..." warning while using https (requests) backend. 0.1.4 (2013-06-01) .................. * Added logging * Added openssh backend * Tests are reorganized 0.1.3 (2013-05-30) .................. * Added next tests * Bugfixes 0.1.2 (2013-05-27) .................. * Added first tests * Added support for travis and coveralls * Bugfixes 0.1.1 (2013-05-13) .................. * Initial try. .. |master_build_status| image:: https://travis-ci.org/swayf/proxmoxer.png?branch=master :target: https://travis-ci.org/swayf/proxmoxer .. |master_coverage_status| image:: https://coveralls.io/repos/swayf/proxmoxer/badge.png?branch=master :target: https://coveralls.io/r/swayf/proxmoxer .. |develop_build_status| image:: https://travis-ci.org/swayf/proxmoxer.png?branch=develop :target: https://travis-ci.org/swayf/proxmoxer .. |develop_coverage_status| image:: https://coveralls.io/repos/swayf/proxmoxer/badge.png?branch=develop :target: https://coveralls.io/r/swayf/proxmoxer .. |pypi_version| image:: https://img.shields.io/pypi/v/proxmoxer.svg :target: https://pypi.python.org/pypi/proxmoxer .. |pypi_downloads| image:: https://img.shields.io/pypi/dm/proxmoxer.svg :target: https://pypi.python.org/pypi/proxmoxer Keywords: proxmox,api Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development :: Libraries :: Python Modules proxmoxer-1.0.3/proxmoxer.egg-info/SOURCES.txt0000644000175000017500000000100713345547757021525 0ustar swayfswayf00000000000000LICENSE.txt MANIFEST.in README.rst README.txt setup.py proxmoxer/__init__.py proxmoxer/core.py proxmoxer.egg-info/PKG-INFO proxmoxer.egg-info/SOURCES.txt proxmoxer.egg-info/dependency_links.txt proxmoxer.egg-info/top_level.txt proxmoxer/backends/__init__.py proxmoxer/backends/base_ssh.py proxmoxer/backends/https.py proxmoxer/backends/openssh.py proxmoxer/backends/ssh_paramiko.py tests/__init__.py tests/https_tests.py tests/openssh_tests.py tests/paramiko_tests.py tests/base/__init__.py tests/base/base_ssh_suite.pyproxmoxer-1.0.3/proxmoxer.egg-info/dependency_links.txt0000644000175000017500000000000113345547756023710 0ustar swayfswayf00000000000000 proxmoxer-1.0.3/proxmoxer.egg-info/top_level.txt0000644000175000017500000000002013345547756022364 0ustar swayfswayf00000000000000proxmoxer tests proxmoxer-1.0.3/setup.cfg0000644000175000017500000000004613345547757015727 0ustar swayfswayf00000000000000[egg_info] tag_build = tag_date = 0 proxmoxer-1.0.3/setup.py0000644000175000017500000000335013344210403015567 0ustar swayfswayf00000000000000#!/usr/bin/env python import codecs import re import sys import proxmoxer import os from setuptools import setup if not os.path.exists('README.txt') and 'sdist' in sys.argv: with codecs.open('README.rst', encoding='utf8') as f: rst = f.read() code_block = '(:\n\n)?\.\. code-block::.*' rst = re.sub(code_block, '::', rst) with codecs.open('README.txt', encoding='utf8', mode='wb') as f: f.write(rst) try: readme = 'README.txt' if os.path.exists('README.txt') else 'README.rst' long_description = codecs.open(readme, encoding='utf-8').read() except: long_description = 'Could not read README.txt' setup( name = 'proxmoxer', version = proxmoxer.__version__, description = 'Python Wrapper for the Proxmox 2.x API (HTTP and SSH)', author = 'Oleg Butovich', author_email = 'obutovich@gmail.com', license = "MIT", url = 'https://github.com/swayf/proxmoxer', download_url = 'http://pypi.python.org/pypi/proxmoxer', keywords = ['proxmox', 'api'], packages=['proxmoxer', 'proxmoxer.backends', 'tests', 'tests.base'], classifiers = [ #http://pypi.python.org/pypi?%3Aaction=list_classifiers "Development Status :: 4 - Beta", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Topic :: Software Development :: Libraries :: Python Modules", ], long_description = long_description ) proxmoxer-1.0.3/tests/0000755000175000017500000000000013345547757015250 5ustar swayfswayf00000000000000proxmoxer-1.0.3/tests/__init__.py0000644000175000017500000000014013344210403017322 0ustar swayfswayf00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' proxmoxer-1.0.3/tests/base/0000755000175000017500000000000013345547757016162 5ustar swayfswayf00000000000000proxmoxer-1.0.3/tests/base/__init__.py0000644000175000017500000000014013344210403020234 0ustar swayfswayf00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' proxmoxer-1.0.3/tests/base/base_ssh_suite.py0000644000175000017500000001421513345546241021522 0ustar swayfswayf00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' from itertools import islice try: import itertools.izip as zip except ImportError: pass from nose.tools import eq_, ok_, raises from proxmoxer.core import ResourceException class BaseSSHSuite(object): proxmox = None client = None session = None def __init__(self, sudo=False): self.sudo = sudo def _split_cmd(self, cmd): splitted = cmd.split() if not self.sudo: eq_(splitted[0], 'pvesh') else: eq_(splitted[0], 'sudo') eq_(splitted[1], 'pvesh') splitted.pop(0) options_set = set((' '.join((k, v)) for k, v in zip(islice(splitted, 3, None, 2), islice(splitted, 4, None, 2)))) return ' '.join(splitted[1:3]), options_set def _get_called_cmd(self): raise NotImplementedError() def _set_stdout(self, stdout): raise NotImplementedError() def _set_stderr(self, stderr): raise NotImplementedError() def test_get(self): self._set_stdout(""" [ { "subdir" : "status" }, { "subdir" : "content" }, { "subdir" : "upload" }, { "subdir" : "rrd" }, { "subdir" : "rrddata" } ]""") result = self.proxmox.nodes('proxmox').storage('local').get() eq_(self._get_called_cmd(), self._called_cmd('pvesh get /nodes/proxmox/storage/local')) eq_(result[0]['subdir'], 'status') eq_(result[1]['subdir'], 'content') eq_(result[2]['subdir'], 'upload') eq_(result[3]['subdir'], 'rrd') eq_(result[4]['subdir'], 'rrddata') def test_delete(self): self.proxmox.nodes('proxmox').openvz(100).delete() eq_(self._get_called_cmd(), self._called_cmd('pvesh delete /nodes/proxmox/openvz/100')) self._set_stderr("200 OK") self.proxmox.nodes('proxmox').openvz('101').delete() eq_(self._get_called_cmd(), self._called_cmd('pvesh delete /nodes/proxmox/openvz/101')) self._set_stderr("200 OK") self.proxmox.nodes('proxmox').openvz.delete('102') eq_(self._get_called_cmd(), self._called_cmd('pvesh delete /nodes/proxmox/openvz/102')) def test_post(self): self._set_stderr("200 OK") node = self.proxmox.nodes('proxmox') node.openvz.create(vmid=800, ostemplate='local:vztmpl/debian-6-turnkey-core_12.0-1_i386.tar.gz', hostname='test', storage='local', memory=512, swap=512, cpus=1, disk=4, password='secret', ip_address='10.0.100.222') cmd, options = self._split_cmd(self._get_called_cmd()) eq_(cmd, 'create /nodes/proxmox/openvz') ok_('-cpus 1' in options) ok_('-disk 4' in options) ok_('-hostname test' in options) ok_('-ip_address 10.0.100.222' in options) ok_('-memory 512' in options) ok_('-ostemplate local:vztmpl/debian-6-turnkey-core_12.0-1_i386.tar.gz' in options) ok_('-password secret' in options) ok_('-storage local' in options) ok_('-swap 512' in options) ok_('-vmid 800' in options) self._set_stderr("200 OK") node = self.proxmox.nodes('proxmox1') node.openvz.post(vmid=900, ostemplate='local:vztmpl/debian-7-turnkey-core_12.0-1_i386.tar.gz', hostname='test1', storage='local1', memory=1024, swap=1024, cpus=2, disk=8, password='secret1', ip_address='10.0.100.111') cmd, options = self._split_cmd(self._get_called_cmd()) eq_(cmd, 'create /nodes/proxmox1/openvz') ok_('-cpus 2' in options) ok_('-disk 8' in options) ok_('-hostname test1' in options) ok_('-ip_address 10.0.100.111' in options) ok_('-memory 1024' in options) ok_('-ostemplate local:vztmpl/debian-7-turnkey-core_12.0-1_i386.tar.gz' in options) ok_('-password secret1' in options) ok_('-storage local1' in options) ok_('-swap 1024' in options) ok_('-vmid 900' in options) def test_put(self): self._set_stderr("200 OK") node = self.proxmox.nodes('proxmox') node.openvz(101).config.set(cpus=4, memory=1024, ip_address='10.0.100.100', onboot=True) cmd, options = self._split_cmd(self._get_called_cmd()) eq_(cmd, 'set /nodes/proxmox/openvz/101/config') ok_('-memory 1024' in options) ok_('-ip_address 10.0.100.100' in options) ok_('-onboot True' in options) ok_('-cpus 4' in options) self._set_stderr("200 OK") node = self.proxmox.nodes('proxmox1') node.openvz('102').config.put(cpus=2, memory=512, ip_address='10.0.100.200', onboot=False) cmd, options = self._split_cmd(self._get_called_cmd()) eq_(cmd, 'set /nodes/proxmox1/openvz/102/config') ok_('-memory 512' in options) ok_('-ip_address 10.0.100.200' in options) ok_('-onboot False' in options) ok_('-cpus 2' in options) @raises(ResourceException) def test_error(self): self._set_stderr("500 whoops") self.proxmox.nodes('proxmox').get() def test_no_error_with_extra_output(self): self._set_stderr("Extra output\n200 OK") self.proxmox.nodes('proxmox').get() @raises(ResourceException) def test_error_with_extra_output(self): self._set_stderr("Extra output\n500 whoops") self.proxmox.nodes('proxmox').get() def _called_cmd(self, cmd): called_cmd = cmd if self.sudo: called_cmd = 'sudo ' + cmd return called_cmd proxmoxer-1.0.3/tests/https_tests.py0000644000175000017500000001457113345546241020201 0ustar swayfswayf00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' from mock import patch, MagicMock from nose.tools import eq_, ok_ from proxmoxer import ProxmoxAPI @patch('requests.sessions.Session') def test_https_connection(req_session): response = {'ticket': 'ticket', 'CSRFPreventionToken': 'CSRFPreventionToken'} req_session.request.return_value = response ProxmoxAPI('proxmox', user='root@pam', password='secret', port=123, verify_ssl=False) call = req_session.return_value.request.call_args[1] eq_(call['url'], 'https://proxmox:123/api2/json/access/ticket') eq_(call['data'], {'username': 'root@pam', 'password': 'secret'}) eq_(call['method'], 'post') eq_(call['verify'], False) @patch('requests.sessions.Session') def test_https_connection_wth_port_in_host(req_session): response = {'ticket': 'ticket', 'CSRFPreventionToken': 'CSRFPreventionToken'} req_session.request.return_value = response ProxmoxAPI('proxmox:123', user='root@pam', password='secret', port=124, verify_ssl=False) call = req_session.return_value.request.call_args[1] eq_(call['url'], 'https://proxmox:123/api2/json/access/ticket') eq_(call['data'], {'username': 'root@pam', 'password': 'secret'}) eq_(call['method'], 'post') eq_(call['verify'], False) @patch('requests.sessions.Session') def test_https_connection_wth_bad_port_in_host(req_session): response = {'ticket': 'ticket', 'CSRFPreventionToken': 'CSRFPreventionToken'} req_session.request.return_value = response ProxmoxAPI('proxmox:notaport', user='root@pam', password='secret', port=124, verify_ssl=False) call = req_session.return_value.request.call_args[1] eq_(call['url'], 'https://proxmox:124/api2/json/access/ticket') eq_(call['data'], {'username': 'root@pam', 'password': 'secret'}) eq_(call['method'], 'post') eq_(call['verify'], False) class TestSuite(): proxmox = None serializer = None session = None # noinspection PyMethodOverriding @patch('requests.sessions.Session') def setUp(self, session): response = {'ticket': 'ticket', 'CSRFPreventionToken': 'CSRFPreventionToken'} session.request.return_value = response self.proxmox = ProxmoxAPI('proxmox', user='root@pam', password='secret', port=123, verify_ssl=False) self.serializer = MagicMock() self.session = MagicMock() self.session.request.return_value.status_code = 200 self.proxmox._store['session'] = self.session self.proxmox._store['serializer'] = self.serializer def test_get(self): self.proxmox.nodes('proxmox').storage('local').get() eq_(self.session.request.call_args[0], ('GET', 'https://proxmox:123/api2/json/nodes/proxmox/storage/local')) def test_delete(self): self.proxmox.nodes('proxmox').openvz(100).delete() eq_(self.session.request.call_args[0], ('DELETE', 'https://proxmox:123/api2/json/nodes/proxmox/openvz/100')) self.proxmox.nodes('proxmox').openvz('101').delete() eq_(self.session.request.call_args[0], ('DELETE', 'https://proxmox:123/api2/json/nodes/proxmox/openvz/101')) def test_post(self): node = self.proxmox.nodes('proxmox') node.openvz.create(vmid=800, ostemplate='local:vztmpl/debian-6-turnkey-core_12.0-1_i386.tar.gz', hostname='test', storage='local', memory=512, swap=512, cpus=1, disk=4, password='secret', ip_address='10.0.100.222') eq_(self.session.request.call_args[0], ('POST', 'https://proxmox:123/api2/json/nodes/proxmox/openvz')) ok_('data' in self.session.request.call_args[1]) data = self.session.request.call_args[1]['data'] eq_(data['cpus'], 1) eq_(data['disk'], 4) eq_(data['hostname'], 'test') eq_(data['ip_address'], '10.0.100.222') eq_(data['memory'], 512) eq_(data['ostemplate'], 'local:vztmpl/debian-6-turnkey-core_12.0-1_i386.tar.gz') eq_(data['password'], 'secret') eq_(data['storage'], 'local') eq_(data['swap'], 512) eq_(data['vmid'], 800) node = self.proxmox.nodes('proxmox1') node.openvz.post(vmid=900, ostemplate='local:vztmpl/debian-7-turnkey-core_12.0-1_i386.tar.gz', hostname='test1', storage='local1', memory=1024, swap=1024, cpus=2, disk=8, password='secret1', ip_address='10.0.100.111') eq_(self.session.request.call_args[0], ('POST', 'https://proxmox:123/api2/json/nodes/proxmox1/openvz')) ok_('data' in self.session.request.call_args[1]) data = self.session.request.call_args[1]['data'] eq_(data['cpus'], 2) eq_(data['disk'], 8) eq_(data['hostname'], 'test1') eq_(data['ip_address'], '10.0.100.111') eq_(data['memory'], 1024) eq_(data['ostemplate'], 'local:vztmpl/debian-7-turnkey-core_12.0-1_i386.tar.gz') eq_(data['password'], 'secret1') eq_(data['storage'], 'local1') eq_(data['swap'], 1024) eq_(data['vmid'], 900) def test_put(self): node = self.proxmox.nodes('proxmox') node.openvz(101).config.set(cpus=4, memory=1024, ip_address='10.0.100.100', onboot=True) eq_(self.session.request.call_args[0], ('PUT', 'https://proxmox:123/api2/json/nodes/proxmox/openvz/101/config')) data = self.session.request.call_args[1]['data'] eq_(data['cpus'], 4) eq_(data['memory'], 1024) eq_(data['ip_address'], '10.0.100.100') eq_(data['onboot'], True) node = self.proxmox.nodes('proxmox1') node.openvz(102).config.put(cpus=2, memory=512, ip_address='10.0.100.200', onboot=False) eq_(self.session.request.call_args[0], ('PUT', 'https://proxmox:123/api2/json/nodes/proxmox1/openvz/102/config')) data = self.session.request.call_args[1]['data'] eq_(data['cpus'], 2) eq_(data['memory'], 512) eq_(data['ip_address'], '10.0.100.200') eq_(data['onboot'], False) proxmoxer-1.0.3/tests/openssh_tests.py0000644000175000017500000000153313344210403020473 0ustar swayfswayf00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' from mock import patch from proxmoxer import ProxmoxAPI from tests.base.base_ssh_suite import BaseSSHSuite class TestOpenSSHSuite(BaseSSHSuite): proxmox = None client = None # noinspection PyMethodOverriding @patch('openssh_wrapper.SSHConnection') def setUp(self, _): self.proxmox = ProxmoxAPI('proxmox', user='root', backend='openssh', port=123) self.client = self.proxmox._store['session'].ssh_client self._set_stderr('200 OK') self._set_stdout('') def _get_called_cmd(self): return self.client.run.call_args[0][0] def _set_stdout(self, stdout): self.client.run.return_value.stdout = stdout def _set_stderr(self, stderr): self.client.run.return_value.stderr = stderr proxmoxer-1.0.3/tests/paramiko_tests.py0000644000175000017500000000477313345546241020645 0ustar swayfswayf00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' import io from mock import patch from nose.tools import eq_ from proxmoxer import ProxmoxAPI from .base.base_ssh_suite import BaseSSHSuite @patch('paramiko.SSHClient') def test_paramiko_connection(_): proxmox = ProxmoxAPI('proxmox', user='root', backend='ssh_paramiko', port=123) session = proxmox._store['session'] eq_(session.ssh_client.connect.call_args[0], ('proxmox',)) eq_(session.ssh_client.connect.call_args[1], {'username': 'root', 'allow_agent': True, 'key_filename': None, 'look_for_keys': True, 'timeout': 5, 'password': None, 'port': 123}) class TestParamikoSuite(BaseSSHSuite): # noinspection PyMethodOverriding @patch('paramiko.SSHClient') def setUp(self, _): self.proxmox = ProxmoxAPI('proxmox', user='root', backend='ssh_paramiko', port=123) self.client = self.proxmox._store['session'].ssh_client self.session = self.client.get_transport().open_session() self._set_stderr('200 OK') self._set_stdout('') def _get_called_cmd(self): return self.session.exec_command.call_args[0][0] def _set_stdout(self, stdout): self.session.makefile.return_value = io.BytesIO(stdout.encode('utf-8')) def _set_stderr(self, stderr): self.session.makefile_stderr.return_value = io.BytesIO(stderr.encode('utf-8')) class TestParamikoSuiteWithSudo(BaseSSHSuite): # noinspection PyMethodOverriding @patch('paramiko.SSHClient') def setUp(self, _): super(TestParamikoSuiteWithSudo, self).__init__(sudo=True) self.proxmox = ProxmoxAPI('proxmox', user='root', backend='ssh_paramiko', port=123, sudo=True) self.client = self.proxmox._store['session'].ssh_client self.session = self.client.get_transport().open_session() self._set_stderr('200 OK') self._set_stdout('') def _get_called_cmd(self): return self.session.exec_command.call_args[0][0] def _set_stdout(self, stdout): self.session.makefile.return_value = io.BytesIO(stdout.encode('utf-8')) def _set_stderr(self, stderr): self.session.makefile_stderr.return_value = io.BytesIO(stderr.encode('utf-8'))