def testRemaining(self): self.time_fn = TimeMock([0, 2, 4, 5, 6]) timeout = algo.RunningTimeout(5, True, _time_fn=self.time_fn) self.assertEqual(timeout.Remaining(), 3) self.assertEqual(timeout.Remaining(), 1) self.assertEqual(timeout.Remaining(), 0) self.assertEqual(timeout.Remaining(), -1)
def _RunCmdPipe(cmd, env, via_shell, cwd, interactive, timeout, noclose_fds, input_fd, postfork_fn=None, _linger_timeout=constants.CHILD_LINGER_TIMEOUT): """Run a command and return its output. @type cmd: string or list @param cmd: Command to run @type env: dict @param env: The environment to use @type via_shell: bool @param via_shell: if we should run via the shell @type cwd: string @param cwd: the working directory for the program @type interactive: boolean @param interactive: Run command interactive (without piping) @type timeout: int @param timeout: Timeout after the programm gets terminated @type noclose_fds: list @param noclose_fds: list of additional (fd >=3) file descriptors to leave open for the child process @type input_fd: C{file}-like object or numeric file descriptor @param input_fd: File descriptor for process' standard input @type postfork_fn: Callable receiving PID as parameter @param postfork_fn: Function run after fork but before timeout @rtype: tuple @return: (out, err, status) """ poller = select.poll() if interactive: stderr = None stdout = None else: stderr = subprocess.PIPE stdout = subprocess.PIPE if input_fd: stdin = input_fd elif interactive: stdin = None else: stdin = subprocess.PIPE if noclose_fds: preexec_fn = lambda: CloseFDs(noclose_fds) close_fds = False else: preexec_fn = None close_fds = True child = subprocess.Popen(cmd, shell=via_shell, stderr=stderr, stdout=stdout, stdin=stdin, close_fds=close_fds, env=env, cwd=cwd, preexec_fn=preexec_fn) if postfork_fn: postfork_fn(child.pid) out = StringIO() err = StringIO() linger_timeout = None if timeout is None: poll_timeout = None else: poll_timeout = utils_algo.RunningTimeout(timeout, True).Remaining msg_timeout = ("Command %s (%d) run into execution timeout, terminating" % (cmd, child.pid)) msg_linger = ("Command %s (%d) run into linger timeout, killing" % (cmd, child.pid)) timeout_action = _TIMEOUT_NONE # subprocess: "If the stdin argument is PIPE, this attribute is a file object # that provides input to the child process. Otherwise, it is None." assert (stdin == subprocess.PIPE) ^ (child.stdin is None), \ "subprocess' stdin did not behave as documented" if not interactive: if child.stdin is not None: child.stdin.close() poller.register(child.stdout, select.POLLIN) poller.register(child.stderr, select.POLLIN) fdmap = { child.stdout.fileno(): (out, child.stdout), child.stderr.fileno(): (err, child.stderr), } for fd in fdmap: utils_wrapper.SetNonblockFlag(fd, True) while fdmap: if poll_timeout: pt = poll_timeout() * 1000 if pt < 0: if linger_timeout is None: logging.warning(msg_timeout) if child.poll() is None: timeout_action = _TIMEOUT_TERM utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid, signal.SIGTERM) linger_timeout = \ utils_algo.RunningTimeout(_linger_timeout, True).Remaining pt = linger_timeout() * 1000 if pt < 0: break else: pt = None pollresult = utils_wrapper.RetryOnSignal(poller.poll, pt) for fd, event in pollresult: if event & select.POLLIN or event & select.POLLPRI: data = fdmap[fd][1].read() # no data from read signifies EOF (the same as POLLHUP) if not data: poller.unregister(fd) del fdmap[fd] continue fdmap[fd][0].write(data) if (event & select.POLLNVAL or event & select.POLLHUP or event & select.POLLERR): poller.unregister(fd) del fdmap[fd] if timeout is not None: assert callable(poll_timeout) # We have no I/O left but it might still run if child.poll() is None: _WaitForProcess(child, poll_timeout()) # Terminate if still alive after timeout if child.poll() is None: if linger_timeout is None: logging.warning(msg_timeout) timeout_action = _TIMEOUT_TERM utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid, signal.SIGTERM) lt = _linger_timeout else: lt = linger_timeout() _WaitForProcess(child, lt) # Okay, still alive after timeout and linger timeout? Kill it! if child.poll() is None: timeout_action = _TIMEOUT_KILL logging.warning(msg_linger) utils_wrapper.IgnoreProcessNotFound(os.kill, child.pid, signal.SIGKILL) out = out.getvalue() err = err.getvalue() status = child.wait() return out, err, status, timeout_action
def testRemainingNonNegative(self): timeout = algo.RunningTimeout(5.0, False, _time_fn=self.time_fn) self.assertAlmostEqual(timeout.Remaining(), 4.7) self.assertAlmostEqual(timeout.Remaining(), 0.4) self.assertEqual(timeout.Remaining(), 0.0)
def testRemainingFloat(self): timeout = algo.RunningTimeout(5.0, True, _time_fn=self.time_fn) self.assertAlmostEqual(timeout.Remaining(), 4.7) self.assertAlmostEqual(timeout.Remaining(), 0.4) self.assertAlmostEqual(timeout.Remaining(), -1.5)