From 1e4088f46a54662449d9594f0c0942f1e8edab8e Mon Sep 17 00:00:00 2001 From: Qin Zhao Date: Sat, 15 Nov 2014 00:38:14 +0800 Subject: [PATCH] Truncate encoded instance message to 255 or fewer In nova/compute/utils.py function exception_to_dict truncates unicode message to 255. However, in non-English locales, instance message may be longer than 255 after encoding the unicode message to byte message. Need to truncate the encoded byte message to 255 or fewer, in order to ensure db insert operation succeed. Closes-Bug: 1389102 (cherry picked from commit 4e95505843d05f20ab01716ceee74f6757b16dd9) Conflicts: nova/tests/unit/compute/test_compute_utils.py Change-Id: I62fa2830b22e367eb9486d09d3c8818a18ebd20d --- nova/compute/utils.py | 15 ++++++++++++++- nova/tests/compute/test_compute_utils.py | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/nova/compute/utils.py b/nova/compute/utils.py index 185c0fe0cdbf..1c94a7cff733 100644 --- a/nova/compute/utils.py +++ b/nova/compute/utils.py @@ -19,6 +19,7 @@ import string import traceback from oslo.config import cfg +from oslo.utils import encodeutils from nova import block_device from nova.compute import flavors @@ -64,7 +65,19 @@ def exception_to_dict(fault): # NOTE(dripton) The message field in the database is limited to 255 chars. # MySQL silently truncates overly long messages, but PostgreSQL throws an # error if we don't truncate it. - u_message = unicode(message)[:255] + b_message = encodeutils.safe_encode(message)[:255] + + # NOTE(chaochin) UTF-8 character byte size varies from 1 to 6. If + # truncating a long byte string to 255, the last character may be + # cut in the middle, so that UnicodeDecodeError will occur when + # converting it back to unicode. + decode_ok = False + while not decode_ok: + try: + u_message = encodeutils.safe_decode(b_message) + decode_ok = True + except UnicodeDecodeError: + b_message = b_message[:-1] fault_dict = dict(exception=fault) fault_dict["message"] = u_message diff --git a/nova/tests/compute/test_compute_utils.py b/nova/tests/compute/test_compute_utils.py index 9e8af260b120..1deed99a3467 100644 --- a/nova/tests/compute/test_compute_utils.py +++ b/nova/tests/compute/test_compute_utils.py @@ -21,6 +21,7 @@ import string import mock from oslo.config import cfg +from oslo.utils import encodeutils import six import testtools @@ -804,6 +805,28 @@ class ComputeUtilsGetRebootTypes(test.TestCase): self.assertEqual(reboot_type, 'HARD') +class ComputeUtilsTestCase(test.NoDBTestCase): + def test_exception_to_dict_with_long_message_3_bytes(self): + # Generate Chinese byte string whose length is 300. This Chinese UTF-8 + # character occupies 3 bytes. After truncating, the byte string length + # should be 255. + msg = encodeutils.safe_decode('\xe8\xb5\xb5' * 100) + exc = exception.NovaException(message=msg) + fault_dict = compute_utils.exception_to_dict(exc) + byte_message = encodeutils.safe_encode(fault_dict["message"]) + self.assertEqual(255, len(byte_message)) + + def test_exception_to_dict_with_long_message_2_bytes(self): + # Generate Russian byte string whose length is 300. This Russian UTF-8 + # character occupies 2 bytes. After truncating, the byte string length + # should be 254. + msg = encodeutils.safe_decode('\xd0\x92' * 150) + exc = exception.NovaException(message=msg) + fault_dict = compute_utils.exception_to_dict(exc) + byte_message = encodeutils.safe_encode(fault_dict["message"]) + self.assertEqual(254, len(byte_message)) + + class ComputeUtilsPeriodicTaskSpacingWarning(test.NoDBTestCase): @mock.patch.object(compute_utils, 'LOG')