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 4e95505843)

Conflicts:
	nova/tests/unit/compute/test_compute_utils.py

Change-Id: I62fa2830b22e367eb9486d09d3c8818a18ebd20d
This commit is contained in:
Qin Zhao 2014-11-15 00:38:14 +08:00 committed by Qin Zhao
parent 559ff02dfe
commit 1e4088f46a
2 changed files with 37 additions and 1 deletions

View File

@ -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

View File

@ -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')