commit 55cec0701476b77c2f5c8b3b5cc3c68c54fafe0d
Author: Michele Tartara <mtartara@google.com>
Date:   Fri Mar 22 19:31:56 2013 +0100

    Reason trail implementation for instance reboot
    
    This commits allows ganeti to correctly forward the reason trail information
    regarding instance reboot.
    
    Signed-off-by: Michele Tartara <mtartara@google.com>
    Reviewed-by: Helga Velroyen <helgav@google.com>

diff --git a/lib/backend.py b/lib/backend.py
index abe6b39..7d8315d 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -1424,7 +1424,7 @@ def InstanceShutdown(instance, timeout):
   _RemoveBlockDevLinks(iname, instance.disks)
 
 
-def InstanceReboot(instance, reboot_type, shutdown_timeout):
+def InstanceReboot(instance, reboot_type, shutdown_timeout, reason):
   """Reboot an instance.
 
   @type instance: L{objects.Instance}
@@ -1442,6 +1442,8 @@ def InstanceReboot(instance, reboot_type, shutdown_timeout):
         instance (instead of a call_instance_reboot RPC)
   @type shutdown_timeout: integer
   @param shutdown_timeout: maximum timeout for soft shutdown
+  @type reason: list of reasons
+  @param reason: the reason trail for this reboot
   @rtype: None
 
   """
@@ -1460,6 +1462,7 @@ def InstanceReboot(instance, reboot_type, shutdown_timeout):
     try:
       InstanceShutdown(instance, shutdown_timeout)
       result = StartInstance(instance, False)
+      _StoreInstReasonTrail(instance.name, reason)
       return result
     except errors.HypervisorError, err:
       _Fail("Failed to hard reboot instance %s: %s", instance.name, err)
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 2c6d216..d6f16d0 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -7440,6 +7440,7 @@ class LUInstanceReboot(LogicalUnit):
     instance = self.instance
     ignore_secondaries = self.op.ignore_secondaries
     reboot_type = self.op.reboot_type
+    reason = self.op.reason
 
     remote_info = self.rpc.call_instance_info(instance.primary_node,
                                               instance.name,
@@ -7455,7 +7456,7 @@ class LUInstanceReboot(LogicalUnit):
         self.cfg.SetDiskID(disk, node_current)
       result = self.rpc.call_instance_reboot(node_current, instance,
                                              reboot_type,
-                                             self.op.shutdown_timeout)
+                                             self.op.shutdown_timeout, reason)
       result.Raise("Could not reboot instance")
     else:
       if instance_running:
diff --git a/lib/rapi/client.py b/lib/rapi/client.py
index 5fc576f..ace4d1e 100644
--- a/lib/rapi/client.py
+++ b/lib/rapi/client.py
@@ -1001,7 +1001,7 @@ class GanetiRapiClient(object): # pylint: disable=R0904
                               (GANETI_RAPI_VERSION, instance)), query, None)
 
   def RebootInstance(self, instance, reboot_type=None, ignore_secondaries=None,
-                     dry_run=False):
+                     dry_run=False, reason=None):
     """Reboots an instance.
 
     @type instance: str
@@ -1013,6 +1013,8 @@ class GanetiRapiClient(object): # pylint: disable=R0904
         while re-assembling disks (in hard-reboot mode only)
     @type dry_run: bool
     @param dry_run: whether to perform a dry run
+    @type reason: string
+    @param reason: the reason for the reboot
     @rtype: string
     @return: job id
 
@@ -1022,6 +1024,7 @@ class GanetiRapiClient(object): # pylint: disable=R0904
     _AppendIf(query, reboot_type, ("type", reboot_type))
     _AppendIf(query, ignore_secondaries is not None,
               ("ignore_secondaries", ignore_secondaries))
+    _AppendIf(query, reason, ("reason", reason))
 
     return self._SendRequest(HTTP_POST,
                              ("/%s/instances/%s/reboot" %
diff --git a/lib/rpc_defs.py b/lib/rpc_defs.py
index f5f1c34..bf3cbe8 100644
--- a/lib/rpc_defs.py
+++ b/lib/rpc_defs.py
@@ -230,6 +230,7 @@ _INSTANCE_CALLS = [
     ("inst", ED_INST_DICT, "Instance object"),
     ("reboot_type", None, None),
     ("shutdown_timeout", None, None),
+    ("reason", None, "The reason for the reboot"),
     ], None, None, "Returns the list of running instances on the given nodes"),
   ("instance_shutdown", SINGLE, None, constants.RPC_TMO_NORMAL, [
     ("instance", ED_INST_DICT, "Instance object"),
diff --git a/lib/server/noded.py b/lib/server/noded.py
index 2e1ceaa..c81352b 100644
--- a/lib/server/noded.py
+++ b/lib/server/noded.py
@@ -660,7 +660,10 @@ class NodeRequestHandler(http.server.HttpServerHandler):
     instance = objects.Instance.FromDict(params[0])
     reboot_type = params[1]
     shutdown_timeout = params[2]
-    return backend.InstanceReboot(instance, reboot_type, shutdown_timeout)
+    trail = params[3]
+    _extendReasonTrail(trail, "reboot")
+    return backend.InstanceReboot(instance, reboot_type, shutdown_timeout,
+                                  trail)
 
   @staticmethod
   def perspective_instance_balloon_memory(params):
diff --git a/test/py/ganeti.rapi.client_unittest.py b/test/py/ganeti.rapi.client_unittest.py
index ab518e5..3174c69 100755
--- a/test/py/ganeti.rapi.client_unittest.py
+++ b/test/py/ganeti.rapi.client_unittest.py
@@ -594,13 +594,15 @@ class GanetiRapiClientTests(testutils.GanetiTestCase):
   def testRebootInstance(self):
     self.rapi.AddResponse("6146")
     job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
-                                        ignore_secondaries=True, dry_run=True)
+                                        ignore_secondaries=True, dry_run=True,
+                                        reason="Updates")
     self.assertEqual(6146, job_id)
     self.assertHandler(rlib2.R_2_instances_name_reboot)
     self.assertItems(["i-bar"])
     self.assertDryRun()
     self.assertQuery("type", ["hard"])
     self.assertQuery("ignore_secondaries", ["1"])
+    self.assertQuery("reason", ["Updates"])
 
   def testRebootInstanceDefaultReason(self):
     self.rapi.AddResponse("6146")
@@ -612,6 +614,7 @@ class GanetiRapiClientTests(testutils.GanetiTestCase):
     self.assertDryRun()
     self.assertQuery("type", ["hard"])
     self.assertQuery("ignore_secondaries", ["1"])
+    self.assertQuery("reason", None)
 
   def testShutdownInstance(self):
     self.rapi.AddResponse("1487")
diff --git a/test/py/ganeti.rapi.rlib2_unittest.py b/test/py/ganeti.rapi.rlib2_unittest.py
index d4d3602..e19c925 100755
--- a/test/py/ganeti.rapi.rlib2_unittest.py
+++ b/test/py/ganeti.rapi.rlib2_unittest.py
@@ -370,6 +370,7 @@ class TestInstanceReboot(unittest.TestCase):
     handler = _CreateHandler(rlib2.R_2_instances_name_reboot, ["inst847"], {
       "dry-run": ["1"],
       "ignore_secondaries": ["1"],
+      "reason": ["System update"],
       }, {}, clfactory)
     job_id = handler.POST()
 
@@ -383,6 +384,12 @@ class TestInstanceReboot(unittest.TestCase):
     self.assertEqual(op.reboot_type, constants.INSTANCE_REBOOT_HARD)
     self.assertTrue(op.ignore_secondaries)
     self.assertTrue(op.dry_run)
+    self.assertEqual(op.reason[0][0], constants.OPCODE_REASON_SRC_USER)
+    self.assertEqual(op.reason[0][1], "System update")
+    self.assertEqual(op.reason[1][0],
+                     "%s:%s" % (constants.OPCODE_REASON_SRC_RLIB2,
+                                "instances_name_reboot"))
+    self.assertEqual(op.reason[1][1], "")
 
     self.assertRaises(IndexError, cl.GetNextSubmittedJob)
 
