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):
return self.process.poll()
def get_exit_status(self, timeout=None):
exit_status = self.process.poll()
while exit_status is None:
timeout = self.check_timeout(timeout=timeout)
LOG.debug("Waiting for remote command termination: "
"timeout=%r, command=%r", timeout, self.command)
if TimeoutExpired is None:
# Workaround for old Python versions that don't accept timeout
# as parameters for wait method
if TimeoutExpired is None:
# Workaround for old Python versions
def _get_exit_status(self, time_left):
start_time = now = time.time()
end_time = start_time + time_left
exit_status = self.poll_exit_status()
while exit_status is None and now < end_time:
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:
try:
exit_status = self.process.wait(timeout=min(5., timeout))
except TimeoutExpired:
LOG.exception("Failed waiting for subprocess termination")
return exit_status
assert exit_status is not None
return exit_status
def kill(self):
process = self.process

View File

@ -30,6 +30,9 @@ from tobiko.shell.sh import _io
LOG = log.getLogger(__name__)
MAX_TIMEOUT = 3600. # 1 hour
def process(command=None, environment=None, timeout=None, shell=None,
stdin=None, stdout=None, stderr=None, ssh_client=None, **kwargs):
kwargs.update(command=command, environment=environment, timeout=timeout,
@ -187,7 +190,7 @@ class ShellProcessFixture(tobiko.SharedFixture):
self.close_stdout()
self.close_stderr()
try:
exit_status = self.get_exit_status()
exit_status = self.poll_exit_status()
except Exception:
LOG.exception('Error getting exit status')
exit_status = None
@ -210,6 +213,23 @@ class ShellProcessFixture(tobiko.SharedFixture):
raise NotImplementedError
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
@property
@ -417,7 +437,7 @@ def shell_process_timeout(timeout):
def get_time_left(timeouts, now=None):
now = now or time.time()
min_time_left = float('inf')
min_time_left = float(MAX_TIMEOUT)
min_timeout = None
for timeout in timeouts:
if timeout is not None:
@ -431,14 +451,13 @@ def get_time_left(timeouts, now=None):
class ShellProcessTimeout(object):
timeout = float('inf')
timeout = MAX_TIMEOUT
def __init__(self, timeout=None, start_time=None):
if timeout is None:
timeout = float('inf')
timeout = self.timeout
else:
timeout = float(timeout)
self.timeout = timeout
self.timeout = float(timeout)
start_time = start_time and float(start_time) or time.time()
self.start_time = start_time
self.end_time = start_time + timeout

View File

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

View File

@ -116,7 +116,7 @@ class ExecuteTest(testtools.TestCase):
stdin='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):
ex = self.assertRaises(sh.ShellTimeoutExpired,
self.execute,