2 # -*- coding: utf-8 -*-
4 from constants import TESTBED_ID
9 import nepi.util.server as server
13 from nepi.util.constants import STATUS_NOT_STARTED, STATUS_RUNNING, \
16 class Application(object):
17 def __init__(self, api=None):
30 # Those are filled when the app is configured
32 self.ident_path = None
35 # Those are filled when an actual node is connected
38 # Those are filled when the app is started
39 # Having both pid and ppid makes it harder
40 # for pid rollover to induce tracking mistakes
46 return "%s<command:%s%s>" % (
47 self.__class__.__name__,
48 "sudo " if self.sudo else "",
53 if self.home_path is None:
54 raise AssertionError, "Misconfigured application: missing home path"
55 if self.ident_path is None or not os.access(self.ident_path, os.R_OK):
56 raise AssertionError, "Misconfigured application: missing slice SSH key"
58 raise AssertionError, "Misconfigured application: unconnected node"
59 if self.node.hostname is None:
60 raise AssertionError, "Misconfigured application: misconfigured node"
61 if self.slicename is None:
62 raise AssertionError, "Misconfigured application: unspecified slice"
65 # Start process in a "daemonized" way, using nohup and heavy
66 # stdin/out redirection to avoid connection issues
67 (out,err),proc = server.popen_ssh_command(
68 "cd %(home)s ; rm -f ./pid ; ( echo $$ $PPID > ./pid ; %(sudo)s nohup %(command)s > %(stdout)s 2> %(stderr)s < %(stdin)s ) &" % {
69 'home' : server.shell_escape(self.home_path),
70 'command' : self.command,
71 'stdout' : 'stdout' if self.stdout else '/dev/null' ,
72 'stderr' : 'stderr' if self.stderr else '/dev/null' ,
73 'stdin' : 'stdin' if self.stdin is not None else '/dev/null' ,
74 'sudo' : 'sudo' if self.sudo else '',
76 host = self.node.hostname,
78 user = self.slicename,
80 ident_key = self.ident_path
84 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
90 # NOTE: wait a bit for the pidfile to be created
91 if self._started and not self._pid or not self._ppid:
92 (out,err),proc = server.popen_ssh_command(
94 'pidfile' : server.shell_escape(os.path.join(self.home_path,'pid')),
96 host = self.node.hostname,
98 user = self.slicename,
100 ident_key = self.ident_path
104 self._pid, self._ppid = map(int,out.strip().split(' ',1))
106 # Ignore, many ways to fail that don't matter that much
111 if not self._started:
112 return STATUS_NOT_STARTED
113 elif not self._pid or not self._ppid:
114 return STATUS_NOT_STARTED
116 (out,err),proc = server.popen_ssh_command(
117 "ps --ppid $(ppid)d -o pid | grep -c $(pid)d" % {
121 host = self.node.hostname,
123 user = self.slicename,
125 ident_key = self.ident_path
131 status = bool(int(out.strip()))
133 # Ignore, many ways to fail that don't matter that much
135 return STATUS_RUNNING if status else STATUS_FINISHED
138 status = self.status()
139 if status == STATUS_RUNNING:
140 # kill by ppid+pid - SIGTERM first, then try SIGKILL
141 (out,err),proc = server.popen_ssh_command(
143 kill $(pid)d $(ppid)d
144 for x in 1 2 3 4 5 6 7 8 9 0 ; do
146 if [ `ps --pid $(ppid)d -o pid | grep -c $(pid)d` == `0` ]; then
151 if [ `ps --pid $(ppid)d -o pid | grep -c $(pid)d` != `0` ]; then
152 kill -9 $(pid)d $(ppid)d
158 host = self.node.hostname,
160 user = self.slicename,
162 ident_key = self.ident_path
168 status = bool(int(out.strip()))
170 # Ignore, many ways to fail that don't matter that much
172 return STATUS_RUNNING if status else STATUS_FINISHED
174 def remote_trace_path(self, whichtrace):
175 if whichtrace in ('stdout','stderr'):
176 tracefile = os.path.join(self.home_path, whichtrace)
182 def sync_trace(self, local_dir, whichtrace):
183 tracefile = self.remote_trace_path(whichtrace)
187 local_path = os.path.join(local_dir, tracefile)
189 # create parent local folders
190 proc = subprocess.Popen(
191 ["mkdir", "-p", os.path.dirname(local_path)],
192 stdout = open("/dev/null","w"),
193 stdin = open("/dev/null","r"))
196 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
199 (out,err),proc = server.popen_scp(
200 '%s@%s:%s' % (self.slicename, self.node.hostname,
205 ident_key = self.ident_path
209 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
215 # Make sure all the paths are created where
216 # they have to be created for deployment
217 (out,err),proc = server.popen_ssh_command(
218 "mkdir -p %s" % (server.shell_escape(self.home_path),),
219 host = self.node.hostname,
221 user = self.slicename,
223 ident_key = self.ident_path
227 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
231 # Write program input
232 (out,err),proc = server.popen_scp(
233 cStringIO.StringIO(self.stdin),
234 '%s@%s:%s' % (self.slicename, self.node.hostname,
235 os.path.join(self.home_path, 'stdin') ),
238 ident_key = self.ident_path
242 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)