Introduce --hotplug-if-possible option
authorDimitris Aragiorgis <dimara@grnet.gr>
Tue, 19 Nov 2013 13:34:21 +0000 (15:34 +0200)
committerDimitris Aragiorgis <dimara@grnet.gr>
Thu, 21 Nov 2013 14:34:38 +0000 (16:34 +0200)
This will be useful for an external entity using RAPI that
wants to modify devices of instances.

The common use case for that is:
"I want to add a NIC/disk to an instance. If it is running
then try to hotplug the device. If not, then just add it to config."

Another use case is that if we are unaware of the instance's
hypervisor: "I want do add a NIC/disk to an instance and if
its hypervisor is KVM try to hotplug it."

For all those cases just print a warning in CheckPrereq() and
disable any further hotplug related actions (and let
modifications take place after reboot).

Signed-off-by: Dimitris Aragiorgis <dimara@grnet.gr>

lib/cli.py
lib/client/gnt_instance.py
lib/cmdlib/instance.py
lib/hypervisor/hv_kvm.py
man/gnt-instance.rst
src/Ganeti/OpCodes.hs
src/Ganeti/OpParams.hs

index 3b1cf51..28e345e 100644 (file)
@@ -96,6 +96,7 @@ __all__ = [
   "HID_OS_OPT",
   "GLOBAL_SHARED_FILEDIR_OPT",
   "HOTPLUG_OPT",
+  "HOTPLUG_IF_POSSIBLE_OPT",
   "KEEPDISKS_OPT",
   "HVLIST_OPT",
   "HVOPTS_OPT",
@@ -1648,6 +1649,12 @@ KEEPDISKS_OPT = cli_option("--keep-disks", dest="keep_disks",
                            action="store_true", default=False,
                            help="Do not remove disks")
 
+HOTPLUG_IF_POSSIBLE_OPT = cli_option("--hotplug-if-possible",
+                                     dest="hotplug_if_possible",
+                                     action="store_true", default=False,
+                                     help="Hotplug devices in case"
+                                          " hotplug is supported")
+
 #: Options provided by all commands
 COMMON_OPTS = [DEBUG_OPT, REASON_OPT]
 
index 9febee3..e71c0da 100644 (file)
@@ -1372,6 +1372,7 @@ def SetInstanceParams(opts, args):
                                    nics=nics,
                                    disks=disks,
                                    hotplug=opts.hotplug,
+                                   hotplug_if_possible=opts.hotplug_if_possible,
                                    keep_disks=opts.keep_disks,
                                    disk_template=opts.disk_template,
                                    remote_node=opts.node,
@@ -1581,7 +1582,8 @@ commands = {
      DISK_TEMPLATE_OPT, SINGLE_NODE_OPT, OS_OPT, FORCE_VARIANT_OPT,
      OSPARAMS_OPT, DRY_RUN_OPT, PRIORITY_OPT, NWSYNC_OPT, OFFLINE_INST_OPT,
      ONLINE_INST_OPT, IGNORE_IPOLICY_OPT, RUNTIME_MEM_OPT,
-     NOCONFLICTSCHECK_OPT, NEW_PRIMARY_OPT, HOTPLUG_OPT, KEEPDISKS_OPT],
+     NOCONFLICTSCHECK_OPT, NEW_PRIMARY_OPT, HOTPLUG_OPT, KEEPDISKS_OPT,
+     HOTPLUG_IF_POSSIBLE_OPT],
     "<instance>", "Alters the parameters of an instance"),
   "shutdown": (
     GenericManyOps("shutdown", _ShutdownInstance), [ArgInstance()],
index 8229fb5..a340bc0 100644 (file)
@@ -2665,14 +2665,19 @@ class LUInstanceSetParams(LogicalUnit):
     # dictionary with instance information after the modification
     ispec = {}
 
-    if self.op.hotplug:
+    if self.op.hotplug or self.op.hotplug_if_possible:
       result = self.rpc.call_hotplug_supported(self.instance.primary_node,
                                                self.instance)
-      # result.Raise("Hotplug is not supported.")
       if result.fail_msg:
-        self.LogWarning(result.fail_msg)
-        self.op.hotplug = False
-        self.LogInfo("Continuing execution..")
+        if self.op.hotplug:
+          result.Raise("Hotplug is not possible: %s" % result.fail_msg,
+                       prereq=True)
+        else:
+          self.LogWarning(result.fail_msg)
+          self.op.hotplug = False
+          self.LogInfo("Modification will take place without hotplugging.")
+      else:
+        self.op.hotplug = True
 
     # Check disk modifications. This is done here and not in CheckArguments
     # (as with NICs), because we need to know the instance's disk template
index 8ffb2dc..dfdd6df 100644 (file)
@@ -1991,13 +1991,14 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     """
     try:
       output = self._CallMonitorCommand(instance.name, self._INFO_VERSION_CMD)
-    except errors.HypervisorError, err:
-      raise errors.HotplugError("Cannot hotplug on a stopped VM.")
+    except errors.HypervisorError:
+      raise errors.HotplugError("Instance is probably down")
 
     # TODO: search for netdev_add, drive_add, device_add.....
     match = self._INFO_VERSION_RE.search(output.stdout)
     if not match:
-      raise errors.HotplugError("Cannot parse qemu version via monitor.")
+      raise errors.HotplugError("Cannot parse qemu version via monitor")
+
     v_major, v_min, _, _ = match.groups()
     if (int(v_major), int(v_min)) < (1, 0):
       raise errors.HotplugError("Hotplug not supported for qemu versions < 1.0")
index c854e35..4f296dc 100644 (file)
@@ -1109,6 +1109,7 @@ MODIFY
 | [\--submit]
 | [\--ignore-ipolicy]
 | [\--hotplug]
+| [\--hotplug-if-possible]
 | {*instance*}
 
 Modifies the memory size, number of vcpus, ip address, MAC address
@@ -1189,6 +1190,11 @@ effect without the need of actual reboot. Please note that this feature
 is currently supported only for KVM hypervisor and for versions greater
 than 1.0.
 
+If ``--hotplug-if-possible`` is given then ganeti won't abort in case
+hotplug is not supported. It will continue execution and modification
+will take place after reboot. This covers use cases where instances are
+not running or hypervisor is not KVM.
+
 See **ganeti**\(7) for a description of ``--submit`` and other common
 options.
 
index 41a4371..c679f1d 100644 (file)
@@ -403,6 +403,7 @@ $(genOpCode "OpCode"
      , pOffline
      , pIpConflictsCheck
      , pHotplug
+     , pHotplugIfPossible
      , pKeepDisks
      ])
   , ("OpInstanceGrowDisk",
index 46bd949..80b7746 100644 (file)
@@ -97,6 +97,7 @@ module Ganeti.OpParams
   , pDiskState
   , pIgnoreIpolicy
   , pHotplug
+  , pHotplugIfPossible
   , pKeepDisks
   , pAllowRuntimeChgs
   , pInstDisks
@@ -733,6 +734,9 @@ pDiskParams = optionalField $
 pHotplug :: Field
 pHotplug = defaultFalse "hotplug"
 
+pHotplugIfPossible :: Field
+pHotplugIfPossible = defaultFalse "hotplug_if_possible"
+
 -- | Whether to remove disks.
 pKeepDisks :: Field
 pKeepDisks = defaultFalse "keep_disks"