diff --git a/lib/mcpu.py b/lib/mcpu.py
index 96c589b7f43dcaa4b4fa99fe952c96669d0ede3d..8b3734e0ebf2e6e6c8c459c8041da7c2c5e17813 100644
--- a/lib/mcpu.py
+++ b/lib/mcpu.py
@@ -404,8 +404,8 @@ class Processor(object):
     if not (resultcheck_fn is None or resultcheck_fn(result)):
       logging.error("Expected opcode result matching %s, got %s",
                     resultcheck_fn, result)
-      raise errors.OpResultError("Opcode result does not match %s, got %s" %
-                                 (resultcheck_fn, result[:80]))
+      raise errors.OpResultError("Opcode result does not match %s: %s" %
+                                 (resultcheck_fn, utils.Truncate(result, 80)))
 
     return result
 
diff --git a/lib/utils/text.py b/lib/utils/text.py
index 02382642bff0282a6d831dc09d2e2e4fbadbaefb..0a0e68cebe1d18d3e1c146f7cca8cacad2e34d2f 100644
--- a/lib/utils/text.py
+++ b/lib/utils/text.py
@@ -43,6 +43,9 @@ _MAC_CHECK_RE = re.compile("^([0-9a-f]{2}:){5}[0-9a-f]{2}$", re.I)
 #: Shell param checker regexp
 _SHELLPARAM_REGEX = re.compile(r"^[-a-zA-Z0-9._+/:%@]+$")
 
+#: ASCII equivalent of unicode character 'HORIZONTAL ELLIPSIS' (U+2026)
+_ASCII_ELLIPSIS = "..."
+
 
 def MatchNameComponent(key, name_list, case_sensitive=True):
   """Try to match a name against a list.
@@ -556,3 +559,26 @@ def FormatOrdinal(value):
     suffix = "th"
 
   return "%s%s" % (value, suffix)
+
+
+def Truncate(text, length):
+  """Truncate string and add ellipsis if needed.
+
+  @type text: string
+  @param text: Text
+  @type length: integer
+  @param length: Desired length
+  @rtype: string
+  @return: Truncated text
+
+  """
+  assert length > len(_ASCII_ELLIPSIS)
+
+  # Serialize if necessary
+  if not isinstance(text, basestring):
+    text = str(text)
+
+  if len(text) <= length:
+    return text
+  else:
+    return text[:length - len(_ASCII_ELLIPSIS)] + _ASCII_ELLIPSIS
diff --git a/test/ganeti.utils.text_unittest.py b/test/ganeti.utils.text_unittest.py
index 1e91468e3195670979e02b65907e0ff6b8c998dd..5a9af02e7eba0459797cc2f385c1a77e4a240e5d 100755
--- a/test/ganeti.utils.text_unittest.py
+++ b/test/ganeti.utils.text_unittest.py
@@ -545,5 +545,32 @@ class TestOrdinal(unittest.TestCase):
       self.assertEqual(utils.FormatOrdinal(value), ordinal)
 
 
+class TestTruncate(unittest.TestCase):
+  def _Test(self, text, length):
+    result = utils.Truncate(text, length)
+    self.assertTrue(len(result) <= length)
+    return result
+
+  def test(self):
+    self.assertEqual(self._Test("", 80), "")
+    self.assertEqual(self._Test("abc", 4), "abc")
+    self.assertEqual(self._Test("Hello World", 80), "Hello World")
+    self.assertEqual(self._Test("Hello World", 4), "H...")
+    self.assertEqual(self._Test("Hello World", 5), "He...")
+
+    for i in [4, 10, 100]:
+      data = i * "FooBarBaz"
+      self.assertEqual(self._Test(data, len(data)), data)
+
+    for (length, exp) in [(8, u"T\u00e4st\u2026xyz"), (7, u"T\u00e4st...")]:
+      self.assertEqual(self._Test(u"T\u00e4st\u2026xyz", length), exp)
+
+    self.assertEqual(self._Test(range(100), 20), "[0, 1, 2, 3, 4, 5...")
+
+  def testError(self):
+    for i in range(4):
+      self.assertRaises(AssertionError, utils.Truncate, "", i)
+
+
 if __name__ == "__main__":
   testutils.GanetiTestProgram()