Add support to the Startup command for specifying the reason for the last status
change.

Signed-off-by: Michele Tartara <[email protected]>
---
 lib/backend.py                         |    5 +++--
 lib/client/gnt_instance.py             |    6 ++++--
 lib/cmdlib.py                          |    3 ++-
 lib/constants.py                       |    1 +
 lib/opcodes.py                         |    1 +
 lib/rapi/client.py                     |    6 +++++-
 lib/rapi/rlib2.py                      |    4 ++++
 lib/rpc_defs.py                        |    1 +
 lib/server/noded.py                    |    7 +++++--
 src/Ganeti/OpCodes.hs                  |    1 +
 test/hs/Test/Ganeti/OpCodes.hs         |    2 +-
 test/py/ganeti.rapi.client_unittest.py |   12 ++++++++++++
 test/py/ganeti.rapi.rlib2_unittest.py  |    3 +++
 13 files changed, 43 insertions(+), 9 deletions(-)

diff --git a/lib/backend.py b/lib/backend.py
index 17d746b..359d6ed 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -1355,7 +1355,7 @@ def _GatherAndLinkBlockDevs(instance):
   return block_devices
 
 
-def StartInstance(instance, startup_paused):
+def StartInstance(instance, startup_paused, reason):
   """Start an instance.
 
   @type instance: L{objects.Instance}
@@ -1375,6 +1375,7 @@ def StartInstance(instance, startup_paused):
     block_devices = _GatherAndLinkBlockDevs(instance)
     hyper = hypervisor.GetHypervisor(instance.hypervisor)
     hyper.StartInstance(instance, block_devices, startup_paused)
+    reason.Store(instance.name)
   except errors.BlockDeviceError, err:
     _Fail("Block device error: %s", err, exc=True)
   except errors.HypervisorError, err:
@@ -1487,7 +1488,7 @@ def InstanceReboot(instance, reboot_type, 
shutdown_timeout, reason):
   elif reboot_type == constants.INSTANCE_REBOOT_HARD:
     try:
       InstanceShutdown(instance, shutdown_timeout)
-      result = StartInstance(instance, False)
+      result = StartInstance(instance, False, reason)
       reason.Store(instance.name)
       return result
     except errors.HypervisorError, err:
diff --git a/lib/client/gnt_instance.py b/lib/client/gnt_instance.py
index 4f1538d..e44fb19 100644
--- a/lib/client/gnt_instance.py
+++ b/lib/client/gnt_instance.py
@@ -608,7 +608,9 @@ def _StartupInstance(name, opts):
                                  force=opts.force,
                                  ignore_offline_nodes=opts.ignore_offline,
                                  no_remember=opts.no_remember,
-                                 startup_paused=opts.startup_paused)
+                                 startup_paused=opts.startup_paused,
+                                 reason=(constants.INSTANCE_REASON_SOURCE_CLI,
+                                         opts.reason))
   # do not add these parameters to the opcode unless they're defined
   if opts.hvparams:
     op.hvparams = opts.hvparams
@@ -1548,7 +1550,7 @@ commands = {
      m_node_tags_opt, m_pri_node_tags_opt, m_sec_node_tags_opt,
      m_inst_tags_opt, m_clust_opt, m_inst_opt, SUBMIT_OPT, HVOPTS_OPT,
      BACKEND_OPT, DRY_RUN_OPT, PRIORITY_OPT, IGNORE_OFFLINE_OPT,
-     NO_REMEMBER_OPT, STARTUP_PAUSED_OPT],
+     NO_REMEMBER_OPT, STARTUP_PAUSED_OPT, REASON_OPT],
     "<instance>", "Starts an instance"),
   "reboot": (
     GenericManyOps("reboot", _RebootInstance), [ArgInstance()],
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index b8c2318..0d871fc 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -7295,6 +7295,7 @@ class LUInstanceStartup(LogicalUnit):
     """
     instance = self.instance
     force = self.op.force
+    reason = self.op.reason
 
     if not self.op.no_remember:
       self.cfg.MarkInstanceUp(instance.name)
@@ -7311,7 +7312,7 @@ class LUInstanceStartup(LogicalUnit):
         self.rpc.call_instance_start(node_current,
                                      (instance, self.op.hvparams,
                                       self.op.beparams),
-                                     self.op.startup_paused)
+                                     self.op.startup_paused, reason)
       msg = result.fail_msg
       if msg:
         _ShutdownInstanceDisks(self, instance)
diff --git a/lib/constants.py b/lib/constants.py
index 31bd688..0fbaac8 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -2349,6 +2349,7 @@ INSTANCE_REASON_SOURCES = compat.UniqueFrozenset([
 
 # The default reasons for the change of state of an instance
 INSTANCE_REASON_REBOOT = "reboot"
+INSTANCE_REASON_STARTUP = "startup"
 
 # Do not re-export imported modules
 del re, _vcsversion, _autoconf, socket, pathutils, compat
diff --git a/lib/opcodes.py b/lib/opcodes.py
index faffbac..ba789da 100644
--- a/lib/opcodes.py
+++ b/lib/opcodes.py
@@ -1433,6 +1433,7 @@ class OpInstanceStartup(OpCode):
     ("beparams", ht.EmptyDict, ht.TDict, "Temporary backend parameters"),
     _PNoRemember,
     _PStartupPaused,
+    _PReason,
     ]
   OP_RESULT = ht.TNone
 
diff --git a/lib/rapi/client.py b/lib/rapi/client.py
index d02faff..884b540 100644
--- a/lib/rapi/client.py
+++ b/lib/rapi/client.py
@@ -1054,7 +1054,8 @@ class GanetiRapiClient(object): # pylint: disable=R0904
                              ("/%s/instances/%s/shutdown" %
                               (GANETI_RAPI_VERSION, instance)), query, body)
 
-  def StartupInstance(self, instance, dry_run=False, no_remember=False):
+  def StartupInstance(self, instance, dry_run=False, no_remember=False,
+                      reason_text=None):
     """Starts up an instance.
 
     @type instance: str
@@ -1063,6 +1064,8 @@ class GanetiRapiClient(object): # pylint: disable=R0904
     @param dry_run: whether to perform a dry run
     @type no_remember: bool
     @param no_remember: if true, will not record the state change
+    @type reason_text: string
+    @param reason_text: the reason for the startup
     @rtype: string
     @return: job id
 
@@ -1070,6 +1073,7 @@ class GanetiRapiClient(object): # pylint: disable=R0904
     query = []
     _AppendDryRunIf(query, dry_run)
     _AppendIf(query, no_remember, ("no-remember", 1))
+    _AppendIf(query, reason_text, ("reason_text", reason_text))
 
     return self._SendRequest(HTTP_PUT,
                              ("/%s/instances/%s/startup" %
diff --git a/lib/rapi/rlib2.py b/lib/rapi/rlib2.py
index a475151..1fabbbb 100644
--- a/lib/rapi/rlib2.py
+++ b/lib/rapi/rlib2.py
@@ -1067,6 +1067,10 @@ class 
R_2_instances_name_startup(baserlib.OpcodeResource):
       "force": self.useForce(),
       "dry_run": self.dryRun(),
       "no_remember": bool(self._checkIntVariable("no_remember")),
+      "reason":
+       (constants.INSTANCE_REASON_SOURCE_RAPI,
+        self._checkStringVariable("reason_text",
+                                  default=constants.INSTANCE_REASON_REBOOT)),
       })
 
 
diff --git a/lib/rpc_defs.py b/lib/rpc_defs.py
index a9ed9da..67fe3f3 100644
--- a/lib/rpc_defs.py
+++ b/lib/rpc_defs.py
@@ -278,6 +278,7 @@ _INSTANCE_CALLS = [
   ("instance_start", SINGLE, None, constants.RPC_TMO_NORMAL, [
     ("instance_hvp_bep", ED_INST_DICT_HVP_BEP_DP, None),
     ("startup_paused", None, None),
+    ("reason_text", None, "Reason for the startup"),
     ], None, None, "Starts an instance"),
   ("instance_os_add", SINGLE, None, constants.RPC_TMO_1DAY, [
     ("instance_osp", ED_INST_DICT_OSP_DP, None),
diff --git a/lib/server/noded.py b/lib/server/noded.py
index 96ad1ac..946396b 100644
--- a/lib/server/noded.py
+++ b/lib/server/noded.py
@@ -586,9 +586,12 @@ class NodeRequestHandler(http.server.HttpServerHandler):
     """Start an instance.
 
     """
-    (instance_name, startup_paused) = params
+    (instance_name, startup_paused, (reason_source, reason_text)) = params
+    reason_text = _DefaultAlternative(reason_text,
+                                      constants.INSTANCE_REASON_STARTUP)
+    reason = backend.InstReason(reason_source, reason_text)
     instance = objects.Instance.FromDict(instance_name)
-    return backend.StartInstance(instance, startup_paused)
+    return backend.StartInstance(instance, startup_paused, reason)
 
   @staticmethod
   def perspective_migration_info(params):
diff --git a/src/Ganeti/OpCodes.hs b/src/Ganeti/OpCodes.hs
index f720577..f09e95d 100644
--- a/src/Ganeti/OpCodes.hs
+++ b/src/Ganeti/OpCodes.hs
@@ -331,6 +331,7 @@ $(genOpCode "OpCode"
      , pTempBeParams
      , pNoRemember
      , pStartupPaused
+     , pReason
      ])
   , ("OpInstanceShutdown",
      [ pInstanceName
diff --git a/test/hs/Test/Ganeti/OpCodes.hs b/test/hs/Test/Ganeti/OpCodes.hs
index 4c06ee2..c3c0aa8 100644
--- a/test/hs/Test/Ganeti/OpCodes.hs
+++ b/test/hs/Test/Ganeti/OpCodes.hs
@@ -235,7 +235,7 @@ instance Arbitrary OpCodes.OpCode where
       "OP_INSTANCE_STARTUP" ->
         OpCodes.OpInstanceStartup <$> genFQDN <*> arbitrary <*> arbitrary <*>
           pure emptyJSObject <*> pure emptyJSObject <*>
-          arbitrary <*> arbitrary
+          arbitrary <*> arbitrary <*> ((,) <$> arbitrary <*> genStringNE)
       "OP_INSTANCE_SHUTDOWN" ->
         OpCodes.OpInstanceShutdown <$> genFQDN <*> arbitrary <*> arbitrary <*>
           arbitrary <*> arbitrary
diff --git a/test/py/ganeti.rapi.client_unittest.py 
b/test/py/ganeti.rapi.client_unittest.py
index e78a1d9..11e0969 100755
--- a/test/py/ganeti.rapi.client_unittest.py
+++ b/test/py/ganeti.rapi.client_unittest.py
@@ -626,11 +626,23 @@ class GanetiRapiClientTests(testutils.GanetiTestCase):
 
   def testStartupInstance(self):
     self.rapi.AddResponse("27149")
+    self.assertEqual(27149,
+                     self.client.StartupInstance("bar-instance",
+                                                 dry_run=True,
+                                                 reason_text="NewInstance"))
+    self.assertHandler(rlib2.R_2_instances_name_startup)
+    self.assertItems(["bar-instance"])
+    self.assertDryRun()
+    self.assertQuery("reason_text", ["NewInstance"])
+
+  def testStartupInstanceDefaultReason(self):
+    self.rapi.AddResponse("27149")
     self.assertEqual(27149, self.client.StartupInstance("bar-instance",
                                                         dry_run=True))
     self.assertHandler(rlib2.R_2_instances_name_startup)
     self.assertItems(["bar-instance"])
     self.assertDryRun()
+    self.assertQuery("reason_text", None)
 
   def testReinstallInstance(self):
     self.rapi.AddResponse(serializer.DumpJson([]))
diff --git a/test/py/ganeti.rapi.rlib2_unittest.py 
b/test/py/ganeti.rapi.rlib2_unittest.py
index e19e800..531544e 100755
--- a/test/py/ganeti.rapi.rlib2_unittest.py
+++ b/test/py/ganeti.rapi.rlib2_unittest.py
@@ -396,6 +396,7 @@ class TestInstanceStartup(unittest.TestCase):
     handler = _CreateHandler(rlib2.R_2_instances_name_startup, ["inst31083"], {
       "force": ["1"],
       "no_remember": ["1"],
+      "reason_text": ["New Instance"],
       }, {}, clfactory)
     job_id = handler.PUT()
 
@@ -409,6 +410,8 @@ class TestInstanceStartup(unittest.TestCase):
     self.assertTrue(op.no_remember)
     self.assertTrue(op.force)
     self.assertFalse(op.dry_run)
+    self.assertEqual(op.reason,
+                     (constants.INSTANCE_REASON_SOURCE_RAPI, "New Instance"))
 
     self.assertRaises(IndexError, cl.GetNextSubmittedJob)
 
-- 
1.7.10.4

Reply via email to