Update get_exit_status for shell process classes.

Change-Id: Ic1d8940a152dfce165bfa3648686a2a499c0a28e
This commit is contained in:
Federico Ressi 2019-09-25 11:03:51 +02:00
parent 0e2a97cce4
commit bec0cb0fd8
4 changed files with 56 additions and 31 deletions

View File

@ -106,24 +106,27 @@ class LocalShellProcessFixture(_process.ShellProcessFixture):
def poll_exit_status(self): def poll_exit_status(self):
return self.process.poll() return self.process.poll()
def get_exit_status(self, timeout=None): if TimeoutExpired is None:
exit_status = self.process.poll() # Workaround for old Python versions
while exit_status is None: def _get_exit_status(self, time_left):
timeout = self.check_timeout(timeout=timeout) start_time = now = time.time()
LOG.debug("Waiting for remote command termination: " end_time = start_time + time_left
"timeout=%r, command=%r", timeout, self.command) exit_status = self.poll_exit_status()
if TimeoutExpired is None: while exit_status is None and now < end_time:
# Workaround for old Python versions that don't accept timeout
# as parameters for wait method
time.sleep(0.1) time.sleep(0.1)
exit_status = self.process.poll() exit_status = self.poll_exit_status()
now = time.time()
return exit_status
else:
def _get_exit_status(self, time_left):
try:
exit_status = self.process.wait(timeout=time_left)
except TimeoutExpired:
LOG.exception("Failed waiting for subprocess termination")
return None
else: else:
try: assert exit_status is not None
exit_status = self.process.wait(timeout=min(5., timeout)) return exit_status
except TimeoutExpired:
LOG.exception("Failed waiting for subprocess termination")
return exit_status
def kill(self): def kill(self):
process = self.process process = self.process

View File

@ -30,6 +30,9 @@ from tobiko.shell.sh import _io
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
MAX_TIMEOUT = 3600. # 1 hour
def process(command=None, environment=None, timeout=None, shell=None, def process(command=None, environment=None, timeout=None, shell=None,
stdin=None, stdout=None, stderr=None, ssh_client=None, **kwargs): stdin=None, stdout=None, stderr=None, ssh_client=None, **kwargs):
kwargs.update(command=command, environment=environment, timeout=timeout, kwargs.update(command=command, environment=environment, timeout=timeout,
@ -187,7 +190,7 @@ class ShellProcessFixture(tobiko.SharedFixture):
self.close_stdout() self.close_stdout()
self.close_stderr() self.close_stderr()
try: try:
exit_status = self.get_exit_status() exit_status = self.poll_exit_status()
except Exception: except Exception:
LOG.exception('Error getting exit status') LOG.exception('Error getting exit status')
exit_status = None exit_status = None
@ -210,6 +213,23 @@ class ShellProcessFixture(tobiko.SharedFixture):
raise NotImplementedError raise NotImplementedError
def get_exit_status(self, timeout=None): def get_exit_status(self, timeout=None):
time_left, timeout = get_time_left([self.timeout, timeout])
if time_left > 0.:
exit_status = self._get_exit_status(time_left=time_left)
if exit_status is not None:
return exit_status
ex = _exception.ShellTimeoutExpired(
command=str(self.command),
timeout=timeout and timeout.timeout or None,
stdin=str_from_stream(self.stdin),
stdout=str_from_stream(self.stdout),
stderr=str_from_stream(self.stderr))
LOG.debug("Timed out while waiting for command termination:\n%s",
self.command)
raise ex
def _get_exit_status(self, time_left):
raise NotImplementedError raise NotImplementedError
@property @property
@ -417,7 +437,7 @@ def shell_process_timeout(timeout):
def get_time_left(timeouts, now=None): def get_time_left(timeouts, now=None):
now = now or time.time() now = now or time.time()
min_time_left = float('inf') min_time_left = float(MAX_TIMEOUT)
min_timeout = None min_timeout = None
for timeout in timeouts: for timeout in timeouts:
if timeout is not None: if timeout is not None:
@ -431,14 +451,13 @@ def get_time_left(timeouts, now=None):
class ShellProcessTimeout(object): class ShellProcessTimeout(object):
timeout = float('inf') timeout = MAX_TIMEOUT
def __init__(self, timeout=None, start_time=None): def __init__(self, timeout=None, start_time=None):
if timeout is None: if timeout is None:
timeout = float('inf') timeout = self.timeout
else: else:
timeout = float(timeout) self.timeout = float(timeout)
self.timeout = timeout
start_time = start_time and float(start_time) or time.time() start_time = start_time and float(start_time) or time.time()
self.start_time = start_time self.start_time = start_time
self.end_time = start_time + timeout self.end_time = start_time + timeout

View File

@ -18,10 +18,10 @@ from __future__ import absolute_import
from oslo_log import log from oslo_log import log
import paramiko import paramiko
from tobiko.shell.sh import _execute
from tobiko.shell.sh import _io from tobiko.shell.sh import _io
from tobiko.shell.sh import _local from tobiko.shell.sh import _local
from tobiko.shell.sh import _process from tobiko.shell.sh import _process
from tobiko.shell.sh import _execute
from tobiko.shell import ssh from tobiko.shell import ssh
@ -114,15 +114,18 @@ class SSHShellProcessFixture(_process.ShellProcessFixture):
exit_status = None exit_status = None
return exit_status return exit_status
def get_exit_status(self, timeout=None): def _get_exit_status(self, time_left=None):
process = self.process process = self.process
while not process.exit_status_ready(): if not process.exit_status_ready():
timeout = self.check_timeout(timeout=timeout) LOG.debug('Waiting for command (%s) exit status', self.command)
LOG.debug("Waiting for remote command termination: " # recv_exit_status method doesn't accept timeout parameter
"timeout=%r, command=%r", timeout, self.command) if process.status_event.wait(timeout=time_left):
process.status_event.wait(timeout=min(timeout, 5.)) assert process.exit_status_ready()
else:
assert not process.exit_status_ready()
return None
assert process.status_event.is_set() assert process.exit_status is not None
return process.exit_status return process.exit_status
def kill(self): def kill(self):

View File

@ -116,7 +116,7 @@ class ExecuteTest(testtools.TestCase):
stdin='some input\n', stdin='some input\n',
stdout='some input\n') stdout='some input\n')
def test_timeout_expires(self, command='sleep 5', timeout=0.1, stdin=None, def test_timeout_expires(self, command='sleep 10', timeout=5., stdin=None,
stdout=None, stderr=None, **kwargs): stdout=None, stderr=None, **kwargs):
ex = self.assertRaises(sh.ShellTimeoutExpired, ex = self.assertRaises(sh.ShellTimeoutExpired,
self.execute, self.execute,