From 6263189c05b8692cf5944fea8af3b0e66305c56f Mon Sep 17 00:00:00 2001
From: Guido Trotter <ultrotter@google.com>
Date: Fri, 9 Oct 2009 11:52:38 +0100
Subject: [PATCH] Accept shutdown timeout from the user

Using the new --timeout option:

- gnt-instance shutdown is changed to accept a timeout
- the opcode is changed to hold one
- the LU is changed to optionally get one
- the rpc is changed to carry one
- the backend is changed to take it as a parameter rather than
  hardcoding it in the function

Signed-off-by: Guido Trotter <ultrotter@google.com>
Reviewed-by: Michael Hanselmann <hansmi@google.com>
---
 daemons/ganeti-noded |  3 ++-
 lib/backend.py       |  5 +++--
 lib/cmdlib.py        | 11 ++++++++++-
 lib/opcodes.py       |  2 +-
 lib/rpc.py           |  4 ++--
 scripts/gnt-instance |  5 +++--
 6 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/daemons/ganeti-noded b/daemons/ganeti-noded
index c75281776..7582597b3 100755
--- a/daemons/ganeti-noded
+++ b/daemons/ganeti-noded
@@ -449,7 +449,8 @@ class NodeHttpServer(http.server.HttpServer):
 
     """
     instance = objects.Instance.FromDict(params[0])
-    return backend.InstanceShutdown(instance)
+    timeout = params[1]
+    return backend.InstanceShutdown(instance, timeout)
 
   @staticmethod
   def perspective_instance_start(params):
diff --git a/lib/backend.py b/lib/backend.py
index f35e40ff1..ec87aae30 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -960,13 +960,15 @@ def StartInstance(instance):
     _Fail("Hypervisor error: %s", err, exc=True)
 
 
-def InstanceShutdown(instance):
+def InstanceShutdown(instance, timeout):
   """Shut an instance down.
 
   @note: this functions uses polling with a hardcoded timeout.
 
   @type instance: L{objects.Instance}
   @param instance: the instance object
+  @type timeout: integer
+  @param timeout: maximum timeout for soft shutdown
   @rtype: None
 
   """
@@ -974,7 +976,6 @@ def InstanceShutdown(instance):
   hyper = hypervisor.GetHypervisor(hv_name)
   running_instances = hyper.ListInstances()
   iname = instance.name
-  timeout = constants.DEFAULT_SHUTDOWN_TIMEOUT
 
   if iname not in running_instances:
     logging.info("Instance %s not running, doing nothing", iname)
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 0023cac61..a5e25316b 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -3679,6 +3679,13 @@ class LUShutdownInstance(LogicalUnit):
   _OP_REQP = ["instance_name"]
   REQ_BGL = False
 
+  def CheckArguments(self):
+    """Check the arguments.
+
+    """
+    self.timeout = getattr(self.op, "timeout",
+                           constants.DEFAULT_SHUTDOWN_TIMEOUT)
+
   def ExpandNames(self):
     self._ExpandAndLockInstance()
 
@@ -3689,6 +3696,7 @@ class LUShutdownInstance(LogicalUnit):
 
     """
     env = _BuildInstanceHookEnvByObject(self, self.instance)
+    env["TIMEOUT"] = self.timeout
     nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
     return env, nl, nl
 
@@ -3709,8 +3717,9 @@ class LUShutdownInstance(LogicalUnit):
     """
     instance = self.instance
     node_current = instance.primary_node
+    timeout = self.timeout
     self.cfg.MarkInstanceDown(instance.name)
-    result = self.rpc.call_instance_shutdown(node_current, instance)
+    result = self.rpc.call_instance_shutdown(node_current, instance, timeout)
     msg = result.fail_msg
     if msg:
       self.proc.LogWarning("Could not shutdown instance: %s" % msg)
diff --git a/lib/opcodes.py b/lib/opcodes.py
index 929c62fee..5ff1eb1ff 100644
--- a/lib/opcodes.py
+++ b/lib/opcodes.py
@@ -489,7 +489,7 @@ class OpShutdownInstance(OpCode):
   """Shutdown an instance."""
   OP_ID = "OP_INSTANCE_SHUTDOWN"
   OP_DSC_FIELD = "instance_name"
-  __slots__ = OpCode.__slots__ + ["instance_name"]
+  __slots__ = OpCode.__slots__ + ["instance_name", "timeout"]
 
 
 class OpRebootInstance(OpCode):
diff --git a/lib/rpc.py b/lib/rpc.py
index ea132a9b3..227e355cf 100644
--- a/lib/rpc.py
+++ b/lib/rpc.py
@@ -467,14 +467,14 @@ class RpcRunner(object):
     idict = self._InstDict(instance, hvp=hvp, bep=bep)
     return self._SingleNodeCall(node, "instance_start", [idict])
 
-  def call_instance_shutdown(self, node, instance):
+  def call_instance_shutdown(self, node, instance, timeout):
     """Stops an instance.
 
     This is a single-node call.
 
     """
     return self._SingleNodeCall(node, "instance_shutdown",
-                                [self._InstDict(instance)])
+                                [self._InstDict(instance), timeout])
 
   def call_migration_info(self, node, instance):
     """Gather the information necessary to prepare an instance migration.
diff --git a/scripts/gnt-instance b/scripts/gnt-instance
index 194e69771..8d5351384 100755
--- a/scripts/gnt-instance
+++ b/scripts/gnt-instance
@@ -727,7 +727,8 @@ def _ShutdownInstance(name, opts):
   @return: the opcode needed for the operation
 
   """
-  return opcodes.OpShutdownInstance(instance_name=name)
+  return opcodes.OpShutdownInstance(instance_name=name,
+                                    timeout=opts.timeout)
 
 
 def ReplaceDisks(opts, args):
@@ -1346,7 +1347,7 @@ commands = {
   'shutdown': (
     GenericManyOps("shutdown", _ShutdownInstance), [ArgInstance()],
     [m_node_opt, m_pri_node_opt, m_sec_node_opt, m_clust_opt,
-     m_inst_opt, m_force_multi, SUBMIT_OPT],
+     m_inst_opt, m_force_multi, TIMEOUT_OPT, SUBMIT_OPT],
     "<instance>", "Stops an instance"),
   'startup': (
     GenericManyOps("startup", _StartupInstance), [ArgInstance()],
-- 
GitLab