# Copyright (C) 2013 INRIA
#
# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation;
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
from nepi.execution.attribute import Attribute, Flags, Types
from nepi.execution.trace import Trace, TraceAttr
from nepi.execution.resource import ResourceManager, clsinit_copy, \
- ResourceState, reschedule_delay
+ ResourceState
from nepi.resources.linux.node import LinuxNode
from nepi.util.sshfuncs import ProcStatus
from nepi.util.timefuncs import tnow, tdiffsec
"""
- _rtype = "LinuxApplication"
+ _rtype = "linux::Application"
_help = "Runs an application on a Linux host with a BASH command "
- _backend_type = "linux"
+ _platform = "linux"
@classmethod
def _register_attributes(cls):
"cleanHome is set to True (This will delete all sources). ",
flags = Flags.Design)
files = Attribute("files",
- "Space-separated list of regular miscellaneous files to be uploaded "
+ "semi-colon separated list of regular miscellaneous files to be uploaded "
"to ${SHARE} directory. "
"Files are globally available for all experiments unless "
"cleanHome is set to True (This will delete all files). ",
flags = Flags.Design)
libs = Attribute("libs",
- "Space-separated list of libraries (e.g. .so files) to be uploaded "
+ "semi-colon separated list of libraries (e.g. .so files) to be uploaded "
"to ${LIB} directory. "
"Libraries are globally available for all experiments unless "
"cleanHome is set to True (This will delete all files). ",
flags = Flags.Design)
bins = Attribute("bins",
- "Space-separated list of binary files to be uploaded "
+ "semi-colon separated list of binary files to be uploaded "
"to ${BIN} directory. "
"Binaries are globally available for all experiments unless "
"cleanHome is set to True (This will delete all files). ",
super(LinuxApplication, self).__init__(ec, guid)
self._pid = None
self._ppid = None
+ self._node = None
self._home = "app-%s" % self.guid
+
# whether the command should run in foreground attached
# to a terminal
self._in_foreground = False
@property
def node(self):
- node = self.get_connected(LinuxNode.get_rtype())
- if node: return node[0]
- return None
+ if not self._node:
+ node = self.get_connected(LinuxNode.get_rtype())
+ if not node:
+ msg = "Application %s guid %d NOT connected to Node" % (
+ self._rtype, self.guid)
+ raise RuntimeError(msg)
+
+ self._node = node[0]
+
+ return self._node
@property
def app_home(self):
def do_provision(self):
# take a snapshot of the system if user is root
- # to assure cleanProcess kill every nepi process
+ # to ensure that cleanProcess will not kill
+ # pre-existent processes
if self.node.get("username") == 'root':
import pickle
procs = dict()
ps_aux = "ps aux |awk '{print $2,$11}'"
(out, err), proc = self.node.execute(ps_aux)
- for line in out.strip().split("\n"):
- parts = line.strip().split(" ")
- procs[parts[0]] = parts[1]
- pickle.dump(procs, open("/tmp/save.proc", "wb"))
+ if len(out) != 0:
+ for line in out.strip().split("\n"):
+ parts = line.strip().split(" ")
+ procs[parts[0]] = parts[1]
+ with open("/tmp/save.proc", "wb") as pickle_file:
+ pickle.dump(procs, pickle_file)
# create run dir for application
self.node.mkdir(self.run_home)
# replace application specific paths in the command
command = self.replace_paths(command)
-
# replace application specific paths in the environment
env = self.get("env")
env = env and self.replace_paths(env)
env = env,
overwrite = overwrite)
- def execute_deploy_command(self, command):
+ def execute_deploy_command(self, command, prefix="deploy"):
if command:
+ # replace application specific paths in the command
+ command = self.replace_paths(command)
+
+ # replace application specific paths in the environment
+ env = self.get("env")
+ env = env and self.replace_paths(env)
+
# Upload the command to a bash script and run it
# in background ( but wait until the command has
# finished to continue )
- shfile = os.path.join(self.app_home, "deploy.sh")
+ shfile = os.path.join(self.app_home, "%s.sh" % prefix)
self.node.run_and_wait(command, self.run_home,
shfile = shfile,
overwrite = False,
- pidfile = "deploy_pidfile",
- ecodefile = "deploy_exitcode",
- stdout = "deploy_stdout",
- stderr = "deploy_stderr")
-
- def upload_sources(self, src_dir = None):
- sources = self.get("sources")
+ pidfile = "%s_pidfile" % prefix,
+ ecodefile = "%s_exitcode" % prefix,
+ stdout = "%s_stdout" % prefix,
+ stderr = "%s_stderr" % prefix)
+
+ def upload_sources(self, sources = None, src_dir = None):
+ if not sources:
+ sources = self.get("sources")
command = ""
return command
- def upload_files(self):
- files = self.get("files")
+ def upload_files(self, files = None):
+ if not files:
+ files = self.get("files")
if files:
self.info("Uploading files %s " % files)
self.node.upload(files, self.node.share_dir, overwrite = False)
- def upload_libraries(self):
- libs = self.get("libs")
+ def upload_libraries(self, libs = None):
+ if not libs:
+ libs = self.get("libs")
if libs:
self.info("Uploading libraries %s " % libaries)
self.node.upload(libs, self.node.lib_dir, overwrite = False)
- def upload_binaries(self):
- bins = self.get("bins")
+ def upload_binaries(self, bins = None):
+ if not bins:
+ bins = self.get("bins")
if bins:
self.info("Uploading binaries %s " % binaries)
self.node.upload(bins, self.node.bin_dir, overwrite = False)
- def upload_code(self):
- code = self.get("code")
+ def upload_code(self, code = None):
+ if not code:
+ code = self.get("code")
if code:
self.info("Uploading code")
dst = os.path.join(self.app_home, "code")
self.node.upload(code, dst, overwrite = False, text = True)
- def upload_stdin(self):
- stdin = self.get("stdin")
+ def upload_stdin(self, stdin = None):
+ if not stdin:
+ stdin = self.get("stdin")
+
if stdin:
# create dir for sources
self.info("Uploading stdin")
return command
- def install_dependencies(self):
- depends = self.get("depends")
+ def install_dependencies(self, depends = None):
+ if not depends:
+ depends = self.get("depends")
+
if depends:
self.info("Installing dependencies %s" % depends)
return self.node.install_packages_command(depends)
- def build(self):
- build = self.get("build")
+ def build(self, build = None):
+ if not build:
+ build = self.get("build")
if build:
self.info("Building sources ")
# replace application specific paths in the command
return self.replace_paths(build)
- def install(self):
- install = self.get("install")
+ def install(self, install = None):
+ if not install:
+ install = self.get("install")
if install:
self.info("Installing sources ")
# Wait until node is associated and deployed
node = self.node
if not node or node.state < ResourceState.READY:
- self.debug("---- RESCHEDULING DEPLOY ---- node state %s " % self.node.state )
- self.ec.schedule(reschedule_delay, self.deploy)
+ self.debug("---- RESCHEDULING DEPLOY ---- node state %s " % self.node.state)
+ self.ec.schedule(self.reschedule_delay, self.deploy)
else:
command = self.get("command") or ""
self.info("Deploying command '%s' " % command)
if self._proc.poll():
self.error(msg, out, err)
- raise RuntimeError, msg
+ raise RuntimeError(msg)
def _run_in_background(self):
command = self.get("command")
if proc.poll():
self.error(msg, out, err)
- raise RuntimeError, msg
+ raise RuntimeError(msg)
# Wait for pid file to be generated
pid, ppid = self.node.wait_pid(self.run_home)
if err:
msg = " Failed to start command '%s' " % command
self.error(msg, out, err)
- raise RuntimeError, msg
+ raise RuntimeError(msg)
def do_stop(self):
""" Stops application execution
(out, err), proc = self.node.kill(self.pid, self.ppid,
sudo = self._sudo_kill)
+ """
# TODO: check if execution errors occurred
if (proc and proc.poll()) or err:
msg = " Failed to STOP command '%s' " % self.get("command")
self.error(msg, out, err)
-
+ """
+
super(LinuxApplication, self).do_stop()
def do_release(self):
self.info("Releasing resource")
+ self.do_stop()
+
tear_down = self.get("tearDown")
if tear_down:
self.node.execute(tear_down)
- self.do_stop()
+ hard_release = self.get("hardRelease")
+ if hard_release:
+ self.node.rmdir(self.app_home)
super(LinuxApplication, self).do_release()
return self._state
def execute_command(self, command,
- env = None,
- sudo = False,
- forward_x11 = False,
- blocking = False):
+ env=None,
+ sudo=False,
+ tty=False,
+ forward_x11=False,
+ blocking=False):
environ = ""
if env:
- environ = self.node.format_environment(env, inline = True)
+ environ = self.node.format_environment(env, inline=True)
command = environ + command
command = self.replace_paths(command)
return self.node.execute(command,
- sudo = sudo,
- forward_x11 = forward_x11,
- blocking = blocking)
+ sudo=sudo,
+ tty=tty,
+ forward_x11=forward_x11,
+ blocking=blocking)
- def replace_paths(self, command):
+ def replace_paths(self, command, node=None, app_home=None, run_home=None):
"""
Replace all special path tags with shell-escaped actual paths.
"""
+ if not node:
+ node=self.node
+
+ if not app_home:
+ app_home=self.app_home
+
+ if not run_home:
+ run_home = self.run_home
+
return ( command
- .replace("${USR}", self.node.usr_dir)
- .replace("${LIB}", self.node.lib_dir)
- .replace("${BIN}", self.node.bin_dir)
- .replace("${SRC}", self.node.src_dir)
- .replace("${SHARE}", self.node.share_dir)
- .replace("${EXP}", self.node.exp_dir)
- .replace("${EXP_HOME}", self.node.exp_home)
- .replace("${APP_HOME}", self.app_home)
- .replace("${RUN_HOME}", self.run_home)
- .replace("${NODE_HOME}", self.node.node_home)
- .replace("${HOME}", self.node.home_dir)
+ .replace("${USR}", node.usr_dir)
+ .replace("${LIB}", node.lib_dir)
+ .replace("${BIN}", node.bin_dir)
+ .replace("${SRC}", node.src_dir)
+ .replace("${SHARE}", node.share_dir)
+ .replace("${EXP}", node.exp_dir)
+ .replace("${EXP_HOME}", node.exp_home)
+ .replace("${APP_HOME}", app_home)
+ .replace("${RUN_HOME}", run_home)
+ .replace("${NODE_HOME}", node.node_home)
+ .replace("${HOME}", node.home_dir)
)
def valid_connection(self, guid):