Revision 7e9366f7
b/lib/cmdlib.py | ||
---|---|---|
4255 | 4255 |
_OP_REQP = ["instance_name", "mode", "disks"] |
4256 | 4256 |
REQ_BGL = False |
4257 | 4257 |
|
4258 |
def ExpandNames(self): |
|
4259 |
self._ExpandAndLockInstance() |
|
4260 |
|
|
4258 |
def CheckArguments(self): |
|
4261 | 4259 |
if not hasattr(self.op, "remote_node"): |
4262 | 4260 |
self.op.remote_node = None |
4263 |
|
|
4264 |
ia_name = getattr(self.op, "iallocator", None) |
|
4265 |
if ia_name is not None: |
|
4266 |
if self.op.remote_node is not None: |
|
4261 |
if not hasattr(self.op, "iallocator"): |
|
4262 |
self.op.iallocator = None |
|
4263 |
|
|
4264 |
# check for valid parameter combination |
|
4265 |
cnt = [self.op.remote_node, self.op.iallocator].count(None) |
|
4266 |
if self.op.mode == constants.REPLACE_DISK_CHG: |
|
4267 |
if cnt == 2: |
|
4268 |
raise errors.OpPrereqError("When changing the secondary either an" |
|
4269 |
" iallocator script must be used or the" |
|
4270 |
" new node given") |
|
4271 |
elif cnt == 0: |
|
4267 | 4272 |
raise errors.OpPrereqError("Give either the iallocator or the new" |
4268 | 4273 |
" secondary, not both") |
4274 |
else: # not replacing the secondary |
|
4275 |
if cnt != 2: |
|
4276 |
raise errors.OpPrereqError("The iallocator and new node options can" |
|
4277 |
" be used only when changing the" |
|
4278 |
" secondary node") |
|
4279 |
|
|
4280 |
def ExpandNames(self): |
|
4281 |
self._ExpandAndLockInstance() |
|
4282 |
|
|
4283 |
if self.op.iallocator is not None: |
|
4269 | 4284 |
self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET |
4270 | 4285 |
elif self.op.remote_node is not None: |
4271 | 4286 |
remote_node = self.cfg.ExpandNodeName(self.op.remote_node) |
... | ... | |
4340 | 4355 |
"Cannot retrieve locked instance %s" % self.op.instance_name |
4341 | 4356 |
self.instance = instance |
4342 | 4357 |
|
4343 |
if instance.disk_template not in constants.DTS_NET_MIRROR:
|
|
4344 |
raise errors.OpPrereqError("Instance's disk layout is not"
|
|
4345 |
" network mirrored.")
|
|
4358 |
if instance.disk_template != constants.DT_DRBD8:
|
|
4359 |
raise errors.OpPrereqError("Can only run replace disks for DRBD8-based"
|
|
4360 |
" instances")
|
|
4346 | 4361 |
|
4347 | 4362 |
if len(instance.secondary_nodes) != 1: |
4348 | 4363 |
raise errors.OpPrereqError("The instance has a strange layout," |
... | ... | |
4351 | 4366 |
|
4352 | 4367 |
self.sec_node = instance.secondary_nodes[0] |
4353 | 4368 |
|
4354 |
ia_name = getattr(self.op, "iallocator", None) |
|
4355 |
if ia_name is not None: |
|
4369 |
if self.op.iallocator is not None: |
|
4356 | 4370 |
self._RunAllocator() |
4357 | 4371 |
|
4358 | 4372 |
remote_node = self.op.remote_node |
... | ... | |
4366 | 4380 |
raise errors.OpPrereqError("The specified node is the primary node of" |
4367 | 4381 |
" the instance.") |
4368 | 4382 |
elif remote_node == self.sec_node: |
4369 |
if self.op.mode == constants.REPLACE_DISK_SEC: |
|
4370 |
# this is for DRBD8, where we can't execute the same mode of |
|
4371 |
# replacement as for drbd7 (no different port allocated) |
|
4372 |
raise errors.OpPrereqError("Same secondary given, cannot execute" |
|
4373 |
" replacement") |
|
4374 |
if instance.disk_template == constants.DT_DRBD8: |
|
4375 |
if (self.op.mode == constants.REPLACE_DISK_ALL and |
|
4376 |
remote_node is not None): |
|
4377 |
# switch to replace secondary mode |
|
4378 |
self.op.mode = constants.REPLACE_DISK_SEC |
|
4379 |
|
|
4380 |
if self.op.mode == constants.REPLACE_DISK_ALL: |
|
4381 |
raise errors.OpPrereqError("Template 'drbd' only allows primary or" |
|
4382 |
" secondary disk replacement, not" |
|
4383 |
" both at once") |
|
4384 |
elif self.op.mode == constants.REPLACE_DISK_PRI: |
|
4385 |
if remote_node is not None: |
|
4386 |
raise errors.OpPrereqError("Template 'drbd' does not allow changing" |
|
4387 |
" the secondary while doing a primary" |
|
4388 |
" node disk replacement") |
|
4389 |
self.tgt_node = instance.primary_node |
|
4390 |
self.oth_node = instance.secondary_nodes[0] |
|
4391 |
_CheckNodeOnline(self, self.tgt_node) |
|
4392 |
_CheckNodeOnline(self, self.oth_node) |
|
4393 |
elif self.op.mode == constants.REPLACE_DISK_SEC: |
|
4394 |
self.new_node = remote_node # this can be None, in which case |
|
4395 |
# we don't change the secondary |
|
4396 |
self.tgt_node = instance.secondary_nodes[0] |
|
4397 |
self.oth_node = instance.primary_node |
|
4398 |
_CheckNodeOnline(self, self.oth_node) |
|
4399 |
if self.new_node is not None: |
|
4400 |
_CheckNodeOnline(self, self.new_node) |
|
4401 |
else: |
|
4402 |
_CheckNodeOnline(self, self.tgt_node) |
|
4403 |
else: |
|
4404 |
raise errors.ProgrammerError("Unhandled disk replace mode") |
|
4383 |
raise errors.OpPrereqError("The specified node is already the" |
|
4384 |
" secondary node of the instance.") |
|
4385 |
|
|
4386 |
if self.op.mode == constants.REPLACE_DISK_PRI: |
|
4387 |
n1 = self.tgt_node = instance.primary_node |
|
4388 |
n2 = self.oth_node = self.sec_node |
|
4389 |
elif self.op.mode == constants.REPLACE_DISK_SEC: |
|
4390 |
n1 = self.tgt_node = self.sec_node |
|
4391 |
n2 = self.oth_node = instance.primary_node |
|
4392 |
elif self.op.mode == constants.REPLACE_DISK_CHG: |
|
4393 |
n1 = self.new_node = remote_node |
|
4394 |
n2 = self.oth_node = instance.primary_node |
|
4395 |
self.tgt_node = self.sec_node |
|
4396 |
else: |
|
4397 |
raise errors.ProgrammerError("Unhandled disk replace mode") |
|
4398 |
|
|
4399 |
_CheckNodeOnline(self, n1) |
|
4400 |
_CheckNodeOnline(self, n2) |
|
4405 | 4401 |
|
4406 | 4402 |
if not self.op.disks: |
4407 | 4403 |
self.op.disks = range(len(instance.disks)) |
... | ... | |
4793 | 4789 |
if instance.status == "down": |
4794 | 4790 |
_StartInstanceDisks(self, instance, True) |
4795 | 4791 |
|
4796 |
if instance.disk_template == constants.DT_DRBD8: |
|
4797 |
if self.op.remote_node is None: |
|
4798 |
fn = self._ExecD8DiskOnly |
|
4799 |
else: |
|
4800 |
fn = self._ExecD8Secondary |
|
4792 |
if self.op.mode == constants.REPLACE_DISK_CHG: |
|
4793 |
fn = self._ExecD8Secondary |
|
4801 | 4794 |
else: |
4802 |
raise errors.ProgrammerError("Unhandled disk replacement case")
|
|
4795 |
fn = self._ExecD8DiskOnly
|
|
4803 | 4796 |
|
4804 | 4797 |
ret = fn(feedback_fn) |
4805 | 4798 |
|
b/lib/constants.py | ||
---|---|---|
193 | 193 |
DISK_ACCESS_SET = frozenset([DISK_RDONLY, DISK_RDWR]) |
194 | 194 |
|
195 | 195 |
# disk replacement mode |
196 |
REPLACE_DISK_PRI = "replace_primary" |
|
197 |
REPLACE_DISK_SEC = "replace_secondary" |
|
198 |
REPLACE_DISK_ALL = "replace_all"
|
|
196 |
REPLACE_DISK_PRI = "replace_primary" # replace disks on primary
|
|
197 |
REPLACE_DISK_SEC = "replace_secondary" # replace disks on secondary
|
|
198 |
REPLACE_DISK_CHG = "replace_all" # change secondary node
|
|
199 | 199 |
|
200 | 200 |
# lock recalculate mode |
201 | 201 |
LOCKS_REPLACE = 'replace' |
b/man/gnt-instance.sgml | ||
---|---|---|
1082 | 1082 |
|
1083 | 1083 |
<cmdsynopsis> |
1084 | 1084 |
<command>replace-disks</command> |
1085 |
<arg choice="req">-p</arg> |
|
1086 |
<arg choice="req"><replaceable>instance</replaceable></arg> |
|
1087 |
</cmdsynopsis> |
|
1085 | 1088 |
|
1086 |
<group choice="req"> |
|
1087 |
<arg>--iallocator <replaceable>name</replaceable></arg> |
|
1088 |
<arg>--new-secondary <replaceable>NODE</replaceable></arg> |
|
1089 |
</group> |
|
1090 |
<sbr> |
|
1089 |
<cmdsynopsis> |
|
1090 |
<command>replace-disks</command> |
|
1091 | 1091 |
|
1092 |
<arg choice="opt">-s</arg>
|
|
1092 |
<arg choice="req">-s</arg>
|
|
1093 | 1093 |
<arg choice="req"><replaceable>instance</replaceable></arg> |
1094 | 1094 |
</cmdsynopsis> |
1095 | 1095 |
|
1096 | 1096 |
<cmdsynopsis> |
1097 | 1097 |
<command>replace-disks</command> |
1098 | 1098 |
|
1099 |
<group> |
|
1100 |
<arg choice="req">-s</arg>
|
|
1101 |
<arg choice="req">-p</arg>
|
|
1099 |
<group choice="req">
|
|
1100 |
<arg>--iallocator <replaceable>name</replaceable></arg>
|
|
1101 |
<arg>--new-secondary <replaceable>NODE</replaceable></arg>
|
|
1102 | 1102 |
</group> |
1103 |
|
|
1103 | 1104 |
<arg choice="req"><replaceable>instance</replaceable></arg> |
1104 | 1105 |
</cmdsynopsis> |
1105 | 1106 |
|
... | ... | |
1110 | 1111 |
</para> |
1111 | 1112 |
|
1112 | 1113 |
<para> |
1113 |
The first form will do a secondary node change, while the
|
|
1114 |
second form will replace the disks on either the primary
|
|
1115 |
(<option>-p</option>) or the secondary (<option>-s</option>)
|
|
1116 |
node of the instance only, without changing the node.
|
|
1114 |
The first form (when passing the <option>-p</option> option)
|
|
1115 |
will replace the disks on the primary, while the second form
|
|
1116 |
(when passing the <option>-s</option> option will replace
|
|
1117 |
the disks on the secondary node.
|
|
1117 | 1118 |
</para> |
1118 | 1119 |
|
1119 | 1120 |
<para> |
1120 |
Specifying <option>--iallocator</option> enables secondary node |
|
1121 |
replacement and and makes the new secondary be selected automatically |
|
1122 |
by the specified allocator plugin. |
|
1121 |
The third form (when passing either the |
|
1122 |
<option>--iallocator</option> or the |
|
1123 |
<option>--new-secondary</option> option) is designed to |
|
1124 |
change secondary node of the instance. Specifying |
|
1125 |
<option>--iallocator</option> makes the new secondary be |
|
1126 |
selected automatically by the specified allocator plugin, |
|
1127 |
otherwise the new secondary node will be the one chosen |
|
1128 |
manually via the <option>--new-secondary</option> option. |
|
1123 | 1129 |
</para> |
1124 | 1130 |
</refsect3> |
1125 | 1131 |
|
b/scripts/gnt-instance | ||
---|---|---|
772 | 772 |
disks = [int(i) for i in opts.disks.split(",")] |
773 | 773 |
except ValueError, err: |
774 | 774 |
raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err)) |
775 |
if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed |
|
776 |
mode = constants.REPLACE_DISK_ALL |
|
777 |
elif opts.on_primary: # only on primary: |
|
775 |
cnt = [opts.on_primary, opts.on_secondary, |
|
776 |
new_2ndary is not None, iallocator is not None].count(True) |
|
777 |
if cnt != 1: |
|
778 |
raise errors.OpPrereqError("One and only one of the -p, -s, -n and -i" |
|
779 |
" options must be passed") |
|
780 |
elif opts.on_primary: |
|
778 | 781 |
mode = constants.REPLACE_DISK_PRI |
779 |
if new_2ndary is not None or iallocator is not None: |
|
780 |
raise errors.OpPrereqError("Can't change secondary node on primary disk" |
|
781 |
" replacement") |
|
782 |
elif opts.on_secondary is not None or iallocator is not None: |
|
783 |
# only on secondary |
|
782 |
elif opts.on_secondary: |
|
784 | 783 |
mode = constants.REPLACE_DISK_SEC |
784 |
elif new_2ndary is not None or iallocator is not None: |
|
785 |
# replace secondary |
|
786 |
mode = constants.REPLACE_DISK_CHG |
|
785 | 787 |
|
786 | 788 |
op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks, |
787 | 789 |
remote_node=new_2ndary, mode=mode, |
... | ... | |
1254 | 1256 |
[DEBUG_OPT, |
1255 | 1257 |
make_option("-n", "--new-secondary", dest="new_secondary", |
1256 | 1258 |
help=("New secondary node (for secondary" |
1257 |
" node change)"), metavar="NODE"), |
|
1259 |
" node change)"), metavar="NODE", |
|
1260 |
default=None), |
|
1258 | 1261 |
make_option("-p", "--on-primary", dest="on_primary", |
1259 | 1262 |
default=False, action="store_true", |
1260 | 1263 |
help=("Replace the disk(s) on the primary" |
... | ... | |
1267 | 1270 |
help=("Comma-separated list of disks" |
1268 | 1271 |
" to replace (e.g. sda) (optional," |
1269 | 1272 |
" defaults to all disks")), |
1270 |
make_option("--iallocator", metavar="<NAME>", |
|
1273 |
make_option("-i", "--iallocator", metavar="<NAME>",
|
|
1271 | 1274 |
help="Select new secondary for the instance" |
1272 | 1275 |
" automatically using the" |
1273 | 1276 |
" <NAME> iallocator plugin (enables" |
Also available in: Unified diff