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, log, shell_escape
31 Executes a local command, returns ((stdout,stderr),process)
35 for envkey, envval in env.iteritems():
36 export += '%s=%s ' % (envkey, envval)
37 command = "%s %s" % (export, command)
40 command = "sudo %s" % command
42 # XXX: Doing 'su user' blocks waiting for a password on stdin
44 # command = "su %s ; %s " % (user, command)
46 proc = subprocess.Popen(command,
48 stdout = subprocess.PIPE,
49 stderr = subprocess.PIPE)
52 log_msg = "lexec - command %s " % command
55 out, err = proc.communicate()
56 log(log_msg, logging.DEBUG, out, err)
58 log(log_msg, logging.ERROR, out, err)
61 return ((out, err), proc)
63 def lcopy(source, dest, recursive = False):
65 Copies from/to localy.
72 if isinstance(source, list):
77 if isinstance(dest, list):
82 proc = subprocess.Popen(args,
83 stdout=subprocess.PIPE,
84 stderr=subprocess.PIPE)
87 command = " ".join(args)
88 log_msg = " lcopy - command %s " % command
91 out, err = proc.communicate()
92 log(log_msg, logging.DEBUG, out, err)
94 log(log_msg, logging.ERROR, out, err)
97 return ((out, err), proc)
99 def lspawn(command, pidfile,
100 stdout = '/dev/null',
108 Spawn a local command such that it will continue working asynchronously.
111 command: the command to run - it should be a single line.
113 pidfile: path of a (ideally unique to this task) pidfile for tracking the process.
115 stdout: path of a file to redirect standard output to - must be a string.
116 Defaults to /dev/null
117 stderr: path of a file to redirect standard error to - string or the special STDOUT value
118 to redirect to the same file stdout was redirected to. Defaults to STDOUT.
119 stdin: path of a file with input to be piped into the command's standard input
121 home: path of a folder to use as working directory - should exist, unless you specify create_home
123 create_home: if True, the home folder will be created first with mkdir -p
125 sudo: whether the command needs to be executed as root
128 (stdout, stderr), process
130 Of the spawning process, which only captures errors at spawning time.
131 Usually only useful for diagnostics.
133 # Start process in a "daemonized" way, using nohup and heavy
134 # stdin/out redirection to avoid connection issues
138 stderr = ' ' + stderr
140 daemon_command = '{ { %(command)s > %(stdout)s 2>%(stderr)s < %(stdin)s & } ; echo $! 1 > %(pidfile)s ; }' % {
142 'pidfile' : shell_escape(pidfile),
148 cmd = "%(create)s%(gohome)s rm -f %(pidfile)s ; %(sudo)s bash -c %(command)s " % {
149 'command' : shell_escape(daemon_command),
150 'sudo' : 'sudo -S' if sudo else '',
151 'pidfile' : shell_escape(pidfile),
152 'gohome' : 'cd %s ; ' % (shell_escape(home),) if home else '',
153 'create' : 'mkdir -p %s ; ' % (shell_escape(home),) if create_home else '',
156 (out,err), proc = lexec(cmd)
159 raise RuntimeError, "Failed to set up application on host %s: %s %s" % (host, out,err,)
161 return ((out,err), proc)
163 def lgetpid(pidfile):
165 Check the pidfile of a process spawned with remote_spawn.
168 pidfile: the pidfile passed to remote_span
172 A (pid, ppid) tuple useful for calling remote_status and remote_kill,
173 or None if the pidfile isn't valid yet (maybe the process is still starting).
176 (out,err), proc = lexec("cat %s" % pidfile )
183 return map(int,out.strip().split(' ',1))
185 # Ignore, many ways to fail that don't matter that much
188 def lstatus(pid, ppid):
190 Check the status of a process spawned with remote_spawn.
193 pid/ppid: pid and parent-pid of the spawned process. See remote_check_pid
197 One of NOT_STARTED, RUNNING, FINISHED
200 (out,err), proc = lexec(
201 # Check only by pid. pid+ppid does not always work (especially with sudo)
202 " (( ps --pid %(pid)d -o pid | grep -c %(pid)d && echo 'wait') || echo 'done' ) | tail -n 1" % {
208 return ProcStatus.NOT_STARTED
212 status = (out.strip() == 'wait')
214 return ProcStatus.NOT_STARTED
216 return ProcStatus.RUNNING if status else ProcStatus.FINISHED
218 def lkill(pid, ppid, sudo = False):
220 Kill a process spawned with lspawn.
222 First tries a SIGTERM, and if the process does not end in 10 seconds,
226 pid/ppid: pid and parent-pid of the spawned process. See remote_check_pid
228 sudo: whether the command was run with sudo - careful killing like this.
232 Nothing, should have killed the process
235 subkill = "$(ps --ppid %(pid)d -o pid h)" % { 'pid' : pid }
237 SUBKILL="%(subkill)s" ;
238 %(sudo)s kill -- -%(pid)d $SUBKILL || /bin/true
239 %(sudo)s kill %(pid)d $SUBKILL || /bin/true
240 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
242 if [ `ps --pid %(pid)d -o pid | grep -c %(pid)d` == '0' ]; then
245 %(sudo)s kill -- -%(pid)d $SUBKILL || /bin/true
246 %(sudo)s kill %(pid)d $SUBKILL || /bin/true
250 if [ `ps --pid %(pid)d -o pid | grep -c %(pid)d` != '0' ]; then
251 %(sudo)s kill -9 -- -%(pid)d $SUBKILL || /bin/true
252 %(sudo)s kill -9 %(pid)d $SUBKILL || /bin/true
256 cmd = "( %s ) >/dev/null 2>/dev/null </dev/null &" % (cmd,)
258 (out,err),proc = lexec(
262 'sudo' : 'sudo -S' if sudo else '',