23
23
from gevent import joinall , spawn , Timeout as GTimeout
24
24
from gevent .hub import Hub
25
25
26
- from ..common import _validate_pkey_path
26
+ from ..common import _validate_pkey_path , _validate_pkey
27
27
from ...config import HostConfig
28
28
from ...constants import DEFAULT_RETRIES , RETRY_DELAY
29
29
from ...exceptions import HostArgumentError , Timeout , ShellError , HostConfigError
@@ -39,7 +39,7 @@ class BaseParallelSSHClient(object):
39
39
def __init__ (self , hosts , user = None , password = None , port = None , pkey = None ,
40
40
allow_agent = True ,
41
41
num_retries = DEFAULT_RETRIES ,
42
- timeout = 120 , pool_size = 10 ,
42
+ timeout = 120 , pool_size = 100 ,
43
43
host_config = None , retry_delay = RETRY_DELAY ,
44
44
identity_auth = True ,
45
45
ipv6_only = False ,
@@ -64,7 +64,8 @@ def __init__(self, hosts, user=None, password=None, port=None, pkey=None,
64
64
self .user = user
65
65
self .password = password
66
66
self .port = port
67
- self .pkey = pkey
67
+ self .pkey = _validate_pkey (pkey )
68
+ self .__pkey_data = self ._load_pkey_data (pkey ) if pkey is not None else None
68
69
self .num_retries = num_retries
69
70
self .timeout = timeout
70
71
self ._host_clients = {}
@@ -113,9 +114,26 @@ def hosts(self, _hosts):
113
114
self ._host_clients .pop ((i , host ), None )
114
115
self ._hosts = _hosts
115
116
117
+ def __del__ (self ):
118
+ self .disconnect ()
119
+
120
+ def disconnect (self ):
121
+ if not hasattr (self , '_host_clients' ):
122
+ return
123
+ for s_client in self ._host_clients .values ():
124
+ try :
125
+ s_client .disconnect ()
126
+ except Exception as ex :
127
+ logger .debug ("Client disconnect failed with %s" , ex )
128
+ pass
129
+ del s_client
130
+
116
131
def _check_host_config (self ):
117
132
if self .host_config is None :
118
133
return
134
+ if not isinstance (self .host_config , list ):
135
+ raise HostConfigError ("Host configuration of type %s is invalid - valid types are List[HostConfig]" ,
136
+ type (self .host_config ))
119
137
host_len = len (self .hosts )
120
138
if host_len != len (self .host_config ):
121
139
raise ValueError (
@@ -231,7 +249,7 @@ def _get_output_from_cmds(self, cmds, raise_error=False):
231
249
232
250
def _get_output_from_greenlet (self , cmd_i , cmd , raise_error = False ):
233
251
host = self .hosts [cmd_i ]
234
- alias = self ._get_host_config (cmd_i , host ).alias
252
+ alias = self ._get_host_config (cmd_i ).alias
235
253
try :
236
254
host_out = cmd .get ()
237
255
return host_out
@@ -256,7 +274,7 @@ def get_last_output(self, cmds=None):
256
274
return self ._get_output_from_cmds (
257
275
cmds , raise_error = False )
258
276
259
- def _get_host_config (self , host_i , host ):
277
+ def _get_host_config (self , host_i ):
260
278
if self .host_config is None :
261
279
config = HostConfig (
262
280
user = self .user , port = self .port , password = self .password , private_key = self .pkey ,
@@ -275,17 +293,13 @@ def _get_host_config(self, host_i, host):
275
293
alias = None ,
276
294
)
277
295
return config
278
- elif not isinstance (self .host_config , list ):
279
- raise HostConfigError ("Host configuration of type %s is invalid - valid types are list[HostConfig]" ,
280
- type (self .host_config ))
281
296
config = self .host_config [host_i ]
282
297
return config
283
298
284
299
def _run_command (self , host_i , host , command , sudo = False , user = None ,
285
300
shell = None , use_pty = False ,
286
301
encoding = 'utf-8' , read_timeout = None ):
287
302
"""Make SSHClient if needed, run command on host"""
288
- logger .debug ("_run_command with read timeout %s" , read_timeout )
289
303
try :
290
304
_client = self ._get_ssh_client (host_i , host )
291
305
host_out = _client .run_command (
@@ -311,13 +325,13 @@ def connect_auth(self):
311
325
:returns: list of greenlets to ``joinall`` with.
312
326
:rtype: list(:py:mod:`gevent.greenlet.Greenlet`)
313
327
"""
314
- cmds = [spawn (self ._get_ssh_client , i , host ) for i , host in enumerate (self .hosts )]
328
+ cmds = [self . pool . spawn (self ._get_ssh_client , i , host ) for i , host in enumerate (self .hosts )]
315
329
return cmds
316
330
317
331
def _consume_output (self , stdout , stderr ):
318
- for line in stdout :
332
+ for _ in stdout :
319
333
pass
320
- for line in stderr :
334
+ for _ in stderr :
321
335
pass
322
336
323
337
def join (self , output = None , consume_output = False , timeout = None ):
@@ -346,6 +360,9 @@ def join(self, output=None, consume_output=False, timeout=None):
346
360
:rtype: ``None``"""
347
361
if output is None :
348
362
output = self .get_last_output ()
363
+ if output is None :
364
+ logger .info ("No last output to join on - run_command has never been run." )
365
+ return
349
366
elif not isinstance (output , list ):
350
367
raise ValueError ("Unexpected output object type" )
351
368
cmds = [self .pool .spawn (self ._join , host_out , timeout = timeout ,
@@ -544,32 +561,26 @@ def _copy_remote_file(self, host_i, host, remote_file, local_file, recurse,
544
561
return client .copy_remote_file (
545
562
remote_file , local_file , recurse = recurse , ** kwargs )
546
563
547
- def _handle_greenlet_exc (self , func , host , * args , ** kwargs ):
548
- try :
549
- return func (* args , ** kwargs )
550
- except Exception as ex :
551
- raise ex
552
-
553
564
def _get_ssh_client (self , host_i , host ):
554
565
logger .debug ("Make client request for host %s, (host_i, host) in clients: %s" ,
555
566
host , (host_i , host ) in self ._host_clients )
556
567
_client = self ._host_clients .get ((host_i , host ))
557
568
if _client is not None :
558
569
return _client
559
- cfg = self ._get_host_config (host_i , host )
570
+ cfg = self ._get_host_config (host_i )
560
571
_pkey = self .pkey if cfg .private_key is None else cfg .private_key
561
572
_pkey_data = self ._load_pkey_data (_pkey )
562
573
_client = self ._make_ssh_client (host , cfg , _pkey_data )
563
574
self ._host_clients [(host_i , host )] = _client
564
575
return _client
565
576
566
577
def _load_pkey_data (self , _pkey ):
567
- if isinstance (_pkey , str ):
568
- _validate_pkey_path ( _pkey )
569
- with open (_pkey , 'rb' ) as fh :
570
- _pkey_data = fh . read ()
571
- return _pkey_data
572
- return _pkey
578
+ if not isinstance (_pkey , str ):
579
+ return _pkey
580
+ _pkey = _validate_pkey_path (_pkey )
581
+ with open ( _pkey , 'rb' ) as fh :
582
+ _pkey_data = fh . read ()
583
+ return _pkey_data
573
584
574
585
def _make_ssh_client (self , host , cfg , _pkey_data ):
575
586
raise NotImplementedError
0 commit comments