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
54 return "%s<command:%s%s>" % (
55 self.__class__.__name__,
56 "sudo " if self.sudo else "",
61 if self.home_path is None:
62 raise AssertionError, "Misconfigured application: missing home path"
63 if self.ident_path is None or not os.access(self.ident_path, os.R_OK):
64 raise AssertionError, "Misconfigured application: missing slice SSH key"
66 raise AssertionError, "Misconfigured application: unconnected node"
67 if self.node.hostname is None:
68 raise AssertionError, "Misconfigured application: misconfigured node"
69 if self.slicename is None:
70 raise AssertionError, "Misconfigured application: unspecified slice"
73 # Create shell script with the command
74 # This way, complex commands and scripts can be ran seamlessly
76 (out,err),proc = server.popen_scp(
77 cStringIO.StringIO(self.command),
78 '%s@%s:%s' % (self.slicename, self.node.hostname,
79 os.path.join(self.home_path, "app.sh")),
82 ident_key = self.ident_path,
83 server_key = self.node.server_key
87 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
89 # Start process in a "daemonized" way, using nohup and heavy
90 # stdin/out redirection to avoid connection issues
91 (out,err),proc = rspawn.remote_spawn(
92 self._replace_paths("bash ./app.sh"),
95 home = self.home_path,
96 stdin = 'stdin' if self.stdin is not None else '/dev/null',
97 stdout = 'stdout' if self.stdout else '/dev/null',
98 stderr = 'stderr' if self.stderr else '/dev/null',
101 host = self.node.hostname,
103 user = self.slicename,
105 ident_key = self.ident_path,
106 server_key = self.node.server_key
110 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
116 # NOTE: wait a bit for the pidfile to be created
117 if self._started and not self._pid or not self._ppid:
118 pidtuple = rspawn.remote_check_pid(
119 os.path.join(self.home_path,'pid'),
120 host = self.node.hostname,
122 user = self.slicename,
124 ident_key = self.ident_path,
125 server_key = self.node.server_key
129 self._pid, self._ppid = pidtuple
133 if not self._started:
134 return STATUS_NOT_STARTED
135 elif not self._pid or not self._ppid:
136 return STATUS_NOT_STARTED
138 status = rspawn.remote_status(
139 self._pid, self._ppid,
140 host = self.node.hostname,
142 user = self.slicename,
144 ident_key = self.ident_path
147 if status is rspawn.NOT_STARTED:
148 return STATUS_NOT_STARTED
149 elif status is rspawn.RUNNING:
150 return STATUS_RUNNING
151 elif status is rspawn.FINISHED:
152 return STATUS_FINISHED
155 return STATUS_NOT_STARTED
158 status = self.status()
159 if status == STATUS_RUNNING:
160 # kill by ppid+pid - SIGTERM first, then try SIGKILL
162 self._pid, self._ppid,
163 host = self.node.hostname,
165 user = self.slicename,
167 ident_key = self.ident_path,
168 server_key = self.node.server_key
171 def remote_trace_path(self, whichtrace):
172 if whichtrace in ('stdout','stderr'):
173 tracefile = os.path.join(self.home_path, whichtrace)
179 def sync_trace(self, local_dir, whichtrace):
180 tracefile = self.remote_trace_path(whichtrace)
184 local_path = os.path.join(local_dir, tracefile)
186 # create parent local folders
187 proc = subprocess.Popen(
188 ["mkdir", "-p", os.path.dirname(local_path)],
189 stdout = open("/dev/null","w"),
190 stdin = open("/dev/null","r"))
193 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
196 (out,err),proc = server.popen_scp(
197 '%s@%s:%s' % (self.slicename, self.node.hostname,
202 ident_key = self.ident_path,
203 server_key = self.node.server_key
207 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
216 def _make_home(self):
217 # Make sure all the paths are created where
218 # they have to be created for deployment
219 (out,err),proc = server.popen_ssh_command(
220 "mkdir -p %s" % (server.shell_escape(self.home_path),),
221 host = self.node.hostname,
223 user = self.slicename,
225 ident_key = self.ident_path,
226 server_key = self.node.server_key
230 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
234 # Write program input
235 (out,err),proc = server.popen_scp(
236 cStringIO.StringIO(self.stdin),
237 '%s@%s:%s' % (self.slicename, self.node.hostname,
238 os.path.join(self.home_path, 'stdin') ),
241 ident_key = self.ident_path,
242 server_key = self.node.server_key
246 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
248 def _replace_paths(self, command):
250 Replace all special path tags with shell-escaped actual paths.
252 # need to append ${HOME} if paths aren't absolute, to MAKE them absolute.
253 root = '' if self.home_path.startswith('/') else "${HOME}/"
255 .replace("${SOURCES}", root+server.shell_escape(self.home_path))
256 .replace("${BUILD}", root+server.shell_escape(os.path.join(self.home_path,'build'))) )
260 sources = self.sources.split(' ')
263 (out,err),proc = server.popen_scp(
265 "%s@%s:%s" % (self.slicename, self.node.hostname,
266 os.path.join(self.home_path,'.'),),
267 ident_key = self.ident_path,
268 server_key = self.node.server_key
272 raise RuntimeError, "Failed upload source file %r: %s %s" % (source, out,err,)
274 if self.buildDepends:
275 # Install build dependencies
276 (out,err),proc = server.popen_ssh_command(
277 "sudo -S yum -y install %(packages)s" % {
278 'packages' : self.buildDepends
280 host = self.node.hostname,
282 user = self.slicename,
284 ident_key = self.ident_path,
285 server_key = self.node.server_key
289 raise RuntimeError, "Failed instal build dependencies: %s %s" % (out,err,)
294 (out,err),proc = server.popen_ssh_command(
295 "cd %(home)s && mkdir -p build && cd build && %(command)s" % {
296 'command' : self._replace_paths(self.build),
297 'home' : server.shell_escape(self.home_path),
299 host = self.node.hostname,
301 user = self.slicename,
303 ident_key = self.ident_path,
304 server_key = self.node.server_key
308 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
311 (out,err),proc = server.popen_ssh_command(
312 "cd %(home)s && tar czf build.tar.gz build" % {
313 'command' : self._replace_paths(self.build),
314 'home' : server.shell_escape(self.home_path),
316 host = self.node.hostname,
318 user = self.slicename,
320 ident_key = self.ident_path,
321 server_key = self.node.server_key
325 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
328 # Install application
329 (out,err),proc = server.popen_ssh_command(
330 "cd %(home)s && cd build && %(command)s" % {
331 'command' : self._replace_paths(self.install),
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,)