From 3ef244f5cfd6342ade3f09829b35a62eedc26277 Mon Sep 17 00:00:00 2001 From: mihail Date: Sat, 22 Sep 2018 18:39:52 +0300 Subject: [PATCH 1/6] Don't remember why we had to read().decode() it. --- proxmoxer/backends/ssh_paramiko.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proxmoxer/backends/ssh_paramiko.py b/proxmoxer/backends/ssh_paramiko.py index 482da55..f9cbe11 100644 --- a/proxmoxer/backends/ssh_paramiko.py +++ b/proxmoxer/backends/ssh_paramiko.py @@ -56,8 +56,8 @@ def _exec(self, cmd): 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() + stdout = ''.join(session.makefile('r', -1)) + stderr = ''.join(session.makefile_stderr('r', -1)) return stdout, stderr def upload_file_obj(self, file_obj, remote_path): From 15e35721a11bd3d8db7a03afbd0706b8e03b10ba Mon Sep 17 00:00:00 2001 From: mihail Date: Sat, 22 Sep 2018 18:50:51 +0300 Subject: [PATCH 2/6] resource exception has all the fields separately --- proxmoxer/core.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/proxmoxer/core.py b/proxmoxer/core.py index 43c3a06..b2c9b49 100644 --- a/proxmoxer/core.py +++ b/proxmoxer/core.py @@ -42,7 +42,16 @@ def url_join(self, base, *args): class ResourceException(Exception): - pass + def __init__(self, status_code, status_message, content, reason): + self.status_code = status_code + self.status_message = status_message + self.content = content + self.reason = reason.strip() + super(ResourceException, self).__init__(self.__repr__()) + + def __repr__(self): + return "{0} {1}: {2}, Content:{3}".format( + self.status_code, self.status_message, self.reason, self.content) class ProxmoxResource(ProxmoxResourceBase): @@ -75,8 +84,7 @@ def _request(self, method, data=None, params=None): 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)) + raise ResourceException(resp.status_code, httplib.responses[resp.status_code], resp.content, resp.reason) elif 200 <= resp.status_code <= 299: return self._store["serializer"].loads(resp) From d2a81b5adbb554debcdd55e6b2c648afaadc2ca6 Mon Sep 17 00:00:00 2001 From: mihail Date: Sat, 22 Sep 2018 18:59:51 +0300 Subject: [PATCH 3/6] I don't remember why we did this --- proxmoxer/backends/base_ssh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxmoxer/backends/base_ssh.py b/proxmoxer/backends/base_ssh.py index da624d9..683c846 100644 --- a/proxmoxer/backends/base_ssh.py +++ b/proxmoxer/backends/base_ssh.py @@ -40,7 +40,7 @@ def request(self, method, url, data=None, params=None, headers=None): 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())]) + translated_data = ' '.join(["-{0} {1}".format(k, v if not isinstance(v, str) or " " not in v else '"{}"'.format(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) From b56e25b5ee9d549f46f93e4df5f17c9267200e41 Mon Sep 17 00:00:00 2001 From: mihail Date: Sat, 22 Sep 2018 19:04:00 +0300 Subject: [PATCH 4/6] error code being returned --- proxmoxer/backends/base_ssh.py | 10 ++++------ proxmoxer/backends/openssh.py | 2 +- proxmoxer/backends/ssh_paramiko.py | 3 ++- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/proxmoxer/backends/base_ssh.py b/proxmoxer/backends/base_ssh.py index 683c846..94207e3 100644 --- a/proxmoxer/backends/base_ssh.py +++ b/proxmoxer/backends/base_ssh.py @@ -9,10 +9,11 @@ class Response(object): - def __init__(self, content, status_code): + def __init__(self, content, status_code, reason): self.status_code = status_code self.content = content self.headers = {"content-type": "application/json"} + self.reason = reason class ProxmoxBaseSSHSession(object): @@ -43,16 +44,13 @@ def request(self, method, url, data=None, params=None, headers=None): translated_data = ' '.join(["-{0} {1}".format(k, v if not isinstance(v, str) or " " not in v else '"{}"'.format(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) + stdout, stderr, error_code = 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) + return Response(stdout, status_code, stderr) def upload_file_obj(self, file_obj, remote_path): raise NotImplementedError() diff --git a/proxmoxer/backends/openssh.py b/proxmoxer/backends/openssh.py index 6c18373..4ac5553 100644 --- a/proxmoxer/backends/openssh.py +++ b/proxmoxer/backends/openssh.py @@ -40,7 +40,7 @@ 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 + return ret.stdout, ret.stderr, ret.returncode def upload_file_obj(self, file_obj, remote_path): self.ssh_client.scp((file_obj,), target=remote_path) diff --git a/proxmoxer/backends/ssh_paramiko.py b/proxmoxer/backends/ssh_paramiko.py index f9cbe11..7b0896f 100644 --- a/proxmoxer/backends/ssh_paramiko.py +++ b/proxmoxer/backends/ssh_paramiko.py @@ -56,9 +56,10 @@ def _exec(self, cmd): cmd = 'sudo ' + cmd session = self.ssh_client.get_transport().open_session() session.exec_command(cmd) + error_code = session.recv_exit_status() stdout = ''.join(session.makefile('r', -1)) stderr = ''.join(session.makefile_stderr('r', -1)) - return stdout, stderr + return stdout, stderr, error_code def upload_file_obj(self, file_obj, remote_path): sftp = self.ssh_client.open_sftp() From 23c5a17da203f0f107de6c4c68ebed5d6768d3c8 Mon Sep 17 00:00:00 2001 From: mihail Date: Sat, 22 Sep 2018 19:49:38 +0300 Subject: [PATCH 5/6] jsonify the output from pvesh --- proxmoxer/backends/base_ssh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxmoxer/backends/base_ssh.py b/proxmoxer/backends/base_ssh.py index 94207e3..5ac35bd 100644 --- a/proxmoxer/backends/base_ssh.py +++ b/proxmoxer/backends/base_ssh.py @@ -42,7 +42,7 @@ def request(self, method, url, data=None, params=None, headers=None): data['tmpfilename'] = tmp_filename translated_data = ' '.join(["-{0} {1}".format(k, v if not isinstance(v, str) or " " not in v else '"{}"'.format(v)) for k, v in chain(data.items(), params.items())]) - full_cmd = 'pvesh {0}'.format(' '.join(filter(None, (cmd, url, translated_data)))) + full_cmd = 'pvesh {0} --output-format json-pretty'.format(' '.join(filter(None, (cmd, url, translated_data)))) stdout, stderr, error_code = self._exec(full_cmd) match = lambda s: re.match('\d\d\d [a-zA-Z]', s) From d1a9ecb571dbc13f883d72228aaa7ff5ec951cff Mon Sep 17 00:00:00 2001 From: mihail Date: Sat, 22 Sep 2018 19:52:44 +0300 Subject: [PATCH 6/6] Not all pvesh commands return http-like error code, if the command is with exit status of 0, then it is successful. The exit status from the command is far more important that stdout/stderr when deciding if the command was successful or not. --- proxmoxer/backends/base_ssh.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/proxmoxer/backends/base_ssh.py b/proxmoxer/backends/base_ssh.py index 5ac35bd..10da5ac 100644 --- a/proxmoxer/backends/base_ssh.py +++ b/proxmoxer/backends/base_ssh.py @@ -47,9 +47,13 @@ def request(self, method, url, data=None, params=None, headers=None): stdout, stderr, error_code = 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 (error_code == 0): + status_code=200 + else: + status_code = next( + (int(s.split()[0]) for s in stderr.splitlines() if match(s)), + 500) return Response(stdout, status_code, stderr) def upload_file_obj(self, file_obj, remote_path):