def test_terminate(self): p = CPopen(["sleep", "1"], stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p.terminate() list(cmdutils.receive(p)) self.assertEqual(p.returncode, -signal.SIGTERM)
class QemuImgOperation(object): REGEXPR = re.compile(r'\s*\(([\d.]+)/100%\)\s*') def __init__(self, cmd, cwd=None): self._lock = threading.Lock() self._aborted = False self._progress = 0.0 self._stdout = bytearray() self._stderr = bytearray() self.cmd = wrap_command(cmd, with_nice=utils.NICENESS.HIGH, with_ioclass=utils.IOCLASS.IDLE) _log.debug(cmdutils.command_log_line(self.cmd, cwd=cwd)) self._command = CPopen(self.cmd, cwd=cwd, deathSignal=signal.SIGKILL) self._stream = utils.CommandStream(self._command, self._recvstdout, self._recvstderr) def _recvstderr(self, buffer): self._stderr += buffer def _recvstdout(self, buffer): self._stdout += buffer # Checking the presence of '\r' before splitting will prevent # generating the array when it's not needed. try: idx = self._stdout.rindex('\r') except ValueError: return # qemu-img updates progress by printing \r (0.00/100%) to standard out. # The output could end with a partial progress so we must discard # everything after the last \r and then try to parse a progress record. valid_progress = self._stdout[:idx] last_progress = valid_progress.rsplit('\r', 1)[-1] # No need to keep old progress information around del self._stdout[:idx + 1] m = self.REGEXPR.match(last_progress) if m is None: raise ValueError('Unable to parse: "%r"' % last_progress) self._progress = float(m.group(1)) @property def progress(self): """ Returns operation progress as float between 0 and 100. This method is threadsafe and may be called from any thread. """ return self._progress @property def error(self): return str(self._stderr) @property def finished(self): return self._command.poll() is not None def poll(self, timeout=None): self._stream.receive(timeout=timeout) if not self._stream.closed: return self._command.wait() if self._aborted: raise exception.ActionStopped() cmdutils.retcode_log_line(self._command.returncode, self.error) if self._command.returncode != 0: raise QImgError(self.cmd, self._command.returncode, "", self.error) def wait_for_completion(self): timeout = config.getint("irs", "progress_interval") while not self.finished: self.poll(timeout) _log.debug('qemu-img operation progress: %s%%', self.progress) def abort(self): """ Aborts running operation by sending a termination signal to the underlying qemu-img process. Note: this is asynchronous operation, returning before the process was terminated. You must use wait_for_completion to wait for the underlying qemu-img process. This method is threadsafe and may be called from any thread. """ with self._lock: if self._command is None: return if self._command.poll() is None: self._aborted = True self._command.terminate() def close(self): with self._lock: self._stream.close() self._command = None
class QemuImgOperation(object): REGEXPR = re.compile(r'\s*\(([\d.]+)/100%\)\s*') def __init__(self, cmd, cwd=None): self._lock = threading.Lock() self._aborted = False self._progress = 0.0 self._stdout = bytearray() self._stderr = bytearray() self.cmd = wrap_command( cmd, with_nice=utils.NICENESS.HIGH, with_ioclass=utils.IOCLASS.IDLE) _log.debug(cmdutils.command_log_line(self.cmd, cwd=cwd)) self._command = CPopen(self.cmd, cwd=cwd, deathSignal=signal.SIGKILL) self._stream = utils.CommandStream( self._command, self._recvstdout, self._recvstderr) def _recvstderr(self, buffer): self._stderr += buffer def _recvstdout(self, buffer): self._stdout += buffer # Checking the presence of '\r' before splitting will prevent # generating the array when it's not needed. try: idx = self._stdout.rindex('\r') except ValueError: return # qemu-img updates progress by printing \r (0.00/100%) to standard out. # The output could end with a partial progress so we must discard # everything after the last \r and then try to parse a progress record. valid_progress = self._stdout[:idx] last_progress = valid_progress.rsplit('\r', 1)[-1] # No need to keep old progress information around del self._stdout[:idx + 1] m = self.REGEXPR.match(last_progress) if m is None: raise ValueError('Unable to parse: "%r"' % last_progress) self._progress = float(m.group(1)) @property def progress(self): """ Returns operation progress as float between 0 and 100. This method is threadsafe and may be called from any thread. """ return self._progress @property def error(self): return str(self._stderr) @property def finished(self): return self._command.poll() is not None def poll(self, timeout=None): self._stream.receive(timeout=timeout) if not self._stream.closed: return self._command.wait() if self._aborted: raise exception.ActionStopped() cmdutils.retcode_log_line(self._command.returncode, self.error) if self._command.returncode != 0: raise QImgError(self.cmd, self._command.returncode, "", self.error) def wait_for_completion(self): timeout = config.getint("irs", "progress_interval") while not self.finished: self.poll(timeout) _log.debug('qemu-img operation progress: %s%%', self.progress) def abort(self): """ Aborts running operation by sending a termination signal to the underlying qemu-img process. Note: this is asynchronous operation, returning before the process was terminated. You must use wait_for_completion to wait for the underlying qemu-img process. This method is threadsafe and may be called from any thread. """ with self._lock: if self._command is None: return if self._command.poll() is None: self._aborted = True self._command.terminate() def close(self): with self._lock: self._stream.close() self._command = None