return params_copy
+def _UpdateMinMaxISpecs(ipolicy, new_minmax, group_policy):
+ use_none = use_default = group_policy
+ minmax = ipolicy.setdefault(constants.ISPECS_MINMAX, {})
+ for (key, value) in new_minmax.items():
+ if key not in constants.ISPECS_MINMAX_KEYS:
+ raise errors.OpPrereqError("Invalid key in new ipolicy/%s: %s" %
+ (constants.ISPECS_MINMAX, key),
+ errors.ECODE_INVAL)
+ old_spec = minmax.get(key, {})
+ minmax[key] = _GetUpdatedParams(old_spec, value, use_none=use_none,
+ use_default=use_default)
+ utils.ForceDictType(minmax[key], constants.ISPECS_PARAMETER_TYPES)
+
+
def _GetUpdatedIPolicy(old_ipolicy, new_ipolicy, group_policy=False):
- """Return the new version of a instance policy.
+ """Return the new version of an instance policy.
@param group_policy: whether this policy applies to a group and thus
we should support removal of policy entries
if key not in constants.IPOLICY_ALL_KEYS:
raise errors.OpPrereqError("Invalid key in new ipolicy: %s" % key,
errors.ECODE_INVAL)
- if key in constants.IPOLICY_ISPECS:
- utils.ForceDictType(value, constants.ISPECS_PARAMETER_TYPES)
+ if key == constants.ISPECS_MINMAX:
+ _UpdateMinMaxISpecs(ipolicy, value, group_policy)
+ elif key == constants.ISPECS_STD:
ipolicy[key] = _GetUpdatedParams(old_ipolicy.get(key, {}), value,
use_none=use_none,
use_default=use_default)
+ utils.ForceDictType(ipolicy[key], constants.ISPECS_PARAMETER_TYPES)
else:
if (not value or value == [constants.VALUE_DEFAULT] or
value == constants.VALUE_DEFAULT):
" is down")
-def _ComputeMinMaxSpec(name, qualifier, ipolicy, value):
+def _ComputeMinMaxSpec(name, qualifier, ispecs, value):
"""Computes if value is in the desired range.
@param name: name of the parameter for which we perform the check
@param qualifier: a qualifier used in the error message (e.g. 'disk/1',
not just 'disk')
- @param ipolicy: dictionary containing min, max and std values
+ @param ispecs: dictionary containing min and max values
@param value: actual value that we want to use
- @return: None or element not meeting the criteria
-
+ @return: None or an error string
"""
if value in [None, constants.VALUE_AUTO]:
return None
- max_v = ipolicy[constants.ISPECS_MAX].get(name, value)
- min_v = ipolicy[constants.ISPECS_MIN].get(name, value)
+ max_v = ispecs[constants.ISPECS_MAX].get(name, value)
+ min_v = ispecs[constants.ISPECS_MIN].get(name, value)
if value > max_v or min_v > value:
if qualifier:
fqn = "%s/%s" % (name, qualifier)
def _ComputeIPolicySpecViolation(ipolicy, mem_size, cpu_count, disk_count,
nic_count, disk_sizes, spindle_use,
+ disk_template,
_compute_fn=_ComputeMinMaxSpec):
"""Verifies ipolicy against provided specs.
@param disk_sizes: Disk sizes of used disk (len must match C{disk_count})
@type spindle_use: int
@param spindle_use: The number of spindles this instance uses
+ @type disk_template: string
+ @param disk_template: The disk template of the instance
@param _compute_fn: The compute function (unittest only)
@return: A list of violations, or an empty list of no violations are found
test_settings = [
(constants.ISPEC_MEM_SIZE, "", mem_size),
(constants.ISPEC_CPU_COUNT, "", cpu_count),
- (constants.ISPEC_DISK_COUNT, "", disk_count),
(constants.ISPEC_NIC_COUNT, "", nic_count),
(constants.ISPEC_SPINDLE_USE, "", spindle_use),
] + [(constants.ISPEC_DISK_SIZE, str(idx), d)
for idx, d in enumerate(disk_sizes)]
-
- return filter(None,
- (_compute_fn(name, qualifier, ipolicy, value)
- for (name, qualifier, value) in test_settings))
-
-
-def _ComputeIPolicyInstanceViolation(ipolicy, instance,
+ if disk_template != constants.DT_DISKLESS:
+ # This check doesn't make sense for diskless instances
+ test_settings.append((constants.ISPEC_DISK_COUNT, "", disk_count))
+ ret = []
+ allowed_dts = ipolicy[constants.IPOLICY_DTS]
+ if disk_template not in allowed_dts:
+ ret.append("Disk template %s is not allowed (allowed templates: %s)" %
+ (disk_template, utils.CommaJoin(allowed_dts)))
+
+ minmax = ipolicy[constants.ISPECS_MINMAX]
+ return ret + filter(None,
+ (_compute_fn(name, qualifier, minmax, value)
+ for (name, qualifier, value) in test_settings))
+
+
+def _ComputeIPolicyInstanceViolation(ipolicy, instance, cfg,
_compute_fn=_ComputeIPolicySpecViolation):
"""Compute if instance meets the specs of ipolicy.
@param ipolicy: The ipolicy to verify against
@type instance: L{objects.Instance}
@param instance: The instance to verify
+ @type cfg: L{config.ConfigWriter}
+ @param cfg: Cluster configuration
@param _compute_fn: The function to verify ipolicy (unittest only)
@see: L{_ComputeIPolicySpecViolation}
"""
- mem_size = instance.beparams.get(constants.BE_MAXMEM, None)
- cpu_count = instance.beparams.get(constants.BE_VCPUS, None)
- spindle_use = instance.beparams.get(constants.BE_SPINDLE_USE, None)
+ be_full = cfg.GetClusterInfo().FillBE(instance)
+ mem_size = be_full[constants.BE_MAXMEM]
+ cpu_count = be_full[constants.BE_VCPUS]
+ spindle_use = be_full[constants.BE_SPINDLE_USE]
disk_count = len(instance.disks)
disk_sizes = [disk.size for disk in instance.disks]
nic_count = len(instance.nics)
+ disk_template = instance.disk_template
return _compute_fn(ipolicy, mem_size, cpu_count, disk_count, nic_count,
- disk_sizes, spindle_use)
+ disk_sizes, spindle_use, disk_template)
def _ComputeIPolicyInstanceSpecViolation(
- ipolicy, instance_spec, _compute_fn=_ComputeIPolicySpecViolation):
+ ipolicy, instance_spec, disk_template,
+ _compute_fn=_ComputeIPolicySpecViolation):
"""Compute if instance specs meets the specs of ipolicy.
@type ipolicy: dict
@param ipolicy: The ipolicy to verify against
@param instance_spec: dict
@param instance_spec: The instance spec to verify
+ @type disk_template: string
+ @param disk_template: the disk template of the instance
@param _compute_fn: The function to verify ipolicy (unittest only)
@see: L{_ComputeIPolicySpecViolation}
spindle_use = instance_spec.get(constants.ISPEC_SPINDLE_USE, None)
return _compute_fn(ipolicy, mem_size, cpu_count, disk_count, nic_count,
- disk_sizes, spindle_use)
+ disk_sizes, spindle_use, disk_template)
def _ComputeIPolicyNodeViolation(ipolicy, instance, current_group,
- target_group,
+ target_group, cfg,
_compute_fn=_ComputeIPolicyInstanceViolation):
"""Compute if instance meets the specs of the new target group.
@param instance: The instance object to verify
@param current_group: The current group of the instance
@param target_group: The new group of the instance
+ @type cfg: L{config.ConfigWriter}
+ @param cfg: Cluster configuration
@param _compute_fn: The function to verify ipolicy (unittest only)
@see: L{_ComputeIPolicySpecViolation}
if current_group == target_group:
return []
else:
- return _compute_fn(ipolicy, instance)
+ return _compute_fn(ipolicy, instance, cfg)
-def _CheckTargetNodeIPolicy(lu, ipolicy, instance, node, ignore=False,
+def _CheckTargetNodeIPolicy(lu, ipolicy, instance, node, cfg, ignore=False,
_compute_fn=_ComputeIPolicyNodeViolation):
"""Checks that the target node is correct in terms of instance policy.
@param ipolicy: The ipolicy to verify
@param instance: The instance object to verify
@param node: The new node to relocate
+ @type cfg: L{config.ConfigWriter}
+ @param cfg: Cluster configuration
@param ignore: Ignore violations of the ipolicy
@param _compute_fn: The function to verify ipolicy (unittest only)
@see: L{_ComputeIPolicySpecViolation}
"""
primary_node = lu.cfg.GetNodeInfo(instance.primary_node)
- res = _compute_fn(ipolicy, instance, primary_node.group, node.group)
+ res = _compute_fn(ipolicy, instance, primary_node.group, node.group, cfg)
if res:
msg = ("Instance does not meet target node group's (%s) instance"
raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
-def _ComputeNewInstanceViolations(old_ipolicy, new_ipolicy, instances):
+def _ComputeNewInstanceViolations(old_ipolicy, new_ipolicy, instances, cfg):
"""Computes a set of any instances that would violate the new ipolicy.
@param old_ipolicy: The current (still in-place) ipolicy
@param new_ipolicy: The new (to become) ipolicy
@param instances: List of instances to verify
+ @type cfg: L{config.ConfigWriter}
+ @param cfg: Cluster configuration
@return: A list of instances which violates the new ipolicy but
did not before
"""
- return (_ComputeViolatingInstances(new_ipolicy, instances) -
- _ComputeViolatingInstances(old_ipolicy, instances))
+ return (_ComputeViolatingInstances(new_ipolicy, instances, cfg) -
+ _ComputeViolatingInstances(old_ipolicy, instances, cfg))
def _ExpandItemName(fn, name, kind):
return mc_now < mc_should
-def _ComputeViolatingInstances(ipolicy, instances):
+def _ComputeViolatingInstances(ipolicy, instances, cfg):
"""Computes a set of instances who violates given ipolicy.
@param ipolicy: The ipolicy to verify
- @type instances: object.Instance
+ @type instances: L{objects.Instance}
@param instances: List of instances to verify
+ @type cfg: L{config.ConfigWriter}
+ @param cfg: Cluster configuration
@return: A frozenset of instance names violating the ipolicy
"""
return frozenset([inst.name for inst in instances
- if _ComputeIPolicyInstanceViolation(ipolicy, inst)])
+ if _ComputeIPolicyInstanceViolation(ipolicy, inst, cfg)])
def _CheckNicsBridgesExist(lu, target_nics, target_node):
cluster = self.cfg.GetClusterInfo()
ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
self.group_info)
- err = _ComputeIPolicyInstanceViolation(ipolicy, inst_config)
+ err = _ComputeIPolicyInstanceViolation(ipolicy, inst_config, self.cfg)
_ErrorIf(err, constants.CV_EINSTANCEPOLICY, instance, utils.CommaJoin(err),
code=self.ETYPE_WARNING)
new_ipolicy = objects.FillIPolicy(self.new_ipolicy, group.ipolicy)
ipol = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group)
new = _ComputeNewInstanceViolations(ipol,
- new_ipolicy, instances)
+ new_ipolicy, instances, self.cfg)
if new:
violations.update(new)
hv_class.CheckParameterSyntax(hv_params)
_CheckHVParams(self, node_list, hv_name, hv_params)
+ self._CheckDiskTypeConsistency()
+
if self.op.os_hvp:
# no need to check any newly-enabled hypervisors, since the
# defaults have already been checked in the above code-block
" specified" % self.op.default_iallocator,
errors.ECODE_INVAL)
+ def _CheckDiskTypeConsistency(self):
+ """Check whether the storage types that are going to be disabled
+ are still in use by some instances.
+
+ """
+ if self.op.enabled_storage_types:
+ cluster = self.cfg.GetClusterInfo()
+ instances = self.cfg.GetAllInstancesInfo()
+
+ storage_types_to_remove = set(cluster.enabled_storage_types) \
+ - set(self.op.enabled_storage_types)
+ for instance in instances.itervalues():
+ storage_type = constants.DISK_TEMPLATES_STORAGE_TYPE[
+ instance.disk_template]
+ if storage_type in storage_types_to_remove:
+ raise errors.OpPrereqError("Cannot disable storage type '%s',"
+ " because instance '%s' is using disk"
+ " template '%s'." %
+ (storage_type, instance.name,
+ instance.disk_template))
+
def Exec(self, feedback_fn):
"""Change the parameters of the cluster.
if self.op.enabled_hypervisors is not None:
self.cluster.hvparams = self.new_hvparams
self.cluster.enabled_hypervisors = self.op.enabled_hypervisors
+ if self.op.enabled_storage_types is not None:
+ self.cluster.enabled_storage_types = \
+ list(set(self.op.enabled_storage_types))
if self.op.beparams:
self.cluster.beparams[constants.PP_DEFAULT] = self.new_beparams
if self.op.nicparams:
instance = self.instance
ignore_secondaries = self.op.ignore_secondaries
reboot_type = self.op.reboot_type
+ reason = self.op.reason
remote_info = self.rpc.call_instance_info(instance.primary_node,
instance.name,
self.cfg.SetDiskID(disk, node_current)
result = self.rpc.call_instance_reboot(node_current, instance,
reboot_type,
- self.op.shutdown_timeout)
+ self.op.shutdown_timeout,
+ reason)
result.Raise("Could not reboot instance")
else:
if instance_running:
lu.needed_locks[locking.LEVEL_NODE_RES] = []
lu.recalculate_locks[locking.LEVEL_NODE_RES] = constants.LOCKS_REPLACE
- # The node allocation lock is actually only needed for replicated instances
- # (e.g. DRBD8) and if an iallocator is used.
+ # The node allocation lock is actually only needed for externally replicated
+ # instances (e.g. sharedfile or RBD) and if an iallocator is used.
lu.needed_locks[locking.LEVEL_NODE_ALLOC] = []
cluster = self.cfg.GetClusterInfo()
group_info = self.cfg.GetNodeGroup(node.group)
ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group_info)
- _CheckTargetNodeIPolicy(self, ipolicy, instance, node,
+ _CheckTargetNodeIPolicy(self, ipolicy, instance, node, self.cfg,
ignore=self.op.ignore_ipolicy)
if instance.admin_state == constants.ADMINST_UP:
errors.ECODE_STATE)
if instance.disk_template in constants.DTS_EXT_MIRROR:
- assert locking.NAL in self.lu.owned_locks(locking.LEVEL_NODE_ALLOC)
-
_CheckIAllocatorOrNode(self.lu, "iallocator", "target_node")
if self.lu.op.iallocator:
+ assert locking.NAL in self.lu.owned_locks(locking.LEVEL_NODE_ALLOC)
self._RunAllocator()
else:
# We set set self.target_node as it is required by
group_info = self.cfg.GetNodeGroup(nodeinfo.group)
ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
group_info)
- _CheckTargetNodeIPolicy(self.lu, ipolicy, instance, nodeinfo,
+ _CheckTargetNodeIPolicy(self.lu, ipolicy, instance, nodeinfo, self.cfg,
ignore=self.ignore_ipolicy)
# self.target_node is already populated, either directly or by the
group_info = self.cfg.GetNodeGroup(nodeinfo.group)
ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
group_info)
- _CheckTargetNodeIPolicy(self.lu, ipolicy, instance, nodeinfo,
+ _CheckTargetNodeIPolicy(self.lu, ipolicy, instance, nodeinfo, self.cfg,
ignore=self.ignore_ipolicy)
i_be = cluster.FillBE(instance)
@param lu: the logical unit on whose behalf we execute
@type instance: L{objects.Instance}
@param instance: the instance whose disks we should create
- @return: the success of the wipe
+ @type disks: None or list of tuple of (number, L{objects.Disk}, number)
+ @param disks: Disk details; tuple contains disk index, disk object and the
+ start offset
"""
node = instance.primary_node
nodenames = [pnode.name] + self.secondaries
- # Verify instance specs
- spindle_use = self.be_full.get(constants.BE_SPINDLE_USE, None)
- ispec = {
- constants.ISPEC_MEM_SIZE: self.be_full.get(constants.BE_MAXMEM, None),
- constants.ISPEC_CPU_COUNT: self.be_full.get(constants.BE_VCPUS, None),
- constants.ISPEC_DISK_COUNT: len(self.disks),
- constants.ISPEC_DISK_SIZE: [disk["size"] for disk in self.disks],
- constants.ISPEC_NIC_COUNT: len(self.nics),
- constants.ISPEC_SPINDLE_USE: spindle_use,
- }
-
- group_info = self.cfg.GetNodeGroup(pnode.group)
- ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group_info)
- res = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec)
- if not self.op.ignore_ipolicy and res:
- msg = ("Instance allocation to group %s (%s) violates policy: %s" %
- (pnode.group, group_info.name, utils.CommaJoin(res)))
- raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
-
if not self.adopt_disks:
if self.op.disk_template == constants.DT_RBD:
# _CheckRADOSFreeSpace() is just a placeholder.
group_info = self.cfg.GetNodeGroup(pnode.group)
ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group_info)
- res = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec)
+ res = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec,
+ self.op.disk_template)
if not self.op.ignore_ipolicy and res:
- raise errors.OpPrereqError(("Instance allocation to group %s violates"
- " policy: %s") % (pnode.group,
- utils.CommaJoin(res)),
- errors.ECODE_INVAL)
+ msg = ("Instance allocation to group %s (%s) violates policy: %s" %
+ (pnode.group, group_info.name, utils.CommaJoin(res)))
+ raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
_CheckHVParams(self, nodenames, self.op.hypervisor, self.op.hvparams)
ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
new_group_info)
_CheckTargetNodeIPolicy(self, ipolicy, instance, self.remote_node_info,
- ignore=self.ignore_ipolicy)
+ self.cfg, ignore=self.ignore_ipolicy)
for node in check_nodes:
_CheckNodeOnline(self.lu, node)
def CheckArguments(self):
if not (self.op.nics or self.op.disks or self.op.disk_template or
self.op.hvparams or self.op.beparams or self.op.os_name or
- self.op.offline is not None or self.op.runtime_mem):
+ self.op.offline is not None or self.op.runtime_mem or
+ self.op.pnode):
raise errors.OpPrereqError("No changes submitted", errors.ECODE_INVAL)
if self.op.hvparams:
self._CheckMods("NIC", self.op.nics, constants.INIC_PARAMS_TYPES,
self._VerifyNicModification)
+ if self.op.pnode:
+ self.op.pnode = _ExpandNodeName(self.cfg, self.op.pnode)
+
def ExpandNames(self):
self._ExpandAndLockInstance()
self.needed_locks[locking.LEVEL_NODEGROUP] = []
snode_group = self.cfg.GetNodeGroup(snode_info.group)
ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
snode_group)
- _CheckTargetNodeIPolicy(self, ipolicy, instance, snode_info,
+ _CheckTargetNodeIPolicy(self, ipolicy, instance, snode_info, self.cfg,
ignore=self.op.ignore_ipolicy)
if pnode_info.group != snode_info.group:
self.LogWarning("The primary and secondary nodes are in two"
"Cannot retrieve locked instance %s" % self.op.instance_name
pnode = instance.primary_node
+
+ self.warn = []
+
+ if (self.op.pnode is not None and self.op.pnode != pnode and
+ not self.op.force):
+ # verify that the instance is not up
+ instance_info = self.rpc.call_instance_info(pnode, instance.name,
+ instance.hypervisor)
+ if instance_info.fail_msg:
+ self.warn.append("Can't get instance runtime information: %s" %
+ instance_info.fail_msg)
+ elif instance_info.payload:
+ raise errors.OpPrereqError("Instance is still running on %s" % pnode,
+ errors.ECODE_STATE)
+
assert pnode in self.owned_locks(locking.LEVEL_NODE)
nodelist = list(instance.all_nodes)
pnode_info = self.cfg.GetNodeInfo(pnode)
else:
self.os_inst = {}
- self.warn = []
-
#TODO(dynmem): do the appropriate check involving MINMEM
if (constants.BE_MAXMEM in self.op.beparams and not self.op.force and
be_new[constants.BE_MAXMEM] > be_old[constants.BE_MAXMEM]):
None)
# Copy ispec to verify parameters with min/max values separately
+ if self.op.disk_template:
+ new_disk_template = self.op.disk_template
+ else:
+ new_disk_template = instance.disk_template
ispec_max = ispec.copy()
ispec_max[constants.ISPEC_MEM_SIZE] = \
self.be_new.get(constants.BE_MAXMEM, None)
- res_max = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_max)
+ res_max = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_max,
+ new_disk_template)
ispec_min = ispec.copy()
ispec_min[constants.ISPEC_MEM_SIZE] = \
self.be_new.get(constants.BE_MINMEM, None)
- res_min = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_min)
+ res_min = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_min,
+ new_disk_template)
if (res_max or res_min):
# FIXME: Improve error message by including information about whether
self.LogWarning("Failed to create volume %s (%s) on node '%s': %s",
disk.iv_name, disk, node, err)
+ if self.cluster.prealloc_wipe_disks:
+ # Wipe new disk
+ _WipeDisks(self, instance,
+ disks=[(idx, disk, 0)])
+
return (disk, [
("disk/%d" % idx, "add:size=%s,mode=%s" % (disk.size, disk.mode)),
])
result = []
instance = self.instance
+ # New primary node
+ if self.op.pnode:
+ instance.primary_node = self.op.pnode
+
# runtime memory
if self.op.runtime_mem:
rpcres = self.rpc.call_instance_balloon_memory(instance.primary_node,
violations = \
_ComputeNewInstanceViolations(gmi.CalculateGroupIPolicy(cluster,
self.group),
- new_ipolicy, instances)
+ new_ipolicy, instances, self.cfg)
if violations:
self.LogWarning("After the ipolicy change the following instances"