Update get_exit_status for shell process classes.
Change-Id: Ic1d8940a152dfce165bfa3648686a2a499c0a28e
This commit is contained in:
parent
0e2a97cce4
commit
bec0cb0fd8
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue