diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 380650e4809414660f54c101c5660de4b18c7076..61b19805aaff99325a0a2e8fb9ea0cdc5ebd3611 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -4255,17 +4255,32 @@ class LUReplaceDisks(LogicalUnit): _OP_REQP = ["instance_name", "mode", "disks"] REQ_BGL = False - def ExpandNames(self): - self._ExpandAndLockInstance() - + def CheckArguments(self): if not hasattr(self.op, "remote_node"): self.op.remote_node = None - - ia_name = getattr(self.op, "iallocator", None) - if ia_name is not None: - if self.op.remote_node is not None: + if not hasattr(self.op, "iallocator"): + self.op.iallocator = None + + # check for valid parameter combination + cnt = [self.op.remote_node, self.op.iallocator].count(None) + if self.op.mode == constants.REPLACE_DISK_CHG: + if cnt == 2: + raise errors.OpPrereqError("When changing the secondary either an" + " iallocator script must be used or the" + " new node given") + elif cnt == 0: raise errors.OpPrereqError("Give either the iallocator or the new" " secondary, not both") + else: # not replacing the secondary + if cnt != 2: + raise errors.OpPrereqError("The iallocator and new node options can" + " be used only when changing the" + " secondary node") + + def ExpandNames(self): + self._ExpandAndLockInstance() + + if self.op.iallocator is not None: self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET elif self.op.remote_node is not None: remote_node = self.cfg.ExpandNodeName(self.op.remote_node) @@ -4340,9 +4355,9 @@ class LUReplaceDisks(LogicalUnit): "Cannot retrieve locked instance %s" % self.op.instance_name self.instance = instance - if instance.disk_template not in constants.DTS_NET_MIRROR: - raise errors.OpPrereqError("Instance's disk layout is not" - " network mirrored.") + if instance.disk_template != constants.DT_DRBD8: + raise errors.OpPrereqError("Can only run replace disks for DRBD8-based" + " instances") if len(instance.secondary_nodes) != 1: raise errors.OpPrereqError("The instance has a strange layout," @@ -4351,8 +4366,7 @@ class LUReplaceDisks(LogicalUnit): self.sec_node = instance.secondary_nodes[0] - ia_name = getattr(self.op, "iallocator", None) - if ia_name is not None: + if self.op.iallocator is not None: self._RunAllocator() remote_node = self.op.remote_node @@ -4366,42 +4380,24 @@ class LUReplaceDisks(LogicalUnit): raise errors.OpPrereqError("The specified node is the primary node of" " the instance.") elif remote_node == self.sec_node: - if self.op.mode == constants.REPLACE_DISK_SEC: - # this is for DRBD8, where we can't execute the same mode of - # replacement as for drbd7 (no different port allocated) - raise errors.OpPrereqError("Same secondary given, cannot execute" - " replacement") - if instance.disk_template == constants.DT_DRBD8: - if (self.op.mode == constants.REPLACE_DISK_ALL and - remote_node is not None): - # switch to replace secondary mode - self.op.mode = constants.REPLACE_DISK_SEC - - if self.op.mode == constants.REPLACE_DISK_ALL: - raise errors.OpPrereqError("Template 'drbd' only allows primary or" - " secondary disk replacement, not" - " both at once") - elif self.op.mode == constants.REPLACE_DISK_PRI: - if remote_node is not None: - raise errors.OpPrereqError("Template 'drbd' does not allow changing" - " the secondary while doing a primary" - " node disk replacement") - self.tgt_node = instance.primary_node - self.oth_node = instance.secondary_nodes[0] - _CheckNodeOnline(self, self.tgt_node) - _CheckNodeOnline(self, self.oth_node) - elif self.op.mode == constants.REPLACE_DISK_SEC: - self.new_node = remote_node # this can be None, in which case - # we don't change the secondary - self.tgt_node = instance.secondary_nodes[0] - self.oth_node = instance.primary_node - _CheckNodeOnline(self, self.oth_node) - if self.new_node is not None: - _CheckNodeOnline(self, self.new_node) - else: - _CheckNodeOnline(self, self.tgt_node) - else: - raise errors.ProgrammerError("Unhandled disk replace mode") + raise errors.OpPrereqError("The specified node is already the" + " secondary node of the instance.") + + if self.op.mode == constants.REPLACE_DISK_PRI: + n1 = self.tgt_node = instance.primary_node + n2 = self.oth_node = self.sec_node + elif self.op.mode == constants.REPLACE_DISK_SEC: + n1 = self.tgt_node = self.sec_node + n2 = self.oth_node = instance.primary_node + elif self.op.mode == constants.REPLACE_DISK_CHG: + n1 = self.new_node = remote_node + n2 = self.oth_node = instance.primary_node + self.tgt_node = self.sec_node + else: + raise errors.ProgrammerError("Unhandled disk replace mode") + + _CheckNodeOnline(self, n1) + _CheckNodeOnline(self, n2) if not self.op.disks: self.op.disks = range(len(instance.disks)) @@ -4793,13 +4789,10 @@ class LUReplaceDisks(LogicalUnit): if instance.status == "down": _StartInstanceDisks(self, instance, True) - if instance.disk_template == constants.DT_DRBD8: - if self.op.remote_node is None: - fn = self._ExecD8DiskOnly - else: - fn = self._ExecD8Secondary + if self.op.mode == constants.REPLACE_DISK_CHG: + fn = self._ExecD8Secondary else: - raise errors.ProgrammerError("Unhandled disk replacement case") + fn = self._ExecD8DiskOnly ret = fn(feedback_fn) diff --git a/lib/constants.py b/lib/constants.py index 84e7b5925e5f8180415b16acb3aba0e2b898fb92..ab7f1dd4d95380b94cc0479d70993a050bbe5dc9 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -193,9 +193,9 @@ DISK_RDWR = "rw" DISK_ACCESS_SET = frozenset([DISK_RDONLY, DISK_RDWR]) # disk replacement mode -REPLACE_DISK_PRI = "replace_primary" -REPLACE_DISK_SEC = "replace_secondary" -REPLACE_DISK_ALL = "replace_all" +REPLACE_DISK_PRI = "replace_primary" # replace disks on primary +REPLACE_DISK_SEC = "replace_secondary" # replace disks on secondary +REPLACE_DISK_CHG = "replace_all" # change secondary node # lock recalculate mode LOCKS_REPLACE = 'replace' diff --git a/man/gnt-instance.sgml b/man/gnt-instance.sgml index 9332dfa4b870b1ac793b5df8dafff50272c45a7b..e6d190ecec860a72d892103b4c4f16acd3b64147 100644 --- a/man/gnt-instance.sgml +++ b/man/gnt-instance.sgml @@ -1082,24 +1082,25 @@ <cmdsynopsis> <command>replace-disks</command> + <arg choice="req">-p</arg> + <arg choice="req"><replaceable>instance</replaceable></arg> + </cmdsynopsis> - <group choice="req"> - <arg>--iallocator <replaceable>name</replaceable></arg> - <arg>--new-secondary <replaceable>NODE</replaceable></arg> - </group> - <sbr> + <cmdsynopsis> + <command>replace-disks</command> - <arg choice="opt">-s</arg> + <arg choice="req">-s</arg> <arg choice="req"><replaceable>instance</replaceable></arg> </cmdsynopsis> <cmdsynopsis> <command>replace-disks</command> - <group> - <arg choice="req">-s</arg> - <arg choice="req">-p</arg> + <group choice="req"> + <arg>--iallocator <replaceable>name</replaceable></arg> + <arg>--new-secondary <replaceable>NODE</replaceable></arg> </group> + <arg choice="req"><replaceable>instance</replaceable></arg> </cmdsynopsis> @@ -1110,16 +1111,21 @@ </para> <para> - The first form will do a secondary node change, while the - second form will replace the disks on either the primary - (<option>-p</option>) or the secondary (<option>-s</option>) - node of the instance only, without changing the node. + The first form (when passing the <option>-p</option> option) + will replace the disks on the primary, while the second form + (when passing the <option>-s</option> option will replace + the disks on the secondary node. </para> <para> - Specifying <option>--iallocator</option> enables secondary node - replacement and and makes the new secondary be selected automatically - by the specified allocator plugin. + The third form (when passing either the + <option>--iallocator</option> or the + <option>--new-secondary</option> option) is designed to + change secondary node of the instance. Specifying + <option>--iallocator</option> makes the new secondary be + selected automatically by the specified allocator plugin, + otherwise the new secondary node will be the one chosen + manually via the <option>--new-secondary</option> option. </para> </refsect3> diff --git a/scripts/gnt-instance b/scripts/gnt-instance index ac616f65532bcc9d07e2f9d3a5245faf65ec3c03..e2cea2603e3948782c8573a82ddb8ae4e894c4ad 100755 --- a/scripts/gnt-instance +++ b/scripts/gnt-instance @@ -772,16 +772,18 @@ def ReplaceDisks(opts, args): disks = [int(i) for i in opts.disks.split(",")] except ValueError, err: raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err)) - if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed - mode = constants.REPLACE_DISK_ALL - elif opts.on_primary: # only on primary: + cnt = [opts.on_primary, opts.on_secondary, + new_2ndary is not None, iallocator is not None].count(True) + if cnt != 1: + raise errors.OpPrereqError("One and only one of the -p, -s, -n and -i" + " options must be passed") + elif opts.on_primary: mode = constants.REPLACE_DISK_PRI - if new_2ndary is not None or iallocator is not None: - raise errors.OpPrereqError("Can't change secondary node on primary disk" - " replacement") - elif opts.on_secondary is not None or iallocator is not None: - # only on secondary + elif opts.on_secondary: mode = constants.REPLACE_DISK_SEC + elif new_2ndary is not None or iallocator is not None: + # replace secondary + mode = constants.REPLACE_DISK_CHG op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks, remote_node=new_2ndary, mode=mode, @@ -1254,7 +1256,8 @@ commands = { [DEBUG_OPT, make_option("-n", "--new-secondary", dest="new_secondary", help=("New secondary node (for secondary" - " node change)"), metavar="NODE"), + " node change)"), metavar="NODE", + default=None), make_option("-p", "--on-primary", dest="on_primary", default=False, action="store_true", help=("Replace the disk(s) on the primary" @@ -1267,7 +1270,7 @@ commands = { help=("Comma-separated list of disks" " to replace (e.g. sda) (optional," " defaults to all disks")), - make_option("--iallocator", metavar="<NAME>", + make_option("-i", "--iallocator", metavar="<NAME>", help="Select new secondary for the instance" " automatically using the" " <NAME> iallocator plugin (enables"