2 # -*- coding: utf-8 -*-
4 from constants import TESTBED_ID
9 import nepi.util.server as server
14 from nepi.util.constants import STATUS_NOT_STARTED, STATUS_RUNNING, \
17 class Application(object):
18 def __init__(self, api=None):
30 self.buildDepends = None
38 # Those are filled when the app is configured
40 self.ident_path = None
43 # Those are filled when an actual node is connected
46 # Those are filled when the app is started
47 # Having both pid and ppid makes it harder
48 # for pid rollover to induce tracking mistakes
56 return "%s<command:%s%s>" % (
57 self.__class__.__name__,
58 "sudo " if self.sudo else "",
63 if self.home_path is None:
64 raise AssertionError, "Misconfigured application: missing home path"
65 if self.ident_path is None or not os.access(self.ident_path, os.R_OK):
66 raise AssertionError, "Misconfigured application: missing slice SSH key"
68 raise AssertionError, "Misconfigured application: unconnected node"
69 if self.node.hostname is None:
70 raise AssertionError, "Misconfigured application: misconfigured node"
71 if self.slicename is None:
72 raise AssertionError, "Misconfigured application: unspecified slice"
75 # Create shell script with the command
76 # This way, complex commands and scripts can be ran seamlessly
78 (out,err),proc = server.popen_scp(
79 cStringIO.StringIO(self.command),
80 '%s@%s:%s' % (self.slicename, self.node.hostname,
81 os.path.join(self.home_path, "app.sh")),
84 ident_key = self.ident_path,
85 server_key = self.node.server_key
89 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
91 # Start process in a "daemonized" way, using nohup and heavy
92 # stdin/out redirection to avoid connection issues
93 (out,err),proc = rspawn.remote_spawn(
94 self._replace_paths("bash ./app.sh"),
97 home = self.home_path,
98 stdin = 'stdin' if self.stdin is not None else '/dev/null',
99 stdout = 'stdout' if self.stdout else '/dev/null',
100 stderr = 'stderr' if self.stderr else '/dev/null',
103 host = self.node.hostname,
105 user = self.slicename,
107 ident_key = self.ident_path,
108 server_key = self.node.server_key
112 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
118 # NOTE: wait a bit for the pidfile to be created
119 if self._started and not self._pid or not self._ppid:
120 pidtuple = rspawn.remote_check_pid(
121 os.path.join(self.home_path,'pid'),
122 host = self.node.hostname,
124 user = self.slicename,
126 ident_key = self.ident_path,
127 server_key = self.node.server_key
131 self._pid, self._ppid = pidtuple
135 if not self._started:
136 return STATUS_NOT_STARTED
137 elif not self._pid or not self._ppid:
138 return STATUS_NOT_STARTED
140 status = rspawn.remote_status(
141 self._pid, self._ppid,
142 host = self.node.hostname,
144 user = self.slicename,
146 ident_key = self.ident_path
149 if status is rspawn.NOT_STARTED:
150 return STATUS_NOT_STARTED
151 elif status is rspawn.RUNNING:
152 return STATUS_RUNNING
153 elif status is rspawn.FINISHED:
154 return STATUS_FINISHED
157 return STATUS_NOT_STARTED
160 status = self.status()
161 if status == STATUS_RUNNING:
162 # kill by ppid+pid - SIGTERM first, then try SIGKILL
164 self._pid, self._ppid,
165 host = self.node.hostname,
167 user = self.slicename,
169 ident_key = self.ident_path,
170 server_key = self.node.server_key
173 def remote_trace_path(self, whichtrace):
174 if whichtrace in ('stdout','stderr'):
175 tracefile = os.path.join(self.home_path, whichtrace)
181 def sync_trace(self, local_dir, whichtrace):
182 tracefile = self.remote_trace_path(whichtrace)
186 local_path = os.path.join(local_dir, tracefile)
188 # create parent local folders
189 proc = subprocess.Popen(
190 ["mkdir", "-p", os.path.dirname(local_path)],
191 stdout = open("/dev/null","w"),
192 stdin = open("/dev/null","r"))
195 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
198 (out,err),proc = server.popen_scp(
199 '%s@%s:%s' % (self.slicename, self.node.hostname,
204 ident_key = self.ident_path,
205 server_key = self.node.server_key
209 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
219 def async_setup(self):
220 if not self._setuper:
221 self._setuper = threading.Thread(
223 self._setuper.start()
225 def async_setup_wait(self):
230 raise RuntimeError, "Failed to setup application"
234 def _make_home(self):
235 # Make sure all the paths are created where
236 # they have to be created for deployment
237 (out,err),proc = server.popen_ssh_command(
238 "mkdir -p %s" % (server.shell_escape(self.home_path),),
239 host = self.node.hostname,
241 user = self.slicename,
243 ident_key = self.ident_path,
244 server_key = self.node.server_key
248 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
252 # Write program input
253 (out,err),proc = server.popen_scp(
254 cStringIO.StringIO(self.stdin),
255 '%s@%s:%s' % (self.slicename, self.node.hostname,
256 os.path.join(self.home_path, 'stdin') ),
259 ident_key = self.ident_path,
260 server_key = self.node.server_key
264 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
266 def _replace_paths(self, command):
268 Replace all special path tags with shell-escaped actual paths.
270 # need to append ${HOME} if paths aren't absolute, to MAKE them absolute.
271 root = '' if self.home_path.startswith('/') else "${HOME}/"
273 .replace("${SOURCES}", root+server.shell_escape(self.home_path))
274 .replace("${BUILD}", root+server.shell_escape(os.path.join(self.home_path,'build'))) )
278 sources = self.sources.split(' ')
281 (out,err),proc = server.popen_scp(
283 "%s@%s:%s" % (self.slicename, self.node.hostname,
284 os.path.join(self.home_path,'.'),),
285 ident_key = self.ident_path,
286 server_key = self.node.server_key
290 raise RuntimeError, "Failed upload source file %r: %s %s" % (source, out,err,)
292 if self.buildDepends:
293 # Install build dependencies
294 (out,err),proc = server.popen_ssh_command(
295 "sudo -S yum -y install %(packages)s" % {
296 'packages' : self.buildDepends
298 host = self.node.hostname,
300 user = self.slicename,
302 ident_key = self.ident_path,
303 server_key = self.node.server_key
307 raise RuntimeError, "Failed instal build dependencies: %s %s" % (out,err,)
312 (out,err),proc = server.popen_ssh_command(
313 "cd %(home)s && mkdir -p build && cd build && %(command)s" % {
314 'command' : self._replace_paths(self.build),
315 'home' : server.shell_escape(self.home_path),
317 host = self.node.hostname,
319 user = self.slicename,
321 ident_key = self.ident_path,
322 server_key = self.node.server_key
326 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
329 (out,err),proc = server.popen_ssh_command(
330 "cd %(home)s && tar czf build.tar.gz build" % {
331 'command' : self._replace_paths(self.build),
332 'home' : server.shell_escape(self.home_path),
334 host = self.node.hostname,
336 user = self.slicename,
338 ident_key = self.ident_path,
339 server_key = self.node.server_key
343 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
346 # Install application
347 (out,err),proc = server.popen_ssh_command(
348 "cd %(home)s && cd build && %(command)s" % {
349 'command' : self._replace_paths(self.install),
350 'home' : server.shell_escape(self.home_path),
352 host = self.node.hostname,
354 user = self.slicename,
356 ident_key = self.ident_path,
357 server_key = self.node.server_key
361 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)