Add retry decorator to SSH "execute" method

In case of SSH timeout (TimeoutException, TimeoutError), the
tenacity.retry decorator retries the execution of the SSH
"execute" method up to 10 times.

Some SSH execute calls, related to QoS scenario tests, have been
enhanced by setting a relatively small timeout value. The commands
executed should be quick enough to be executed in this amount of time.
In case of timeout (due to communication problems), the retry decorator
will send again the command to be executed.

Change-Id: Idc0d55b776f499a4bc5d8c9d9a549f0af8f3fac0
Closes-Bug: #1844516
This commit is contained in:
Rodolfo Alonso Hernandez 2019-09-18 11:30:04 +00:00
parent 31993d50fd
commit aa65dfb526
3 changed files with 23 additions and 4 deletions

View File

@ -14,12 +14,15 @@
import locale import locale
import os import os
import socket
import time import time
from oslo_log import log from oslo_log import log
import paramiko import paramiko
import six
from tempest.lib.common import ssh from tempest.lib.common import ssh
from tempest.lib import exceptions from tempest.lib import exceptions
import tenacity
from neutron_tempest_plugin import config from neutron_tempest_plugin import config
from neutron_tempest_plugin import exceptions as exc from neutron_tempest_plugin import exceptions as exc
@ -29,6 +32,16 @@ CONF = config.CONF
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
RETRY_EXCEPTIONS = (exceptions.TimeoutException, paramiko.SSHException,
socket.error)
if six.PY2:
# NOTE(ralonsoh): TimeoutError was added in 3.3 and corresponds to
# OSError(errno.ETIMEDOUT)
RETRY_EXCEPTIONS += (OSError, )
else:
RETRY_EXCEPTIONS += (TimeoutError, )
class Client(ssh.Client): class Client(ssh.Client):
default_ssh_lang = 'en_US.UTF-8' default_ssh_lang = 'en_US.UTF-8'
@ -179,6 +192,11 @@ class Client(ssh.Client):
user=self.username, user=self.username,
password=self.password) password=self.password)
@tenacity.retry(
stop=tenacity.stop_after_attempt(10),
wait=tenacity.wait_fixed(1),
retry=tenacity.retry_if_exception_type(RETRY_EXCEPTIONS),
reraise=True)
def exec_command(self, cmd, encoding="utf-8", timeout=None): def exec_command(self, cmd, encoding="utf-8", timeout=None):
if timeout: if timeout:
original_timeout = self.timeout original_timeout = self.timeout

View File

@ -85,9 +85,9 @@ class QoSTestMixin(object):
cmd = ("(dd if=/dev/zero bs=%(bs)d count=%(count)d of=%(file_path)s) " cmd = ("(dd if=/dev/zero bs=%(bs)d count=%(count)d of=%(file_path)s) "
% {'bs': self.BUFFER_SIZE, 'count': self.COUNT, % {'bs': self.BUFFER_SIZE, 'count': self.COUNT,
'file_path': self.FILE_PATH}) 'file_path': self.FILE_PATH})
ssh_client.exec_command(cmd) ssh_client.exec_command(cmd, timeout=5)
cmd = "stat -c %%s %s" % self.FILE_PATH cmd = "stat -c %%s %s" % self.FILE_PATH
filesize = ssh_client.exec_command(cmd) filesize = ssh_client.exec_command(cmd, timeout=5)
if int(filesize.strip()) != self.FILE_SIZE: if int(filesize.strip()) != self.FILE_SIZE:
raise sc_exceptions.FileCreationFailedException( raise sc_exceptions.FileCreationFailedException(
file=self.FILE_PATH) file=self.FILE_PATH)
@ -96,7 +96,7 @@ class QoSTestMixin(object):
def _kill_nc_process(ssh_client): def _kill_nc_process(ssh_client):
cmd = "killall -q nc" cmd = "killall -q nc"
try: try:
ssh_client.exec_command(cmd) ssh_client.exec_command(cmd, timeout=5)
except exceptions.SSHExecCommandFailed: except exceptions.SSHExecCommandFailed:
pass pass
@ -104,7 +104,7 @@ class QoSTestMixin(object):
self._kill_nc_process(ssh_client) self._kill_nc_process(ssh_client)
cmd = ("(nc -ll -p %(port)d < %(file_path)s > /dev/null &)" % { cmd = ("(nc -ll -p %(port)d < %(file_path)s > /dev/null &)" % {
'port': port, 'file_path': self.FILE_PATH}) 'port': port, 'file_path': self.FILE_PATH})
ssh_client.exec_command(cmd) ssh_client.exec_command(cmd, timeout=5)
# Open TCP socket to remote VM and download big file # Open TCP socket to remote VM and download big file
start_time = time.time() start_time = time.time()

View File

@ -13,6 +13,7 @@ oslo.utils>=3.33.0 # Apache-2.0
paramiko>=2.0.0 # LGPLv2.1+ paramiko>=2.0.0 # LGPLv2.1+
six>=1.10.0 # MIT six>=1.10.0 # MIT
tempest>=17.1.0 # Apache-2.0 tempest>=17.1.0 # Apache-2.0
tenacity>=3.2.1 # Apache-2.0
ddt>=1.0.1 # MIT ddt>=1.0.1 # MIT
testtools>=2.2.0 # MIT testtools>=2.2.0 # MIT
testscenarios>=0.4 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD