blind review on list-operations added by 2to3:
[nepi.git] / src / nepi / util / sshfuncs.py
index a6aeb14..f33b9c6 100644 (file)
@@ -38,13 +38,11 @@ _re_inet = re.compile("\d+:\s+(?P<name>[a-z0-9_-]+)\s+inet6?\s+(?P<inet>[a-f0-9.
 
 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"):
@@ -59,6 +57,7 @@ class STDOUT:
     Special value that when given to rspawn in stderr causes stderr to 
     redirect to whatever stdout was redirected to.
     """
+    pass
 
 class ProcStatus:
     """
@@ -80,8 +79,13 @@ def resolve_hostname(host):
     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)
+        p = subprocess.Popen(
+            "ip -o addr list",
+            shell=True,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            universal_newlines = True,
+        )
         stdout, stderr = p.communicate()
         m = _re_inet.findall(stdout)
         ip = m[0][1].split("/")[0]
@@ -117,10 +121,13 @@ def openssh_has_persist():
     """
     global OPENSSH_HAS_PERSIST
     if OPENSSH_HAS_PERSIST is None:
-        proc = subprocess.Popen(["ssh","-v"],
+        proc = subprocess.Popen(
+            ["ssh", "-v"],
             stdout = subprocess.PIPE,
             stderr = subprocess.STDOUT,
-            stdin = open("/dev/null","r") )
+            stdin = subprocess.DEVNULL,
+            universal_newlines = True,
+        )
         out,err = proc.communicate()
         proc.wait()
         
@@ -189,12 +196,12 @@ def shell_escape(s):
         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):
@@ -303,13 +310,13 @@ def rexec(command, host, user,
         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,
@@ -403,22 +410,22 @@ def rcopy(source, dest,
             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. 
@@ -504,15 +511,15 @@ def rspawn(command, pidfile,
 
 @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.
@@ -549,7 +556,7 @@ def rgetpid(pidfile,
     
     if out:
         try:
-            return list(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
@@ -683,26 +690,28 @@ fi
     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):
+                 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)
         # 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,
+            universal_newlines = True,
+        )        
         # 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
@@ -716,7 +725,9 @@ def _retry_rexec(args,
                 # 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()
@@ -833,15 +844,25 @@ def _communicate(proc, input, timeout=None, err_on_timeout=True):
                 write_set.remove(proc.stdin)
 
         if proc.stdout in rlist:
-            data = os.read(proc.stdout.fileno(), 1024)
-            if data == "":
+            # python2 version used to do this
+            # data = os.read(proc.stdout.fileno(), 1024)
+            # however this always returned bytes...
+            data = proc.stdout.read()
+            log('we have read {}'.format(data))
+            # data should be str and not bytes because we use
+            # universal_lines = True, but to be clean
+            # instead of saying data != "" 
+            if not data:
+                log('closing stdout')
                 proc.stdout.close()
                 read_set.remove(proc.stdout)
             stdout.append(data)
 
         if proc.stderr in rlist:
-            data = os.read(proc.stderr.fileno(), 1024)
-            if data == "":
+            # likewise (see above)
+            # data = os.read(proc.stderr.fileno(), 1024)
+            data = proc.stderr.read()
+            if not data:
                 proc.stderr.close()
                 read_set.remove(proc.stderr)
             stderr.append(data)
@@ -852,15 +873,15 @@ def _communicate(proc, input, timeout=None, err_on_timeout=True):
     if stderr is not None:
         stderr = ''.join(stderr)
 
-    # Translate newlines, if requested.  We cannot let the file
-    # 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)
+#    # Translate newlines, if requested.  We cannot let the file
+#    # 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)
 
     if killed and err_on_timeout:
         errcode = proc.poll()