Skip to content
This repository has been archived by the owner on Jan 24, 2018. It is now read-only.

WIP: Bugfix: 'NoneType' object has no attribute 'get_transport' #227

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 69 additions & 66 deletions clouder/models/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -621,85 +623,86 @@ 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 <username>',
# '<service> <shell> -c', '"<cmd>"']

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
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))
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
Expand Down
4 changes: 0 additions & 4 deletions clouder/ssh/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,6 @@ def __init__(self, *args, **kwargs):

def __getattr__(self, key):
""" Provide passthrough to paramiko Client while locking the conn """
try:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this, all attributes for the SSHEnvironment object will be inaccessible. This try/except block first attempts attributes on the current object, then escalates to paramiko. Did you run into some sort of name conflict or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No name conflicts, just attempt to make code cleaner and simpler...
I have some experience with __getattr__ and __getattribute__ method in few of my projects.

For example this and this do same thing, and both working.

__getattr__ method is called only if attribute is not found in usual way. Thus all normal attributes of SSHEnvironment object will be accessible. The super call is required if __getattribute__ method is used.

Simple repl test:

class MyObject(object):
    def __init__(self, obj):
        self.attr = 42
        self.obj = obj 

    def __getattr__(self, name):
        return getattr(self.obj, name)

class C2(object):
    def __init__(self):
        self.obj_attr = 81


obj = MyObject(C2())

print obj.attr      # output: 42
print obj.obj_attr  # output: 81

May be i have something missed?

Also i can revert this change in next commit...

return super(SSHEnvironment, self).__getattr__(key)
except AttributeError:
pass
method = getattr(self.client, key)
if not callable(method):
return method
Expand Down