]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Set timeout in RestClient calls
authorVolker Theile <vtheile@suse.com>
Tue, 24 Jul 2018 09:52:29 +0000 (11:52 +0200)
committerNathan Cutler <ncutler@suse.com>
Fri, 7 Sep 2018 10:26:02 +0000 (12:26 +0200)
Set a default timeout of 45 seconds to all REST client calls. This can be customized via 'ceph dashboard set-rest-requests-timeout <seconds>'. Currently the REST client is only used by the RGW controller.

Signed-off-by: Volker Theile <vtheile@suse.com>
(cherry picked from commit 23128391986bf89b29fad14e0f26e9cb6ecf5cc3)

Conflicts:
doc/mgr/dashboard.rst

doc/mgr/dashboard.rst
src/pybind/mgr/dashboard/rest_client.py
src/pybind/mgr/dashboard/settings.py
src/pybind/mgr/dashboard/tests/test_rest_client.py [new file with mode: 0644]

index 858444dca79628785d519ff8dcb70ebea8a9b43e..7af26568d9183e166eb260cf46404bd0db029972 100644 (file)
@@ -188,7 +188,7 @@ The password will be stored in the configuration database in encrypted form
 using ``bcrypt``. This is a global setting that applies to all dashboard instances.
 
 Enabling the Object Gateway management frontend
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 To use the Object Gateway management functionality of the dashboard, you will
 need to provide the login credentials of a user with the ``system`` flag
@@ -230,6 +230,13 @@ exist and you may find yourself in the situation that you have to use them::
   $ ceph dashboard set-rgw-api-admin-resource <admin_resource>
   $ ceph dashboard set-rgw-api-user-id <user_id>
 
+If the Object Gateway takes too long to process requests and the dashboard runs
+into timeouts, then you can set the timeout value to your needs::
+
+  $ ceph dashboard set-rest-requests-timeout <seconds>
+
+The default value is 45 seconds.
+
 Accessing the dashboard
 ^^^^^^^^^^^^^^^^^^^^^^^
 
index 3105545fc3beb73019453542917a161e482dc4e3..6099fe945254fae52ddbd4d902877191487110da 100644 (file)
 """
 from __future__ import absolute_import
 
+from .settings import Settings
 from .tools import build_url
 import inspect
 import re
 import requests
-from requests.exceptions import ConnectionError, InvalidURL
+from requests.exceptions import ConnectionError, InvalidURL, Timeout
 from . import logger
 
 try:
@@ -26,6 +27,18 @@ except ImportError:
     from urllib3.exceptions import SSLError
 
 
+class TimeoutRequestsSession(requests.Session):
+    """
+    Set timeout argument for all requests if this is not already done.
+    """
+    def request(self, *args, **kwargs):
+        if ((args[8] if len(args) > 8 else None) is None) \
+                and kwargs.get('timeout') is None:
+            if Settings.REST_REQUESTS_TIMEOUT > 0:
+                kwargs['timeout'] = Settings.REST_REQUESTS_TIMEOUT
+        return super(TimeoutRequestsSession, self).request(*args, **kwargs)
+
+
 class RequestException(Exception):
     def __init__(self,
                  message,
@@ -315,7 +328,7 @@ class RestClient(object):
         logger.debug("REST service base URL: %s", self.base_url)
         self.headers = {'Accept': 'application/json'}
         self.auth = auth
-        self.session = requests.Session()
+        self.session = TimeoutRequestsSession()
 
     def _login(self, request=None):
         pass
@@ -465,6 +478,12 @@ class RestClient(object):
             logger.exception("%s REST API failed %s: %s", self.client_name,
                              method.upper(), str(ex))
             raise RequestException(str(ex))
+        except Timeout as ex:
+            msg = "{} REST API {} timed out after {} seconds (url={}).".format(
+                self.client_name, ex.request.method, Settings.REST_REQUESTS_TIMEOUT,
+                ex.request.url)
+            logger.exception(msg)
+            raise RequestException(msg)
 
     @staticmethod
     def api(path, **api_kwargs):
index 3875f268254a9d374d36b7ea152c8c74c345abe0..a942cc9c5e686fac895a1190d7f4f2ed138fb313 100644 (file)
@@ -19,6 +19,7 @@ class Options(object):
         GRAFANA_API_PORT = (3000, int)
     """
     ENABLE_BROWSABLE_API = (True, bool)
+    REST_REQUESTS_TIMEOUT = (45, int)
 
     # RGW settings
     RGW_API_HOST = ('', str)
diff --git a/src/pybind/mgr/dashboard/tests/test_rest_client.py b/src/pybind/mgr/dashboard/tests/test_rest_client.py
new file mode 100644 (file)
index 0000000..7a15319
--- /dev/null
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+import unittest
+
+from mock import patch
+from .. import mgr
+from ..rest_client import RestClient
+
+
+class RestClientTest(unittest.TestCase):
+    def setUp(self):
+        settings = {'REST_REQUESTS_TIMEOUT': 45}
+        mgr.get_config.side_effect = settings.get
+
+    def test_timeout_auto_set(self):
+        with patch('requests.Session.request') as mock_request:
+            rest_client = RestClient('localhost', 8000)
+            rest_client.session.request('GET', '/test')
+            mock_request.assert_called_with('GET', '/test', timeout=45)
+
+    def test_timeout_auto_set_arg(self):
+        with patch('requests.Session.request') as mock_request:
+            rest_client = RestClient('localhost', 8000)
+            rest_client.session.request(
+                'GET', '/test', None, None, None, None,
+                None, None, None)
+            mock_request.assert_called_with(
+                'GET', '/test', None, None, None, None,
+                None, None, None, timeout=45)
+
+    def test_timeout_no_auto_set_kwarg(self):
+        with patch('requests.Session.request') as mock_request:
+            rest_client = RestClient('localhost', 8000)
+            rest_client.session.request('GET', '/test', timeout=20)
+            mock_request.assert_called_with('GET', '/test', timeout=20)
+
+    def test_timeout_no_auto_set_arg(self):
+        with patch('requests.Session.request') as mock_request:
+            rest_client = RestClient('localhost', 8000)
+            rest_client.session.request(
+                'GET', '/test', None, None, None, None,
+                None, None, 40)
+            mock_request.assert_called_with(
+                'GET', '/test', None, None, None, None,
+                None, None, 40)