From 1e475dca0c3884199b7345b719dea9a05601bc36 Mon Sep 17 00:00:00 2001 From: Luan Nunes Utimura Date: Wed, 1 Feb 2023 10:10:03 -0300 Subject: [PATCH] Debian: Fix nova actions Since the platform migration to Debian, it was observed that the following Nova actions stopped working: - pause; - unpause; - suspend; - resume; - live-migration. The reason behind that is that some packages related to Nova, which have already been migrated to Debian, still have some incompatibilities with Python 3. Consequently, whenever these Nova actions were executed, some exceptions occurred on the nova-api-proxy and NFV side, preventing them from working. Therefore, this change aims to improve this compatibility. Most of the changes were necessary due to the fact that in Python 3 there is more of a distinction between `bytes` and `str`, whereas in Python 2 `bytes` is just an alias for `str`. Test Plan (on AIO-DX): PASS - Successfully perform a VM pause, unpause, suspend, resume. PASS - Successfully perform a VM live-migration. Closes-Bug: 2003813 Signed-off-by: Luan Nunes Utimura Change-Id: I918fe6e3deaa68630c797449649012e9fbf16fe4 --- nfv/nfv-common/nfv_common/selectable.py | 4 +++- .../nfv_plugins/nfvi_plugins/nfvi_compute_api.py | 6 ++++-- .../nfvi_plugins/openstack/rest_api.py | 7 +++++-- nfv/nfv-vim/nfv_vim/nfvi/objects/v1/_instance.py | 16 ++++++++++++++-- .../nova_api_proxy/apps/acceptor.py | 10 +++++----- .../nova-api-proxy/nova_api_proxy/apps/proxy.py | 11 ++++++++++- 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/nfv/nfv-common/nfv_common/selectable.py b/nfv/nfv-common/nfv_common/selectable.py index a60f378b..a4245c16 100755 --- a/nfv/nfv-common/nfv_common/selectable.py +++ b/nfv/nfv-common/nfv_common/selectable.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2016 Wind River Systems, Inc. +# Copyright (c) 2015-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -11,6 +11,8 @@ from six.moves import queue as threading_queue class ThreadQueue(object): def __init__(self, queue_id): + if hasattr(queue_id, "encode"): + queue_id = queue_id.encode() self._queue_id = queue_id self._send_socket, self._receive_socket = socket.socketpair() self._receive_socket.setblocking(False) diff --git a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_compute_api.py b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_compute_api.py index cd5c1683..374670a9 100755 --- a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_compute_api.py +++ b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_compute_api.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2018 Wind River Systems, Inc. +# Copyright (c) 2015-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -816,7 +816,9 @@ class NFVIComputeAPI(nfvi.api.v1.NFVIComputeAPI): request_dispatch.send_header(key, value) request_dispatch.end_headers() if http_body is not None: - request_dispatch.wfile.write(http_body.encode()) + if hasattr(http_body, "encode"): + http_body = http_body.encode() + request_dispatch.wfile.write(http_body) request_dispatch.done() DLOG.info("Sent response for request %s." % request_uuid) diff --git a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/rest_api.py b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/rest_api.py index a01acb47..a06d2630 100755 --- a/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/rest_api.py +++ b/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/openstack/rest_api.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2016 Wind River Systems, Inc. +# Copyright (c) 2015-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -323,7 +323,10 @@ def _rest_api_request(token_id, request_info.add_header(header_type, header_value) if api_cmd_payload is not None: - request_info.data = api_cmd_payload.encode() + if hasattr(api_cmd_payload, "encode"): + request_info.data = api_cmd_payload.encode() + else: + request_info.data = api_cmd_payload DLOG.verbose("Rest-API method=%s, api_cmd=%s, api_cmd_headers=%s, " "api_cmd_payload=%s" % (method, api_cmd, api_cmd_headers, diff --git a/nfv/nfv-vim/nfv_vim/nfvi/objects/v1/_instance.py b/nfv/nfv-vim/nfv_vim/nfvi/objects/v1/_instance.py index f39233d5..42b68bf0 100755 --- a/nfv/nfv-vim/nfv_vim/nfvi/objects/v1/_instance.py +++ b/nfv/nfv-vim/nfv_vim/nfvi/objects/v1/_instance.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2016 Wind River Systems, Inc. +# Copyright (c) 2015-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -379,7 +379,19 @@ class InstanceActionData(ObjectData): if self.context is None: data['context'] = dict() else: - data['context'] = self.context.as_dict() + context = self.context.as_dict().copy() + + # In Python 3, it has been observed that some values present + # in the `context` dictionary are bytes instead of strings. + # This can lead to some exceptions later in the code when + # attempting to serialize this object into JSON. + if six.PY3: + for key, value in context.items(): + if isinstance(value, bytes): + context[key] = value.decode() + + data['context'] = context + return data def __str__(self): diff --git a/nova-api-proxy/nova-api-proxy/nova_api_proxy/apps/acceptor.py b/nova-api-proxy/nova-api-proxy/nova_api_proxy/apps/acceptor.py index 78f3a8b5..a9f151bd 100644 --- a/nova-api-proxy/nova-api-proxy/nova_api_proxy/apps/acceptor.py +++ b/nova-api-proxy/nova-api-proxy/nova_api_proxy/apps/acceptor.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2018 Wind River Systems, Inc. +# Copyright (c) 2015-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -61,7 +61,7 @@ class APIController(Middleware): return self._default_dispatcher def _is_nfvi_request(self, request): - body = get_jason_request_body(request) + body = get_json_request_body(request) data = json.loads(body) for action in self._actions: if action in list(data): @@ -86,7 +86,7 @@ class APIController(Middleware): def _generate_log(self, req): environ = req.environ - body = get_jason_request_body(req) + body = get_json_request_body(req) if CONF.debug and body is not None: data = json.loads(body) self._print_data(data) @@ -203,12 +203,12 @@ class DebugHeaders(Middleware): LOG.info('-' * 70 + '\n') -def get_jason_request_body(request): +def get_json_request_body(request): content_type = request.content_type if not content_type or content_type.startswith('text/plain'): LOG.info("Content type null or plain text") content_type = 'application/json' if content_type in ('JSON', 'application/json') and \ - request.body.startswith('{'): + request.body.startswith(b'{'): LOG.debug("Req body: (%s)" % request.body) return request.body diff --git a/nova-api-proxy/nova-api-proxy/nova_api_proxy/apps/proxy.py b/nova-api-proxy/nova-api-proxy/nova_api_proxy/apps/proxy.py index ef11964e..30d47103 100644 --- a/nova-api-proxy/nova-api-proxy/nova_api_proxy/apps/proxy.py +++ b/nova-api-proxy/nova-api-proxy/nova_api_proxy/apps/proxy.py @@ -7,7 +7,7 @@ # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php # -# Copyright (c) 2015-2018 Wind River Systems, Inc. +# Copyright (c) 2015-2023 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -39,6 +39,15 @@ class Proxy(Application): LOG.debug("Proxy the request to the remote host: (%s)", environ[ 'HTTP_HOST']) start_ms = get_monotonic_timestamp_in_ms() + + # In Python 3, the builtin `http` library raises an exception if one + # or more headers are set to `NoneType`. See: + # https://github.com/python/cpython/blob/3.9/Lib/http/client.py#L1253 + for key, value in environ.items(): + if key.startswith("HTTP_"): + if value is None: + environ[key] = "" + result = self.proxy_app(environ, start_response) now_ms = get_monotonic_timestamp_in_ms() elapsed_secs = (now_ms - start_ms) // 1000