# Copyright (C) 2013 INRIA
#
# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation;
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
import time
import tempfile
+from six import PY2
+
_re_inet = re.compile("\d+:\s+(?P<name>[a-z0-9_-]+)\s+inet6?\s+(?P<inet>[a-f0-9.:/]+)\s+(brd\s+[0-9.]+)?.*scope\s+global.*")
logger = logging.getLogger("sshfuncs")
-def log(msg, level, out = None, err = None):
+def log(msg, level = logging.DEBUG, out = None, err = None):
if out:
msg += " - OUT: %s " % out
-
if err:
msg += " - ERROR: %s " % err
-
logger.log(level, msg)
if hasattr(os, "devnull"):
Special value that when given to rspawn in stderr causes stderr to
redirect to whatever stdout was redirected to.
"""
+ pass
class ProcStatus:
"""
ip = None
if host in ["localhost", "127.0.0.1", "::1"]:
- p = subprocess.Popen("ip -o addr list", shell=True,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ extras = {} if PY2 else {'universal_newlines' : True}
+ p = subprocess.Popen(
+ "ip -o addr list",
+ shell=True,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ **extras
+ )
stdout, stderr = p.communicate()
m = _re_inet.findall(stdout)
ip = m[0][1].split("/")[0]
"""
global OPENSSH_HAS_PERSIST
if OPENSSH_HAS_PERSIST is None:
- proc = subprocess.Popen(["ssh","-v"],
- stdout = subprocess.PIPE,
- stderr = subprocess.STDOUT,
- stdin = open("/dev/null","r") )
- out,err = proc.communicate()
- proc.wait()
+ extras = {} if PY2 else {'universal_newlines' : True}
+ with open("/dev/null") as null:
+ proc = subprocess.Popen(
+ ["ssh", "-v"],
+ stdout = subprocess.PIPE,
+ stderr = subprocess.STDOUT,
+ stdin = null,
+ **extras
+ )
+ out,err = proc.communicate()
+ proc.wait()
vre = re.compile(r'OpenSSH_(?:[6-9]|5[.][8-9]|5[.][1-9][0-9]|[1-9][0-9]).*', re.I)
OPENSSH_HAS_PERSIST = bool(vre.match(out))
if os.environ.get('NEPI_STRICT_AUTH_MODE',"").lower() not in ('1','true','on'):
user_hosts_path = '%s/.ssh/known_hosts' % (os.environ.get('HOME',""),)
if os.access(user_hosts_path, os.R_OK):
- f = open(user_hosts_path, "r")
- tmp_known_hosts.write(f.read())
- f.close()
+ with open(user_hosts_path, "r") as f:
+ tmp_known_hosts.write(f.read())
tmp_known_hosts.flush()
return s
else:
# unsafe string - escape
- def escp(c):
+ def escape(c):
if (32 <= ord(c) < 127 or c in ('\r','\n','\t')) and c not in ("'",'"'):
return c
else:
return "'$'\\x%02x''" % (ord(c),)
- s = ''.join(map(escp,s))
+ s = ''.join(map(escape, s))
return "'%s'" % (s,)
def eintr_retry(func):
@functools.wraps(func)
def rv(*p, **kw):
retry = kw.pop("_retry", False)
- for i in xrange(0 if retry else 4):
+ for i in range(0 if retry else 4):
try:
return func(*p, **kw)
- except (select.error, socket.error), args:
+ except (select.error, socket.error) as args:
if args[0] == errno.EINTR:
continue
else:
raise
- except OSError, e:
+ except OSError as e:
if e.errno == errno.EINTR:
continue
else:
stdout = stderr = stdin = None
return _retry_rexec(args, log_msg,
- stderr = stderr,
- stdin = stdin,
- stdout = stdout,
- env = env,
- retry = retry,
- tmp_known_hosts = tmp_known_hosts,
- blocking = blocking)
+ stderr = stderr,
+ stdin = stdin,
+ stdout = stdout,
+ env = env,
+ retry = retry,
+ tmp_known_hosts = tmp_known_hosts,
+ blocking = blocking)
def rcopy(source, dest,
port = None,
elif isinstance(source, str) and ':' in source:
remspec, path = source.split(':',1)
else:
- raise ValueError, "Both endpoints cannot be local"
+ raise ValueError("Both endpoints cannot be local")
user,host = remspec.rsplit('@',1)
# plain scp
tmp_known_hosts = None
args = ['scp', '-q', '-p', '-C',
+ # 2015-06-01 Thierry: I am commenting off blowfish
+ # as this is not available on a plain ubuntu 15.04 install
+ # this IMHO is too fragile, shoud be something the user
+ # decides explicitly (so he is at least aware of that dependency)
# Speed up transfer using blowfish cypher specification which is
# faster than the default one (3des)
- '-c', 'blowfish',
+ # '-c', 'blowfish',
# Don't bother with localhost. Makes test easier
'-o', 'NoHostAuthenticationForLocalhost=yes',
'-o', 'ConnectTimeout=60',
blocking = True)
def rspawn(command, pidfile,
- stdout = '/dev/null',
- stderr = STDOUT,
- stdin = '/dev/null',
- home = None,
- create_home = False,
- sudo = False,
- host = None,
- port = None,
- user = None,
- gwuser = None,
- gw = None,
- agent = None,
- identity = None,
- server_key = None,
- tty = False,
- strict_host_checking = True):
+ stdout = '/dev/null',
+ stderr = STDOUT,
+ stdin = '/dev/null',
+ home = None,
+ create_home = False,
+ sudo = False,
+ host = None,
+ port = None,
+ user = None,
+ gwuser = None,
+ gw = None,
+ agent = None,
+ identity = None,
+ server_key = None,
+ tty = False,
+ strict_host_checking = True):
"""
Spawn a remote command such that it will continue working asynchronously in
background.
)
if proc.wait():
- raise RuntimeError, "Failed to set up application on host %s: %s %s" % (host, out,err,)
+ raise RuntimeError("Failed to set up application on host %s: %s %s" % (host, out,err,))
return ((out, err), proc)
@eintr_retry
def rgetpid(pidfile,
- host = None,
- port = None,
- user = None,
- gwuser = None,
- gw = None,
- agent = None,
- identity = None,
- server_key = None,
- strict_host_checking = True):
+ host = None,
+ port = None,
+ user = None,
+ gwuser = None,
+ gw = None,
+ agent = None,
+ identity = None,
+ server_key = None,
+ strict_host_checking = True):
"""
Returns the pid and ppid of a process from a remote file where the
information was stored.
if out:
try:
- return map(int,out.strip().split(' ',1))
+ return [ int(x) for x in out.strip().split(' ', 1) ]
except:
# Ignore, many ways to fail that don't matter that much
return None
return (out, err), proc
def _retry_rexec(args,
- log_msg,
- stdout = subprocess.PIPE,
- stdin = subprocess.PIPE,
- stderr = subprocess.PIPE,
- env = None,
- retry = 3,
- tmp_known_hosts = None,
- blocking = True):
-
- for x in xrange(retry):
+ log_msg,
+ stdout = subprocess.PIPE,
+ stdin = subprocess.PIPE,
+ stderr = subprocess.PIPE,
+ env = None,
+ retry = 3,
+ tmp_known_hosts = None,
+ blocking = True):
+
+ for x in range(retry):
+ # display command actually invoked when debug is turned on
+ message = " ".join( [ "'{}'".format(arg) for arg in args ] )
+ log("sshfuncs: invoking {}".format(message), logging.DEBUG)
+ extras = {} if PY2 else {'universal_newlines' : True}
# connects to the remote host and starts a remote connection
- proc = subprocess.Popen(args,
- env = env,
- stdout = stdout,
- stdin = stdin,
- stderr = stderr)
-
+ proc = subprocess.Popen(
+ args,
+ env = env,
+ stdout = stdout,
+ stdin = stdin,
+ stderr = stderr,
+ **extras
+ )
# attach tempfile object to the process, to make sure the file stays
# alive until the process is finished with it
proc._known_hosts = tmp_known_hosts
# The method communicate was re implemented for performance issues
# when using python subprocess communicate method the ssh commands
# last one minute each
+ #log("BEFORE communicate", level=logging.INFO); import time; beg=time.time()
out, err = _communicate(proc, input=None)
+ #log("AFTER communicate - {}s".format(time.time()-beg), level=logging.INFO)
elif stdout:
out = proc.stdout.read()
time.sleep(t)
continue
break
- except RuntimeError, e:
+ except RuntimeError as e:
msg = " rexec EXCEPTION - TIMEOUT -> %s \n %s" % ( e.args, log_msg )
log(msg, logging.DEBUG, out, err)
try:
rlist, wlist, xlist = select.select(read_set, write_set, [], select_timeout)
- except select.error,e:
+ except select.error as e:
if e[0] != 4:
raise
else:
proc.stdin.close()
write_set.remove(proc.stdin)
+ # xxx possible distortion when upgrading to python3
+ # original py2 version used to do
+ # data = os.read(proc.stdout.fileno(), 1024)
+ # but this would return bytes, so..
if proc.stdout in rlist:
- data = os.read(proc.stdout.fileno(), 1024)
- if data == "":
+ data = proc.stdout.read()
+ if not data:
proc.stdout.close()
read_set.remove(proc.stdout)
stdout.append(data)
+ # likewise
if proc.stderr in rlist:
- data = os.read(proc.stderr.fileno(), 1024)
- if data == "":
+ data = proc.stderr.read()
+ if not data:
proc.stderr.close()
read_set.remove(proc.stderr)
stderr.append(data)
# object do the translation: It is based on stdio, which is
# impossible to combine with select (unless forcing no
# buffering).
- if proc.universal_newlines and hasattr(file, 'newlines'):
- if stdout:
- stdout = proc._translate_newlines(stdout)
- if stderr:
- stderr = proc._translate_newlines(stderr)
+ # this however seems to make no sense in the context of python3
+ if PY2:
+ if proc.universal_newlines and hasattr(file, 'newlines'):
+ if stdout:
+ stdout = proc._translate_newlines(stdout)
+ if stderr:
+ stderr = proc._translate_newlines(stderr)
if killed and err_on_timeout:
errcode = proc.poll()
- raise RuntimeError, ("Operation timed out", errcode, stdout, stderr)
+ raise RuntimeError("Operation timed out", errcode, stdout, stderr)
else:
if killed:
proc.poll()