proxmoxer-1.0.2/0000755000076500000240000000000013210541045013561 5ustar Olegstaff00000000000000proxmoxer-1.0.2/LICENSE.txt0000644000076500000240000000206113210537530015407 0ustar Olegstaff00000000000000The 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.2/MANIFEST.in0000644000076500000240000000014213210537530015320 0ustar Olegstaff00000000000000include LICENSE.txt include README.txt include README.rst global-exclude *.orig *.pyc *.log *.swp proxmoxer-1.0.2/PKG-INFO0000644000076500000240000002440413210541045014662 0ustar Olegstaff00000000000000Metadata-Version: 1.1 Name: proxmoxer Version: 1.0.2 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 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.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.2/proxmoxer/0000755000076500000240000000000013210541045015624 5ustar Olegstaff00000000000000proxmoxer-1.0.2/proxmoxer/__init__.py0000644000076500000240000000021213210540746017737 0ustar Olegstaff00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __version__ = '1.0.2' __licence__ = 'MIT' from .core import * proxmoxer-1.0.2/proxmoxer/backends/0000755000076500000240000000000013210541045017376 5ustar Olegstaff00000000000000proxmoxer-1.0.2/proxmoxer/backends/__init__.py0000644000076500000240000000013713050131772021514 0ustar Olegstaff00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' proxmoxer-1.0.2/proxmoxer/backends/base_ssh.py0000644000076500000240000000435213202111642021540 0ustar Olegstaff00000000000000__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) return Response(stdout, 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.2/proxmoxer/backends/https.py0000644000076500000240000001100013210537530021106 0ustar Olegstaff00000000000000__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): #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): 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.2/proxmoxer/backends/openssh.py0000644000076500000240000000425713050131772021443 0ustar Olegstaff00000000000000__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.2/proxmoxer/backends/ssh_paramiko.py0000644000076500000240000000501313050131772022433 0ustar Olegstaff00000000000000__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 = ''.join(session.makefile('rb', -1)) stderr = ''.join(session.makefile_stderr('rb', -1)) 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.2/proxmoxer/core.py0000644000076500000240000000715613202112751017136 0ustar Olegstaff00000000000000__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.2/proxmoxer.egg-info/0000755000076500000240000000000013210541045017316 5ustar Olegstaff00000000000000proxmoxer-1.0.2/proxmoxer.egg-info/dependency_links.txt0000644000076500000240000000000113210541045023364 0ustar Olegstaff00000000000000 proxmoxer-1.0.2/proxmoxer.egg-info/PKG-INFO0000644000076500000240000002440413210541045020417 0ustar Olegstaff00000000000000Metadata-Version: 1.1 Name: proxmoxer Version: 1.0.2 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 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.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.2/proxmoxer.egg-info/SOURCES.txt0000644000076500000240000000100713210541045021200 0ustar Olegstaff00000000000000LICENSE.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.2/proxmoxer.egg-info/top_level.txt0000644000076500000240000000002013210541045022040 0ustar Olegstaff00000000000000proxmoxer tests proxmoxer-1.0.2/README.rst0000644000076500000240000001745613210540746015274 0ustar Olegstaff00000000000000========================================= 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 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.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.2/README.txt0000644000076500000240000001676313210541045015274 0ustar Olegstaff00000000000000========================================= 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 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.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.2/setup.cfg0000644000076500000240000000007313210541045015402 0ustar Olegstaff00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 proxmoxer-1.0.2/setup.py0000644000076500000240000000335013210540746015303 0ustar Olegstaff00000000000000#!/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.2/tests/0000755000076500000240000000000013210541045014723 5ustar Olegstaff00000000000000proxmoxer-1.0.2/tests/__init__.py0000644000076500000240000000014013050131772017033 0ustar Olegstaff00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' proxmoxer-1.0.2/tests/base/0000755000076500000240000000000013210541045015635 5ustar Olegstaff00000000000000proxmoxer-1.0.2/tests/base/__init__.py0000644000076500000240000000014013050131772017745 0ustar Olegstaff00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' proxmoxer-1.0.2/tests/base/base_ssh_suite.py0000644000076500000240000001367313202111642021216 0ustar Olegstaff00000000000000__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.proxmox.nodes('proxmox').openvz('101').delete() eq_(self._get_called_cmd(), self._called_cmd('pvesh delete /nodes/proxmox/openvz/101')) 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): 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) 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): 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) 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.2/tests/https_tests.py0000644000076500000240000001231613050131772017670 0ustar Olegstaff00000000000000__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) 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.2/tests/openssh_tests.py0000644000076500000240000000153313050131772020204 0ustar Olegstaff00000000000000__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.2/tests/paramiko_tests.py0000644000076500000240000000461113050131772020330 0ustar Olegstaff00000000000000__author__ = 'Oleg Butovich' __copyright__ = '(c) Oleg Butovich 2013-2017' __licence__ = 'MIT' 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 = [stdout] def _set_stderr(self, stderr): self.session.makefile_stderr.return_value = [stderr] 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 = [stdout] def _set_stderr(self, stderr): self.session.makefile_stderr.return_value = [stderr]