diff --git a/neutronclient/common/clientmanager.py b/neutronclient/common/clientmanager.py index 69d1b0c4a..c159e05d3 100644 --- a/neutronclient/common/clientmanager.py +++ b/neutronclient/common/clientmanager.py @@ -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: diff --git a/neutronclient/neutron/client.py b/neutronclient/neutron/client.py index f87c8311b..2dd40dea5 100644 --- a/neutronclient/neutron/client.py +++ b/neutronclient/neutron/client.py @@ -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 " diff --git a/neutronclient/shell.py b/neutronclient/shell.py index f1f2e2e06..a3e51080b 100644 --- a/neutronclient/shell.py +++ b/neutronclient/shell.py @@ -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 diff --git a/neutronclient/tests/unit/test_shell.py b/neutronclient/tests/unit/test_shell.py index ecda0bfa1..70acc5961 100644 --- a/neutronclient/tests/unit/test_shell.py +++ b/neutronclient/tests/unit/test_shell.py @@ -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 ' diff --git a/neutronclient/tests/unit/test_ssl.py b/neutronclient/tests/unit/test_ssl.py index f22f0d35a..9c0816d45 100644 --- a/neutronclient/tests/unit/test_ssl.py +++ b/neutronclient/tests/unit/test_ssl.py @@ -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() diff --git a/neutronclient/v2_0/client.py b/neutronclient/v2_0/client.py index a102781b2..630d41341 100644 --- a/neutronclient/v2_0/client.py +++ b/neutronclient/v2_0/client.py @@ -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,