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, failtrap
+ ResourceState, reschedule_delay
from nepi.resources.linux.node import LinuxNode
from nepi.util.sshfuncs import ProcStatus
from nepi.util.timefuncs import tnow, tdiffsec
.. note::
- A LinuxApplication RM represents a process that can be executed in
- a remote Linux host using SSH.
+ A LinuxApplication RM represents a process that can be executed in
+ a remote Linux host using SSH.
- The LinuxApplication RM takes care of uploadin sources and any files
- needed to run the experiment, to the remote host.
- It also allows to provide source compilation (build) and installation
- instructions, and takes care of automating the sources build and
- installation tasks for the user.
+ The LinuxApplication RM takes care of uploadin sources and any files
+ needed to run the experiment, to the remote host.
+ It also allows to provide source compilation (build) and installation
+ instructions, and takes care of automating the sources build and
+ installation tasks for the user.
- It is important to note that files uploaded to the remote host have
- two possible scopes: single-experiment or multi-experiment.
- Single experiment files are those that will not be re-used by other
- experiments. Multi-experiment files are those that will.
- Sources and shared files are always made available to all experiments.
+ It is important to note that files uploaded to the remote host have
+ two possible scopes: single-experiment or multi-experiment.
+ Single experiment files are those that will not be re-used by other
+ experiments. Multi-experiment files are those that will.
+ Sources and shared files are always made available to all experiments.
- Directory structure:
+ Directory structure:
- The directory structure used by LinuxApplication RM at the Linux
- host is the following:
+ The directory structure used by LinuxApplication RM at the Linux
+ host is the following:
${HOME}/nepi-usr --> Base directory for multi-experiment files
|
command = Attribute("command", "Command to execute at application start. "
"Note that commands will be executed in the ${RUN_HOME} directory, "
"make sure to take this into account when using relative paths. ",
- flags = Flags.ExecReadOnly)
+ flags = Flags.Design)
forward_x11 = Attribute("forwardX11", "Enables X11 forwarding for SSH connections",
- flags = Flags.ExecReadOnly)
+ flags = Flags.Design)
env = Attribute("env", "Environment variables string for command execution",
- flags = Flags.ExecReadOnly)
+ flags = Flags.Design)
sudo = Attribute("sudo", "Run with root privileges",
- flags = Flags.ExecReadOnly)
+ flags = Flags.Design)
depends = Attribute("depends",
"Space-separated list of packages required to run the application",
- flags = Flags.ExecReadOnly)
+ flags = Flags.Design)
sources = Attribute("sources",
"Space-separated list of regular files to be uploaded to ${SRC} "
"directory prior to building. Archives won't be expanded automatically. "
"Sources are globally available for all experiments unless "
"cleanHome is set to True (This will delete all sources). ",
- flags = Flags.ExecReadOnly)
+ flags = Flags.Design)
files = Attribute("files",
"Space-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.ExecReadOnly)
+ flags = Flags.Design)
libs = Attribute("libs",
"Space-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.ExecReadOnly)
+ flags = Flags.Design)
bins = Attribute("bins",
"Space-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). ",
- flags = Flags.ExecReadOnly)
+ flags = Flags.Design)
code = Attribute("code",
"Plain text source code to be uploaded to the ${APP_HOME} directory. ",
- flags = Flags.ExecReadOnly)
+ flags = Flags.Design)
build = Attribute("build",
"Build commands to execute after deploying the sources. "
"Sources are uploaded to the ${SRC} directory and code "
"./configure && make && make clean.\n"
"Make sure to make the build commands return with a nonzero exit "
"code on error.",
- flags = Flags.ReadOnly)
+ flags = Flags.Design)
install = Attribute("install",
"Commands to transfer built files to their final destinations. "
"Install commands are executed after build commands. ",
- flags = Flags.ReadOnly)
+ flags = Flags.Design)
stdin = Attribute("stdin", "Standard input for the 'command'",
- flags = Flags.ExecReadOnly)
+ flags = Flags.Design)
tear_down = Attribute("tearDown", "Command to be executed just before "
"releasing the resource",
- flags = Flags.ReadOnly)
+ flags = Flags.Design)
cls._register_attribute(command)
cls._register_attribute(forward_x11)
@classmethod
def _register_traces(cls):
- stdout = Trace("stdout", "Standard output stream")
- stderr = Trace("stderr", "Standard error stream")
+ stdout = Trace("stdout", "Standard output stream", enabled = True)
+ stderr = Trace("stderr", "Standard error stream", enabled = True)
cls._register_trace(stdout)
cls._register_trace(stderr)
@property
def node(self):
- node = self.get_connected(LinuxNode.rtype())
+ node = self.get_connected(LinuxNode.get_rtype())
if node: return node[0]
return None
"""
return self.get("forwardX11") or self._in_foreground
+ def trace_filepath(self, filename):
+ return os.path.join(self.run_home, filename)
+
def trace(self, name, attr = TraceAttr.ALL, block = 512, offset = 0):
self.info("Retrieving '%s' trace %s " % (name, attr))
- path = os.path.join(self.run_home, name)
+ path = self.trace_filepath(name)
command = "(test -f %s && echo 'success') || echo 'error'" % path
(out, err), proc = self.node.execute(command)
return out
- @failtrap
- def provision(self):
+ def do_provision(self):
# create run dir for application
self.node.mkdir(self.run_home)
self.info("Provisioning finished")
- super(LinuxApplication, self).provision()
+ super(LinuxApplication, self).do_provision()
- def upload_start_command(self):
+ def upload_start_command(self, overwrite = False):
# Upload command to remote bash script
# - only if command can be executed in background and detached
command = self.get("command")
self.node.upload_command(command,
shfile = shfile,
env = env,
- overwrite = False)
+ overwrite = overwrite)
def execute_deploy_command(self, command):
if command:
stdout = "deploy_stdout",
stderr = "deploy_stderr")
- def upload_sources(self):
+ def upload_sources(self, src_dir = None):
sources = self.get("sources")
command = ""
+ if not src_dir:
+ src_dir = self.node.src_dir
+
if sources:
self.info("Uploading sources ")
command.append( " ( "
# Check if the source already exists
- " ls ${SRC}/%(basename)s "
+ " ls %(src_dir)s/%(basename)s "
" || ( "
# If source doesn't exist, download it and check
# that it it downloaded ok
- " wget -c --directory-prefix=${SRC} %(source)s && "
- " ls ${SRC}/%(basename)s "
+ " wget -c --directory-prefix=%(src_dir)s %(source)s && "
+ " ls %(src_dir)s/%(basename)s "
" ) ) " % {
"basename": os.path.basename(source),
- "source": source
+ "source": source,
+ "src_dir": src_dir
})
command = " && ".join(command)
if sources:
sources = ' '.join(sources)
- self.node.upload(sources, self.node.src_dir, overwrite = False)
+ self.node.upload(sources, src_dir, overwrite = False)
return command
# replace application specific paths in the command
return self.replace_paths(install)
- @failtrap
- def deploy(self):
+ def do_deploy(self):
# Wait until node is associated and deployed
node = self.node
if not node or node.state < ResourceState.READY:
else:
command = self.get("command") or ""
self.info("Deploying command '%s' " % command)
- self.discover()
- self.provision()
+ self.do_discover()
+ self.do_provision()
- super(LinuxApplication, self).deploy()
+ super(LinuxApplication, self).do_deploy()
- @failtrap
- def start(self):
+ def do_start(self):
command = self.get("command")
self.info("Starting command '%s'" % command)
if not command:
# If no command was given (i.e. Application was used for dependency
- # installation), then the application is directly marked as FINISHED
- self.set_finished()
+ # installation), then the application is directly marked as STOPPED
+ super(LinuxApplication, self).set_stopped()
else:
if self.in_foreground:
self._run_in_foreground()
else:
self._run_in_background()
- super(LinuxApplication, self).start()
+ super(LinuxApplication, self).do_start()
def _run_in_foreground(self):
command = self.get("command")
x11 = self.get("forwardX11")
env = self.get("env")
- # For a command being executed in foreground, if there is stdin,
- # it is expected to be text string not a file or pipe
- stdin = self.get("stdin") or None
-
# Command will be launched in foreground and attached to the
# terminal using the node 'execute' in non blocking mode.
(out, err), self._proc = self.execute_command(command,
env = env,
sudo = sudo,
- stdin = stdin,
forward_x11 = x11,
blocking = False)
self.error(msg, out, err)
raise RuntimeError, msg
- @failtrap
- def stop(self):
+ def do_stop(self):
""" Stops application execution
"""
command = self.get('command') or ''
sudo = self._sudo_kill)
# TODO: check if execution errors occurred
- if proc.poll() or err:
+ if (proc and proc.poll()) or err:
msg = " Failed to STOP command '%s' " % self.get("command")
self.error(msg, out, err)
- super(LinuxApplication, self).stop()
+ super(LinuxApplication, self).do_stop()
- def release(self):
+ def do_release(self):
self.info("Releasing resource")
- try:
- tear_down = self.get("tearDown")
- if tear_down:
- self.node.execute(tear_down)
+ tear_down = self.get("tearDown")
+ if tear_down:
+ self.node.execute(tear_down)
- self.stop()
- except:
- import traceback
- err = traceback.format_exc()
- self.error(err)
+ self.do_stop()
- super(LinuxApplication, self).release()
+ super(LinuxApplication, self).do_release()
@property
def state(self):
msg = " Failed to execute command '%s'" % self.get("command")
err = self._proc.stderr.read()
self.error(msg, out, err)
- self.fail()
+ self.do_fail()
elif retcode == 0:
- self.finish()
+ self.set_stopped()
else:
# We need to query the status of the command we launched in
# background. In order to avoid overwhelming the remote host and
msg = "Failed to execute command '%s'" % \
self.get("command")
self.error(msg, out, err)
- self.fail()
+ self.do_fail()
else:
- self.finish()
+ self.set_stopped()
self._last_state_check = tnow()
def execute_command(self, command,
env = None,
sudo = False,
- stdin = None,
forward_x11 = False,
blocking = False):
return self.node.execute(command,
sudo = sudo,
- stdin = stdin,
forward_x11 = forward_x11,
blocking = blocking)