blob: e77b9a82ca87e9ee98e7e1a097fe51c2e0711bc7 [file] [log] [blame]
koder aka kdanilove06762a2015-03-22 23:32:09 +02001import re
koder aka kdanilov3a6633e2015-03-26 18:20:00 +02002import time
koder aka kdanilov416b87a2015-05-12 00:26:04 +03003import errno
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +03004import random
koder aka kdanilov652cd802015-04-13 12:21:07 +03005import socket
koder aka kdanilov0c598a12015-04-21 03:01:40 +03006import shutil
koder aka kdanilove06762a2015-03-22 23:32:09 +02007import logging
8import os.path
koder aka kdanilov3a6633e2015-03-26 18:20:00 +02009import getpass
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030010import StringIO
koder aka kdanilov652cd802015-04-13 12:21:07 +030011import threading
koder aka kdanilov0c598a12015-04-21 03:01:40 +030012import subprocess
koder aka kdanilov652cd802015-04-13 12:21:07 +030013
koder aka kdanilov3a6633e2015-03-26 18:20:00 +020014import paramiko
koder aka kdanilove06762a2015-03-22 23:32:09 +020015
koder aka kdanilove06762a2015-03-22 23:32:09 +020016
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030017logger = logging.getLogger("wally")
koder aka kdanilove06762a2015-03-22 23:32:09 +020018
19
koder aka kdanilov0c598a12015-04-21 03:01:40 +030020class Local(object):
21 "placeholder for local node"
22 @classmethod
23 def open_sftp(cls):
koder aka kdanilovafd98742015-04-24 01:27:22 +030024 return cls()
koder aka kdanilov0c598a12015-04-21 03:01:40 +030025
26 @classmethod
27 def mkdir(cls, remotepath, mode=None):
28 os.mkdir(remotepath)
29 if mode is not None:
30 os.chmod(remotepath, mode)
31
32 @classmethod
33 def put(cls, localfile, remfile):
koder aka kdanilov4d4771c2015-04-23 01:32:02 +030034 dirname = os.path.dirname(remfile)
35 if not os.path.exists(dirname):
36 os.makedirs(dirname)
koder aka kdanilov0c598a12015-04-21 03:01:40 +030037 shutil.copyfile(localfile, remfile)
38
39 @classmethod
koder aka kdanilova8253ce2015-06-13 03:10:21 +030040 def get(cls, remfile, localfile):
41 dirname = os.path.dirname(localfile)
42 if not os.path.exists(dirname):
43 os.makedirs(dirname)
44 shutil.copyfile(remfile, localfile)
45
46 @classmethod
koder aka kdanilov0c598a12015-04-21 03:01:40 +030047 def chmod(cls, path, mode):
48 os.chmod(path, mode)
49
50 @classmethod
51 def copytree(cls, src, dst):
52 shutil.copytree(src, dst)
53
54 @classmethod
55 def remove(cls, path):
56 os.unlink(path)
57
58 @classmethod
59 def close(cls):
60 pass
61
62 @classmethod
63 def open(cls, *args, **kwarhgs):
64 return open(*args, **kwarhgs)
65
koder aka kdanilove2de58c2015-04-24 22:59:36 +030066 @classmethod
67 def stat(cls, path):
68 return os.stat(path)
69
koder aka kdanilov783b4542015-04-23 18:57:04 +030070 def __enter__(self):
71 return self
72
73 def __exit__(self, x, y, z):
74 return False
75
koder aka kdanilov0c598a12015-04-21 03:01:40 +030076
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030077NODE_KEYS = {}
78
79
koder aka kdanilov416b87a2015-05-12 00:26:04 +030080def exists(sftp, path):
81 """os.path.exists for paramiko's SCP object
82 """
83 try:
84 sftp.stat(path)
85 return True
86 except IOError as e:
87 if e.errno == errno.ENOENT:
88 return False
89 raise
90
91
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030092def set_key_for_node(host_port, key):
93 sio = StringIO.StringIO(key)
94 NODE_KEYS[host_port] = paramiko.RSAKey.from_private_key(sio)
95 sio.close()
96
97
koder aka kdanilovbb5fe072015-05-21 02:50:23 +030098def ssh_connect(creds, conn_timeout=60, reuse_conn=None):
koder aka kdanilov0c598a12015-04-21 03:01:40 +030099 if creds == 'local':
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300100 return Local()
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300101
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300102 tcp_timeout = 15
103 banner_timeout = 30
104
koder aka kdanilovbb5fe072015-05-21 02:50:23 +0300105 if reuse_conn is None:
106 ssh = paramiko.SSHClient()
107 ssh.load_host_keys('/dev/null')
108 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
109 ssh.known_hosts = None
110 else:
111 ssh = reuse_conn
koder aka kdanilov168f6092015-04-19 02:33:38 +0300112
koder aka kdanilov6b1341a2015-04-21 22:44:21 +0300113 etime = time.time() + conn_timeout
114
115 while True:
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200116 try:
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300117 tleft = etime - time.time()
118 c_tcp_timeout = min(tcp_timeout, tleft)
119 c_banner_timeout = min(banner_timeout, tleft)
120
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200121 if creds.passwd is not None:
122 ssh.connect(creds.host,
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300123 timeout=c_tcp_timeout,
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300124 username=creds.user,
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200125 password=creds.passwd,
126 port=creds.port,
127 allow_agent=False,
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300128 look_for_keys=False,
129 banner_timeout=c_banner_timeout)
130 elif creds.key_file is not None:
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200131 ssh.connect(creds.host,
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300132 username=creds.user,
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300133 timeout=c_tcp_timeout,
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200134 key_filename=creds.key_file,
135 look_for_keys=False,
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300136 port=creds.port,
137 banner_timeout=c_banner_timeout)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300138 elif (creds.host, creds.port) in NODE_KEYS:
139 ssh.connect(creds.host,
140 username=creds.user,
141 timeout=c_tcp_timeout,
142 pkey=NODE_KEYS[(creds.host, creds.port)],
143 look_for_keys=False,
144 port=creds.port,
145 banner_timeout=c_banner_timeout)
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300146 else:
147 key_file = os.path.expanduser('~/.ssh/id_rsa')
148 ssh.connect(creds.host,
149 username=creds.user,
150 timeout=c_tcp_timeout,
151 key_filename=key_file,
152 look_for_keys=False,
153 port=creds.port,
154 banner_timeout=c_banner_timeout)
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200155 return ssh
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200156 except paramiko.PasswordRequiredException:
157 raise
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300158 except (socket.error, paramiko.SSHException):
koder aka kdanilov6b1341a2015-04-21 22:44:21 +0300159 if time.time() > etime:
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200160 raise
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300161 time.sleep(1)
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200162
163
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300164def save_to_remote(sftp, path, content):
165 with sftp.open(path, "wb") as fd:
166 fd.write(content)
167
168
169def read_from_remote(sftp, path):
170 with sftp.open(path, "rb") as fd:
171 return fd.read()
172
173
koder aka kdanilove06762a2015-03-22 23:32:09 +0200174def normalize_dirpath(dirpath):
175 while dirpath.endswith("/"):
176 dirpath = dirpath[:-1]
177 return dirpath
178
179
koder aka kdanilov2c473092015-03-29 17:12:13 +0300180ALL_RWX_MODE = ((1 << 9) - 1)
181
182
183def ssh_mkdir(sftp, remotepath, mode=ALL_RWX_MODE, intermediate=False):
koder aka kdanilove06762a2015-03-22 23:32:09 +0200184 remotepath = normalize_dirpath(remotepath)
185 if intermediate:
186 try:
187 sftp.mkdir(remotepath, mode=mode)
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300188 except (IOError, OSError):
koder aka kdanilov168f6092015-04-19 02:33:38 +0300189 upper_dir = remotepath.rsplit("/", 1)[0]
190
191 if upper_dir == '' or upper_dir == '/':
192 raise
193
194 ssh_mkdir(sftp, upper_dir, mode=mode, intermediate=True)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200195 return sftp.mkdir(remotepath, mode=mode)
196 else:
197 sftp.mkdir(remotepath, mode=mode)
198
199
200def ssh_copy_file(sftp, localfile, remfile, preserve_perm=True):
201 sftp.put(localfile, remfile)
202 if preserve_perm:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300203 sftp.chmod(remfile, os.stat(localfile).st_mode & ALL_RWX_MODE)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200204
205
206def put_dir_recursively(sftp, localpath, remotepath, preserve_perm=True):
207 "upload local directory to remote recursively"
208
209 # hack for localhost connection
210 if hasattr(sftp, "copytree"):
211 sftp.copytree(localpath, remotepath)
212 return
213
214 assert remotepath.startswith("/"), "%s must be absolute path" % remotepath
215
216 # normalize
217 localpath = normalize_dirpath(localpath)
218 remotepath = normalize_dirpath(remotepath)
219
220 try:
221 sftp.chdir(remotepath)
222 localsuffix = localpath.rsplit("/", 1)[1]
223 remotesuffix = remotepath.rsplit("/", 1)[1]
224 if localsuffix != remotesuffix:
225 remotepath = os.path.join(remotepath, localsuffix)
226 except IOError:
227 pass
228
229 for root, dirs, fls in os.walk(localpath):
230 prefix = os.path.commonprefix([localpath, root])
231 suffix = root.split(prefix, 1)[1]
232 if suffix.startswith("/"):
233 suffix = suffix[1:]
234
235 remroot = os.path.join(remotepath, suffix)
236
237 try:
238 sftp.chdir(remroot)
239 except IOError:
240 if preserve_perm:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300241 mode = os.stat(root).st_mode & ALL_RWX_MODE
koder aka kdanilove06762a2015-03-22 23:32:09 +0200242 else:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300243 mode = ALL_RWX_MODE
koder aka kdanilove06762a2015-03-22 23:32:09 +0200244 ssh_mkdir(sftp, remroot, mode=mode, intermediate=True)
245 sftp.chdir(remroot)
246
247 for f in fls:
248 remfile = os.path.join(remroot, f)
249 localfile = os.path.join(root, f)
250 ssh_copy_file(sftp, localfile, remfile, preserve_perm)
251
252
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300253def delete_file(conn, path):
254 sftp = conn.open_sftp()
255 sftp.remove(path)
256 sftp.close()
257
258
koder aka kdanilove06762a2015-03-22 23:32:09 +0200259def copy_paths(conn, paths):
260 sftp = conn.open_sftp()
261 try:
262 for src, dst in paths.items():
263 try:
264 if os.path.isfile(src):
265 ssh_copy_file(sftp, src, dst)
266 elif os.path.isdir(src):
267 put_dir_recursively(sftp, src, dst)
268 else:
269 templ = "Can't copy {0!r} - " + \
270 "it neither a file not a directory"
koder aka kdanilov168f6092015-04-19 02:33:38 +0300271 raise OSError(templ.format(src))
koder aka kdanilove06762a2015-03-22 23:32:09 +0200272 except Exception as exc:
273 tmpl = "Scp {0!r} => {1!r} failed - {2!r}"
koder aka kdanilov168f6092015-04-19 02:33:38 +0300274 raise OSError(tmpl.format(src, dst, exc))
koder aka kdanilove06762a2015-03-22 23:32:09 +0200275 finally:
276 sftp.close()
277
278
279class ConnCreds(object):
koder aka kdanilov2c473092015-03-29 17:12:13 +0300280 conn_uri_attrs = ("user", "passwd", "host", "port", "path")
281
koder aka kdanilove06762a2015-03-22 23:32:09 +0200282 def __init__(self):
koder aka kdanilov2c473092015-03-29 17:12:13 +0300283 for name in self.conn_uri_attrs:
koder aka kdanilove06762a2015-03-22 23:32:09 +0200284 setattr(self, name, None)
285
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300286 def __str__(self):
287 return str(self.__dict__)
288
koder aka kdanilove06762a2015-03-22 23:32:09 +0200289
290uri_reg_exprs = []
291
292
293class URIsNamespace(object):
294 class ReParts(object):
295 user_rr = "[^:]*?"
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300296 host_rr = "[^:@]*?"
koder aka kdanilove06762a2015-03-22 23:32:09 +0200297 port_rr = "\\d+"
298 key_file_rr = "[^:@]*"
299 passwd_rr = ".*?"
300
301 re_dct = ReParts.__dict__
302
303 for attr_name, val in re_dct.items():
304 if attr_name.endswith('_rr'):
305 new_rr = "(?P<{0}>{1})".format(attr_name[:-3], val)
306 setattr(ReParts, attr_name, new_rr)
307
308 re_dct = ReParts.__dict__
309
310 templs = [
311 "^{host_rr}$",
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300312 "^{host_rr}:{port_rr}$",
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300313 "^{host_rr}::{key_file_rr}$",
314 "^{host_rr}:{port_rr}:{key_file_rr}$",
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300315 "^{user_rr}@{host_rr}$",
316 "^{user_rr}@{host_rr}:{port_rr}$",
koder aka kdanilove06762a2015-03-22 23:32:09 +0200317 "^{user_rr}@{host_rr}::{key_file_rr}$",
318 "^{user_rr}@{host_rr}:{port_rr}:{key_file_rr}$",
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300319 "^{user_rr}:{passwd_rr}@{host_rr}$",
320 "^{user_rr}:{passwd_rr}@{host_rr}:{port_rr}$",
koder aka kdanilove06762a2015-03-22 23:32:09 +0200321 ]
322
323 for templ in templs:
324 uri_reg_exprs.append(templ.format(**re_dct))
325
326
327def parse_ssh_uri(uri):
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300328 # user:passwd@ip_host:port
329 # user:passwd@ip_host
koder aka kdanilove06762a2015-03-22 23:32:09 +0200330 # user@ip_host:port
331 # user@ip_host
332 # ip_host:port
333 # ip_host
334 # user@ip_host:port:path_to_key_file
335 # user@ip_host::path_to_key_file
336 # ip_host:port:path_to_key_file
337 # ip_host::path_to_key_file
338
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300339 if uri.startswith("ssh://"):
340 uri = uri[len("ssh://"):]
341
koder aka kdanilove06762a2015-03-22 23:32:09 +0200342 res = ConnCreds()
343 res.port = "22"
344 res.key_file = None
345 res.passwd = None
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300346 res.user = getpass.getuser()
koder aka kdanilove06762a2015-03-22 23:32:09 +0200347
348 for rr in uri_reg_exprs:
349 rrm = re.match(rr, uri)
350 if rrm is not None:
351 res.__dict__.update(rrm.groupdict())
352 return res
koder aka kdanilov652cd802015-04-13 12:21:07 +0300353
koder aka kdanilove06762a2015-03-22 23:32:09 +0200354 raise ValueError("Can't parse {0!r} as ssh uri value".format(uri))
355
356
koder aka kdanilovbb5fe072015-05-21 02:50:23 +0300357def reconnect(conn, uri, **params):
358 if uri == 'local':
359 return conn
360
361 creds = parse_ssh_uri(uri)
362 creds.port = int(creds.port)
363 return ssh_connect(creds, reuse_conn=conn, **params)
364
365
koder aka kdanilov168f6092015-04-19 02:33:38 +0300366def connect(uri, **params):
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300367 if uri == 'local':
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300368 res = Local()
369 else:
370 creds = parse_ssh_uri(uri)
371 creds.port = int(creds.port)
372 res = ssh_connect(creds, **params)
373 return res
koder aka kdanilove06762a2015-03-22 23:32:09 +0200374
375
koder aka kdanilov652cd802015-04-13 12:21:07 +0300376all_sessions_lock = threading.Lock()
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300377all_sessions = {}
378
379
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300380class BGSSHTask(object):
381 def __init__(self, node, use_sudo):
382 self.node = node
383 self.pid = None
384 self.use_sudo = use_sudo
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300385
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300386 def start(self, orig_cmd, **params):
387 uniq_name = 'test'
388 cmd = "screen -S {0} -d -m {1}".format(uniq_name, orig_cmd)
389 run_over_ssh(self.node.connection, cmd,
390 timeout=10, node=self.node.get_conn_id(),
391 **params)
392 processes = run_over_ssh(self.node.connection, "ps aux", nolog=True)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300393
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300394 for proc in processes.split("\n"):
395 if orig_cmd in proc and "SCREEN" not in proc:
396 self.pid = proc.split()[1]
397 break
398 else:
399 self.pid = -1
400
401 def check_running(self):
402 assert self.pid is not None
403 try:
404 run_over_ssh(self.node.connection,
405 "ls /proc/{0}".format(self.pid),
406 timeout=10, nolog=True)
407 return True
408 except OSError:
409 return False
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300410
411 def kill(self, soft=True, use_sudo=True):
412 assert self.pid is not None
413 try:
414 if soft:
415 cmd = "kill {0}"
416 else:
417 cmd = "kill -9 {0}"
418
419 if self.use_sudo:
420 cmd = "sudo " + cmd
421
422 run_over_ssh(self.node.connection,
423 cmd.format(self.pid), nolog=True)
424 return True
425 except OSError:
426 return False
427
428 def wait(self, soft_timeout, timeout):
429 end_of_wait_time = timeout + time.time()
430 soft_end_of_wait_time = soft_timeout + time.time()
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300431
koder aka kdanilova8253ce2015-06-13 03:10:21 +0300432 # time_till_check = random.randint(5, 10)
433 time_till_check = 2
434
435 # time_till_first_check = random.randint(2, 6)
436 time_till_first_check = 2
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300437 time.sleep(time_till_first_check)
438 if not self.check_running():
439 return True
440
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300441 while self.check_running() and time.time() < soft_end_of_wait_time:
442 time.sleep(soft_end_of_wait_time - time.time())
443
444 while end_of_wait_time > time.time():
445 time.sleep(time_till_check)
446 if not self.check_running():
447 break
448 else:
449 self.kill()
koder aka kdanilova8253ce2015-06-13 03:10:21 +0300450 time.sleep(1)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300451 if self.check_running():
452 self.kill(soft=False)
453 return False
454 return True
koder aka kdanilove06762a2015-03-22 23:32:09 +0200455
koder aka kdanilove06762a2015-03-22 23:32:09 +0200456
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300457def run_over_ssh(conn, cmd, stdin_data=None, timeout=60,
458 nolog=False, node=None):
koder aka kdanilov652cd802015-04-13 12:21:07 +0300459 "should be replaces by normal implementation, with select"
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300460
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300461 if isinstance(conn, Local):
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300462 if not nolog:
463 logger.debug("SSH:local Exec {0!r}".format(cmd))
464 proc = subprocess.Popen(cmd, shell=True,
465 stdin=subprocess.PIPE,
466 stdout=subprocess.PIPE,
467 stderr=subprocess.STDOUT)
468
469 stdoutdata, _ = proc.communicate(input=stdin_data)
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300470 if proc.returncode != 0:
471 templ = "SSH:{0} Cmd {1!r} failed with code {2}. Output: {3}"
472 raise OSError(templ.format(node, cmd, proc.returncode, stdoutdata))
473
474 return stdoutdata
475
koder aka kdanilov652cd802015-04-13 12:21:07 +0300476 transport = conn.get_transport()
477 session = transport.open_session()
koder aka kdanilove06762a2015-03-22 23:32:09 +0200478
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300479 if node is None:
480 node = ""
481
koder aka kdanilov652cd802015-04-13 12:21:07 +0300482 with all_sessions_lock:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300483 all_sessions[id(session)] = session
koder aka kdanilove06762a2015-03-22 23:32:09 +0200484
koder aka kdanilov652cd802015-04-13 12:21:07 +0300485 try:
486 session.set_combine_stderr(True)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200487
koder aka kdanilov652cd802015-04-13 12:21:07 +0300488 stime = time.time()
koder aka kdanilove06762a2015-03-22 23:32:09 +0200489
koder aka kdanilov652cd802015-04-13 12:21:07 +0300490 if not nolog:
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300491 logger.debug("SSH:{0} Exec {1!r}".format(node, cmd))
koder aka kdanilove06762a2015-03-22 23:32:09 +0200492
koder aka kdanilov652cd802015-04-13 12:21:07 +0300493 session.exec_command(cmd)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200494
koder aka kdanilov652cd802015-04-13 12:21:07 +0300495 if stdin_data is not None:
496 session.sendall(stdin_data)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200497
koder aka kdanilov652cd802015-04-13 12:21:07 +0300498 session.settimeout(1)
499 session.shutdown_write()
500 output = ""
501
502 while True:
503 try:
504 ndata = session.recv(1024)
505 output += ndata
506 if "" == ndata:
507 break
508 except socket.timeout:
509 pass
510
511 if time.time() - stime > timeout:
512 raise OSError(output + "\nExecution timeout")
513
514 code = session.recv_exit_status()
515 finally:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300516 found = False
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300517 with all_sessions_lock:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300518 if id(session) in all_sessions:
519 found = True
520 del all_sessions[id(session)]
521
522 if found:
523 session.close()
koder aka kdanilov652cd802015-04-13 12:21:07 +0300524
525 if code != 0:
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300526 templ = "SSH:{0} Cmd {1!r} failed with code {2}. Output: {3}"
527 raise OSError(templ.format(node, cmd, code, output))
koder aka kdanilov652cd802015-04-13 12:21:07 +0300528
529 return output
530
531
532def close_all_sessions():
533 with all_sessions_lock:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300534 for session in all_sessions.values():
koder aka kdanilov652cd802015-04-13 12:21:07 +0300535 try:
536 session.sendall('\x03')
537 session.close()
538 except:
539 pass
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300540 all_sessions.clear()