From 5110400e3bfcfa5668fb4350a1411fa9426bf51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=BE=20=D0=9A=D0=B0=D1=82?= =?UTF-8?q?=D1=8E=D1=85=D0=B0?= Date: Wed, 17 May 2017 10:47:20 +0300 Subject: [PATCH] Bugfix: 'NoneType' object has no attribute 'get_transport' - partly refactored method `ClouderModel.__execute` - `SSHEnvironment.__getattr__`: if we define getattr we do not need to call *super* method, because __getattr__ is called only if attribute is not found by other methods, such as: __dict__ lookup, __getattribute__, etc. - [SO question](http://stackoverflow.com/questions/3278077/difference-between-getattr-vs-getattribute) - [Py documentation](https://docs.python.org/2/reference/datamodel.html#object.__getattr__) --- clouder/models/model.py | 135 +++++++++++++++++++------------------ clouder/ssh/environment.py | 4 -- 2 files changed, 69 insertions(+), 70 deletions(-) diff --git a/clouder/models/model.py b/clouder/models/model.py index a0104a7..69cc48c 100644 --- a/clouder/models/model.py +++ b/clouder/models/model.py @@ -601,17 +601,19 @@ def execute(self, cmd, stdin_arg=None, @api.multi def __execute(self, cmd, stdin_arg, path, ssh, username, shell): - """ It executes a command on a pre-existing SSH channel """ + """ It executes a command on a pre-existing SSH channel + :param list[str] cmd: commands to execute + """ + # TODO: refactor this method self.ensure_one() - if all([self._name == 'clouder.service', - 'exec' in getattr(self, 'childs', []), - ]): - return self.childs['exec'].execute( - cmd, stdin_arg=stdin_arg, path=path, ssh=ssh, - username=username, shell=shell, - ) + if (self._name == 'clouder.service' and + 'exec' in getattr(self, 'childs', [])): + return self.childs['exec'].execute( + cmd, stdin_arg=stdin_arg, path=path, ssh=ssh, + username=username, shell=shell, + ) if path: self.log('path : ' + path) @@ -621,47 +623,67 @@ def __execute(self, cmd, stdin_arg, path, ssh, username, shell): service = self.pod - cmd_temp = [] - first = True - for cmd_arg in cmd: - cmd_arg = cmd_arg.replace('"', '\\"') - if first: - cmd_arg = '"' + cmd_arg - first = False - cmd_temp.append(cmd_arg) - cmd = cmd_temp - cmd.append('"') + # make command look like: + # [ 'docker exec', '-u ', + # ' -c', '""'] - cmd.insert(0, '%s %s -c ' % (service, shell)) + # escape quotes + cmd = [c_arg.replace('"', '\\"') + for c_arg in cmd] + # specify service and shell to execute cmd in + cmd = ['%s %s -c ' % (service, shell), + '"%s"' % ' '.join(cmd)] # wrap cmd in quotes + + # add user opt if specified if username: - cmd.insert(0, '-u ' + username) + cmd.insert(0, '-u %s' % username) + cmd.insert(0, 'docker exec') + self.log('') self.log('host : ' + ssh.host) self.log('command : ' + ' '.join(cmd)) cmd = [c.replace('$$$', '') for c in cmd] - transport = ssh.get_transport() - channel = transport.open_session() - channel.exec_command(' '.join(cmd)) - - # Pushing additional input - if stdin_arg: - chnl_stdin = channel.makefile('wb', -1) - for arg in stdin_arg: - self.log('command : ' + arg) - chnl_stdin.write(arg) - chnl_stdin.flush() - - # Reading outputs - stdout_read = '' - chnl_out = '' - chnl_err = '' - chnl_buffer_size = 4096 - # As long as the command is running - while not channel.exit_status_ready(): + with ssh.get_channel() as channel: + channel.exec_command(' '.join(cmd)) + + # Pushing additional input + if stdin_arg: + chnl_stdin = channel.makefile('wb', -1) + for arg in stdin_arg: + self.log('command : ' + arg) + chnl_stdin.write(arg) + chnl_stdin.flush() + + # Reading outputs + stdout_read = '' + chnl_out = '' + chnl_err = '' + chnl_buffer_size = 4096 + # As long as the command is running + while not channel.exit_status_ready(): + rl, _, _ = select.select([channel], [], [], 0.0) + if len(rl) > 0: + # Polling and printing stdout + if channel.recv_ready(): + chnl_out += channel.recv(chnl_buffer_size) + stdout_read += chnl_out + chnl_pending_out = chnl_out.split('\n') + chnl_out = chnl_pending_out[-1] + for output_to_print in chnl_pending_out[:-1]: + self.log('stdout : {0}'.format(output_to_print)) + # Polling and printing stderr + if channel.recv_stderr_ready(): + chnl_err += channel.recv_stderr(chnl_buffer_size) + chnl_pending_err = chnl_err.split('\n') + chnl_err = chnl_pending_err[-1] + for err_to_print in chnl_pending_err[:-1]: + self.log('stderr : {0}'.format(err_to_print)) + + # Polling last outputs if any: rl, _, _ = select.select([channel], [], [], 0.0) if len(rl) > 0: # Polling and printing stdout @@ -669,37 +691,18 @@ def __execute(self, cmd, stdin_arg, path, ssh, username, shell): chnl_out += channel.recv(chnl_buffer_size) stdout_read += chnl_out chnl_pending_out = chnl_out.split('\n') - chnl_out = chnl_pending_out[-1] - for output_to_print in chnl_pending_out[:-1]: - self.log('stdout : {0}'.format(output_to_print)) + for output_to_print in chnl_pending_out: + # The last one MAY be empty + if output_to_print: + self.log('stdout : {0}'.format(output_to_print)) # Polling and printing stderr if channel.recv_stderr_ready(): chnl_err += channel.recv_stderr(chnl_buffer_size) chnl_pending_err = chnl_err.split('\n') - chnl_err = chnl_pending_err[-1] - for err_to_print in chnl_pending_err[:-1]: - self.log('stderr : {0}'.format(err_to_print)) - - # Polling last outputs if any: - rl, _, _ = select.select([channel], [], [], 0.0) - if len(rl) > 0: - # Polling and printing stdout - if channel.recv_ready(): - chnl_out += channel.recv(chnl_buffer_size) - stdout_read += chnl_out - chnl_pending_out = chnl_out.split('\n') - for output_to_print in chnl_pending_out: - # The last one MAY be empty - if output_to_print: - self.log('stdout : {0}'.format(output_to_print)) - # Polling and printing stderr - if channel.recv_stderr_ready(): - chnl_err += channel.recv_stderr(chnl_buffer_size) - chnl_pending_err = chnl_err.split('\n') - for err_to_print in chnl_pending_err: - # The last one MAY be empty - if err_to_print: - self.log('stderr : {0}'.format(err_to_print)) + for err_to_print in chnl_pending_err: + # The last one MAY be empty + if err_to_print: + self.log('stderr : {0}'.format(err_to_print)) return stdout_read @api.multi diff --git a/clouder/ssh/environment.py b/clouder/ssh/environment.py index 055aafb..880311a 100644 --- a/clouder/ssh/environment.py +++ b/clouder/ssh/environment.py @@ -213,10 +213,6 @@ def __init__(self, *args, **kwargs): def __getattr__(self, key): """ Provide passthrough to paramiko Client while locking the conn """ - try: - return super(SSHEnvironment, self).__getattr__(key) - except AttributeError: - pass method = getattr(self.client, key) if not callable(method): return method