]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
pybind/mgr: add additional decorators for mgr response handling
authorJohn Mulligan <jmulligan@redhat.com>
Thu, 5 May 2022 19:30:17 +0000 (15:30 -0400)
committerAdam King <adking@redhat.com>
Mon, 5 Sep 2022 18:30:24 +0000 (14:30 -0400)
Add a ErrorResponseHandler decorator for catching and automatically
handling ErrorResponseBase exceptions like Responder does but it *only*
handles exceptions. Normal return value tuples are passed on to the mgr.

Add a EmptyResponder decorator that catches errors but returns "empty"
strings as a response to the mgr. This matches a number of preexisting
functions that don't return json or any other value on success.

Both these decorators are added in order to match behaviors of Responder
decorator without opting into auto formatted responses wholesale. They
are intended to be used when converting older apis to use object_format
module.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
(cherry picked from commit a41678a4c700867ba23a299eeafdad23b6840bd0)

src/pybind/mgr/object_format.py

index 4a843e5c26cf69bc2ffde1dc9a6977ee90d2df3a..ab3e0c707624a0b1be887b8d2a0bce5fea2928fa 100644 (file)
@@ -529,3 +529,76 @@ class Responder:
         # on the ceph cli/api
         setattr(_format_response, "extra_args", {"format": str})
         return _format_response
+
+
+class ErrorResponseHandler:
+    """ErrorResponseHandler is a very simple decorator that handles functions that
+    raise exceptions inheriting from ErrorResponseBase. If such an exception
+    is raised that exception can and will be converted to a mgr response tuple.
+    This is similar to Responder but error handling is all this decorator does.
+    """
+
+    def __call__(self, f: Callable[..., Tuple[int, str, str]]) -> HandlerFuncType:
+        """Wrap a python function so that if the function raises an exception inheriting
+        ErrorResponderBase the error is correctly converted to a mgr response.
+        """
+
+        @wraps(f)
+        def _format_response(*args: Any, **kwargs: Any) -> Tuple[int, str, str]:
+            try:
+                retval, body, sts = f(*args, **kwargs)
+            except ErrorResponseBase as e:
+                return e.format_response()
+            return retval, body, sts
+
+        return _format_response
+
+
+class ConstantResponderBase:
+    """The constant responder base assumes that a wrapped function should not
+    be passing data back to the manager. It only responds with the default
+    (constant) values provided. The process_response function allows a subclass
+    to handle/log/validate any values that were returned from the wrapped
+    function.
+
+    This class can be used a building block for special decorators that
+    do not normally emit response data.
+    """
+
+    def mgr_return_value(self) -> int:
+        return 0
+
+    def mgr_body_value(self) -> str:
+        return ""
+
+    def mgr_status_value(self) -> str:
+        return ""
+
+    def process_response(self, result: Any) -> None:
+        return None
+
+    def __call__(self, f: Callable) -> HandlerFuncType:
+        """Wrap a python function so that if the function raises an exception
+        inheriting ErrorResponderBase the error is correctly converted to a mgr
+        response. Otherwise, it returns a default set of constant values.
+        """
+
+        @wraps(f)
+        def _format_response(*args: Any, **kwargs: Any) -> Tuple[int, str, str]:
+            try:
+                self.process_response(f(*args, **kwargs))
+            except ErrorResponseBase as e:
+                return e.format_response()
+            return self.mgr_return_value(), self.mgr_body_value(), self.mgr_status_value()
+        return _format_response
+
+
+class EmptyResponder(ConstantResponderBase):
+    """Always respond with an empty (string) body. Checks that the wrapped function
+    returned None in order to ensure it is not being used on functions that
+    return data objects.
+    """
+
+    def process_response(self, result: Any) -> None:
+        if result is not None:
+            raise ValueError("EmptyResponder expects None from wrapped functions")