Add option for retry number of connection attempts

This patch adds a new option (-r | --retries) to specify how many times the
client should attempt to connect to the Neutron server when using idempotent
methods (GET, PUT and DELETE). The patch also provides more user-friendly
message when it's impossible to connect to Neutron server from CLI and
ensures that connection-related exceptions are raised to the caller by
default when neutronclient is used as a library.

DocImpact
Closes-Bug: #1312225

Change-Id: Id74d7cf9a0e8c5d2cd3ee4851c883d5286bea19d
This commit is contained in:
Jakub Libosvar 2014-04-24 16:58:50 +02:00
parent b4f4544eaa
commit 65883ba4eb
6 changed files with 53 additions and 7 deletions

@ -64,7 +64,9 @@ class ClientManager(object):
ca_cert=None,
log_credentials=False,
service_type=None,
timeout=None
timeout=None,
retries=0,
raise_errors=True
):
self._token = token
self._url = url
@ -84,7 +86,8 @@ class ClientManager(object):
self._ca_cert = ca_cert
self._log_credentials = log_credentials
self._timeout = timeout
return
self._retries = retries
self._raise_errors = raise_errors
def initialize(self):
if not self._url:

@ -46,7 +46,9 @@ def make_client(instance):
token=instance._token,
auth_strategy=instance._auth_strategy,
insecure=instance._insecure,
ca_cert=instance._ca_cert)
ca_cert=instance._ca_cert,
retries=instance._retries,
raise_errors=instance._raise_errors)
return client
else:
raise exceptions.UnsupportedVersion(_("API version %s is not "

@ -97,6 +97,17 @@ def env(*_vars, **kwargs):
return kwargs.get('default', '')
def check_non_negative_int(value):
try:
value = int(value)
except ValueError:
raise argparse.ArgumentTypeError(_("invalid int value: %r") % value)
if value < 0:
raise argparse.ArgumentTypeError(_("input value %d is negative") %
value)
return value
COMMAND_V2 = {
'net-list': network.ListNetwork,
'net-external-list': network.ListExternalNetwork,
@ -368,6 +379,13 @@ class NeutronShell(app.App):
nargs=0,
default=self, # tricky
help=_("Show this help message and exit."))
parser.add_argument(
'-r', '--retries',
metavar="NUM",
type=check_non_negative_int,
default=0,
help=_("How many times the request to the Neutron server should "
"be retried if it fails."))
# Global arguments
parser.add_argument(
'--os-auth-strategy', metavar='<auth-strategy>',
@ -650,6 +668,8 @@ class NeutronShell(app.App):
insecure=self.options.insecure,
ca_cert=self.options.os_cacert,
timeout=self.options.timeout,
retries=self.options.retries,
raise_errors=False,
log_credentials=True)
return

@ -128,8 +128,8 @@ class ShellTest(testtools.TestCase):
username='test', user_id='',
password='test', region_name='', api_version={'network': '2.0'},
auth_strategy='keystone', service_type='network',
endpoint_type='publicURL', insecure=False, ca_cert=None,
log_credentials=True, timeout=None)
endpoint_type='publicURL', insecure=False, ca_cert=None, retries=0,
raise_errors=False, log_credentials=True, timeout=None)
neutron_shell.run_subcommand(['quota-list'])
self.mox.ReplayAll()
cmdline = ('--os-username test '

@ -62,6 +62,8 @@ class TestSSL(testtools.TestCase):
url=mox.IgnoreArg(),
username=mox.IgnoreArg(),
user_id=mox.IgnoreArg(),
retries=mox.IgnoreArg(),
raise_errors=mox.IgnoreArg(),
log_credentials=mox.IgnoreArg(),
timeout=mox.IgnoreArg(),
)
@ -94,6 +96,8 @@ class TestSSL(testtools.TestCase):
url=mox.IgnoreArg(),
username=mox.IgnoreArg(),
user_id=mox.IgnoreArg(),
retries=mox.IgnoreArg(),
raise_errors=mox.IgnoreArg(),
log_credentials=mox.IgnoreArg(),
timeout=mox.IgnoreArg(),
)
@ -117,6 +121,8 @@ class TestSSL(testtools.TestCase):
tenant_name=mox.IgnoreArg(),
token=mox.IgnoreArg(),
username=mox.IgnoreArg(),
retries=mox.IgnoreArg(),
raise_errors=mox.IgnoreArg(),
)
self.mox.ReplayAll()

@ -129,6 +129,12 @@ class Client(object):
http requests. (optional)
:param bool insecure: SSL certificate validation. (optional)
:param string ca_cert: SSL CA bundle file to use. (optional)
:param integer retries: How many times idempotent (GET, PUT, DELETE)
requests to Neutron server should be retried if
they fail (default: 0).
:param bool raise_errors: If True then exceptions caused by connection
failure are propagated to the caller.
(default: True)
Example::
@ -1194,7 +1200,8 @@ class Client(object):
self.version = '2.0'
self.format = 'json'
self.action_prefix = "/v%s" % (self.version)
self.retries = 0
self.retries = kwargs.get('retries', 0)
self.raise_errors = kwargs.get('raise_errors', True)
self.retry_interval = 1
def _handle_fault_response(self, status_code, response_body):
@ -1303,8 +1310,16 @@ class Client(object):
if i < self.retries:
_logger.debug('Retrying connection to Neutron service')
time.sleep(self.retry_interval)
elif self.raise_errors:
raise
raise exceptions.ConnectionFailed(reason=_("Maximum attempts reached"))
if self.retries:
msg = (_("Failed to connect to Neutron server after %d attempts")
% max_attempts)
else:
msg = _("Failed to connect Neutron server")
raise exceptions.ConnectionFailed(reason=msg)
def delete(self, action, body=None, headers=None, params=None):
return self.retry_request("DELETE", action, body=body,