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 # Start process in a "daemonized" way, using nohup and heavy
74 # stdin/out redirection to avoid connection issues
75 (out,err),proc = rspawn.remote_spawn(
76 self._replace_paths(self.command),
79 home = self.home_path,
80 stdin = 'stdin' if self.stdin is not None else '/dev/null',
81 stdout = 'stdout' if self.stdout else '/dev/null',
82 stderr = 'stderr' if self.stderr else '/dev/null',
85 host = self.node.hostname,
87 user = self.slicename,
89 ident_key = self.ident_path,
90 server_key = self.node.server_key
94 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
100 # NOTE: wait a bit for the pidfile to be created
101 if self._started and not self._pid or not self._ppid:
102 pidtuple = rspawn.remote_check_pid(
103 os.path.join(self.home_path,'pid'),
104 host = self.node.hostname,
106 user = self.slicename,
108 ident_key = self.ident_path,
109 server_key = self.node.server_key
113 self._pid, self._ppid = pidtuple
117 if not self._started:
118 return STATUS_NOT_STARTED
119 elif not self._pid or not self._ppid:
120 return STATUS_NOT_STARTED
122 status = rspawn.remote_status(
123 self._pid, self._ppid,
124 host = self.node.hostname,
126 user = self.slicename,
128 ident_key = self.ident_path
131 if status is rspawn.NOT_STARTED:
132 return STATUS_NOT_STARTED
133 elif status is rspawn.RUNNING:
134 return STATUS_RUNNING
135 elif status is rspawn.FINISHED:
136 return STATUS_FINISHED
139 return STATUS_NOT_STARTED
142 status = self.status()
143 if status == STATUS_RUNNING:
144 # kill by ppid+pid - SIGTERM first, then try SIGKILL
146 self._pid, self._ppid,
147 host = self.node.hostname,
149 user = self.slicename,
151 ident_key = self.ident_path,
152 server_key = self.node.server_key
155 def remote_trace_path(self, whichtrace):
156 if whichtrace in ('stdout','stderr'):
157 tracefile = os.path.join(self.home_path, whichtrace)
163 def sync_trace(self, local_dir, whichtrace):
164 tracefile = self.remote_trace_path(whichtrace)
168 local_path = os.path.join(local_dir, tracefile)
170 # create parent local folders
171 proc = subprocess.Popen(
172 ["mkdir", "-p", os.path.dirname(local_path)],
173 stdout = open("/dev/null","w"),
174 stdin = open("/dev/null","r"))
177 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
180 (out,err),proc = server.popen_scp(
181 '%s@%s:%s' % (self.slicename, self.node.hostname,
186 ident_key = self.ident_path,
187 server_key = self.node.server_key
191 raise RuntimeError, "Failed to synchronize trace: %s %s" % (out,err,)
200 def _make_home(self):
201 # Make sure all the paths are created where
202 # they have to be created for deployment
203 (out,err),proc = server.popen_ssh_command(
204 "mkdir -p %s" % (server.shell_escape(self.home_path),),
205 host = self.node.hostname,
207 user = self.slicename,
209 ident_key = self.ident_path,
210 server_key = self.node.server_key
214 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
218 # Write program input
219 (out,err),proc = server.popen_scp(
220 cStringIO.StringIO(self.stdin),
221 '%s@%s:%s' % (self.slicename, self.node.hostname,
222 os.path.join(self.home_path, 'stdin') ),
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,)
232 def _replace_paths(self, command):
234 Replace all special path tags with shell-escaped actual paths.
236 # need to append ${HOME} if paths aren't absolute, to MAKE them absolute.
237 root = '' if self.home_path.startswith('/') else "${HOME}/"
239 .replace("${SOURCES}", root+server.shell_escape(self.home_path))
240 .replace("${BUILD}", root+server.shell_escape(os.path.join(self.home_path,'build'))) )
244 sources = self.sources.split(' ')
247 for source in sources:
248 (out,err),proc = server.popen_scp(
250 "%s@%s:%s" % (self.slicename, self.node.hostname,
251 os.path.join(self.home_path,'.'),),
252 ident_key = self.ident_path,
253 server_key = self.node.server_key
257 raise RuntimeError, "Failed upload source file %r: %s %s" % (source, out,err,)
259 if self.buildDepends:
260 # Install build dependencies
261 (out,err),proc = server.popen_ssh_command(
262 "sudo -S yum -y install %(packages)s" % {
263 'packages' : self.buildDepends
265 host = self.node.hostname,
267 user = self.slicename,
269 ident_key = self.ident_path,
270 server_key = self.node.server_key
274 raise RuntimeError, "Failed instal build dependencies: %s %s" % (out,err,)
279 (out,err),proc = server.popen_ssh_command(
280 "cd %(home)s && mkdir -p build && cd build && %(command)s" % {
281 'command' : self._replace_paths(self.build),
282 'home' : server.shell_escape(self.home_path),
284 host = self.node.hostname,
286 user = self.slicename,
288 ident_key = self.ident_path,
289 server_key = self.node.server_key
293 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
296 (out,err),proc = server.popen_ssh_command(
297 "cd %(home)s && tar czf build.tar.gz build" % {
298 'command' : self._replace_paths(self.build),
299 'home' : server.shell_escape(self.home_path),
301 host = self.node.hostname,
303 user = self.slicename,
305 ident_key = self.ident_path,
306 server_key = self.node.server_key
310 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)
313 # Install application
314 (out,err),proc = server.popen_ssh_command(
315 "cd %(home)s && cd build && %(command)s" % {
316 'command' : self._replace_paths(self.install),
317 'home' : server.shell_escape(self.home_path),
319 host = self.node.hostname,
321 user = self.slicename,
323 ident_key = self.ident_path,
324 server_key = self.node.server_key
328 raise RuntimeError, "Failed instal build sources: %s %s" % (out,err,)