Implement instance recreate-disks
authorIustin Pop <iustin@google.com>
Fri, 14 Aug 2009 12:33:00 +0000 (14:33 +0200)
committerIustin Pop <iustin@google.com>
Fri, 14 Aug 2009 13:00:12 +0000 (15:00 +0200)
This can be used for a 'plain' type instance when the underlying storage
went away, to recreate the storage (and reinstall) instead of removing
the instance and readding it.

Signed-off-by: Iustin Pop <iustin@google.com>
Reviewed-by: Michael Hanselmann <hansmi@google.com>

lib/cmdlib.py
lib/mcpu.py
lib/opcodes.py
man/gnt-instance.sgml
scripts/gnt-instance

index 1c07c1f..6223ee3 100644 (file)
@@ -3626,6 +3626,89 @@ class LUReinstallInstance(LogicalUnit):
       _ShutdownInstanceDisks(self, inst)
 
 
+class LURecreateInstanceDisks(LogicalUnit):
+  """Recreate an instance's missing disks.
+
+  """
+  HPATH = "instance-recreate-disks"
+  HTYPE = constants.HTYPE_INSTANCE
+  _OP_REQP = ["instance_name", "disks"]
+  REQ_BGL = False
+
+  def CheckArguments(self):
+    """Check the arguments.
+
+    """
+    if not isinstance(self.op.disks, list):
+      raise errors.OpPrereqError("Invalid disks parameter")
+    for item in self.op.disks:
+      if (not isinstance(item, int) or
+          item < 0):
+        raise errors.OpPrereqError("Invalid disk specification '%s'" %
+                                   str(item))
+
+  def ExpandNames(self):
+    self._ExpandAndLockInstance()
+
+  def BuildHooksEnv(self):
+    """Build hooks env.
+
+    This runs on master, primary and secondary nodes of the instance.
+
+    """
+    env = _BuildInstanceHookEnvByObject(self, self.instance)
+    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
+    return env, nl, nl
+
+  def CheckPrereq(self):
+    """Check prerequisites.
+
+    This checks that the instance is in the cluster and is not running.
+
+    """
+    instance = self.cfg.GetInstanceInfo(self.op.instance_name)
+    assert instance is not None, \
+      "Cannot retrieve locked instance %s" % self.op.instance_name
+    _CheckNodeOnline(self, instance.primary_node)
+
+    if instance.disk_template == constants.DT_DISKLESS:
+      raise errors.OpPrereqError("Instance '%s' has no disks" %
+                                 self.op.instance_name)
+    if instance.admin_up:
+      raise errors.OpPrereqError("Instance '%s' is marked to be up" %
+                                 self.op.instance_name)
+    remote_info = self.rpc.call_instance_info(instance.primary_node,
+                                              instance.name,
+                                              instance.hypervisor)
+    remote_info.Raise("Error checking node %s" % instance.primary_node,
+                      prereq=True)
+    if remote_info.payload:
+      raise errors.OpPrereqError("Instance '%s' is running on the node %s" %
+                                 (self.op.instance_name,
+                                  instance.primary_node))
+
+    if not self.op.disks:
+      self.op.disks = range(len(instance.disks))
+    else:
+      for idx in self.op.disks:
+        if idx >= len(instance.disks):
+          raise errors.OpPrereqError("Invalid disk index passed '%s'" % idx)
+
+    self.instance = instance
+
+  def Exec(self, feedback_fn):
+    """Recreate the disks.
+
+    """
+    to_skip = []
+    for idx, disk in enumerate(self.instance.disks):
+      if idx not in self.op.disks: # disk idx has not been passed in
+        to_skip.append(idx)
+        continue
+
+    _CreateDisks(self, self.instance, to_skip=to_skip)
+
+
 class LURenameInstance(LogicalUnit):
   """Rename an instance.
 
@@ -4818,7 +4901,7 @@ def _GetInstanceInfoText(instance):
   return "originstname+%s" % instance.name
 
 
-def _CreateDisks(lu, instance):
+def _CreateDisks(lu, instance, to_skip=None):
   """Create all disks for an instance.
 
   This abstracts away some work from AddInstance.
@@ -4827,6 +4910,8 @@ def _CreateDisks(lu, instance):
   @param lu: the logical unit on whose behalf we execute
   @type instance: L{objects.Instance}
   @param instance: the instance whose disks we should create
+  @type to_skip: list
+  @param to_skip: list of indices to skip
   @rtype: boolean
   @return: the success of the creation
 
@@ -4843,7 +4928,9 @@ def _CreateDisks(lu, instance):
 
   # Note: this needs to be kept in sync with adding of disks in
   # LUSetInstanceParams
-  for device in instance.disks:
+  for idx, device in enumerate(instance.disks):
+    if to_skip and idx in to_skip:
+      continue
     logging.info("Creating volume %s for instance %s",
                  device.iv_name, instance.name)
     #HARDCODE
index 26a377a..1c1579b 100644 (file)
@@ -74,6 +74,7 @@ class Processor(object):
     opcodes.OpRebootInstance: cmdlib.LURebootInstance,
     opcodes.OpDeactivateInstanceDisks: cmdlib.LUDeactivateInstanceDisks,
     opcodes.OpReplaceDisks: cmdlib.LUReplaceDisks,
+    opcodes.OpRecreateInstanceDisks: cmdlib.LURecreateInstanceDisks,
     opcodes.OpFailoverInstance: cmdlib.LUFailoverInstance,
     opcodes.OpMigrateInstance: cmdlib.LUMigrateInstance,
     opcodes.OpConnectConsole: cmdlib.LUConnectConsole,
index 5cbd369..dc2882b 100644 (file)
@@ -540,6 +540,13 @@ class OpDeactivateInstanceDisks(OpCode):
   __slots__ = OpCode.__slots__ + ["instance_name"]
 
 
+class OpRecreateInstanceDisks(OpCode):
+  """Deactivate an instance's disks."""
+  OP_ID = "OP_INSTANCE_RECREATE_DISKS"
+  OP_DSC_FIELD = "instance_name"
+  __slots__ = OpCode.__slots__ + ["instance_name", "disks"]
+
+
 class OpQueryInstances(OpCode):
   """Compute the list of instances."""
   OP_ID = "OP_INSTANCE_QUERY"
index f364a4b..7b907d0 100644 (file)
@@ -1893,6 +1893,31 @@ node1.example.com:disk/1:/dev/drbd1
         </para>
       </refsect3>
 
+      <refsect3>
+        <title>RECREATE-DISKS</title>
+
+        <cmdsynopsis>
+          <command>recreate-disks</command>
+          <arg>--submit</arg>
+          <arg>--disks=<option>indices</option></arg>
+          <arg choice="req"><replaceable>instance</replaceable></arg>
+        </cmdsynopsis>
+        <para>
+          Recreates the disks of the given instance, or only a subset
+          of the disks (if the option <option>disks</option> is
+          passed, which must be a comma-separated list of disk
+          indices, starting from zero).
+        </para>
+
+        <para>
+          The <option>--submit</option> option is used 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
+          <command>gnt-job info</command>.
+        </para>
+
+      </refsect3>
+
     </refsect2>
 
     <refsect2>
index 13bb6ed..f89e497 100755 (executable)
@@ -660,7 +660,7 @@ def ActivateDisks(opts, args):
 
 
 def DeactivateDisks(opts, args):
-  """Deactivate an instance's disks..
+  """Deactivate an instance's disks.
 
   This function takes the instance name, looks for its primary node
   and the tries to shutdown its block devices on that node.
@@ -678,6 +678,32 @@ def DeactivateDisks(opts, args):
   return 0
 
 
+def RecreateDisks(opts, args):
+  """Recreate an instance's disks.
+
+  @param opts: the command line options selected by the user
+  @type args: list
+  @param args: should contain only one element, the instance name
+  @rtype: int
+  @return: the desired exit code
+
+  """
+  instance_name = args[0]
+  if opts.disks:
+    try:
+      opts.disks = [int(v) for v in opts.disks.split(",")]
+    except (ValueError, TypeError), err:
+      ToStderr("Invalid disks value: %s" % str(err))
+      return 1
+  else:
+    opts.disks = []
+
+  op = opcodes.OpRecreateInstanceDisks(instance_name=instance_name,
+                                       disks=opts.disks)
+  SubmitOrSend(op, opts)
+  return 0
+
+
 def GrowDisk(opts, args):
   """Grow an instance's disks.
 
@@ -1553,6 +1579,15 @@ commands = {
   'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
                        "<instance>",
                        "Deactivate an instance's disks"),
+  'recreate-disks': (RecreateDisks, ARGS_ONE,
+                     [DEBUG_OPT, SUBMIT_OPT,
+                     make_option("--disks", dest="disks", default=None,
+                                 help="Comma-separated list of disks"
+                                 " indices to replace (e.g. 0,2) (optional,"
+                                 " defaults to all disks)"),
+                      ],
+                     "<instance>",
+                     "Recreate an instance's disks"),
   'grow-disk': (GrowDisk, ARGS_FIXED(3),
                 [DEBUG_OPT, SUBMIT_OPT,
                  make_option("--no-wait-for-sync",