diff --git a/lib/opcodes.py b/lib/opcodes.py
index fd616395319fd2329b79018ba6f8bf4ba0baf3e6..6e3fed502e857c0917ad14aebde5ade53fd44057 100644
--- a/lib/opcodes.py
+++ b/lib/opcodes.py
@@ -434,6 +434,10 @@ class _AutoOpParamSlots(objectutils.AutoSlots):
     slots = mcs._GetSlots(attrs)
     assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \
       "Class '%s' uses unknown field in OP_DSC_FIELD" % name
+    assert ("OP_DSC_FORMATTER" not in attrs or
+            callable(attrs["OP_DSC_FORMATTER"])), \
+      ("Class '%s' uses non-callable in OP_DSC_FORMATTER (%s)" %
+       (name, type(attrs["OP_DSC_FORMATTER"])))
 
     attrs["OP_ID"] = _NameToId(name)
 
@@ -598,6 +602,9 @@ class OpCode(BaseOpCode):
   @cvar OP_DSC_FIELD: The name of a field whose value will be included in the
                       string returned by Summary(); see the docstring of that
                       method for details).
+  @cvar OP_DSC_FORMATTER: A callable that should format the OP_DSC_FIELD; if
+                          not present, then the field will be simply converted
+                          to string
   @cvar OP_PARAMS: List of opcode attributes, the default values they should
                    get if not already defined, and types they must match.
   @cvar OP_RESULT: Callable to verify opcode result
@@ -685,7 +692,10 @@ class OpCode(BaseOpCode):
     field_name = getattr(self, "OP_DSC_FIELD", None)
     if field_name:
       field_value = getattr(self, field_name, None)
-      if isinstance(field_value, (list, tuple)):
+      field_formatter = getattr(self, "OP_DSC_FORMATTER", None)
+      if callable(field_formatter):
+        field_value = field_formatter(field_value)
+      elif isinstance(field_value, (list, tuple)):
         field_value = ",".join(str(i) for i in field_value)
       txt = "%s(%s)" % (txt, field_value)
     return txt
@@ -1958,6 +1968,16 @@ class OpTestDelay(OpCode):
     ("repeat", 0, ht.TNonNegativeInt, None),
     ]
 
+  def OP_DSC_FORMATTER(self, value): # pylint: disable=C0103,R0201
+    """Custom formatter for duration.
+
+    """
+    try:
+      v = float(value)
+    except TypeError:
+      v = value
+    return str(v)
+
 
 class OpTestAllocator(OpCode):
   """Allocator framework testing.
diff --git a/test/ganeti.opcodes_unittest.py b/test/ganeti.opcodes_unittest.py
index 3e2db17a5b0b0209bac8847a7429e820d8be7005..9cc80ffea08e26365db2da0672d2f49bbcc26137 100755
--- a/test/ganeti.opcodes_unittest.py
+++ b/test/ganeti.opcodes_unittest.py
@@ -121,6 +121,16 @@ class TestOpcodes(unittest.TestCase):
     self.assertEqual(OpTest(data="node1.example.com").Summary(),
                      "TEST(node1.example.com)")
 
+  def testSummaryFormatter(self):
+    class OpTest(opcodes.OpCode):
+      OP_DSC_FIELD = "data"
+      OP_DSC_FORMATTER = lambda _, v: "a"
+      OP_PARAMS = [
+        ("data", ht.NoDefault, ht.TString, None),
+        ]
+    self.assertEqual(OpTest(data="").Summary(), "TEST(a)")
+    self.assertEqual(OpTest(data="b").Summary(), "TEST(a)")
+
   def testTinySummary(self):
     self.assertFalse(utils.FindDuplicates(opcodes._SUMMARY_PREFIX.values()))
     self.assertTrue(compat.all(prefix.endswith("_") and supplement.endswith("_")