+from neco.util.sshfuncs import RUNNING, FINISHED, NOT_STARTED, STDOUT
+
+import subprocess
+
+def lexec(command,
+ user = None,
+ sudo = False,
+ stdin = None,
+ env = None):
+ """
+ Executes a local command, returns ((stdout,stderr),process)
+ """
+ if env:
+ export = ''
+ for envkey, envval in env.iteritems():
+ export += '%s=%s ' % (envkey, envval)
+ command = "%s %s" % (export, command)
+
+ if sudo:
+ command = "sudo %s" % command
+ elif user:
+ command = "su %s ; %s " % (user, command)
+
+ p = subprocess.Popen(command,
+ stdout = subprocess.PIPE,
+ stderr = subprocess.PIPE,
+ stdin = stdin)
+
+ out, err = p.communicate()
+ return ((out, err), proc)
+
+def lcopy(source, dest, recursive = False):
+ """
+ Copies from/to localy.
+ """
+
+ if TRACE:
+ print "scp", source, dest
+
+ command = ["cp"]
+ if recursive:
+ command.append("-R")
+
+ command.append(src)
+ command.append(dst)
+
+ p = subprocess.Popen(command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+ out, err = p.communicate()
+ return ((out, err), proc)
+
+def lspawn(command, pidfile,
+ stdout = '/dev/null',
+ stderr = STDOUT,
+ stdin = '/dev/null',
+ home = None,
+ create_home = False,
+ sudo = False,
+ user = None):
+ """
+ Spawn a local command such that it will continue working asynchronously.
+
+ Parameters:
+ command: the command to run - it should be a single line.
+
+ pidfile: path of a (ideally unique to this task) pidfile for tracking the process.
+
+ stdout: path of a file to redirect standard output to - must be a string.
+ Defaults to /dev/null
+ stderr: path of a file to redirect standard error to - string or the special STDOUT value
+ to redirect to the same file stdout was redirected to. Defaults to STDOUT.
+ stdin: path of a file with input to be piped into the command's standard input
+
+ home: path of a folder to use as working directory - should exist, unless you specify create_home
+
+ create_home: if True, the home folder will be created first with mkdir -p
+
+ sudo: whether the command needs to be executed as root
+
+ Returns:
+ (stdout, stderr), process
+
+ Of the spawning process, which only captures errors at spawning time.
+ Usually only useful for diagnostics.
+ """
+ # Start process in a "daemonized" way, using nohup and heavy
+ # stdin/out redirection to avoid connection issues
+ if stderr is STDOUT:
+ stderr = '&1'
+ else:
+ stderr = ' ' + stderr
+
+ daemon_command = '{ { %(command)s > %(stdout)s 2>%(stderr)s < %(stdin)s & } ; echo $! 1 > %(pidfile)s ; }' % {
+ 'command' : command,
+ 'pidfile' : shell_escape(pidfile),
+ 'stdout' : stdout,
+ 'stderr' : stderr,
+ 'stdin' : stdin,
+ }
+
+ cmd = "%(create)s%(gohome)s rm -f %(pidfile)s ; %(sudo)s nohup bash -c %(command)s " % {
+ 'command' : shell_escape(daemon_command),
+ 'sudo' : 'sudo -S' if sudo else '',
+ 'pidfile' : shell_escape(pidfile),
+ 'gohome' : 'cd %s ; ' % (shell_escape(home),) if home else '',
+ 'create' : 'mkdir -p %s ; ' % (shell_escape(home),) if create_home else '',
+ }
+
+ (out,err),proc = lexec(cmd)
+
+ if proc.wait():
+ raise RuntimeError, "Failed to set up application on host %s: %s %s" % (host, out,err,)
+
+ return (out,err),proc
+
+def lcheckpid(pidfile):
+ """
+ Check the pidfile of a process spawned with remote_spawn.
+
+ Parameters:
+ pidfile: the pidfile passed to remote_span
+
+ Returns:
+
+ A (pid, ppid) tuple useful for calling remote_status and remote_kill,
+ or None if the pidfile isn't valid yet (maybe the process is still starting).
+ """
+
+ (out,err),proc = lexec("cat %s" % pidfile )
+
+ if proc.wait():
+ return None
+
+ if out:
+ try:
+ return map(int,out.strip().split(' ',1))
+ except:
+ # Ignore, many ways to fail that don't matter that much
+ return None
+
+def lstatus(pid, ppid):
+ """
+ Check the status of a process spawned with remote_spawn.
+
+ Parameters:
+ pid/ppid: pid and parent-pid of the spawned process. See remote_check_pid
+
+ Returns:
+
+ One of NOT_STARTED, RUNNING, FINISHED
+ """
+
+ (out,err),proc = lexec(
+ # Check only by pid. pid+ppid does not always work (especially with sudo)
+ " (( ps --pid %(pid)d -o pid | grep -c %(pid)d && echo 'wait') || echo 'done' ) | tail -n 1" % {
+ 'ppid' : ppid,
+ 'pid' : pid,
+ })
+
+ if proc.wait():
+ return NOT_STARTED
+
+ status = False
+ if out:
+ status = (out.strip() == 'wait')
+ else:
+ return NOT_STARTED
+ return RUNNING if status else FINISHED
+
+
+def lkill(pid, ppid, sudo = False):
+ """
+ Kill a process spawned with lspawn.
+
+ First tries a SIGTERM, and if the process does not end in 10 seconds,
+ it sends a SIGKILL.
+
+ Parameters:
+ pid/ppid: pid and parent-pid of the spawned process. See remote_check_pid
+
+ sudo: whether the command was run with sudo - careful killing like this.
+
+ Returns:
+
+ Nothing, should have killed the process
+ """
+
+ subkill = "$(ps --ppid %(pid)d -o pid h)" % { 'pid' : pid }
+ cmd = """
+SUBKILL="%(subkill)s" ;
+%(sudo)s kill -- -%(pid)d $SUBKILL || /bin/true
+%(sudo)s kill %(pid)d $SUBKILL || /bin/true
+for x in 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 ; do
+ sleep 0.2
+ if [ `ps --pid %(pid)d -o pid | grep -c %(pid)d` == '0' ]; then
+ break
+ else
+ %(sudo)s kill -- -%(pid)d $SUBKILL || /bin/true
+ %(sudo)s kill %(pid)d $SUBKILL || /bin/true
+ fi
+ sleep 1.8
+done
+if [ `ps --pid %(pid)d -o pid | grep -c %(pid)d` != '0' ]; then
+ %(sudo)s kill -9 -- -%(pid)d $SUBKILL || /bin/true
+ %(sudo)s kill -9 %(pid)d $SUBKILL || /bin/true
+fi
+"""
+ if nowait:
+ cmd = "( %s ) >/dev/null 2>/dev/null </dev/null &" % (cmd,)
+
+ (out,err),proc = lexec(
+ cmd % {
+ 'ppid' : ppid,
+ 'pid' : pid,
+ 'sudo' : 'sudo -S' if sudo else '',
+ 'subkill' : subkill,
+ })
+
+