raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
-def _CheckNodeOnline(lu, node):
+def _CheckNodeOnline(lu, node, msg=None):
"""Ensure that a given node is online.
@param lu: the LU on behalf of which we make the check
@param node: the node to check
+ @param msg: if passed, should be a message to replace the default one
@raise errors.OpPrereqError: if the node is offline
"""
+ if msg is None:
+ msg = "Can't use offline node"
if lu.cfg.GetNodeInfo(node).offline:
- raise errors.OpPrereqError("Can't use offline node %s" % node,
- errors.ECODE_INVAL)
+ raise errors.OpPrereqError("%s: %s" % (msg, node), errors.ECODE_STATE)
def _CheckNodeNotDrained(lu, node):
"""
if lu.cfg.GetNodeInfo(node).drained:
raise errors.OpPrereqError("Can't use drained node %s" % node,
- errors.ECODE_INVAL)
+ errors.ECODE_STATE)
+
+
+def _CheckNodeVmCapable(lu, node):
+ """Ensure that a given node is vm capable.
+
+ @param lu: the LU on behalf of which we make the check
+ @param node: the node to check
+ @raise errors.OpPrereqError: if the node is not vm capable
+
+ """
+ if not lu.cfg.GetNodeInfo(node).vm_capable:
+ raise errors.OpPrereqError("Can't use non-vm_capable node %s" % node,
+ errors.ECODE_STATE)
def _CheckNodeHasOS(lu, node, os_name, force_variant):
_CheckOSVariant(result.payload, os_name)
+def _CheckNodeHasSecondaryIP(lu, node, secondary_ip, prereq):
+ """Ensure that a node has the given secondary ip.
+
+ @type lu: L{LogicalUnit}
+ @param lu: the LU on behalf of which we make the check
+ @type node: string
+ @param node: the node to check
+ @type secondary_ip: string
+ @param secondary_ip: the ip to check
+ @type prereq: boolean
+ @param prereq: whether to throw a prerequisite or an execute error
+ @raise errors.OpPrereqError: if the node doesn't have the ip, and prereq=True
+ @raise errors.OpExecError: if the node doesn't have the ip, and prereq=False
+
+ """
+ result = lu.rpc.call_node_has_ip_address(node, secondary_ip)
+ result.Raise("Failure checking secondary ip on node %s" % node,
+ prereq=prereq, ecode=errors.ECODE_ENVIRON)
+ if not result.payload:
+ msg = ("Node claims it doesn't have the secondary ip you gave (%s),"
+ " please fix and re-run this command" % secondary_ip)
+ if prereq:
+ raise errors.OpPrereqError(msg, errors.ECODE_ENVIRON)
+ else:
+ raise errors.OpExecError(msg)
+
+
def _RequireFileStorage():
"""Checks that file storage is enabled.
_ErrorIf(test, self.EINSTANCEWRONGNODE, instance,
"instance should not run on node %s", node)
- diskdata = [(nname, disk, idx)
+ diskdata = [(nname, success, status, idx)
for (nname, disks) in diskstatus.items()
- for idx, disk in enumerate(disks)]
+ for idx, (success, status) in enumerate(disks)]
- for nname, bdev_status, idx in diskdata:
- _ErrorIf(not bdev_status,
+ for nname, success, bdev_status, idx in diskdata:
+ _ErrorIf(instanceconfig.admin_up and not success,
self.EINSTANCEFAULTYDISK, instance,
- "couldn't retrieve status for disk/%s on %s", idx, nname)
- _ErrorIf(bdev_status and bdev_status.ldisk_status == constants.LDS_FAULTY,
+ "couldn't retrieve status for disk/%s on %s: %s",
+ idx, nname, bdev_status)
+ _ErrorIf((instanceconfig.admin_up and success and
+ bdev_status.ldisk_status == constants.LDS_FAULTY),
self.EINSTANCEFAULTYDISK, instance,
"disk/%s on %s is faulty", idx, nname)
ht.TNone)),
("hvparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
ht.TNone)),
- ("beparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
- ht.TNone)),
+ ("beparams", None, ht.TOr(ht.TDict, ht.TNone)),
("os_hvp", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
ht.TNone)),
("osparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
("maintain_node_health", None, ht.TMaybeBool),
("prealloc_wipe_disks", None, ht.TMaybeBool),
("nicparams", None, ht.TOr(ht.TDict, ht.TNone)),
+ ("ndparams", None, ht.TOr(ht.TDict, ht.TNone)),
("drbd_helper", None, ht.TOr(ht.TString, ht.TNone)),
("default_iallocator", None, ht.TOr(ht.TString, ht.TNone)),
("reserved_lvs", None, ht.TOr(ht.TListOf(ht.TNonEmptyString), ht.TNone)),
utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
self.new_beparams = cluster.SimpleFillBE(self.op.beparams)
+ if self.op.ndparams:
+ utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
+ self.new_ndparams = cluster.SimpleFillND(self.op.ndparams)
+
if self.op.nicparams:
utils.ForceDictType(self.op.nicparams, constants.NICS_PARAMETER_TYPES)
self.new_nicparams = cluster.SimpleFillNIC(self.op.nicparams)
self.cluster.nicparams[constants.PP_DEFAULT] = self.new_nicparams
if self.op.osparams:
self.cluster.osparams = self.new_osp
+ if self.op.ndparams:
+ self.cluster.ndparams = self.new_ndparams
if self.op.candidate_pool_size is not None:
self.cluster.candidate_pool_size = self.op.candidate_pool_size
for key, val in mods:
if key == constants.DDM_ADD:
if val in lst:
- feedback_fn("OS %s already in %s, ignoring", val, desc)
+ feedback_fn("OS %s already in %s, ignoring" % (val, desc))
else:
lst.append(val)
elif key == constants.DDM_REMOVE:
if val in lst:
lst.remove(val)
else:
- feedback_fn("OS %s not found in %s, ignoring", val, desc)
+ feedback_fn("OS %s not found in %s, ignoring" % (val, desc))
else:
raise errors.ProgrammerError("Invalid modification '%s'" % key)
lu.proc.LogWarning(msg)
-def _RedistributeAncillaryFiles(lu, additional_nodes=None):
+def _RedistributeAncillaryFiles(lu, additional_nodes=None, additional_vm=True):
"""Distribute additional files which are part of the cluster configuration.
ConfigWriter takes care of distributing the config and ssconf files, but
@param lu: calling logical unit
@param additional_nodes: list of nodes not in the config to distribute to
+ @type additional_vm: boolean
+ @param additional_vm: whether the additional nodes are vm-capable or not
"""
# 1. Gather target nodes
myself = lu.cfg.GetNodeInfo(lu.cfg.GetMasterNode())
dist_nodes = lu.cfg.GetOnlineNodeList()
+ nvm_nodes = lu.cfg.GetNonVmCapableNodeList()
+ vm_nodes = [name for name in dist_nodes if name not in nvm_nodes]
if additional_nodes is not None:
dist_nodes.extend(additional_nodes)
+ if additional_vm:
+ vm_nodes.extend(additional_nodes)
if myself.name in dist_nodes:
dist_nodes.remove(myself.name)
+ if myself.name in vm_nodes:
+ vm_nodes.remove(myself.name)
# 2. Gather files to distribute
dist_files = set([constants.ETC_HOSTS,
constants.CLUSTER_DOMAIN_SECRET_FILE,
])
+ vm_files = set()
enabled_hypervisors = lu.cfg.GetClusterInfo().enabled_hypervisors
for hv_name in enabled_hypervisors:
hv_class = hypervisor.GetHypervisor(hv_name)
- dist_files.update(hv_class.GetAncillaryFiles())
+ vm_files.update(hv_class.GetAncillaryFiles())
# 3. Perform the files upload
for fname in dist_files:
_UploadHelper(lu, dist_nodes, fname)
+ for fname in vm_files:
+ _UploadHelper(lu, vm_nodes, fname)
class LURedistributeConfig(NoHooksLU):
"pinst_cnt", "sinst_cnt",
"pinst_list", "sinst_list",
"pip", "sip", "tags",
- "master",
- "role"] + _SIMPLE_FIELDS
+ "master", "role",
+ "group.uuid", "group",
+ ] + _SIMPLE_FIELDS
)
def CheckArguments(self):
nodenames = utils.NiceSort(nodenames)
nodelist = [all_info[name] for name in nodenames]
+ if "group" in self.op.output_fields:
+ groups = self.cfg.GetAllNodeGroupsInfo()
+ else:
+ groups = {}
+
# begin data gathering
if self.do_node_query:
val = "O"
else:
val = "R"
+ elif field == "group.uuid":
+ val = node.group
+ elif field == "group":
+ ng = groups.get(node.group, None)
+ if ng is None:
+ val = "<unknown>"
+ else:
+ val = ng.name
else:
raise errors.ParameterError(field)
node_output.append(val)
"""
_OP_PARAMS = [
+ _POutputFields,
("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
- ("output_fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
]
REQ_BGL = False
_FIELDS_DYNAMIC = utils.FieldSet("phys", "vg", "name", "size", "instance")
"""
_FIELDS_STATIC = utils.FieldSet(constants.SF_NODE)
_OP_PARAMS = [
+ _POutputFields,
("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
("storage_type", ht.NoDefault, _CheckStorageType),
- ("output_fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
("name", None, ht.TMaybeString),
]
REQ_BGL = False
("primary_ip", None, ht.NoType),
("secondary_ip", None, ht.TMaybeString),
("readd", False, ht.TBool),
- ("group", None, ht.TMaybeString)
+ ("group", None, ht.TMaybeString),
+ ("master_capable", None, ht.TMaybeBool),
+ ("vm_capable", None, ht.TMaybeBool),
+ ("ndparams", None, ht.TOr(ht.TDict, ht.TNone)),
]
+ _NFLAGS = ["master_capable", "vm_capable"]
def CheckArguments(self):
self.primary_ip_family = self.cfg.GetPrimaryIPFamily()
"NODE_NAME": self.op.node_name,
"NODE_PIP": self.op.primary_ip,
"NODE_SIP": self.op.secondary_ip,
+ "MASTER_CAPABLE": str(self.op.master_capable),
+ "VM_CAPABLE": str(self.op.vm_capable),
}
nodes_0 = self.cfg.GetNodeList()
nodes_1 = nodes_0 + [self.op.node_name, ]
" existing node %s" % existing_node.name,
errors.ECODE_NOTUNIQUE)
+ # After this 'if' block, None is no longer a valid value for the
+ # _capable op attributes
+ if self.op.readd:
+ old_node = self.cfg.GetNodeInfo(node)
+ assert old_node is not None, "Can't retrieve locked node %s" % node
+ for attr in self._NFLAGS:
+ if getattr(self.op, attr) is None:
+ setattr(self.op, attr, getattr(old_node, attr))
+ else:
+ for attr in self._NFLAGS:
+ if getattr(self.op, attr) is None:
+ setattr(self.op, attr, True)
+
+ if self.op.readd and not self.op.vm_capable:
+ pri, sec = cfg.GetNodeInstances(node)
+ if pri or sec:
+ raise errors.OpPrereqError("Node %s being re-added with vm_capable"
+ " flag set to false, but it already holds"
+ " instances" % node,
+ errors.ECODE_STATE)
+
# check that the type of the node (single versus dual homed) is the
# same as for the master
myself = cfg.GetNodeInfo(self.cfg.GetMasterNode())
newbie_singlehomed = secondary_ip == primary_ip
if master_singlehomed != newbie_singlehomed:
if master_singlehomed:
- raise errors.OpPrereqError("The master has no private ip but the"
+ raise errors.OpPrereqError("The master has no secondary ip but the"
" new node has one",
errors.ECODE_INVAL)
else:
- raise errors.OpPrereqError("The master has a private ip but the"
+ raise errors.OpPrereqError("The master has a secondary ip but the"
" new node doesn't have one",
errors.ECODE_INVAL)
if not netutils.TcpPing(secondary_ip, constants.DEFAULT_NODED_PORT,
source=myself.secondary_ip):
raise errors.OpPrereqError("Node secondary ip not reachable by TCP"
- " based ping to noded port",
+ " based ping to node daemon port",
errors.ECODE_ENVIRON)
if self.op.readd:
else:
exceptions = []
- self.master_candidate = _DecideSelfPromotion(self, exceptions=exceptions)
+ if self.op.master_capable:
+ self.master_candidate = _DecideSelfPromotion(self, exceptions=exceptions)
+ else:
+ self.master_candidate = False
if self.op.readd:
- self.new_node = self.cfg.GetNodeInfo(node)
- assert self.new_node is not None, "Can't retrieve locked node %s" % node
+ self.new_node = old_node
else:
node_group = cfg.LookupNodeGroup(self.op.group)
self.new_node = objects.Node(name=node,
primary_ip=primary_ip,
secondary_ip=secondary_ip,
master_candidate=self.master_candidate,
- master_capable=True,
- vm_capable=True,
offline=False, drained=False,
group=node_group)
+ if self.op.ndparams:
+ utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
+
def Exec(self, feedback_fn):
"""Adds the new node to the cluster.
if self.changed_primary_ip:
new_node.primary_ip = self.op.primary_ip
+ # copy the master/vm_capable flags
+ for attr in self._NFLAGS:
+ setattr(new_node, attr, getattr(self.op, attr))
+
# notify the user about any possible mc promotion
if new_node.master_candidate:
self.LogInfo("Node will be a master candidate")
+ if self.op.ndparams:
+ new_node.ndparams = self.op.ndparams
+
# check connectivity
result = self.rpc.call_version([node])[node]
result.Raise("Can't get version information from node %s" % node)
result.Raise("Can't update hosts file with new host data")
if new_node.secondary_ip != new_node.primary_ip:
- result = self.rpc.call_node_has_ip_address(new_node.name,
- new_node.secondary_ip)
- result.Raise("Failure checking secondary ip on node %s" % new_node.name,
- prereq=True, ecode=errors.ECODE_ENVIRON)
- if not result.payload:
- raise errors.OpExecError("Node claims it doesn't have the secondary ip"
- " you gave (%s). Please fix and re-run this"
- " command." % new_node.secondary_ip)
+ _CheckNodeHasSecondaryIP(self, new_node.name, new_node.secondary_ip,
+ False)
node_verify_list = [self.cfg.GetMasterNode()]
node_verify_param = {
self.LogWarning("Node failed to demote itself from master"
" candidate status: %s" % msg)
else:
- _RedistributeAncillaryFiles(self, additional_nodes=[node])
+ _RedistributeAncillaryFiles(self, additional_nodes=[node],
+ additional_vm=self.op.vm_capable)
self.context.AddNode(new_node, self.proc.GetECId())
("auto_promote", False, ht.TBool),
("master_capable", None, ht.TMaybeBool),
("vm_capable", None, ht.TMaybeBool),
+ ("secondary_ip", None, ht.TMaybeString),
+ ("ndparams", None, ht.TOr(ht.TDict, ht.TNone)),
_PForce,
]
REQ_BGL = False
def CheckArguments(self):
self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name)
all_mods = [self.op.offline, self.op.master_candidate, self.op.drained,
- self.op.master_capable, self.op.vm_capable]
+ self.op.master_capable, self.op.vm_capable,
+ self.op.secondary_ip]
if all_mods.count(None) == len(all_mods):
raise errors.OpPrereqError("Please pass at least one modification",
errors.ECODE_INVAL)
self.op.drained == True or
self.op.master_capable == False)
+ if self.op.secondary_ip:
+ if not netutils.IP4Address.IsValid(self.op.secondary_ip):
+ raise errors.OpPrereqError("Secondary IP (%s) needs to be a valid IPv4"
+ " address" % self.op.secondary_ip,
+ errors.ECODE_INVAL)
+
self.lock_all = self.op.auto_promote and self.might_demote
+ self.lock_instances = self.op.secondary_ip is not None
def ExpandNames(self):
if self.lock_all:
else:
self.needed_locks = {locking.LEVEL_NODE: self.op.node_name}
+ if self.lock_instances:
+ self.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET
+
+ def DeclareLocks(self, level):
+ # If we have locked all instances, before waiting to lock nodes, release
+ # all the ones living on nodes unrelated to the current operation.
+ if level == locking.LEVEL_NODE and self.lock_instances:
+ instances_release = []
+ instances_keep = []
+ self.affected_instances = []
+ if self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET:
+ for instance_name in self.acquired_locks[locking.LEVEL_INSTANCE]:
+ instance = self.context.cfg.GetInstanceInfo(instance_name)
+ i_mirrored = instance.disk_template in constants.DTS_NET_MIRROR
+ if i_mirrored and self.op.node_name in instance.all_nodes:
+ instances_keep.append(instance_name)
+ self.affected_instances.append(instance)
+ else:
+ instances_release.append(instance_name)
+ if instances_release:
+ self.context.glm.release(locking.LEVEL_INSTANCE, instances_release)
+ self.acquired_locks[locking.LEVEL_INSTANCE] = instances_keep
+
def BuildHooksEnv(self):
"""Build hooks env.
self.old_flags = old_flags = (node.master_candidate,
node.drained, node.offline)
assert old_flags in self._F2R, "Un-handled old flags %s" % str(old_flags)
- self.old_role = self._F2R[old_flags]
+ self.old_role = old_role = self._F2R[old_flags]
# Check for ineffective changes
for attr in self._FLAGS:
self.LogInfo("Demoting from master candidate")
self.op.master_candidate = False
- def Exec(self, feedback_fn):
- """Modifies a node.
-
- """
- node = self.node
- old_role = self.old_role
-
+ # Compute new role
assert [getattr(self.op, attr) for attr in self._FLAGS].count(True) <= 1
-
- # compute new flags
if self.op.master_candidate:
new_role = self._ROLE_CANDIDATE
elif self.op.drained:
else: # no new flags, nothing, keep old role
new_role = old_role
+ self.new_role = new_role
+
+ if old_role == self._ROLE_OFFLINE and new_role != old_role:
+ # Trying to transition out of offline status
+ result = self.rpc.call_version([node.name])[node.name]
+ if result.fail_msg:
+ raise errors.OpPrereqError("Node %s is being de-offlined but fails"
+ " to report its version: %s" %
+ (node.name, result.fail_msg),
+ errors.ECODE_STATE)
+ else:
+ self.LogWarning("Transitioning node from offline to online state"
+ " without using re-add. Please make sure the node"
+ " is healthy!")
+
+ if self.op.secondary_ip:
+ # Ok even without locking, because this can't be changed by any LU
+ master = self.cfg.GetNodeInfo(self.cfg.GetMasterNode())
+ master_singlehomed = master.secondary_ip == master.primary_ip
+ if master_singlehomed and self.op.secondary_ip:
+ raise errors.OpPrereqError("Cannot change the secondary ip on a single"
+ " homed cluster", errors.ECODE_INVAL)
+
+ if node.offline:
+ if self.affected_instances:
+ raise errors.OpPrereqError("Cannot change secondary ip: offline"
+ " node has instances (%s) configured"
+ " to use it" % self.affected_instances)
+ else:
+ # On online nodes, check that no instances are running, and that
+ # the node has the new ip and we can reach it.
+ for instance in self.affected_instances:
+ _CheckInstanceDown(self, instance, "cannot change secondary ip")
+
+ _CheckNodeHasSecondaryIP(self, node.name, self.op.secondary_ip, True)
+ if master.name != node.name:
+ # check reachability from master secondary ip to new secondary ip
+ if not netutils.TcpPing(self.op.secondary_ip,
+ constants.DEFAULT_NODED_PORT,
+ source=master.secondary_ip):
+ raise errors.OpPrereqError("Node secondary ip not reachable by TCP"
+ " based ping to node daemon port",
+ errors.ECODE_ENVIRON)
+
+ if self.op.ndparams:
+ new_ndparams = _GetUpdatedParams(self.node.ndparams, self.op.ndparams)
+ utils.ForceDictType(new_ndparams, constants.NDS_PARAMETER_TYPES)
+ self.new_ndparams = new_ndparams
+
+ def Exec(self, feedback_fn):
+ """Modifies a node.
+
+ """
+ node = self.node
+ old_role = self.old_role
+ new_role = self.new_role
+
result = []
+ if self.op.ndparams:
+ node.ndparams = self.new_ndparams
+
for attr in ["master_capable", "vm_capable"]:
val = getattr(self.op, attr)
if val is not None:
if self.lock_all:
_AdjustCandidatePool(self, [node.name])
+ if self.op.secondary_ip:
+ node.secondary_ip = self.op.secondary_ip
+ result.append(("secondary_ip", self.op.secondary_ip))
+
# this will trigger configuration file update, if needed
self.cfg.Update(node, feedback_fn)
errors.ECODE_NORES)
-def _CheckNodesFreeDisk(lu, nodenames, requested):
- """Checks if nodes have enough free disk space in the default VG.
+def _CheckNodesFreeDiskPerVG(lu, nodenames, req_sizes):
+ """Checks if nodes have enough free disk space in the all VGs.
This function check if all given nodes have the needed amount of
free disk. In case any node has less disk or we cannot get the
@param lu: a logical unit from which we get configuration data
@type nodenames: C{list}
@param nodenames: the list of node names to check
+ @type req_sizes: C{dict}
+ @param req_sizes: the hash of vg and corresponding amount of disk in
+ MiB to check for
+ @raise errors.OpPrereqError: if the node doesn't have enough disk,
+ or we cannot check the node
+
+ """
+ if req_sizes is not None:
+ for vg, req_size in req_sizes.iteritems():
+ _CheckNodesFreeDiskOnVG(lu, nodenames, vg, req_size)
+
+
+def _CheckNodesFreeDiskOnVG(lu, nodenames, vg, requested):
+ """Checks if nodes have enough free disk space in the specified VG.
+
+ This function check if all given nodes have the needed amount of
+ free disk. In case any node has less disk or we cannot get the
+ information from the node, this function raise an OpPrereqError
+ exception.
+
+ @type lu: C{LogicalUnit}
+ @param lu: a logical unit from which we get configuration data
+ @type nodenames: C{list}
+ @param nodenames: the list of node names to check
+ @type vg: C{str}
+ @param vg: the volume group to check
@type requested: C{int}
@param requested: the amount of disk in MiB to check for
- @raise errors.OpPrereqError: if the node doesn't have enough disk, or
- we cannot check the node
+ @raise errors.OpPrereqError: if the node doesn't have enough disk,
+ or we cannot check the node
"""
- nodeinfo = lu.rpc.call_node_info(nodenames, lu.cfg.GetVGName(),
+ nodeinfo = lu.rpc.call_node_info(nodenames, vg,
lu.cfg.GetHypervisorType())
for node in nodenames:
info = nodeinfo[node]
prereq=True, ecode=errors.ECODE_ENVIRON)
vg_free = info.payload.get("vg_free", None)
if not isinstance(vg_free, int):
- raise errors.OpPrereqError("Can't compute free disk space on node %s,"
- " result was '%s'" % (node, vg_free),
- errors.ECODE_ENVIRON)
+ raise errors.OpPrereqError("Can't compute free disk space on node"
+ " %s for vg %s, result was '%s'" %
+ (node, vg, vg_free), errors.ECODE_ENVIRON)
if requested > vg_free:
- raise errors.OpPrereqError("Not enough disk space on target node %s:"
- " required %d MiB, available %d MiB" %
- (node, requested, vg_free),
+ raise errors.OpPrereqError("Not enough disk space on target node %s"
+ " vg %s: required %d MiB, available %d MiB" %
+ (node, vg, requested, vg_free),
errors.ECODE_NORES)
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)
+ _CheckNodeOnline(self, instance.primary_node, "Instance primary node"
+ " offline, cannot reinstall")
+ for node in instance.secondary_nodes:
+ _CheckNodeOnline(self, node, "Instance secondary node offline,"
+ " cannot reinstall")
if instance.disk_template == constants.DT_DISKLESS:
raise errors.OpPrereqError("Instance '%s' has no disks" %
"""
# pylint: disable-msg=W0142
_OP_PARAMS = [
- ("output_fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
+ _POutputFields,
("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString)),
("use_locking", False, ht.TBool),
]
_CheckNodeOnline(self, target_node)
_CheckNodeNotDrained(self, target_node)
+ _CheckNodeVmCapable(self, target_node)
if instance.admin_up:
# check memory requirements on the secondary node
return results
-def _GenerateDRBD8Branch(lu, primary, secondary, size, names, iv_name,
+def _GenerateDRBD8Branch(lu, primary, secondary, size, vgname, names, iv_name,
p_minor, s_minor):
"""Generate a drbd8 device complete with its children.
"""
port = lu.cfg.AllocatePort()
- vgname = lu.cfg.GetVGName()
shared_secret = lu.cfg.GenerateDRBDSecret(lu.proc.GetECId())
dev_data = objects.Disk(dev_type=constants.LD_LV, size=size,
logical_id=(vgname, names[0]))
instance_name, primary_node,
secondary_nodes, disk_info,
file_storage_dir, file_driver,
- base_index):
+ base_index, feedback_fn):
"""Generate the entire disk layout for a given template type.
"""
for i in range(disk_count)])
for idx, disk in enumerate(disk_info):
disk_index = idx + base_index
+ vg = disk.get("vg", vgname)
+ feedback_fn("* disk %i, vg %s, name %s" % (idx, vg, names[idx]))
disk_dev = objects.Disk(dev_type=constants.LD_LV, size=disk["size"],
- logical_id=(vgname, names[idx]),
+ logical_id=(vg, names[idx]),
iv_name="disk/%d" % disk_index,
mode=disk["mode"])
disks.append(disk_dev)
names.append(lv_prefix + "_meta")
for idx, disk in enumerate(disk_info):
disk_index = idx + base_index
+ vg = disk.get("vg", vgname)
disk_dev = _GenerateDRBD8Branch(lu, primary_node, remote_node,
- disk["size"], names[idx*2:idx*2+2],
+ disk["size"], vg, names[idx*2:idx*2+2],
"disk/%d" % disk_index,
minors[idx*2], minors[idx*2+1])
disk_dev.mode = disk["mode"]
return all_result
+def _ComputeDiskSizePerVG(disk_template, disks):
+ """Compute disk size requirements in the volume group
+
+ """
+ def _compute(disks, payload):
+ """Universal algorithm
+
+ """
+ vgs = {}
+ for disk in disks:
+ vgs[disk["vg"]] = vgs.get("vg", 0) + disk["size"] + payload
+
+ return vgs
+
+ # Required free disk space as a function of disk and swap space
+ req_size_dict = {
+ constants.DT_DISKLESS: None,
+ constants.DT_PLAIN: _compute(disks, 0),
+ # 128 MB are added for drbd metadata for each disk
+ constants.DT_DRBD8: _compute(disks, 128),
+ constants.DT_FILE: None,
+ }
+
+ if disk_template not in req_size_dict:
+ raise errors.ProgrammerError("Disk template '%s' size requirement"
+ " is unknown" % disk_template)
+
+ return req_size_dict[disk_template]
+
def _ComputeDiskSize(disk_template, disks):
"""Compute disk size requirements in the volume group
except (TypeError, ValueError):
raise errors.OpPrereqError("Invalid disk size '%s'" % size,
errors.ECODE_INVAL)
- new_disk = {"size": size, "mode": mode}
+ vg = disk.get("vg", self.cfg.GetVGName())
+ new_disk = {"size": size, "mode": mode, "vg": vg}
if "adopt" in disk:
new_disk["adopt"] = disk["adopt"]
self.disks.append(new_disk)
if pnode.drained:
raise errors.OpPrereqError("Cannot use drained primary node '%s'" %
pnode.name, errors.ECODE_STATE)
+ if not pnode.vm_capable:
+ raise errors.OpPrereqError("Cannot use non-vm_capable primary node"
+ " '%s'" % pnode.name, errors.ECODE_STATE)
self.secondaries = []
" primary node.", errors.ECODE_INVAL)
_CheckNodeOnline(self, self.op.snode)
_CheckNodeNotDrained(self, self.op.snode)
+ _CheckNodeVmCapable(self, self.op.snode)
self.secondaries.append(self.op.snode)
nodenames = [pnode.name] + self.secondaries
- req_size = _ComputeDiskSize(self.op.disk_template,
- self.disks)
+ if not self.adopt_disks:
+ # Check lv size requirements, if not adopting
+ req_sizes = _ComputeDiskSizePerVG(self.op.disk_template, self.disks)
+ _CheckNodesFreeDiskPerVG(self, nodenames, req_sizes)
- # Check lv size requirements, if not adopting
- if req_size is not None and not self.adopt_disks:
- _CheckNodesFreeDisk(self, nodenames, req_size)
-
- if self.adopt_disks: # instead, we must check the adoption data
+ else: # instead, we must check the adoption data
all_lvs = set([i["adopt"] for i in self.disks])
if len(all_lvs) != len(self.disks):
raise errors.OpPrereqError("Duplicate volume names given for adoption",
errors.ECODE_INVAL)
for lv_name in all_lvs:
try:
+ # FIXME: VG must be provided here. Else all LVs with the
+ # same name will be locked on all VGs.
self.cfg.ReserveLV(lv_name, self.proc.GetECId())
except errors.ReservationError:
raise errors.OpPrereqError("LV named %s used by another instance" %
self.disks,
file_storage_dir,
self.op.file_driver,
- 0)
+ 0,
+ feedback_fn)
iobj = objects.Instance(name=instance, os=self.op.os_type,
primary_node=pnode_name,
check_nodes = [self.new_node, self.other_node]
_CheckNodeNotDrained(self.lu, remote_node)
+ _CheckNodeVmCapable(self.lu, remote_node)
old_node_info = self.cfg.GetNodeInfo(secondary_node)
assert old_node_info is not None
self.disk = instance.FindDisk(self.op.disk)
if instance.disk_template != constants.DT_FILE:
- # TODO: check the free disk space for file, when that feature will be
- # supported
- _CheckNodesFreeDisk(self, nodenames, self.op.amount)
+ # TODO: check the free disk space for file, when that feature
+ # will be supported
+ _CheckNodesFreeDiskPerVG(self, nodenames,
+ {self.disk.physical_id[0]: self.op.amount})
def Exec(self, feedback_fn):
"""Execute disk grow.
self.op.remote_node, errors.ECODE_STATE)
_CheckNodeOnline(self, self.op.remote_node)
_CheckNodeNotDrained(self, self.op.remote_node)
- disks = [{"size": d.size} for d in instance.disks]
- required = _ComputeDiskSize(self.op.disk_template, disks)
- _CheckNodesFreeDisk(self, [self.op.remote_node], required)
+ disks = [{"size": d.size, "vg": d.vg} for d in instance.disks]
+ required = _ComputeDiskSizePerVG(self.op.disk_template, disks)
+ _CheckNodesFreeDiskPerVG(self, [self.op.remote_node], required)
# hvparams processing
if self.op.hvparams:
disk_info = [{"size": d.size, "mode": d.mode} for d in instance.disks]
new_disks = _GenerateDiskTemplate(self, self.op.disk_template,
instance.name, pnode, [snode],
- disk_info, None, None, 0)
+ disk_info, None, None, 0, feedback_fn)
info = _GetInstanceInfoText(instance)
feedback_fn("Creating aditional volumes...")
# first, create the missing data and meta devices
[disk_dict],
file_path,
file_driver,
- disk_idx_base)[0]
+ disk_idx_base, feedback_fn)[0]
instance.disks.append(new_disk)
info = _GetInstanceInfoText(instance)
self.x509_key_name = self.op.x509_key_name
self.dest_x509_ca_pem = self.op.destination_x509_ca
- if self.op.remove_instance and not self.op.shutdown:
- raise errors.OpPrereqError("Can not remove instance without shutting it"
- " down before")
-
if self.op.mode == constants.EXPORT_MODE_REMOTE:
if not self.x509_key_name:
raise errors.OpPrereqError("Missing X509 key name for encryption",
"Cannot retrieve locked instance %s" % self.op.instance_name
_CheckNodeOnline(self, self.instance.primary_node)
+ if (self.op.remove_instance and self.instance.admin_up and
+ not self.op.shutdown):
+ raise errors.OpPrereqError("Can not remove instance without shutting it"
+ " down before")
+
if self.op.mode == constants.EXPORT_MODE_LOCAL:
self.op.target_node = _ExpandNodeName(self.cfg, self.op.target_node)
self.dst_node = self.cfg.GetNodeInfo(self.op.target_node)