If we change the template of an instance to DRBD, a secondary node needs to be chosen. Instead of insisting that it be specified explicitly, also support asking an IAllocator.
Signed-off-by: Klaus Aehlig <[email protected]> --- lib/cmdlib/instance_set_params.py | 44 +++++++++++++++++++++++++++++-------- test/py/cmdlib/instance_unittest.py | 7 ++++-- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/lib/cmdlib/instance_set_params.py b/lib/cmdlib/instance_set_params.py index 4958a91..12d573a 100644 --- a/lib/cmdlib/instance_set_params.py +++ b/lib/cmdlib/instance_set_params.py @@ -39,6 +39,7 @@ from ganeti import errors from ganeti import ht from ganeti import hypervisor from ganeti import locking +from ganeti.masterd import iallocator from ganeti import netutils from ganeti import objects from ganeti import utils @@ -51,7 +52,8 @@ from ganeti.cmdlib.common import INSTANCE_DOWN, \ CheckParamsNotGlobal, \ IsExclusiveStorageEnabledNode, CheckHVParams, CheckOSParams, \ GetUpdatedParams, CheckInstanceState, ExpandNodeUuidAndName, \ - IsValidDiskAccessModeCombination, AnnotateDiskParams + IsValidDiskAccessModeCombination, AnnotateDiskParams, \ + CheckIAllocatorOrNode from ganeti.cmdlib.instance_storage import CalculateFileStorageDir, \ CheckDiskExtProvider, CheckNodesFreeDiskPerVG, CheckRADOSFreeSpace, \ CheckSpindlesExclusiveStorage, ComputeDiskSizePerVG, ComputeDisksInfo, \ @@ -357,10 +359,7 @@ class LUInstanceSetParams(LogicalUnit): # mirrored template node checks if self.op.disk_template in constants.DTS_INT_MIRROR: - if not self.op.remote_node: - raise errors.OpPrereqError("Changing the disk template to a mirrored" - " one requires specifying a secondary" - " node", errors.ECODE_INVAL) + CheckIAllocatorOrNode(self, "iallocator", "remote_node") elif self.op.remote_node: self.LogWarning("Changing the disk template to a non-mirrored one," " the secondary node will be ignored") @@ -433,6 +432,12 @@ class LUInstanceSetParams(LogicalUnit): ExpandNodeUuidAndName(self.cfg, self.op.remote_node_uuid, self.op.remote_node) self.needed_locks[locking.LEVEL_NODE].append(self.op.remote_node_uuid) + elif self.op.disk_template in constants.DTS_INT_MIRROR: + # If we have to find the secondary node for a conversion to DRBD, + # close node locks to the whole node group. + self.needed_locks[locking.LEVEL_NODE] = \ + list(self.cfg.GetNodeGroupMembersByNodes( + self.needed_locks[locking.LEVEL_NODE])) elif level == locking.LEVEL_NODE_RES and self.op.disk_template: # Copy node locks self.needed_locks[locking.LEVEL_NODE_RES] = \ @@ -674,7 +679,8 @@ class LUInstanceSetParams(LogicalUnit): default_vg, self.op.ext_params) # mirror node verification - if self.op.disk_template in constants.DTS_INT_MIRROR: + if self.op.disk_template in constants.DTS_INT_MIRROR \ + and self.op.remote_node_uuid: if self.op.remote_node_uuid == pnode_uuid: raise errors.OpPrereqError("Given new secondary node %s is the same" " as the primary node of the instance" % @@ -709,7 +715,8 @@ class LUInstanceSetParams(LogicalUnit): if not self.op.disk_template in constants.DTS_EXCL_STORAGE: # Make sure none of the nodes require exclusive storage nodes = [pnode_info] - if self.op.disk_template in constants.DTS_INT_MIRROR: + if self.op.disk_template in constants.DTS_INT_MIRROR \ + and self.op.remote_node_uuid: assert snode_info nodes.append(snode_info) has_es = lambda n: IsExclusiveStorageEnabledNode(self.cfg, n) @@ -731,8 +738,9 @@ class LUInstanceSetParams(LogicalUnit): utils.AllDiskOfType(inst_disks, [constants.DT_PLAIN])): # for conversions from the 'plain' to the 'drbd' disk template, check # only the remote node's capacity - req_sizes = ComputeDiskSizePerVG(self.op.disk_template, self.disks_info) - CheckNodesFreeDiskPerVG(self, [self.op.remote_node_uuid], req_sizes) + if self.op.remote_node_uuid: + req_sizes = ComputeDiskSizePerVG(self.op.disk_template, self.disks_info) + CheckNodesFreeDiskPerVG(self, [self.op.remote_node_uuid], req_sizes) elif self.op.disk_template in constants.DTS_LVM: # rest lvm-based capacity checks node_uuids = [pnode_uuid] @@ -1395,6 +1403,24 @@ class LUInstanceSetParams(LogicalUnit): """ feedback_fn("Converting disk template from 'plain' to 'drbd'") + if not self.op.remote_node_uuid: + feedback_fn("Using %s to choose new secondary" % self.op.iallocator) + + req = iallocator.IAReqInstanceAllocateSecondary( + name=self.op.instance_name) + ial = iallocator.IAllocator(self.cfg, self.rpc, req) + ial.Run(self.op.iallocator) + + if not ial.success: + raise errors.OpPrereqError("Can's find secondary node using" + " iallocator %s: %s" % + (self.op.iallocator, ial.info), + errors.ECODE_NORES) + feedback_fn("%s choose %s as new secondary" + % (self.op.iallocator, ial.result)) + self.op.remote_node = ial.result + self.op.remote_node_uuid = self.cfg.GetNodeInfoByName(ial.result).uuid + pnode_uuid = self.instance.primary_node snode_uuid = self.op.remote_node_uuid old_disks = self.cfg.GetInstanceDisks(self.instance.uuid) diff --git a/test/py/cmdlib/instance_unittest.py b/test/py/cmdlib/instance_unittest.py index 582d822..f00d4dc 100644 --- a/test/py/cmdlib/instance_unittest.py +++ b/test/py/cmdlib/instance_unittest.py @@ -2048,6 +2048,8 @@ class TestLUInstanceSetParams(CmdlibTestCase): self.inst = self.cfg.AddNewInstance(disk_template=self.dev_type) self.op = opcodes.OpInstanceSetParams(instance_name=self.inst.name) + self.cfg._cluster.default_iallocator=None + self.running_inst = \ self.cfg.AddNewInstance(admin_state=constants.ADMINST_UP) self.running_op = \ @@ -2157,8 +2159,9 @@ class TestLUInstanceSetParams(CmdlibTestCase): op = self.CopyOpCode(self.op, disk_template=constants.DT_DRBD8) self.ExecOpCodeExpectOpPrereqError( - op, "Changing the disk template to a mirrored one requires specifying" - " a secondary node") + op, "No iallocator or node given and no cluster-wide default iallocator" + " found; please specify either an iallocator or a node, or set a" + " cluster-wide default iallocator") def testPrimaryNodeToOldPrimaryNode(self): op = self.CopyOpCode(self.op, -- 2.2.0.rc0.207.ga3a616c
