From bd315bfa67accb422e0dfd900d41948157279390 Mon Sep 17 00:00:00 2001 From: Iustin Pop <iustin@google.com> Date: Fri, 14 Aug 2009 14:33:00 +0200 Subject: [PATCH] Implement instance recreate-disks 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 | 91 ++++++++++++++++++++++++++++++++++++++++++- lib/mcpu.py | 1 + lib/opcodes.py | 7 ++++ man/gnt-instance.sgml | 25 ++++++++++++ scripts/gnt-instance | 37 +++++++++++++++++- 5 files changed, 158 insertions(+), 3 deletions(-) diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 1c07c1f87..6223ee331 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -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 diff --git a/lib/mcpu.py b/lib/mcpu.py index 26a377a32..1c1579b50 100644 --- a/lib/mcpu.py +++ b/lib/mcpu.py @@ -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, diff --git a/lib/opcodes.py b/lib/opcodes.py index 5cbd369f1..dc2882ba3 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -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" diff --git a/man/gnt-instance.sgml b/man/gnt-instance.sgml index f364a4b96..7b907d0f4 100644 --- a/man/gnt-instance.sgml +++ b/man/gnt-instance.sgml @@ -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> diff --git a/scripts/gnt-instance b/scripts/gnt-instance index 13bb6edce..f89e49754 100755 --- a/scripts/gnt-instance +++ b/scripts/gnt-instance @@ -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", -- GitLab