2 # NEPI, a framework to manage network experiments
3 # Copyright (C) 2013 INRIA
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
20 from nepi.util.sshfuncs import ProcStatus, STDOUT
29 Executes a local command, returns ((stdout,stderr),process)
33 for envkey, envval in env.iteritems():
34 export += '%s=%s ' % (envkey, envval)
35 command = "%s %s" % (export, command)
38 command = "sudo %s" % command
40 command = "su %s ; %s " % (user, command)
43 proc = subprocess.Popen(command, shell=True,
44 stdout = subprocess.PIPE,
45 stderr = subprocess.PIPE)
47 out, err = proc.communicate()
48 return ((out, err), proc)
50 def lcopy(source, dest, recursive = False):
52 Copies from/to localy.
56 print "scp", source, dest
62 if isinstance(dest, str):
63 dest = dest.split(";")
65 if isinstance(src, str):
72 proc = subprocess.Popen(command,
73 stdout=subprocess.PIPE,
74 stderr=subprocess.PIPE)
76 out, err = proc.communicate()
77 return ((out, err), proc)
79 def lspawn(command, pidfile,
88 Spawn a local command such that it will continue working asynchronously.
91 command: the command to run - it should be a single line.
93 pidfile: path of a (ideally unique to this task) pidfile for tracking the process.
95 stdout: path of a file to redirect standard output to - must be a string.
97 stderr: path of a file to redirect standard error to - string or the special STDOUT value
98 to redirect to the same file stdout was redirected to. Defaults to STDOUT.
99 stdin: path of a file with input to be piped into the command's standard input
101 home: path of a folder to use as working directory - should exist, unless you specify create_home
103 create_home: if True, the home folder will be created first with mkdir -p
105 sudo: whether the command needs to be executed as root
108 (stdout, stderr), process
110 Of the spawning process, which only captures errors at spawning time.
111 Usually only useful for diagnostics.
113 # Start process in a "daemonized" way, using nohup and heavy
114 # stdin/out redirection to avoid connection issues
118 stderr = ' ' + stderr
120 daemon_command = '{ { %(command)s > %(stdout)s 2>%(stderr)s < %(stdin)s & } ; echo $! 1 > %(pidfile)s ; }' % {
122 'pidfile' : shell_escape(pidfile),
128 cmd = "%(create)s%(gohome)s rm -f %(pidfile)s ; %(sudo)s nohup bash -c %(command)s " % {
129 'command' : shell_escape(daemon_command),
130 'sudo' : 'sudo -S' if sudo else '',
131 'pidfile' : shell_escape(pidfile),
132 'gohome' : 'cd %s ; ' % (shell_escape(home),) if home else '',
133 'create' : 'mkdir -p %s ; ' % (shell_escape(home),) if create_home else '',
136 (out,err), proc = lexec(cmd)
139 raise RuntimeError, "Failed to set up application on host %s: %s %s" % (host, out,err,)
141 return ((out,err), proc)
143 def lgetpid(pidfile):
145 Check the pidfile of a process spawned with remote_spawn.
148 pidfile: the pidfile passed to remote_span
152 A (pid, ppid) tuple useful for calling remote_status and remote_kill,
153 or None if the pidfile isn't valid yet (maybe the process is still starting).
156 (out,err), proc = lexec("cat %s" % pidfile )
163 return map(int,out.strip().split(' ',1))
165 # Ignore, many ways to fail that don't matter that much
168 def lstatus(pid, ppid):
170 Check the status of a process spawned with remote_spawn.
173 pid/ppid: pid and parent-pid of the spawned process. See remote_check_pid
177 One of NOT_STARTED, RUNNING, FINISHED
180 (out,err), proc = lexec(
181 # Check only by pid. pid+ppid does not always work (especially with sudo)
182 " (( ps --pid %(pid)d -o pid | grep -c %(pid)d && echo 'wait') || echo 'done' ) | tail -n 1" % {
188 return ProcStatus.NOT_STARTED
192 status = (out.strip() == 'wait')
194 return ProcStatus.NOT_STARTED
196 return ProcStatus.RUNNING if status else ProcStatus.FINISHED
198 def lkill(pid, ppid, sudo = False):
200 Kill a process spawned with lspawn.
202 First tries a SIGTERM, and if the process does not end in 10 seconds,
206 pid/ppid: pid and parent-pid of the spawned process. See remote_check_pid
208 sudo: whether the command was run with sudo - careful killing like this.
212 Nothing, should have killed the process
215 subkill = "$(ps --ppid %(pid)d -o pid h)" % { 'pid' : pid }
217 SUBKILL="%(subkill)s" ;
218 %(sudo)s kill -- -%(pid)d $SUBKILL || /bin/true
219 %(sudo)s kill %(pid)d $SUBKILL || /bin/true
220 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
222 if [ `ps --pid %(pid)d -o pid | grep -c %(pid)d` == '0' ]; then
225 %(sudo)s kill -- -%(pid)d $SUBKILL || /bin/true
226 %(sudo)s kill %(pid)d $SUBKILL || /bin/true
230 if [ `ps --pid %(pid)d -o pid | grep -c %(pid)d` != '0' ]; then
231 %(sudo)s kill -9 -- -%(pid)d $SUBKILL || /bin/true
232 %(sudo)s kill -9 %(pid)d $SUBKILL || /bin/true
236 cmd = "( %s ) >/dev/null 2>/dev/null </dev/null &" % (cmd,)
238 (out,err),proc = lexec(
242 'sudo' : 'sudo -S' if sudo else '',