@@ -80,7 +80,8 @@ class SSHClient(object):
8080 def __init__ (self , host ,
8181 user = None , password = None , port = None ,
8282 pkey = None , forward_ssh_agent = True ,
83- num_retries = DEFAULT_RETRIES , _agent = None , timeout = None ):
83+ num_retries = DEFAULT_RETRIES , _agent = None , timeout = None ,
84+ proxy_host = None , proxy_port = 22 ):
8485 """Connect to host honouring any user set configuration in ~/.ssh/config \
8586 or /etc/ssh/ssh_config
8687
@@ -135,36 +136,53 @@ def __init__(self, host,
135136 client .set_missing_host_key_policy (paramiko .MissingHostKeyPolicy ())
136137 self .forward_ssh_agent = forward_ssh_agent
137138 self .client = client
138- self .channel = None
139139 self .user = user
140140 self .password = password
141141 self .pkey = pkey
142142 self .port = port if port else 22
143143 self .host = resolved_address
144- self .proxy_command = paramiko .ProxyCommand (host_config ['proxycommand' ]) if 'proxycommand' in \
145- host_config else None
146- if self .proxy_command :
147- logger .debug ("Proxy configured for destination host %s - ProxyCommand: '%s'" ,
148- self .host , " " .join (self .proxy_command .cmd ),)
149144 if _agent :
150145 self .client ._agent = _agent
151146 self .num_retries = num_retries
152147 self .timeout = timeout
153- self ._connect ()
154-
155- def _connect (self , retries = 1 ):
148+ self .proxy_host , self .proxy_port = proxy_host , proxy_port
149+ if self .proxy_host and self .proxy_port :
150+ logger .debug ("Proxy configured for destination host %s - Proxy host: %s:%s" ,
151+ self .host , self .proxy_host , self .proxy_port ,)
152+ self ._connect_proxy ()
153+ else :
154+ self ._connect (self .client , self .host , self .port )
155+
156+ def _connect_proxy (self ):
157+ """Connects to SSH server and returns transport channel to be used
158+ to forward proxy to another SSH server.
159+ client (me) -> proxy (ssh server to proxy through) -> \
160+ destination (ssh server to run command)
161+ :rtype: `:mod:paramiko.Channel` Channel to proxy through"""
162+ self .proxy_client = paramiko .SSHClient ()
163+ self .proxy_client .set_missing_host_key_policy (paramiko .MissingHostKeyPolicy ())
164+ self ._connect (self .proxy_client , self .proxy_host , self .proxy_port )
165+ logger .info ("Connecting via SSH proxy %s:%s -> %s:%s" , self .proxy_host ,
166+ self .proxy_port , self .host , self .port ,)
167+ proxy_channel = self .proxy_client .get_transport ().\
168+ open_channel ('direct-tcpip' , (self .host , self .port ,),
169+ ('127.0.0.1' , 0 ))
170+ return self ._connect (self .client , self .host , self .port , sock = proxy_channel )
171+
172+ def _connect (self , client , host , port , sock = None , retries = 1 ):
156173 """Connect to host, throw UnknownHost exception on DNS errors"""
157174 try :
158- self . client .connect (self . host , username = self .user ,
159- password = self .password , port = self . port ,
160- pkey = self .pkey ,
161- sock = self . proxy_command , timeout = self .timeout )
175+ client .connect (host , username = self .user ,
176+ password = self .password , port = port ,
177+ pkey = self .pkey ,
178+ sock = sock , timeout = self .timeout )
162179 except sock_gaierror , e :
163180 logger .error ("Could not resolve host '%s' - retry %s/%s" ,
164181 self .host , retries , self .num_retries )
165182 while retries < self .num_retries :
166183 gevent .sleep (5 )
167- return self ._connect (retries = retries + 1 )
184+ return self ._connect (client , host , port , sock = sock ,
185+ retries = retries + 1 )
168186 raise UnknownHostException ("%s - %s - retry %s/%s" ,
169187 str (e .args [1 ]),
170188 self .host , retries , self .num_retries )
@@ -173,8 +191,8 @@ def _connect(self, retries=1):
173191 self .host , self .port , retries , self .num_retries )
174192 while retries < self .num_retries :
175193 gevent .sleep (5 )
176- return self ._connect (retries = retries + 1 )
177-
194+ return self ._connect (client , host , port , sock = sock ,
195+ retries = retries + 1 )
178196 error_type = e .args [1 ] if len (e .args ) > 1 else e .args [0 ]
179197 raise ConnectionErrorException ("%s for host '%s:%s' - retry %s/%s" ,
180198 str (error_type ), self .host , self .port ,
@@ -184,7 +202,7 @@ def _connect(self, retries=1):
184202 except paramiko .ProxyCommandFailure , e :
185203 logger .error ("Error executing ProxyCommand - %s" , e .message ,)
186204 raise ProxyCommandException (e .message )
187- # SSHException is more general so should below other types
205+ # SSHException is more general so should be below other types
188206 # of SSH failure
189207 except paramiko .SSHException , e :
190208 raise SSHException (e )
@@ -211,7 +229,7 @@ def exec_command(self, command, sudo=False, user=None, **kwargs):
211229 channel = self .client .get_transport ().open_session ()
212230 if self .forward_ssh_agent :
213231 agent_handler = paramiko .agent .AgentRequestHandler (channel )
214- channel .get_pty ()
232+ # channel.get_pty()
215233 if self .timeout :
216234 channel .settimeout (self .timeout )
217235 _stdout , _stderr = channel .makefile ('rb' ), \
@@ -301,7 +319,7 @@ class ParallelSSHClient(object):
301319 def __init__ (self , hosts ,
302320 user = None , password = None , port = None , pkey = None ,
303321 forward_ssh_agent = True , num_retries = DEFAULT_RETRIES , timeout = None ,
304- pool_size = 10 ):
322+ pool_size = 10 , proxy_host = None , proxy_port = 22 ):
305323 """
306324 :param hosts: Hosts to connect to
307325 :type hosts: list(str)
@@ -401,6 +419,7 @@ def __init__(self, hosts,
401419 self .pkey = pkey
402420 self .num_retries = num_retries
403421 self .timeout = timeout
422+ self .proxy_host , self .proxy_port = proxy_host , proxy_port
404423 # To hold host clients
405424 self .host_clients = dict ((host , None ) for host in hosts )
406425
@@ -500,7 +519,9 @@ def _exec_command(self, host, *args, **kwargs):
500519 port = self .port , pkey = self .pkey ,
501520 forward_ssh_agent = self .forward_ssh_agent ,
502521 num_retries = self .num_retries ,
503- timeout = self .timeout )
522+ timeout = self .timeout ,
523+ proxy_host = self .proxy_host ,
524+ proxy_port = self .proxy_port )
504525 return self .host_clients [host ].exec_command (* args , ** kwargs )
505526
506527 def get_output (self , commands = None ):
0 commit comments