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
30 Executes a local command, returns ((stdout,stderr),process)
34 for envkey, envval in env.iteritems():
35 export += '%s=%s ' % (envkey, envval)
36 command = "%s %s" % (export, command)
39 command = "sudo %s" % command
41 command = "su %s ; %s " % (user, command)
43 p = subprocess.Popen(command,
44 stdout = subprocess.PIPE,
45 stderr = subprocess.PIPE,
48 out, err = p.communicate()
49 return ((out, err), proc)
51 def lcopy(source, dest, recursive = False):
53 Copies from/to localy.
57 print "scp", source, dest
66 p = subprocess.Popen(command,
67 stdout=subprocess.PIPE,
68 stderr=subprocess.PIPE)
70 out, err = p.communicate()
71 return ((out, err), proc)
73 def lspawn(command, pidfile,
82 Spawn a local command such that it will continue working asynchronously.
85 command: the command to run - it should be a single line.
87 pidfile: path of a (ideally unique to this task) pidfile for tracking the process.
89 stdout: path of a file to redirect standard output to - must be a string.
91 stderr: path of a file to redirect standard error to - string or the special STDOUT value
92 to redirect to the same file stdout was redirected to. Defaults to STDOUT.
93 stdin: path of a file with input to be piped into the command's standard input
95 home: path of a folder to use as working directory - should exist, unless you specify create_home
97 create_home: if True, the home folder will be created first with mkdir -p
99 sudo: whether the command needs to be executed as root
102 (stdout, stderr), process
104 Of the spawning process, which only captures errors at spawning time.
105 Usually only useful for diagnostics.
107 # Start process in a "daemonized" way, using nohup and heavy
108 # stdin/out redirection to avoid connection issues
112 stderr = ' ' + stderr
114 daemon_command = '{ { %(command)s > %(stdout)s 2>%(stderr)s < %(stdin)s & } ; echo $! 1 > %(pidfile)s ; }' % {
116 'pidfile' : shell_escape(pidfile),
122 cmd = "%(create)s%(gohome)s rm -f %(pidfile)s ; %(sudo)s nohup bash -c %(command)s " % {
123 'command' : shell_escape(daemon_command),
124 'sudo' : 'sudo -S' if sudo else '',
125 'pidfile' : shell_escape(pidfile),
126 'gohome' : 'cd %s ; ' % (shell_escape(home),) if home else '',
127 'create' : 'mkdir -p %s ; ' % (shell_escape(home),) if create_home else '',
130 (out,err),proc = lexec(cmd)
133 raise RuntimeError, "Failed to set up application on host %s: %s %s" % (host, out,err,)
135 return (out,err),proc
137 def lgetpid(pidfile):
139 Check the pidfile of a process spawned with remote_spawn.
142 pidfile: the pidfile passed to remote_span
146 A (pid, ppid) tuple useful for calling remote_status and remote_kill,
147 or None if the pidfile isn't valid yet (maybe the process is still starting).
150 (out,err),proc = lexec("cat %s" % pidfile )
157 return map(int,out.strip().split(' ',1))
159 # Ignore, many ways to fail that don't matter that much
162 def lstatus(pid, ppid):
164 Check the status of a process spawned with remote_spawn.
167 pid/ppid: pid and parent-pid of the spawned process. See remote_check_pid
171 One of NOT_STARTED, RUNNING, FINISHED
174 (out,err),proc = lexec(
175 # Check only by pid. pid+ppid does not always work (especially with sudo)
176 " (( ps --pid %(pid)d -o pid | grep -c %(pid)d && echo 'wait') || echo 'done' ) | tail -n 1" % {
182 return ProcStatus.NOT_STARTED
186 status = (out.strip() == 'wait')
188 return ProcStatus.NOT_STARTED
189 return ProcStatus.RUNNING if status else ProcStatus.FINISHED
192 def lkill(pid, ppid, sudo = False):
194 Kill a process spawned with lspawn.
196 First tries a SIGTERM, and if the process does not end in 10 seconds,
200 pid/ppid: pid and parent-pid of the spawned process. See remote_check_pid
202 sudo: whether the command was run with sudo - careful killing like this.
206 Nothing, should have killed the process
209 subkill = "$(ps --ppid %(pid)d -o pid h)" % { 'pid' : pid }
211 SUBKILL="%(subkill)s" ;
212 %(sudo)s kill -- -%(pid)d $SUBKILL || /bin/true
213 %(sudo)s kill %(pid)d $SUBKILL || /bin/true
214 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
216 if [ `ps --pid %(pid)d -o pid | grep -c %(pid)d` == '0' ]; then
219 %(sudo)s kill -- -%(pid)d $SUBKILL || /bin/true
220 %(sudo)s kill %(pid)d $SUBKILL || /bin/true
224 if [ `ps --pid %(pid)d -o pid | grep -c %(pid)d` != '0' ]; then
225 %(sudo)s kill -9 -- -%(pid)d $SUBKILL || /bin/true
226 %(sudo)s kill -9 %(pid)d $SUBKILL || /bin/true
230 cmd = "( %s ) >/dev/null 2>/dev/null </dev/null &" % (cmd,)
232 (out,err),proc = lexec(
236 'sudo' : 'sudo -S' if sudo else '',