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("_")