From 07923a3c0e85ad3061cabc258945067a72684c8b Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Fri, 13 Apr 2012 23:28:21 +0200
Subject: [PATCH] Copy debug level, priority and set comment for LU-generated
 opcodes

Before this patch, a node evacuation submitted with high priority would
only compute the solution at that priority, but the actual evacuation
ran at normal priority.

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Iustin Pop <iustin@google.com>
---
 lib/cmdlib.py                |  2 +-
 lib/mcpu.py                  | 72 +++++++++++++++++++++++++-----------
 test/ganeti.mcpu_unittest.py | 70 +++++++++++++++++++++++++++++++++++
 3 files changed, 122 insertions(+), 22 deletions(-)

diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 71076b945..f4c6f932d 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -82,7 +82,7 @@ class ResultWithJobs:
   """Data container for LU results with jobs.
 
   Instances of this class returned from L{LogicalUnit.Exec} will be recognized
-  by L{mcpu.Processor._ProcessResult}. The latter will then submit the jobs
+  by L{mcpu._ProcessResult}. The latter will then submit the jobs
   contained in the C{jobs} attribute and include the job IDs in the opcode
   result.
 
diff --git a/lib/mcpu.py b/lib/mcpu.py
index 8b3734e0e..2bf875f87 100644
--- a/lib/mcpu.py
+++ b/lib/mcpu.py
@@ -31,6 +31,7 @@ are two kinds of classes defined:
 import logging
 import random
 import time
+import itertools
 
 from ganeti import opcodes
 from ganeti import constants
@@ -171,6 +172,54 @@ def _ComputeDispatchTable():
               if op.WITH_LU)
 
 
+def _SetBaseOpParams(src, defcomment, dst):
+  """Copies basic opcode parameters.
+
+  @type src: L{opcodes.OpCode}
+  @param src: Source opcode
+  @type defcomment: string
+  @param defcomment: Comment to specify if not already given
+  @type dst: L{opcodes.OpCode}
+  @param dst: Destination opcode
+
+  """
+  if hasattr(src, "debug_level"):
+    dst.debug_level = src.debug_level
+
+  if (getattr(dst, "priority", None) is None and
+      hasattr(src, "priority")):
+    dst.priority = src.priority
+
+  if not getattr(dst, opcodes.COMMENT_ATTR, None):
+    dst.comment = defcomment
+
+
+def _ProcessResult(submit_fn, op, result):
+  """Examines opcode result.
+
+  If necessary, additional processing on the result is done.
+
+  """
+  if isinstance(result, cmdlib.ResultWithJobs):
+    # Copy basic parameters (e.g. priority)
+    map(compat.partial(_SetBaseOpParams, op,
+                       "Submitted by %s" % op.OP_ID),
+        itertools.chain(*result.jobs))
+
+    # Submit jobs
+    job_submission = submit_fn(result.jobs)
+
+    # Build dictionary
+    result = result.other
+
+    assert constants.JOB_IDS_KEY not in result, \
+      "Key '%s' found in additional return values" % constants.JOB_IDS_KEY
+
+    result[constants.JOB_IDS_KEY] = job_submission
+
+  return result
+
+
 def _RpcResultsToHooksResults(rpc_results):
   """Function to convert RPC results to the format expected by HooksMaster.
 
@@ -230,26 +279,6 @@ class Processor(object):
 
     return acquired
 
-  def _ProcessResult(self, result):
-    """Examines opcode result.
-
-    If necessary, additional processing on the result is done.
-
-    """
-    if isinstance(result, cmdlib.ResultWithJobs):
-      # Submit jobs
-      job_submission = self._cbs.SubmitManyJobs(result.jobs)
-
-      # Build dictionary
-      result = result.other
-
-      assert constants.JOB_IDS_KEY not in result, \
-        "Key '%s' found in additional return values" % constants.JOB_IDS_KEY
-
-      result[constants.JOB_IDS_KEY] = job_submission
-
-    return result
-
   def _ExecLU(self, lu):
     """Logical Unit execution sequence.
 
@@ -271,7 +300,8 @@ class Processor(object):
       return lu.dry_run_result
 
     try:
-      result = self._ProcessResult(lu.Exec(self.Log))
+      result = _ProcessResult(self._cbs.SubmitManyJobs, lu.op,
+                              lu.Exec(self.Log))
       h_results = hm.RunPhase(constants.HOOKS_PHASE_POST)
       result = lu.HooksCallBack(constants.HOOKS_PHASE_POST, h_results,
                                 self.Log, result)
diff --git a/test/ganeti.mcpu_unittest.py b/test/ganeti.mcpu_unittest.py
index 4285932aa..067814039 100755
--- a/test/ganeti.mcpu_unittest.py
+++ b/test/ganeti.mcpu_unittest.py
@@ -23,9 +23,12 @@
 
 
 import unittest
+import itertools
 
 from ganeti import mcpu
 from ganeti import opcodes
+from ganeti import cmdlib
+from ganeti import constants
 from ganeti.constants import \
     LOCK_ATTEMPTS_TIMEOUT, \
     LOCK_ATTEMPTS_MAXWAIT, \
@@ -96,5 +99,72 @@ class TestDispatchTable(unittest.TestCase):
                               opcls.OP_ID))
 
 
+class TestProcessResult(unittest.TestCase):
+  def setUp(self):
+    self._submitted = []
+    self._count = itertools.count(200)
+
+  def _Submit(self, jobs):
+    job_ids = [self._count.next() for _ in jobs]
+    self._submitted.extend(zip(job_ids, jobs))
+    return job_ids
+
+  def testNoJobs(self):
+    for i in [object(), [], False, True, None, 1, 929, {}]:
+      self.assertEqual(mcpu._ProcessResult(NotImplemented, NotImplemented, i),
+                       i)
+
+  def testDefaults(self):
+    src = opcodes.OpTestDummy()
+
+    res = mcpu._ProcessResult(self._Submit, src, cmdlib.ResultWithJobs([[
+      opcodes.OpTestDelay(),
+      opcodes.OpTestDelay(),
+      ], [
+      opcodes.OpTestDelay(),
+      ]]))
+
+    self.assertEqual(res, {
+      constants.JOB_IDS_KEY: [200, 201],
+      })
+
+    (_, (op1, op2)) = self._submitted.pop(0)
+    (_, (op3, )) = self._submitted.pop(0)
+    self.assertRaises(IndexError, self._submitted.pop)
+
+    for op in [op1, op2, op3]:
+      self.assertTrue("OP_TEST_DUMMY" in op.comment)
+      self.assertFalse(hasattr(op, "priority"))
+      self.assertFalse(hasattr(op, "debug_level"))
+
+  def testParams(self):
+    src = opcodes.OpTestDummy(priority=constants.OP_PRIO_HIGH,
+                              debug_level=3)
+
+    res = mcpu._ProcessResult(self._Submit, src, cmdlib.ResultWithJobs([[
+      opcodes.OpTestDelay(priority=constants.OP_PRIO_LOW),
+      ], [
+      opcodes.OpTestDelay(comment="foobar", debug_level=10),
+      ]], other=True, value=range(10)))
+
+    self.assertEqual(res, {
+      constants.JOB_IDS_KEY: [200, 201],
+      "other": True,
+      "value": range(10),
+      })
+
+    (_, (op1, )) = self._submitted.pop(0)
+    (_, (op2, )) = self._submitted.pop(0)
+    self.assertRaises(IndexError, self._submitted.pop)
+
+    self.assertEqual(op1.priority, constants.OP_PRIO_LOW)
+    self.assertTrue("OP_TEST_DUMMY" in op1.comment)
+    self.assertEqual(op1.debug_level, 3)
+
+    self.assertEqual(op2.priority, constants.OP_PRIO_HIGH)
+    self.assertEqual(op2.comment, "foobar")
+    self.assertEqual(op2.debug_level, 3)
+
+
 if __name__ == "__main__":
   testutils.GanetiTestProgram()
-- 
GitLab