clients/tests: read stdout/stderr buffers during test-clients.py

We need to actually read the stdout/stderr of the nmcli programs.
Otherwise, the pipe might fill uup and block to process (eventually
leading to timeout).
This commit is contained in:
Thomas Haller
2019-11-06 15:30:55 +01:00
parent cdb95062e7
commit e96bc190d6

View File

@@ -91,11 +91,13 @@ import itertools
import subprocess import subprocess
import shlex import shlex
import re import re
import fcntl
import dbus import dbus
import time import time
import random import random
import dbus.service import dbus.service
import dbus.mainloop.glib import dbus.mainloop.glib
import io
############################################################################### ###############################################################################
@@ -173,26 +175,52 @@ class Util:
@staticmethod @staticmethod
def popen_wait(p, timeout = 0): def popen_wait(p, timeout = 0):
if Util.python_has_version(3, 3): (res, b_stdout, b_stderr) = Util.popen_wait_read(p, timeout = timeout, read_std_pipes = False)
if timeout == 0: return res
return p.poll()
try: @staticmethod
return p.wait(timeout) def popen_wait_read(p, timeout = 0, read_std_pipes = True):
except subprocess.TimeoutExpired:
return None
start = NM.utils_get_timestamp_msec() start = NM.utils_get_timestamp_msec()
delay = 0.0005 delay = 0.0005
b_stdout = b''
b_stderr = b''
res = None
while True: while True:
if read_std_pipes:
b_stdout += Util.buffer_read(p.stdout)
b_stderr += Util.buffer_read(p.stderr)
if p.poll() is not None: if p.poll() is not None:
return p.returncode res = p.returncode
break
if timeout == 0: if timeout == 0:
return None break
assert(timeout > 0) assert(timeout > 0)
remaining = timeout - ((NM.utils_get_timestamp_msec() - start) / 1000.0) remaining = timeout - ((NM.utils_get_timestamp_msec() - start) / 1000.0)
if remaining <= 0: if remaining <= 0:
return None break
delay = min(delay * 2, remaining, 0.05) delay = min(delay * 2, remaining, 0.05)
time.sleep(delay) time.sleep(delay)
return (res, b_stdout, b_stderr)
@staticmethod
def buffer_read(buf):
b = b''
while True:
try:
b1 = buf.read()
except io.BlockingIOError:
b1 = b''
except IOError:
b1 = b''
if not b1:
return b
b += b1
@staticmethod
def buffer_set_nonblock(buf):
fd = buf.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
@staticmethod @staticmethod
def random_job(jobs): def random_job(jobs):
@@ -459,10 +487,14 @@ class AsyncProcess():
def start(self): def start(self):
if not hasattr(self, '_p'): if not hasattr(self, '_p'):
self._p_start_timestamp = NM.utils_get_timestamp_msec() self._p_start_timestamp = NM.utils_get_timestamp_msec()
self._p_stdout_buf = b''
self._p_stderr_buf = b''
self._p = subprocess.Popen(self._args, self._p = subprocess.Popen(self._args,
stdout = subprocess.PIPE, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
env = self._env) env = self._env)
Util.buffer_set_nonblock(self._p.stdout)
Util.buffer_set_nonblock(self._p.stderr)
def _timeout_remaining_time(self): def _timeout_remaining_time(self):
# note that we call this during poll() and wait_and_complete(). # note that we call this during poll() and wait_and_complete().
@@ -476,7 +508,11 @@ class AsyncProcess():
def poll(self, timeout = 0): def poll(self, timeout = 0):
self.start() self.start()
return_code = Util.popen_wait(self._p, timeout) (return_code, b_stdout, b_stderr) = Util.popen_wait_read(self._p, timeout)
self._p_stdout_buf += b_stdout
self._p_stderr_buf += b_stderr
if return_code is None \ if return_code is None \
and self._timeout_remaining_time() <= 0: and self._timeout_remaining_time() <= 0:
raise Exception("process is still running after timeout: %s" % (' '.join(self._args))) raise Exception("process is still running after timeout: %s" % (' '.join(self._args)))
@@ -488,11 +524,16 @@ class AsyncProcess():
p = self._p p = self._p
self._p = None self._p = None
return_code = Util.popen_wait(p, max(0, self._timeout_remaining_time()) / 1000) (return_code, b_stdout, b_stderr) = Util.popen_wait_read(p, max(0, self._timeout_remaining_time()) / 1000)
(stdout, stderr) = (p.stdout.read(), p.stderr.read()) (stdout, stderr) = (p.stdout.read(), p.stderr.read())
p.stdout.close() p.stdout.close()
p.stderr.close() p.stderr.close()
stdout = self._p_stdout_buf + b_stdout + stdout
stderr = self._p_stderr_buf + b_stderr + stderr
del self._p_stdout_buf
del self._p_stderr_buf
if return_code is None: if return_code is None:
print(stdout) print(stdout)
print(stderr) print(stderr)