Add gnt-instance start --pause
authorStephen Shirley <diamond@google.com>
Thu, 23 Jun 2011 15:15:28 +0000 (17:15 +0200)
committerStephen Shirley <diamond@google.com>
Fri, 8 Jul 2011 12:15:43 +0000 (14:15 +0200)
Creates the instance, but pauses execution before booting. This combined
with 'gnt-instance console' unpausing instances means that the entire
boot process can be viewed and monitored.

Signed-off-by: Stephen Shirley <diamond@google.com>
Reviewed-by: Michael Hanselmann <hansmi@google.com>

14 files changed:
lib/backend.py
lib/cli.py
lib/client/gnt_instance.py
lib/cmdlib.py
lib/hypervisor/hv_base.py
lib/hypervisor/hv_chroot.py
lib/hypervisor/hv_fake.py
lib/hypervisor/hv_kvm.py
lib/hypervisor/hv_lxc.py
lib/hypervisor/hv_xen.py
lib/opcodes.py
lib/rpc.py
lib/server/noded.py
man/gnt-instance.rst

index d2cc885..f4616fd 100644 (file)
@@ -1086,11 +1086,13 @@ def _GatherAndLinkBlockDevs(instance):
   return block_devices
 
 
-def StartInstance(instance):
+def StartInstance(instance, startup_paused):
   """Start an instance.
 
   @type instance: L{objects.Instance}
   @param instance: the instance object
+  @type startup_paused: bool
+  @param instance: pause instance at startup?
   @rtype: None
 
   """
@@ -1103,7 +1105,7 @@ def StartInstance(instance):
   try:
     block_devices = _GatherAndLinkBlockDevs(instance)
     hyper = hypervisor.GetHypervisor(instance.hypervisor)
-    hyper.StartInstance(instance, block_devices)
+    hyper.StartInstance(instance, block_devices, startup_paused)
   except errors.BlockDeviceError, err:
     _Fail("Block device error: %s", err, exc=True)
   except errors.HypervisorError, err:
index d539add..13050dd 100644 (file)
@@ -162,6 +162,7 @@ __all__ = [
   "SRC_DIR_OPT",
   "SRC_NODE_OPT",
   "SUBMIT_OPT",
+  "STARTUP_PAUSED_OPT",
   "STATIC_OPT",
   "SYNC_OPT",
   "TAG_ADD_OPT",
@@ -1223,6 +1224,10 @@ SECONDARY_ONLY_OPT = cli_option("-s", "--secondary-only",
                                      " disk templates, e.g. %s)" %
                                      utils.CommaJoin(constants.DTS_INT_MIRROR))
 
+STARTUP_PAUSED_OPT = cli_option("--paused", dest="startup_paused",
+                                action="store_true", default=False,
+                                help="Pause instance at startup")
+
 
 #: Options provided by all commands
 COMMON_OPTS = [DEBUG_OPT]
index 1d6faa4..158662c 100644 (file)
@@ -662,7 +662,8 @@ def _StartupInstance(name, opts):
   op = opcodes.OpInstanceStartup(instance_name=name,
                                  force=opts.force,
                                  ignore_offline_nodes=opts.ignore_offline,
-                                 no_remember=opts.no_remember)
+                                 no_remember=opts.no_remember,
+                                 startup_paused=opts.startup_paused)
   # do not add these parameters to the opcode unless they're defined
   if opts.hvparams:
     op.hvparams = opts.hvparams
@@ -1453,7 +1454,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],
+     NO_REMEMBER_OPT, STARTUP_PAUSED_OPT],
     "<instance>", "Starts an instance"),
   'reboot': (
     GenericManyOps("reboot", _RebootInstance), [ArgInstance()],
index 607c820..8b9fa17 100644 (file)
@@ -5705,7 +5705,8 @@ class LUInstanceStartup(LogicalUnit):
       _StartInstanceDisks(self, instance, force)
 
       result = self.rpc.call_instance_start(node_current, instance,
-                                            self.op.hvparams, self.op.beparams)
+                                            self.op.hvparams, self.op.beparams,
+                                            self.op.startup_paused)
       msg = result.fail_msg
       if msg:
         _ShutdownInstanceDisks(self, instance)
@@ -5795,7 +5796,8 @@ class LUInstanceReboot(LogicalUnit):
         self.LogInfo("Instance %s was already stopped, starting now",
                      instance.name)
       _StartInstanceDisks(self, instance, ignore_secondaries)
-      result = self.rpc.call_instance_start(node_current, instance, None, None)
+      result = self.rpc.call_instance_start(node_current, instance,
+                                            None, None, False)
       msg = result.fail_msg
       if msg:
         _ShutdownInstanceDisks(self, instance)
@@ -6646,7 +6648,8 @@ class LUInstanceMove(LogicalUnit):
         _ShutdownInstanceDisks(self, instance)
         raise errors.OpExecError("Can't activate the instance's disks")
 
-      result = self.rpc.call_instance_start(target_node, instance, None, None)
+      result = self.rpc.call_instance_start(target_node, instance,
+                                            None, None, False)
       msg = result.fail_msg
       if msg:
         _ShutdownInstanceDisks(self, instance)
@@ -8771,7 +8774,8 @@ class LUInstanceCreate(LogicalUnit):
       self.cfg.Update(iobj, feedback_fn)
       logging.info("Starting instance %s on node %s", instance, pnode_name)
       feedback_fn("* starting instance...")
-      result = self.rpc.call_instance_start(pnode_name, iobj, None, None)
+      result = self.rpc.call_instance_start(pnode_name, iobj,
+                                            None, None, False)
       result.Raise("Could not start instance")
 
     return list(iobj.all_nodes)
@@ -11190,7 +11194,8 @@ class LUBackupExport(LogicalUnit):
             not self.op.remove_instance):
           assert not activate_disks
           feedback_fn("Starting instance %s" % instance.name)
-          result = self.rpc.call_instance_start(src_node, instance, None, None)
+          result = self.rpc.call_instance_start(src_node, instance,
+                                                None, None, False)
           msg = result.fail_msg
           if msg:
             feedback_fn("Failed to start instance: %s" % msg)
index ec47400..17b74e5 100644 (file)
@@ -134,7 +134,7 @@ class BaseHypervisor(object):
   def __init__(self):
     pass
 
-  def StartInstance(self, instance, block_devices):
+  def StartInstance(self, instance, block_devices, startup_paused):
     """Start an instance."""
     raise NotImplementedError
 
index 5b26e8b..8505cad 100644 (file)
@@ -135,7 +135,7 @@ class ChrootManager(hv_base.BaseHypervisor):
         data.append((file_name, 0, 0, 0, 0, 0))
     return data
 
-  def StartInstance(self, instance, block_devices):
+  def StartInstance(self, instance, block_devices, startup_paused):
     """Start an instance.
 
     For the chroot manager, we try to mount the block device and
index 9e5f96e..ff6b10a 100644 (file)
@@ -147,7 +147,7 @@ class FakeHypervisor(hv_base.BaseHypervisor):
     file_name = self._InstanceFile(instance_name)
     utils.RemoveFile(file_name)
 
-  def StartInstance(self, instance, block_devices):
+  def StartInstance(self, instance, block_devices, startup_paused):
     """Start an instance.
 
     For the fake hypervisor, it just creates a file in the base dir,
index 3253911..5a876a8 100644 (file)
@@ -502,7 +502,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
         data.append(info)
     return data
 
-  def _GenerateKVMRuntime(self, instance, block_devices):
+  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
     """Generate KVM information to start an instance.
 
     """
@@ -523,6 +523,8 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     kvm_cmd.extend(['-daemonize'])
     if not instance.hvparams[constants.HV_ACPI]:
       kvm_cmd.extend(['-no-acpi'])
+    if startup_paused:
+      kvm_cmd.extend(['-S'])
 
     hvp = instance.hvparams
     boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
@@ -901,12 +903,12 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     for filename in temp_files:
       utils.RemoveFile(filename)
 
-  def StartInstance(self, instance, block_devices):
+  def StartInstance(self, instance, block_devices, startup_paused):
     """Start an instance.
 
     """
     self._CheckDown(instance.name)
-    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
+    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices, startup_paused)
     self._SaveKVMRuntime(instance, kvm_runtime)
     self._ExecuteKVMRuntime(instance, kvm_runtime)
 
index 78c1f34..9d56bd2 100644 (file)
@@ -266,7 +266,7 @@ class LXCHypervisor(hv_base.BaseHypervisor):
 
     return "\n".join(out) + "\n"
 
-  def StartInstance(self, instance, block_devices):
+  def StartInstance(self, instance, block_devices, startup_paused):
     """Start an instance.
 
     For LCX, we try to mount the block device and execute 'lxc-start'.
index 33127ef..ff067c0 100644 (file)
@@ -180,12 +180,16 @@ class XenHypervisor(hv_base.BaseHypervisor):
     xm_list = self._GetXMList(False)
     return xm_list
 
-  def StartInstance(self, instance, block_devices):
+  def StartInstance(self, instance, block_devices, startup_paused):
     """Start an instance.
 
     """
     self._WriteConfigFile(instance, block_devices)
-    result = utils.RunCmd(["xm", "create", instance.name])
+    cmd = ["xm", "create"]
+    if startup_paused:
+      cmd.extend(["--paused"])
+    cmd.extend([instance.name])
+    result = utils.RunCmd(cmd)
 
     if result.failed:
       raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
index d0f6208..75ff7a0 100644 (file)
@@ -125,6 +125,10 @@ _PNoRemember = ("no_remember", False, ht.TBool,
 _PMigrationTargetNode = ("target_node", None, ht.TMaybeString,
                          "Target node for shared-storage instances")
 
+_PStartupPaused = ("startup_paused", False, ht.TBool,
+                   "Pause instance at startup")
+
+
 #: OP_ID conversion regular expression
 _OPID_RE = re.compile("([a-z])([A-Z])")
 
@@ -1018,6 +1022,7 @@ class OpInstanceStartup(OpCode):
      "Temporary hypervisor parameters, hypervisor-dependent"),
     ("beparams", ht.EmptyDict, ht.TDict, "Temporary backend parameters"),
     _PNoRemember,
+    _PStartupPaused,
     ]
 
 
index e482a20..6205dcf 100644 (file)
@@ -661,14 +661,14 @@ class RpcRunner(object):
     return self._SingleNodeCall(node, "bridges_exist", [bridges_list])
 
   @_RpcTimeout(_TMO_NORMAL)
-  def call_instance_start(self, node, instance, hvp, bep):
+  def call_instance_start(self, node, instance, hvp, bep, startup_paused):
     """Starts an instance.
 
     This is a single-node call.
 
     """
     idict = self._InstDict(instance, hvp=hvp, bep=bep)
-    return self._SingleNodeCall(node, "instance_start", [idict])
+    return self._SingleNodeCall(node, "instance_start", [idict, startup_paused])
 
   @_RpcTimeout(_TMO_NORMAL)
   def call_instance_shutdown(self, node, instance, timeout):
index 6bdf4fa..683f701 100644 (file)
@@ -557,8 +557,9 @@ class NodeHttpServer(http.server.HttpServer):
     """Start an instance.
 
     """
-    instance = objects.Instance.FromDict(params[0])
-    return backend.StartInstance(instance)
+    (instance_name, startup_paused) = params
+    instance = objects.Instance.FromDict(instance_name)
+    return backend.StartInstance(instance, startup_paused)
 
   @staticmethod
   def perspective_migration_info(params):
index 98b4196..02edb1f 100644 (file)
@@ -848,7 +848,7 @@ STARTUP
 | --tags \| --node-tags \| --pri-node-tags \| --sec-node-tags]
 | [{-H|--hypervisor-parameters} ``key=value...``]
 | [{-B|--backend-parameters} ``key=value...``]
-| [--submit]
+| [--submit] [--paused]
 | {*name*...}
 
 Starts one or more instances, depending on the following options.  The
@@ -889,7 +889,6 @@ four available modes are:
     will start all instances in the cluster on secondary nodes with the
     tags given as arguments
 
-
 Note that although you can pass more than one selection option, the
 last one wins, so in order to guarantee the desired result, don't pass
 more than one such option.
@@ -927,6 +926,11 @@ to send the job to the master daemon but not wait for its
 completion. The job ID will be shown so that it can be examined via
 **gnt-job info**.
 
+The ``--paused`` option is only valid for Xen and kvm hypervisors.  This
+pauses the instance at the start of bootup, awaiting ``gnt-instance
+console`` to unpause it, allowing the entire boot process to be
+monitored for debugging.
+
 Example::
 
     # gnt-instance start instance1.example.com
@@ -1039,6 +1043,10 @@ of the instance. To connect to the virtualized "physical" console of a
 HVM instance, use a VNC client with the connection info from the
 **info** command.
 
+For Xen/kvm instances, if the instance is paused, this attempts to
+unpause the instance after waiting a few seconds for the connection to
+the console to be made.
+
 Example::
 
     # gnt-instance console instance1.example.com