blob: ada4af6b62485a79ed0ddbdbfcc868f25890757b [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):
koder aka kdanilov76471642015-08-14 11:44:43 +030021 "simulate ssh connection to local"
koder aka kdanilov0c598a12015-04-21 03:01:40 +030022 @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):
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030081 "os.path.exists for paramiko's SCP object"
koder aka kdanilov416b87a2015-05-12 00:26:04 +030082 try:
83 sftp.stat(path)
84 return True
85 except IOError as e:
86 if e.errno == errno.ENOENT:
87 return False
88 raise
89
90
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030091def set_key_for_node(host_port, key):
92 sio = StringIO.StringIO(key)
93 NODE_KEYS[host_port] = paramiko.RSAKey.from_private_key(sio)
94 sio.close()
95
96
koder aka kdanilovbb5fe072015-05-21 02:50:23 +030097def ssh_connect(creds, conn_timeout=60, reuse_conn=None):
koder aka kdanilov0c598a12015-04-21 03:01:40 +030098 if creds == 'local':
koder aka kdanilov416b87a2015-05-12 00:26:04 +030099 return Local()
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300100
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300101 tcp_timeout = 15
102 banner_timeout = 30
103
koder aka kdanilovbb5fe072015-05-21 02:50:23 +0300104 if reuse_conn is None:
105 ssh = paramiko.SSHClient()
106 ssh.load_host_keys('/dev/null')
107 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
108 ssh.known_hosts = None
109 else:
110 ssh = reuse_conn
koder aka kdanilov168f6092015-04-19 02:33:38 +0300111
koder aka kdanilov6b1341a2015-04-21 22:44:21 +0300112 etime = time.time() + conn_timeout
113
114 while True:
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200115 try:
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300116 tleft = etime - time.time()
117 c_tcp_timeout = min(tcp_timeout, tleft)
koder aka kdanilov8bc48022015-07-15 11:54:47 +0300118
119 if paramiko.__version_info__ >= (1, 15, 2):
120 banner_timeout = {'banner_timeout': min(banner_timeout, tleft)}
121 else:
122 banner_timeout = {}
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300123
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200124 if creds.passwd is not None:
125 ssh.connect(creds.host,
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300126 timeout=c_tcp_timeout,
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300127 username=creds.user,
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200128 password=creds.passwd,
129 port=creds.port,
130 allow_agent=False,
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300131 look_for_keys=False,
koder aka kdanilov8bc48022015-07-15 11:54:47 +0300132 **banner_timeout)
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300133 elif creds.key_file is not None:
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200134 ssh.connect(creds.host,
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300135 username=creds.user,
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300136 timeout=c_tcp_timeout,
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200137 key_filename=creds.key_file,
138 look_for_keys=False,
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300139 port=creds.port,
koder aka kdanilov8bc48022015-07-15 11:54:47 +0300140 **banner_timeout)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300141 elif (creds.host, creds.port) in NODE_KEYS:
142 ssh.connect(creds.host,
143 username=creds.user,
144 timeout=c_tcp_timeout,
145 pkey=NODE_KEYS[(creds.host, creds.port)],
146 look_for_keys=False,
147 port=creds.port,
koder aka kdanilov8bc48022015-07-15 11:54:47 +0300148 **banner_timeout)
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300149 else:
150 key_file = os.path.expanduser('~/.ssh/id_rsa')
151 ssh.connect(creds.host,
152 username=creds.user,
153 timeout=c_tcp_timeout,
154 key_filename=key_file,
155 look_for_keys=False,
156 port=creds.port,
koder aka kdanilov8bc48022015-07-15 11:54:47 +0300157 **banner_timeout)
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200158 return ssh
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200159 except paramiko.PasswordRequiredException:
160 raise
koder aka kdanilov46d4f392015-04-24 11:35:00 +0300161 except (socket.error, paramiko.SSHException):
koder aka kdanilov6b1341a2015-04-21 22:44:21 +0300162 if time.time() > etime:
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200163 raise
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300164 time.sleep(1)
koder aka kdanilov3a6633e2015-03-26 18:20:00 +0200165
166
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300167def save_to_remote(sftp, path, content):
168 with sftp.open(path, "wb") as fd:
169 fd.write(content)
170
171
172def read_from_remote(sftp, path):
173 with sftp.open(path, "rb") as fd:
174 return fd.read()
175
176
koder aka kdanilove06762a2015-03-22 23:32:09 +0200177def normalize_dirpath(dirpath):
178 while dirpath.endswith("/"):
179 dirpath = dirpath[:-1]
180 return dirpath
181
182
koder aka kdanilov2c473092015-03-29 17:12:13 +0300183ALL_RWX_MODE = ((1 << 9) - 1)
184
185
186def ssh_mkdir(sftp, remotepath, mode=ALL_RWX_MODE, intermediate=False):
koder aka kdanilove06762a2015-03-22 23:32:09 +0200187 remotepath = normalize_dirpath(remotepath)
188 if intermediate:
189 try:
190 sftp.mkdir(remotepath, mode=mode)
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300191 except (IOError, OSError):
koder aka kdanilov168f6092015-04-19 02:33:38 +0300192 upper_dir = remotepath.rsplit("/", 1)[0]
193
194 if upper_dir == '' or upper_dir == '/':
195 raise
196
197 ssh_mkdir(sftp, upper_dir, mode=mode, intermediate=True)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200198 return sftp.mkdir(remotepath, mode=mode)
199 else:
200 sftp.mkdir(remotepath, mode=mode)
201
202
203def ssh_copy_file(sftp, localfile, remfile, preserve_perm=True):
204 sftp.put(localfile, remfile)
205 if preserve_perm:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300206 sftp.chmod(remfile, os.stat(localfile).st_mode & ALL_RWX_MODE)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200207
208
209def put_dir_recursively(sftp, localpath, remotepath, preserve_perm=True):
210 "upload local directory to remote recursively"
211
212 # hack for localhost connection
213 if hasattr(sftp, "copytree"):
214 sftp.copytree(localpath, remotepath)
215 return
216
217 assert remotepath.startswith("/"), "%s must be absolute path" % remotepath
218
219 # normalize
220 localpath = normalize_dirpath(localpath)
221 remotepath = normalize_dirpath(remotepath)
222
223 try:
224 sftp.chdir(remotepath)
225 localsuffix = localpath.rsplit("/", 1)[1]
226 remotesuffix = remotepath.rsplit("/", 1)[1]
227 if localsuffix != remotesuffix:
228 remotepath = os.path.join(remotepath, localsuffix)
229 except IOError:
230 pass
231
232 for root, dirs, fls in os.walk(localpath):
233 prefix = os.path.commonprefix([localpath, root])
234 suffix = root.split(prefix, 1)[1]
235 if suffix.startswith("/"):
236 suffix = suffix[1:]
237
238 remroot = os.path.join(remotepath, suffix)
239
240 try:
241 sftp.chdir(remroot)
242 except IOError:
243 if preserve_perm:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300244 mode = os.stat(root).st_mode & ALL_RWX_MODE
koder aka kdanilove06762a2015-03-22 23:32:09 +0200245 else:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300246 mode = ALL_RWX_MODE
koder aka kdanilove06762a2015-03-22 23:32:09 +0200247 ssh_mkdir(sftp, remroot, mode=mode, intermediate=True)
248 sftp.chdir(remroot)
249
250 for f in fls:
251 remfile = os.path.join(remroot, f)
252 localfile = os.path.join(root, f)
253 ssh_copy_file(sftp, localfile, remfile, preserve_perm)
254
255
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300256def delete_file(conn, path):
257 sftp = conn.open_sftp()
258 sftp.remove(path)
259 sftp.close()
260
261
koder aka kdanilove06762a2015-03-22 23:32:09 +0200262def copy_paths(conn, paths):
263 sftp = conn.open_sftp()
264 try:
265 for src, dst in paths.items():
266 try:
267 if os.path.isfile(src):
268 ssh_copy_file(sftp, src, dst)
269 elif os.path.isdir(src):
270 put_dir_recursively(sftp, src, dst)
271 else:
272 templ = "Can't copy {0!r} - " + \
273 "it neither a file not a directory"
koder aka kdanilov168f6092015-04-19 02:33:38 +0300274 raise OSError(templ.format(src))
koder aka kdanilove06762a2015-03-22 23:32:09 +0200275 except Exception as exc:
276 tmpl = "Scp {0!r} => {1!r} failed - {2!r}"
koder aka kdanilov168f6092015-04-19 02:33:38 +0300277 raise OSError(tmpl.format(src, dst, exc))
koder aka kdanilove06762a2015-03-22 23:32:09 +0200278 finally:
279 sftp.close()
280
281
282class ConnCreds(object):
koder aka kdanilov2c473092015-03-29 17:12:13 +0300283 conn_uri_attrs = ("user", "passwd", "host", "port", "path")
284
koder aka kdanilove06762a2015-03-22 23:32:09 +0200285 def __init__(self):
koder aka kdanilov2c473092015-03-29 17:12:13 +0300286 for name in self.conn_uri_attrs:
koder aka kdanilove06762a2015-03-22 23:32:09 +0200287 setattr(self, name, None)
288
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300289 def __str__(self):
290 return str(self.__dict__)
291
koder aka kdanilove06762a2015-03-22 23:32:09 +0200292
293uri_reg_exprs = []
294
295
296class URIsNamespace(object):
297 class ReParts(object):
298 user_rr = "[^:]*?"
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300299 host_rr = "[^:@]*?"
koder aka kdanilove06762a2015-03-22 23:32:09 +0200300 port_rr = "\\d+"
301 key_file_rr = "[^:@]*"
302 passwd_rr = ".*?"
303
304 re_dct = ReParts.__dict__
305
306 for attr_name, val in re_dct.items():
307 if attr_name.endswith('_rr'):
308 new_rr = "(?P<{0}>{1})".format(attr_name[:-3], val)
309 setattr(ReParts, attr_name, new_rr)
310
311 re_dct = ReParts.__dict__
312
313 templs = [
314 "^{host_rr}$",
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300315 "^{host_rr}:{port_rr}$",
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300316 "^{host_rr}::{key_file_rr}$",
317 "^{host_rr}:{port_rr}:{key_file_rr}$",
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300318 "^{user_rr}@{host_rr}$",
319 "^{user_rr}@{host_rr}:{port_rr}$",
koder aka kdanilove06762a2015-03-22 23:32:09 +0200320 "^{user_rr}@{host_rr}::{key_file_rr}$",
321 "^{user_rr}@{host_rr}:{port_rr}:{key_file_rr}$",
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300322 "^{user_rr}:{passwd_rr}@{host_rr}$",
323 "^{user_rr}:{passwd_rr}@{host_rr}:{port_rr}$",
koder aka kdanilove06762a2015-03-22 23:32:09 +0200324 ]
325
326 for templ in templs:
327 uri_reg_exprs.append(templ.format(**re_dct))
328
329
330def parse_ssh_uri(uri):
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300331 # [ssh://]+
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300332 # user:passwd@ip_host:port
333 # user:passwd@ip_host
koder aka kdanilove06762a2015-03-22 23:32:09 +0200334 # user@ip_host:port
335 # user@ip_host
336 # ip_host:port
337 # ip_host
338 # user@ip_host:port:path_to_key_file
339 # user@ip_host::path_to_key_file
340 # ip_host:port:path_to_key_file
341 # ip_host::path_to_key_file
342
koder aka kdanilov4d4771c2015-04-23 01:32:02 +0300343 if uri.startswith("ssh://"):
344 uri = uri[len("ssh://"):]
345
koder aka kdanilove06762a2015-03-22 23:32:09 +0200346 res = ConnCreds()
347 res.port = "22"
348 res.key_file = None
349 res.passwd = None
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300350 res.user = getpass.getuser()
koder aka kdanilove06762a2015-03-22 23:32:09 +0200351
352 for rr in uri_reg_exprs:
353 rrm = re.match(rr, uri)
354 if rrm is not None:
355 res.__dict__.update(rrm.groupdict())
356 return res
koder aka kdanilov652cd802015-04-13 12:21:07 +0300357
koder aka kdanilove06762a2015-03-22 23:32:09 +0200358 raise ValueError("Can't parse {0!r} as ssh uri value".format(uri))
359
360
koder aka kdanilovbb5fe072015-05-21 02:50:23 +0300361def reconnect(conn, uri, **params):
362 if uri == 'local':
363 return conn
364
365 creds = parse_ssh_uri(uri)
366 creds.port = int(creds.port)
367 return ssh_connect(creds, reuse_conn=conn, **params)
368
369
koder aka kdanilov168f6092015-04-19 02:33:38 +0300370def connect(uri, **params):
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300371 if uri == 'local':
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300372 res = Local()
373 else:
374 creds = parse_ssh_uri(uri)
375 creds.port = int(creds.port)
376 res = ssh_connect(creds, **params)
377 return res
koder aka kdanilove06762a2015-03-22 23:32:09 +0200378
379
koder aka kdanilov652cd802015-04-13 12:21:07 +0300380all_sessions_lock = threading.Lock()
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300381all_sessions = {}
382
383
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300384class BGSSHTask(object):
koder aka kdanilov76471642015-08-14 11:44:43 +0300385 CHECK_RETRY = 5
386
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300387 def __init__(self, node, use_sudo):
388 self.node = node
389 self.pid = None
390 self.use_sudo = use_sudo
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300391
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300392 def start(self, orig_cmd, **params):
393 uniq_name = 'test'
394 cmd = "screen -S {0} -d -m {1}".format(uniq_name, orig_cmd)
395 run_over_ssh(self.node.connection, cmd,
396 timeout=10, node=self.node.get_conn_id(),
397 **params)
398 processes = run_over_ssh(self.node.connection, "ps aux", nolog=True)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300399
koder aka kdanilov76471642015-08-14 11:44:43 +0300400 for iter in range(self.CHECK_RETRY):
401 for proc in processes.split("\n"):
402 if orig_cmd in proc and "SCREEN" not in proc:
403 self.pid = proc.split()[1]
404 break
405 if self.pid is not None:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300406 break
koder aka kdanilov76471642015-08-14 11:44:43 +0300407 time.sleep(1)
408
409 if self.pid is None:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300410 self.pid = -1
411
412 def check_running(self):
413 assert self.pid is not None
koder aka kdanilov76471642015-08-14 11:44:43 +0300414 if -1 == self.pid:
415 return False
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300416 try:
417 run_over_ssh(self.node.connection,
418 "ls /proc/{0}".format(self.pid),
419 timeout=10, nolog=True)
420 return True
421 except OSError:
422 return False
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300423
424 def kill(self, soft=True, use_sudo=True):
425 assert self.pid is not None
koder aka kdanilov76471642015-08-14 11:44:43 +0300426 if self.pid == -1:
427 return True
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300428 try:
429 if soft:
430 cmd = "kill {0}"
431 else:
432 cmd = "kill -9 {0}"
433
434 if self.use_sudo:
435 cmd = "sudo " + cmd
436
437 run_over_ssh(self.node.connection,
438 cmd.format(self.pid), nolog=True)
439 return True
440 except OSError:
441 return False
442
443 def wait(self, soft_timeout, timeout):
444 end_of_wait_time = timeout + time.time()
445 soft_end_of_wait_time = soft_timeout + time.time()
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300446
koder aka kdanilova8253ce2015-06-13 03:10:21 +0300447 # time_till_check = random.randint(5, 10)
448 time_till_check = 2
449
450 # time_till_first_check = random.randint(2, 6)
451 time_till_first_check = 2
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300452 time.sleep(time_till_first_check)
453 if not self.check_running():
454 return True
455
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300456 while self.check_running() and time.time() < soft_end_of_wait_time:
koder aka kdanilov76471642015-08-14 11:44:43 +0300457 # time.sleep(soft_end_of_wait_time - time.time())
koder aka kdanilova94dfe12015-08-19 13:04:51 +0300458 time.sleep(time_till_check)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300459
460 while end_of_wait_time > time.time():
461 time.sleep(time_till_check)
462 if not self.check_running():
463 break
464 else:
465 self.kill()
koder aka kdanilova8253ce2015-06-13 03:10:21 +0300466 time.sleep(1)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300467 if self.check_running():
468 self.kill(soft=False)
469 return False
470 return True
koder aka kdanilove06762a2015-03-22 23:32:09 +0200471
koder aka kdanilove06762a2015-03-22 23:32:09 +0200472
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300473def run_over_ssh(conn, cmd, stdin_data=None, timeout=60,
474 nolog=False, node=None):
koder aka kdanilov652cd802015-04-13 12:21:07 +0300475 "should be replaces by normal implementation, with select"
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300476
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300477 if isinstance(conn, Local):
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300478 if not nolog:
479 logger.debug("SSH:local Exec {0!r}".format(cmd))
480 proc = subprocess.Popen(cmd, shell=True,
481 stdin=subprocess.PIPE,
482 stdout=subprocess.PIPE,
483 stderr=subprocess.STDOUT)
484
485 stdoutdata, _ = proc.communicate(input=stdin_data)
koder aka kdanilov0c598a12015-04-21 03:01:40 +0300486 if proc.returncode != 0:
487 templ = "SSH:{0} Cmd {1!r} failed with code {2}. Output: {3}"
488 raise OSError(templ.format(node, cmd, proc.returncode, stdoutdata))
489
490 return stdoutdata
491
koder aka kdanilov652cd802015-04-13 12:21:07 +0300492 transport = conn.get_transport()
493 session = transport.open_session()
koder aka kdanilove06762a2015-03-22 23:32:09 +0200494
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300495 if node is None:
496 node = ""
497
koder aka kdanilov652cd802015-04-13 12:21:07 +0300498 with all_sessions_lock:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300499 all_sessions[id(session)] = session
koder aka kdanilove06762a2015-03-22 23:32:09 +0200500
koder aka kdanilov652cd802015-04-13 12:21:07 +0300501 try:
502 session.set_combine_stderr(True)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200503
koder aka kdanilov652cd802015-04-13 12:21:07 +0300504 stime = time.time()
koder aka kdanilove06762a2015-03-22 23:32:09 +0200505
koder aka kdanilov652cd802015-04-13 12:21:07 +0300506 if not nolog:
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300507 logger.debug("SSH:{0} Exec {1!r}".format(node, cmd))
koder aka kdanilove06762a2015-03-22 23:32:09 +0200508
koder aka kdanilov652cd802015-04-13 12:21:07 +0300509 session.exec_command(cmd)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200510
koder aka kdanilov652cd802015-04-13 12:21:07 +0300511 if stdin_data is not None:
512 session.sendall(stdin_data)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200513
koder aka kdanilov652cd802015-04-13 12:21:07 +0300514 session.settimeout(1)
515 session.shutdown_write()
516 output = ""
517
518 while True:
519 try:
520 ndata = session.recv(1024)
521 output += ndata
522 if "" == ndata:
523 break
524 except socket.timeout:
525 pass
526
527 if time.time() - stime > timeout:
528 raise OSError(output + "\nExecution timeout")
529
530 code = session.recv_exit_status()
531 finally:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300532 found = False
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300533 with all_sessions_lock:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300534 if id(session) in all_sessions:
535 found = True
536 del all_sessions[id(session)]
537
538 if found:
539 session.close()
koder aka kdanilov652cd802015-04-13 12:21:07 +0300540
541 if code != 0:
koder aka kdanilov4500a5f2015-04-17 16:55:17 +0300542 templ = "SSH:{0} Cmd {1!r} failed with code {2}. Output: {3}"
543 raise OSError(templ.format(node, cmd, code, output))
koder aka kdanilov652cd802015-04-13 12:21:07 +0300544
545 return output
546
547
548def close_all_sessions():
549 with all_sessions_lock:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300550 for session in all_sessions.values():
koder aka kdanilov652cd802015-04-13 12:21:07 +0300551 try:
552 session.sendall('\x03')
553 session.close()
554 except:
555 pass
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300556 all_sessions.clear()