X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=src%2Fnepi%2Fresources%2Flinux%2Fapplication.py;h=4f4b64eaace10e745a858a5a4796192c6afea89d;hb=573bc6721a33d323047f204841dd58ef2b83d195;hp=117392da964fd1bb7bcf09f5fc816743d2186627;hpb=3ec952ecb376f66a3c083249c6deec2f19b82947;p=nepi.git diff --git a/src/nepi/resources/linux/application.py b/src/nepi/resources/linux/application.py index 117392da..4f4b64ea 100644 --- a/src/nepi/resources/linux/application.py +++ b/src/nepi/resources/linux/application.py @@ -20,7 +20,7 @@ from nepi.execution.attribute import Attribute, Flags, Types from nepi.execution.trace import Trace, TraceAttr from nepi.execution.resource import ResourceManager, clsinit, ResourceState, \ - reschedule_delay + reschedule_delay from nepi.resources.linux.node import LinuxNode from nepi.util.sshfuncs import ProcStatus from nepi.util.timefuncs import tnow, tdiffsec @@ -29,6 +29,7 @@ import os import subprocess # TODO: Resolve wildcards in commands!! +# TODO: When a failure occurs during deployment, scp and ssh processes are left running behind!! @clsinit class LinuxApplication(ResourceManager): @@ -81,6 +82,9 @@ class LinuxApplication(ResourceManager): """ _rtype = "LinuxApplication" + _help = "Runs an application on a Linux host with a BASH command " + _backend_type = "linux" + @classmethod def _register_attributes(cls): @@ -334,7 +338,8 @@ class LinuxApplication(ResourceManager): self.node.upload_command(command, shfile = shfile, - env = env) + env = env, + overwrite = False) def execute_deploy_command(self, command): if command: @@ -435,7 +440,9 @@ class LinuxApplication(ResourceManager): self.node.upload(stdin, dst, overwrite = False, text = True) # create "stdin" symlink on ${APP_HOME} directory - command = "( cd %s ; ln -s %s stdin )" % ( self.app_home, dst) + command = "( cd %(app_home)s ; [ ! -f stdin ] && ln -s %(stdin)s stdin )" % ({ + "app_home": self.app_home, + "stdin": dst }) return command @@ -443,7 +450,7 @@ class LinuxApplication(ResourceManager): depends = self.get("depends") if depends: self.info("Installing dependencies %s" % depends) - self.node.install_packages(depends, self.app_home, self.run_home) + return self.node.install_packages_command(depends) def build(self): build = self.get("build") @@ -480,7 +487,7 @@ class LinuxApplication(ResourceManager): raise super(LinuxApplication, self).deploy() - + def start(self): command = self.get("command") @@ -489,7 +496,7 @@ class LinuxApplication(ResourceManager): 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._state = ResourceState.FINISHED + self.set_finished() else: if self.in_foreground: @@ -582,33 +589,29 @@ class LinuxApplication(ResourceManager): if self.state == ResourceState.STARTED: - stopped = True - - self.info("Stopping command '%s'" % command) + self.info("Stopping command '%s' " % command) # If the command is running in foreground (it was launched using # the node 'execute' method), then we use the handler to the Popen # process to kill it. Else we send a kill signal using the pid and ppid # retrieved after running the command with the node 'run' method - if self._proc: self._proc.kill() else: # Only try to kill the process if the pid and ppid # were retrieved if self.pid and self.ppid: - (out, err), proc = self.node.kill(self.pid, self.ppid, sudo = - self._sudo_kill) + (out, err), proc = self.node.kill(self.pid, self.ppid, + sudo = self._sudo_kill) - if out or err: - # check if execution errors occurred + # TODO: check if execution errors occurred + if proc.poll() or err: msg = " Failed to STOP command '%s' " % self.get("command") self.error(msg, out, err) self.fail() - stopped = False - - if stopped: - super(LinuxApplication, self).stop() + + if self.state == ResourceState.STARTED: + super(LinuxApplication, self).stop() def release(self): self.info("Releasing resource") @@ -619,9 +622,11 @@ class LinuxApplication(ResourceManager): self.stop() - if self.state == ResourceState.STOPPED: + if self.state != ResourceState.FAILED: + self.info("Resource released") + super(LinuxApplication, self).release() - + @property def state(self): """ Returns the state of the application @@ -641,31 +646,33 @@ class LinuxApplication(ResourceManager): err = self._proc.stderr.read() self.error(msg, out, err) self.fail() - elif retcode == 0: - self._state = ResourceState.FINISHED + elif retcode == 0: + self.finish() else: # We need to query the status of the command we launched in - # background. In oredr to avoid overwhelming the remote host and + # background. In order to avoid overwhelming the remote host and # the local processor with too many ssh queries, the state is only # requested every 'state_check_delay' seconds. state_check_delay = 0.5 if tdiffsec(tnow(), self._last_state_check) > state_check_delay: - # check if execution errors occurred - (out, err), proc = self.node.check_errors(self.run_home) - - if err: - msg = " Failed to execute command '%s'" % self.get("command") - self.error(msg, out, err) - self.fail() - - elif self.pid and self.ppid: - # No execution errors occurred. Make sure the background - # process with the recorded pid is still running. + if self.pid and self.ppid: + # Make sure the process is still running in background status = self.node.status(self.pid, self.ppid) if status == ProcStatus.FINISHED: - self._state = ResourceState.FINISHED + # If the program finished, check if execution + # errors occurred + (out, err), proc = self.node.check_errors( + self.run_home) + + if err: + msg = "Failed to execute command '%s'" % \ + self.get("command") + self.error(msg, out, err) + self.fail() + else: + self.finish() self._last_state_check = tnow()