Commit 7e9366f7 authored by Iustin Pop's avatar Iustin Pop
Browse files

Cleanup replace-disks modes and options

In 1.2, due to the md+drbd7 legacy, we had a complex choice of replace
modes, and the new drbd8 modes where forced into this syntax, with some
complicated rules of transition from one mode to another (if REPLACE_ALL
but not new node passed, switch to REPLACE_SEC, etc.).

This patch cleans this situation by making a clear separation between
the two main modes:
  - replace on current nodes (with the two sub-cases on primary and on
  - change to a new node (either via manually specified node or via

Reviewed-by: imsnah
parent 82e37788
......@@ -4255,17 +4255,32 @@ class LUReplaceDisks(LogicalUnit):
_OP_REQP = ["instance_name", "mode", "disks"]
REQ_BGL = False
def ExpandNames(self):
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):
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:
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)
_CheckNodeOnline(self, self.tgt_node)
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
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
fn = self._ExecD8Secondary
if self.op.mode == constants.REPLACE_DISK_CHG:
fn = self._ExecD8Secondary
raise errors.ProgrammerError("Unhandled disk replacement case")
fn = self._ExecD8DiskOnly
ret = fn(feedback_fn)
......@@ -193,9 +193,9 @@ DISK_RDWR = "rw"
# 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'
......@@ -1082,24 +1082,25 @@
<arg choice="req">-p</arg>
<arg choice="req"><replaceable>instance</replaceable></arg>
<group choice="req">
<arg>--iallocator <replaceable>name</replaceable></arg>
<arg>--new-secondary <replaceable>NODE</replaceable></arg>
<arg choice="opt">-s</arg>
<arg choice="req">-s</arg>
<arg choice="req"><replaceable>instance</replaceable></arg>
<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>
<arg choice="req"><replaceable>instance</replaceable></arg>
......@@ -1110,16 +1111,21 @@
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.
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.
......@@ -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 = {
make_option("-n", "--new-secondary", dest="new_secondary",
help=("New secondary node (for secondary"
" node change)"), metavar="NODE"),
" node change)"), metavar="NODE",
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"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment