Commit 1ce03fb1 authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

Add ht-based result checks to opcodes



This adds the infrastructure necessary to check opcode results using
ht-based functions. Checks are added for two opcodes.
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent ae1a845c
......@@ -189,6 +189,12 @@ class OpExecError(GenericError):
"""
class OpResultError(GenericError):
"""Issue with OpCode result.
"""
class OpCodeUnknown(GenericError):
"""Unknown opcode submitted.
......
......@@ -373,8 +373,8 @@ class Processor(object):
assert lu.needed_locks is not None, "needed_locks not set by LU"
try:
return self._LockAndExecLU(lu, locking.LEVEL_INSTANCE, calc_timeout,
priority)
result = self._LockAndExecLU(lu, locking.LEVEL_INSTANCE, calc_timeout,
priority)
finally:
if self._ec_id:
self.context.cfg.DropECReservations(self._ec_id)
......@@ -383,6 +383,15 @@ class Processor(object):
finally:
self._cbs = None
resultcheck_fn = op.OP_RESULT
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" %
resultcheck_fn)
return result
def Log(self, *args):
"""Forward call to feedback callback function.
......
......@@ -419,6 +419,9 @@ def _BuildJobDepCheck(relative):
TNoRelativeJobDependencies = _BuildJobDepCheck(False)
#: List of submission status and job ID as returned by C{SubmitManyJobs}
TJobIdList = ht.TListOf(ht.TItems([ht.TBool, ht.TOr(ht.TString, ht.TJobId)]))
class OpCode(BaseOpCode):
"""Abstract OpCode.
......@@ -433,6 +436,7 @@ class OpCode(BaseOpCode):
method for details).
@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
@cvar WITH_LU: Boolean that specifies whether this should be included in
mcpu's dispatch table
@ivar dry_run: Whether the LU should be run in dry-run mode, i.e. just
......@@ -454,6 +458,7 @@ class OpCode(BaseOpCode):
(COMMENT_ATTR, None, ht.TMaybeString,
"Comment describing the purpose of the opcode"),
]
OP_RESULT = None
def __getstate__(self):
"""Specialized getstate for opcodes.
......@@ -595,6 +600,9 @@ class OpClusterVerifyDisks(OpCode):
"""Verify the cluster disks.
"""
OP_RESULT = ht.TStrictDict(True, True, {
constants.JOB_IDS_KEY: TJobIdList,
})
class OpGroupVerifyDisks(OpCode):
......@@ -619,6 +627,11 @@ class OpGroupVerifyDisks(OpCode):
OP_PARAMS = [
_PGroupName,
]
OP_RESULT = \
ht.TAnd(ht.TIsLength(3),
ht.TItems([ht.TDictOf(ht.TString, ht.TString),
ht.TListOf(ht.TString),
ht.TDictOf(ht.TString, ht.TListOf(ht.TString))]))
class OpClusterRepairDiskSizes(OpCode):
......
......@@ -49,6 +49,7 @@ class TestOpcodes(unittest.TestCase):
self.assertEqual(cls.OP_ID, opcodes._NameToId(cls.__name__))
self.assertFalse(compat.any(cls.OP_ID.startswith(prefix)
for prefix in opcodes._SUMMARY_PREFIX.keys()))
self.assertTrue(cls.OP_RESULT is None or callable(cls.OP_RESULT))
self.assertRaises(TypeError, cls, unsupported_parameter="some value")
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment