Improved LinuxApplication behavior
[nepi.git] / src / nepi / resources / linux / ccnd.py
index 06a735e..655bc62 100644 (file)
@@ -184,6 +184,92 @@ class LinuxCCND(LinuxApplication):
 
         super(LinuxCCND, self).deploy()
 
+    def start(self):
+        command = self.get("command")
+        env = self.get("env")
+        stdin = "stdin" if self.get("stdin") else None
+        stdout = "stdout" if self.get("stdout") else "stdout"
+        stderr = "stderr" if self.get("stderr") else "stderr"
+        sudo = self.get("sudo") or False
+        x11 = self.get("forwardX11") or False
+        failed = False
+
+        if not command:
+            # If no command was given, then the application 
+            # is directly marked as FINISHED
+            self._state = ResourceState.FINISHED
+    
+        self.info("Starting command '%s'" % command)
+
+        if x11:
+            # If X11 forwarding was specified, then the application
+            # can not run detached, so instead of invoking asynchronous
+            # 'run' we invoke synchronous 'execute'.
+            if not command:
+                msg = "No command is defined but X11 forwarding has been set"
+                self.error(msg)
+                self._state = ResourceState.FAILED
+                raise RuntimeError, msg
+
+            # Export environment
+            environ = "\n".join(map(lambda e: "export %s" % e, env.split(" ")))\
+                if env else ""
+
+            command = environ + command
+            command = self.replace_paths(command)
+
+            # Mark application as started before executing the command
+            # since after the thread will be blocked by the execution
+            # until it finished
+            super(LinuxApplication, self).start()
+            
+            # If the command requires X11 forwarding, we
+            # can't run it asynchronously
+            (out, err), proc = self.node.execute(command,
+                    sudo = sudo,
+                    stdin = stdin,
+                    forward_x11 = x11)
+
+            self._state = ResourceState.FINISHED
+
+            if proc.poll() and err:
+                failed = True
+        else:
+            # Command was  previously uploaded, now run the remote
+            # bash file asynchronously
+            cmd = "bash ./app.sh"
+            (out, err), proc = self.node.run(cmd, self.app_home, 
+                stdin = stdin, 
+                stdout = stdout,
+                stderr = stderr,
+                sudo = sudo)
+
+            # check if execution errors occurred
+            msg = " Failed to start command '%s' " % command
+            
+            if proc.poll() and err:
+                self.error(msg, out, err)
+                raise RuntimeError, msg
+        
+            # Check status of process running in background
+            pid, ppid = self.node.wait_pid(self.app_home)
+            if pid: self._pid = int(pid)
+            if ppid: self._ppid = int(ppid)
+
+            # If the process is not running, check for error information
+            # on the remote machine
+            if not self.pid or not self.ppid:
+                (out, err), proc = self.node.check_output(self.app_home, 'stderr')
+                self.error(msg, out, err)
+
+                msg2 = " Setting state to Failed"
+                self.debug(msg2)
+                self._state = ResourceState.FAILED
+
+                raise RuntimeError, msg
+            
+            super(LinuxApplication, self).start()
+
     def stop(self):
         command = self.get('command') or ''
         state = self.state
@@ -191,17 +277,56 @@ class LinuxCCND(LinuxApplication):
         if state == ResourceState.STARTED:
             self.info("Stopping command '%s'" % command)
 
-            (out, err), proc = self.node.kill(self.pid, self.ppid)
+            command = "ccndstop"
+            env = self.get("env") 
+
+            # replace application specific paths in the command
+            command = self.replace_paths(command)
+            env = env and self.replace_paths(env)
+
+            # Upload the command to a file, and execute asynchronously
+            self.node.run_and_wait(command, self.app_home,
+                        shfile = "ccndstop.sh",
+                        env = env,
+                        pidfile = "ccndstop_pidfile", 
+                        ecodefile = "ccndstop_exitcode", 
+                        stdout = "ccndstop_stdout", 
+                        stderr = "ccndstop_stderr")
+
 
-            if out or err:
+            super(LinuxCCND, self).stop()
+
+    @property
+    def state(self):
+        if self._state == ResourceState.STARTED:
+            # To avoid overwhelming the remote hosts 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 strfdiff(strfnow(), self._last_state_check) > state_check_delay:
                 # check if execution errors occurred
-                msg = " Failed to STOP command '%s' " % self.get("command")
-                self.error(msg, out, err)
-                self._state = ResourceState.FAILED
-                stopped = False
-            else:
-                super(LinuxApplication, self).stop()
+                (out, err), proc = self.node.check_errors(self.app_home)
+
+                if out or err:
+                    if err.find("No such file or directory") >= 0 :
+                        # The resource is marked as started, but the
+                        # command was not yet executed
+                        return ResourceState.READY
+
+                    msg = " Failed to execute command '%s'" % self.get("command")
+                    self.error(msg, out, err)
+                    self._state = ResourceState.FAILED
+
+                elif self.pid and self.ppid:
+                    status = self.node.status(self.pid, self.ppid)
+
+                    if status == ProcStatus.FINISHED:
+                        self._state = ResourceState.FINISHED
+
+
+                self._last_state_check = strfnow()
 
+        return self._state
 
     @property
     def _default_command(self):