LGTM, thanks for the extensive test! On Tue, May 17, 2016 at 12:15 PM, 'Brian Foley' via ganeti-devel < [email protected]> wrote:
> Improve mocks and add a unit test to test this case. > > Signed-off-by: Brian Foley <[email protected]> > --- > lib/mcpu.py | 4 +-- > test/py/ganeti.mcpu_unittest.py | 35 +++++++++++++++++++++++ > test/py/mocks.py | 61 > +++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 98 insertions(+), 2 deletions(-) > > diff --git a/lib/mcpu.py b/lib/mcpu.py > index 689b89c..2255cec 100644 > --- a/lib/mcpu.py > +++ b/lib/mcpu.py > @@ -538,9 +538,9 @@ class Processor(object): > try: > result = self._ExecLU(lu) > except errors.OpPrereqError, err: > - (_, ecode) = err.args > - if ecode != errors.ECODE_TEMP_NORES: > + if len(err.args) < 2 or err.args[1] != errors.ECODE_TEMP_NORES: > raise > + > logging.debug("Temporarily out of resources; will retry > internally") > try: > lu.PrepareRetry(self.Log) > diff --git a/test/py/ganeti.mcpu_unittest.py b/test/py/ > ganeti.mcpu_unittest.py > index 18fabd8..796c2ba 100755 > --- a/test/py/ganeti.mcpu_unittest.py > +++ b/test/py/ganeti.mcpu_unittest.py > @@ -33,12 +33,16 @@ > > import unittest > import itertools > +import mocks > +from cmdlib.testsupport.rpc_runner_mock import CreateRpcRunnerMock > > from ganeti import compat > from ganeti import mcpu > from ganeti import opcodes > from ganeti import cmdlib > from ganeti import locking > +from ganeti import ht > +from ganeti import errors > from ganeti import constants > from ganeti.constants import \ > LOCK_ATTEMPTS_TIMEOUT, \ > @@ -175,6 +179,37 @@ class TestProcessResult(unittest.TestCase): > self.assertEqual(op2.comment, "foobar") > self.assertEqual(op2.debug_level, 3) > > +class TestExecLU(unittest.TestCase): > + class OpTest(opcodes.OpCode): > + OP_DSC_FIELD = "data" > + OP_PARAMS = [ > + ("data", ht.NoDefault, ht.TString, None), > + ] > + > + def setUp(self): > + self.ctx = mocks.FakeContext() > + self.cfg = self.ctx.GetConfig("ec_id") > + self.rpc = CreateRpcRunnerMock() > + self.proc = mcpu.Processor(self.ctx, "ec_id", enable_locks = False) > + self.op = self.OpTest() > + self.calc_timeout = lambda: 42 > + > + def testRunLU(self): > + lu = mocks.FakeLU(self.proc, self.op, self.cfg, self.rpc, None) > + self.proc._ExecLU(lu) > + > + def testRunLUWithPrereqError(self): > + prereq = errors.OpPrereqError(self.op, errors.ECODE_INVAL) > + lu = mocks.FakeLU(self.proc, self.op, self.cfg, self.rpc, prereq) > + self.assertRaises(errors.OpPrereqError, self.proc._LockAndExecLU, > + lu, locking.LEVEL_CLUSTER, self.calc_timeout) > + > + def testRunLUWithPrereqErrorMissingECode(self): > + prereq = errors.OpPrereqError(self.op) > + lu = mocks.FakeLU(self.proc, self.op, self.cfg, self.rpc, prereq) > + self.assertRaises(errors.OpPrereqError, self.proc._LockAndExecLU, > + lu, locking.LEVEL_CLUSTER, self.calc_timeout) > + > > if __name__ == "__main__": > testutils.GanetiTestProgram() > diff --git a/test/py/mocks.py b/test/py/mocks.py > index 649b032..406caca 100644 > --- a/test/py/mocks.py > +++ b/test/py/mocks.py > @@ -33,6 +33,7 @@ > > import os > > +from ganeti import locking > from ganeti import netutils > > > @@ -47,6 +48,11 @@ FAKE_CLUSTER_KEY = > ("AAAAB3NzaC1yc2EAAAABIwAAAQEAsuGLw70et3eApJ/ZEJkAVZogIrm" > > class FakeConfig(object): > """Fake configuration object""" > + def __init__(self): > + self.write_count = 0 > + > + def OutDate(self): > + pass > > def IsCluster(self): > return True > @@ -112,6 +118,14 @@ class FakeContext(object): > self.cfg = FakeConfig() > self.glm = FakeGLM() > > + def GetConfig(self, ec_id): > + return self.cfg > + > + def GetRpc(self, cfg): > + return None > + > + def GetWConfdContext(self, _ec_id): > + return (None, None, None) > > class FakeGetentResolver(object): > """Fake runtime.GetentResolver""" > @@ -139,3 +153,50 @@ class FakeGetentResolver(object): > > def LookupGid(self, gid): > return "group%s" % gid > + > +class FakeLU(object): > + HPATH = "fake-lu" > + HTYPE = None > + > + def __init__(self, processor, op, cfg, rpc_runner, prereq_err): > + self.proc = processor > + self.cfg = cfg > + self.op = op > + self.rpc = rpc_runner > + self.prereq_err = prereq_err > + > + self.needed_locks = {} > + self.opportunistic_locks = dict.fromkeys(locking.LEVELS, False) > + self.dont_collate_locks = dict.fromkeys(locking.LEVELS, False) > + self.add_locks = {} > + > + self.LogWarning = processor.LogWarning > + > + def CheckArguments(self): > + pass > + > + def ExpandNames(self): > + pass > + > + def DeclareLocks(self, level): > + pass > + > + def CheckPrereq(self): > + if self.prereq_err: > + raise self.prereq_err > + pass > + > + def Exec(self, feedback_fn): > + pass > + > + def BuildHooksNodes(self): > + return ([], []) > + > + def BuildHooksEnv(self): > + return {} > + > + def PreparePostHookNodes(self, post_hook_node_uuids): > + return [] > + > + def HooksCallBack(self, phase, hook_results, feedback_fn, lu_result): > + return lu_result > -- > 2.8.0.rc3.226.g39d4020 > >
