From 94321e9d571922a453917e80cebd0835b9bf7e40 Mon Sep 17 00:00:00 2001 From: Al Bailey Date: Tue, 14 Feb 2023 15:21:13 +0000 Subject: [PATCH] Debian: python3 fix for OpenStackRestAPIExceptions When the NFV uses tasks and futures and coroutines to interact with openstack APIs, an OpenStackRestAPIException can be returned as a task result. The exception needs to be 'pickled' when sent across the queue/socket for the 'simulated' asyncio workflow. However, the pickle code for that exception was broken in python3. It was relying on a python2 'message' attribute of the base Exception class to exist, which no longer exists (in python3) This was causing the pickle command to quietly fail and the code waiting for the task result would timeout and not report back the failure information. The fix is to ensure that there is a 'message' property on that exception type. Unit tests have been added for all the pickleable exceptions, to ensure their '__reduce__' and other interactions with 'pickle' are not reporting any failures. Test Plan: PASS: create and apply a kube-upgrade-strategy for an older version of kubernetes and observe it reports its failure error (rather than a timeout) Closes-Bug: #2007285 Signed-off-by: Al Bailey Change-Id: I3a8776163a78330810ae1097ddd1831b1b26a212 --- .../nfvi_plugins/openstack/exceptions.py | 19 +++++-- .../nfv_unit_tests/tests/test_exceptions.py | 55 +++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) create mode 100755 nfv/nfv-tests/nfv_unit_tests/tests/test_exceptions.py diff --git a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/exceptions.py b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/exceptions.py index 914ce5d3..51200db2 100755 --- a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/exceptions.py +++ b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/exceptions.py @@ -67,12 +67,13 @@ class OpenStackRestAPIException(exceptions.PickleableException): """ Create an OpenStack Rest-API exception """ - super(OpenStackRestAPIException, self).__init__(message) + super(OpenStackRestAPIException, self).__init__(message, reason) self._method = method self._url = url self._headers = headers self._body = body self._status_code = status_code # as defined in RFC 2616 + self._message = message self._reason = reason # a message string or another exception self._response_headers = response_headers self._response_body = response_body @@ -99,9 +100,12 @@ class OpenStackRestAPIException(exceptions.PickleableException): """ Return a tuple so that we can properly pickle the exception """ - return (OpenStackRestAPIException, (self._method, self._url, - self._headers, self._body, - self._status_code, self.message, + return (OpenStackRestAPIException, (self._method, + self._url, + self._headers, + self._body, + self._status_code, + self.message, self._reason, self._response_headers, self._response_body, @@ -135,6 +139,13 @@ class OpenStackRestAPIException(exceptions.PickleableException): """ return self._response_reason + @property + def message(self): + """ + Returns the message for the exception + """ + return self._message + @property def reason(self): """ diff --git a/nfv/nfv-tests/nfv_unit_tests/tests/test_exceptions.py b/nfv/nfv-tests/nfv_unit_tests/tests/test_exceptions.py new file mode 100755 index 00000000..cb366d8a --- /dev/null +++ b/nfv/nfv-tests/nfv_unit_tests/tests/test_exceptions.py @@ -0,0 +1,55 @@ +# +# Copyright (c) 2023 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import pickle + +from nfv_plugins.nfvi_plugins.openstack.exceptions import NotFound +from nfv_plugins.nfvi_plugins.openstack.exceptions import OpenStackException +from nfv_plugins.nfvi_plugins.openstack.exceptions import OpenStackRestAPIException + +from nfv_unit_tests.tests import testcase + + +class TestPickleableExceptions(testcase.NFVTestCase): + """Unit tests that verify pickleable exceptions""" + + def setUp(self): + """Setup for testing.""" + super(TestPickleableExceptions, self).setUp() + + def tearDown(self): + """Cleanup testing setup.""" + super(TestPickleableExceptions, self).tearDown() + + def _do_pickling_test(self, ex): + data = pickle.dumps(ex) + obj = pickle.loads(data) + self.assertEqual(obj.__reduce__(), ex.__reduce__()) + + def test_pickling_not_found(self): + ex = NotFound("message") + self._do_pickling_test(ex) + + def test_pickling_openstack_exception(self): + ex = OpenStackException("method", + "url", + "headers", + "body", + "message", + "reason") + self._do_pickling_test(ex) + + def test_pickling_openstack_rest_api_exception(self): + ex = OpenStackRestAPIException("method", + "url", + "headers", + "body", + "status_code", + "message", + "reason", + "response_headers", + "response_body", + "response_reason") + self._do_pickling_test(ex)