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):
@type vcpus: string
@param vcpus: the count of VCPUs the instance has
@type nics: list
- @param nics: list of tuples (ip, mac, mode, link, net, netinfo) representing
- the NICs the instance has
+ @param nics: list of tuples (name, uuid, ip, mac, mode, link, net, netinfo)
+ representing the NICs the instance has
@type disk_template: string
@param disk_template: the disk template of the instance
@type disks: list
- @param disks: the list of (size, mode) pairs
+ @param disks: list of tuples (name, uuid, size, mode)
@type bep: dict
@param bep: the backend parameters for the instance
@type hvp: dict
"INSTANCE_STATUS": status,
"INSTANCE_MINMEM": minmem,
"INSTANCE_MAXMEM": maxmem,
- # TODO(2.7) remove deprecated "memory" value
+ # TODO(2.9) remove deprecated "memory" value
"INSTANCE_MEMORY": maxmem,
"INSTANCE_VCPUS": vcpus,
"INSTANCE_DISK_TEMPLATE": disk_template,
}
if nics:
nic_count = len(nics)
- for idx, (ip, mac, mode, link, net, netinfo) in enumerate(nics):
+ for idx, (name, _, ip, mac, mode, link, net, netinfo) in enumerate(nics):
if ip is None:
ip = ""
+ env["INSTANCE_NIC%d_NAME" % idx] = name
env["INSTANCE_NIC%d_IP" % idx] = ip
env["INSTANCE_NIC%d_MAC" % idx] = mac
env["INSTANCE_NIC%d_MODE" % idx] = mode
if disks:
disk_count = len(disks)
- for idx, (size, mode) in enumerate(disks):
+ for idx, (name, size, mode) in enumerate(disks):
+ env["INSTANCE_DISK%d_NAME" % idx] = name
env["INSTANCE_DISK%d_SIZE" % idx] = size
env["INSTANCE_DISK%d_MODE" % idx] = mode
else:
if nic.network:
nobj = lu.cfg.GetNetwork(nic.network)
netinfo = objects.Network.ToDict(nobj)
- return (nic.ip, nic.mac, mode, link, nic.network, netinfo)
+ return (nic.name, nic.uuid, nic.ip, nic.mac, mode, link, nic.network, netinfo)
def _NICListToTuple(lu, nics):
"vcpus": bep[constants.BE_VCPUS],
"nics": _NICListToTuple(lu, instance.nics),
"disk_template": instance.disk_template,
- "disks": [(disk.size, disk.mode) for disk in instance.disks],
+ "disks": [(disk.name, disk.size, disk.mode)
+ for disk in instance.disks],
"bep": bep,
"hvp": hvp,
"hypervisor_name": instance.hypervisor,
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)
mn = self.cfg.GetMasterNode()
return ([mn], [mn])
- def CheckPrereq(self):
- """Check prerequisites.
-
- This checks whether the given params don't conflict and
- if the given volume group is valid.
+ def _CheckVgName(self, node_list, enabled_disk_templates,
+ new_enabled_disk_templates):
+ """Check the consistency of the vg name on all nodes and in case it gets
+ unset whether there are instances still using it.
"""
if self.op.vg_name is not None and not self.op.vg_name:
raise errors.OpPrereqError("Cannot disable lvm storage while lvm-based"
" instances exist", errors.ECODE_INVAL)
+ if (self.op.vg_name is not None and
+ utils.IsLvmEnabled(enabled_disk_templates)) or \
+ (self.cfg.GetVGName() is not None and
+ utils.LvmGetsEnabled(enabled_disk_templates,
+ new_enabled_disk_templates)):
+ self._CheckVgNameOnNodes(node_list)
+
+ def _CheckVgNameOnNodes(self, node_list):
+ """Check the status of the volume group on each node.
+
+ """
+ vglist = self.rpc.call_vg_list(node_list)
+ for node in node_list:
+ msg = vglist[node].fail_msg
+ if msg:
+ # ignoring down node
+ self.LogWarning("Error while gathering data on node %s"
+ " (ignoring node): %s", node, msg)
+ continue
+ vgstatus = utils.CheckVolumeGroupSize(vglist[node].payload,
+ self.op.vg_name,
+ constants.MIN_VG_SIZE)
+ if vgstatus:
+ raise errors.OpPrereqError("Error on node '%s': %s" %
+ (node, vgstatus), errors.ECODE_ENVIRON)
+
+ def _GetEnabledDiskTemplates(self, cluster):
+ """Determines the enabled disk templates and the subset of disk templates
+ that are newly enabled by this operation.
+
+ """
+ enabled_disk_templates = None
+ new_enabled_disk_templates = []
+ if self.op.enabled_disk_templates:
+ enabled_disk_templates = self.op.enabled_disk_templates
+ new_enabled_disk_templates = \
+ list(set(enabled_disk_templates)
+ - set(cluster.enabled_disk_templates))
+ else:
+ enabled_disk_templates = cluster.enabled_disk_templates
+ return (enabled_disk_templates, new_enabled_disk_templates)
+
+ def CheckPrereq(self):
+ """Check prerequisites.
+
+ This checks whether the given params don't conflict and
+ if the given volume group is valid.
+
+ """
if self.op.drbd_helper is not None and not self.op.drbd_helper:
if self.cfg.HasAnyDiskOfType(constants.LD_DRBD8):
raise errors.OpPrereqError("Cannot disable drbd helper while"
errors.ECODE_INVAL)
node_list = self.owned_locks(locking.LEVEL_NODE)
+ self.cluster = cluster = self.cfg.GetClusterInfo()
- # if vg_name not None, checks given volume group on all nodes
- if self.op.vg_name:
- vglist = self.rpc.call_vg_list(node_list)
- for node in node_list:
- msg = vglist[node].fail_msg
- if msg:
- # ignoring down node
- self.LogWarning("Error while gathering data on node %s"
- " (ignoring node): %s", node, msg)
- continue
- vgstatus = utils.CheckVolumeGroupSize(vglist[node].payload,
- self.op.vg_name,
- constants.MIN_VG_SIZE)
- if vgstatus:
- raise errors.OpPrereqError("Error on node '%s': %s" %
- (node, vgstatus), errors.ECODE_ENVIRON)
+ (enabled_disk_templates, new_enabled_disk_templates) = \
+ self._GetEnabledDiskTemplates(cluster)
+
+ self._CheckVgName(node_list, enabled_disk_templates,
+ new_enabled_disk_templates)
if self.op.drbd_helper:
# checks given drbd helper on all nodes
raise errors.OpPrereqError("Error on node '%s': drbd helper is %s" %
(node, node_helper), errors.ECODE_ENVIRON)
- self.cluster = cluster = self.cfg.GetClusterInfo()
# validate params changes
if self.op.beparams:
objects.UpgradeBeParams(self.op.beparams)
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._CheckDiskTemplateConsistency()
+
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 Exec(self, feedback_fn):
- """Change the parameters of the cluster.
+ def _CheckDiskTemplateConsistency(self):
+ """Check whether the disk templates that are going to be disabled
+ are still in use by some instances.
+
+ """
+ if self.op.enabled_disk_templates:
+ cluster = self.cfg.GetClusterInfo()
+ instances = self.cfg.GetAllInstancesInfo()
+
+ disk_templates_to_remove = set(cluster.enabled_disk_templates) \
+ - set(self.op.enabled_disk_templates)
+ for instance in instances.itervalues():
+ if instance.disk_template in disk_templates_to_remove:
+ raise errors.OpPrereqError("Cannot disable disk template '%s',"
+ " because instance '%s' is using it." %
+ (instance.disk_template, instance.name))
+
+
+ def _SetVgName(self, feedback_fn):
+ """Determines and sets the new volume group name.
"""
if self.op.vg_name is not None:
+ if self.op.vg_name and not \
+ utils.IsLvmEnabled(self.cluster.enabled_disk_templates):
+ feedback_fn("Note that you specified a volume group, but did not"
+ " enable any lvm disk template.")
new_volume = self.op.vg_name
if not new_volume:
+ if utils.IsLvmEnabled(self.cluster.enabled_disk_templates):
+ raise errors.OpPrereqError("Cannot unset volume group if lvm-based"
+ " disk templates are enabled.")
new_volume = None
if new_volume != self.cfg.GetVGName():
self.cfg.SetVGName(new_volume)
else:
feedback_fn("Cluster LVM configuration already in desired"
" state, not changing")
+ else:
+ if utils.IsLvmEnabled(self.cluster.enabled_disk_templates) and \
+ not self.cfg.GetVGName():
+ raise errors.OpPrereqError("Please specify a volume group when"
+ " enabling lvm-based disk-templates.")
+
+ def Exec(self, feedback_fn):
+ """Change the parameters of the cluster.
+
+ """
+ if self.op.enabled_disk_templates:
+ self.cluster.enabled_disk_templates = \
+ list(set(self.op.enabled_disk_templates))
+
+ self._SetVgName(feedback_fn)
+
if self.op.drbd_helper is not None:
+ if not constants.DT_DRBD8 in self.cluster.enabled_disk_templates:
+ feedback_fn("Note that you specified a drbd user helper, but did"
+ " enabled the drbd disk template.")
new_helper = self.op.drbd_helper
if not new_helper:
new_helper = None
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:
constants.IDISK_VG,
constants.IDISK_METAVG,
constants.IDISK_PROVIDER,
+ constants.IDISK_NAME,
]))
def _RunAllocator(self):
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] = []
assert self.instance is not None, \
"Cannot retrieve locked instance %s" % self.op.instance_name
+ if instance.disk_template not in constants.DTS_COPYABLE:
+ raise errors.OpPrereqError("Disk template %s not suitable for copying" %
+ instance.disk_template, errors.ECODE_STATE)
+
node = self.cfg.GetNodeInfo(self.op.target_node)
assert node is not None, \
"Cannot retrieve locked node %s" % self.op.target_node
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:
try:
_CreateDisks(self, instance, target_node=target_node)
except errors.OpExecError:
- self.LogWarning("Device creation failed, reverting...")
- try:
- _RemoveDisks(self, instance, target_node=target_node)
- finally:
- self.cfg.ReleaseDRBDMinors(instance.name)
- raise
+ self.LogWarning("Device creation failed")
+ self.cfg.ReleaseDRBDMinors(instance.name)
+ raise
cluster_name = self.cfg.GetClusterInfo().cluster_name
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)
dev_data = objects.Disk(dev_type=constants.LD_LV, size=size,
logical_id=(vgnames[0], names[0]),
params={})
+ dev_data.uuid = lu.cfg.GenerateUniqueID(lu.proc.GetECId())
dev_meta = objects.Disk(dev_type=constants.LD_LV,
size=constants.DRBD_META_SIZE,
logical_id=(vgnames[1], names[1]),
params={})
+ dev_meta.uuid = lu.cfg.GenerateUniqueID(lu.proc.GetECId())
drbd_dev = objects.Disk(dev_type=constants.LD_DRBD8, size=size,
logical_id=(primary, secondary, port,
p_minor, s_minor,
shared_secret),
children=[dev_data, dev_meta],
iv_name=iv_name, params={})
+ drbd_dev.uuid = lu.cfg.GenerateUniqueID(lu.proc.GetECId())
return drbd_dev
"disk/%d" % disk_index,
minors[idx * 2], minors[idx * 2 + 1])
disk_dev.mode = disk[constants.IDISK_MODE]
+ disk_dev.name = disk.get(constants.IDISK_NAME, None)
disks.append(disk_dev)
else:
if secondary_nodes:
size = disk[constants.IDISK_SIZE]
feedback_fn("* disk %s, size %s" %
(disk_index, utils.FormatUnit(size, "h")))
- disks.append(objects.Disk(dev_type=dev_type, size=size,
- logical_id=logical_id_fn(idx, disk_index, disk),
- iv_name="disk/%d" % disk_index,
- mode=disk[constants.IDISK_MODE],
- params=params))
+ disk_dev = objects.Disk(dev_type=dev_type, size=size,
+ logical_id=logical_id_fn(idx, disk_index, disk),
+ iv_name="disk/%d" % disk_index,
+ mode=disk[constants.IDISK_MODE],
+ params=params)
+ disk_dev.name = disk.get(constants.IDISK_NAME, None)
+ disk_dev.uuid = lu.cfg.GenerateUniqueID(lu.proc.GetECId())
+ disks.append(disk_dev)
return disks
@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
result.Raise("Failed to create directory '%s' on"
" node %s" % (file_storage_dir, pnode))
+ disks_created = []
# Note: this needs to be kept in sync with adding of disks in
# LUInstanceSetParams
for idx, device in enumerate(instance.disks):
#HARDCODE
for node in all_nodes:
f_create = node == pnode
- _CreateBlockDev(lu, node, instance, device, f_create, info, f_create)
+ try:
+ _CreateBlockDev(lu, node, instance, device, f_create, info, f_create)
+ disks_created.append((node, device))
+ except errors.OpExecError:
+ logging.warning("Creating disk %s for instance '%s' failed",
+ idx, instance.name)
+ for (node, disk) in disks_created:
+ lu.cfg.SetDiskID(disk, node)
+ result = lu.rpc.call_blockdev_remove(node, disk)
+ if result.fail_msg:
+ logging.warning("Failed to remove newly-created disk %s on node %s:"
+ " %s", device, node, result.fail_msg)
+ raise
def _RemoveDisks(lu, instance, target_node=None, ignore_failures=False):
This abstracts away some work from `AddInstance()` and
`RemoveInstance()`. Note that in case some of the devices couldn't
- be removed, the removal will continue with the other ones (compare
- with `_CreateDisks()`).
+ be removed, the removal will continue with the other ones.
@type lu: L{LogicalUnit}
@param lu: the logical unit on whose behalf we execute
check_params = cluster.SimpleFillNIC(nicparams)
objects.NIC.CheckParameterSyntax(check_params)
net_uuid = cfg.LookupNetwork(net)
- nics.append(objects.NIC(mac=mac, ip=nic_ip,
- network=net_uuid, nicparams=nicparams))
+ name = nic.get(constants.INIC_NAME, None)
+ if name is not None and name.lower() == constants.VALUE_NONE:
+ name = None
+ nic_obj = objects.NIC(mac=mac, ip=nic_ip, name=name,
+ network=net_uuid, nicparams=nicparams)
+ nic_obj.uuid = cfg.GenerateUniqueID(ec_id)
+ nics.append(nic_obj)
return nics
op.disk_template), errors.ECODE_INVAL)
data_vg = disk.get(constants.IDISK_VG, default_vg)
+ name = disk.get(constants.IDISK_NAME, None)
+ if name is not None and name.lower() == constants.VALUE_NONE:
+ name = None
new_disk = {
constants.IDISK_SIZE: size,
constants.IDISK_MODE: mode,
constants.IDISK_VG: data_vg,
+ constants.IDISK_NAME: name,
}
if constants.IDISK_METAVG in disk:
# check nics' parameter names
for nic in self.op.nics:
utils.ForceDictType(nic, constants.INIC_PARAMS_TYPES)
+ # check that NIC's parameters names are unique and valid
+ utils.ValidateDeviceNames("NIC", self.op.nics)
+
+ # check that disk's names are unique and valid
+ utils.ValidateDeviceNames("disk", self.op.disks)
+
+ cluster = self.cfg.GetClusterInfo()
+ if not self.op.disk_template in cluster.enabled_disk_templates:
+ raise errors.OpPrereqError("Cannot create an instance with disk template"
+ " '%s', because it is not enabled in the"
+ " cluster. Enabled disk templates are: %s." %
+ (self.op.disk_template,
+ ",".join(cluster.enabled_disk_templates)))
# check disks. parameter names and consistent adopt/no-adopt strategy
has_adopt = has_no_adopt = False
vcpus=self.be_full[constants.BE_VCPUS],
nics=_NICListToTuple(self, self.nics),
disk_template=self.op.disk_template,
- disks=[(d[constants.IDISK_SIZE], d[constants.IDISK_MODE])
- for d in self.disks],
+ disks=[(d[constants.IDISK_NAME], d[constants.IDISK_SIZE],
+ d[constants.IDISK_MODE]) for d in self.disks],
bep=self.be_full,
hvp=self.hv_full,
hypervisor_name=self.op.hypervisor,
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:
msg = ("Instance allocation to group %s (%s) violates policy: %s" %
(pnode.group, group_info.name, utils.CommaJoin(res)))
try:
_CreateDisks(self, iobj)
except errors.OpExecError:
- self.LogWarning("Device creation failed, reverting...")
- try:
- _RemoveDisks(self, iobj)
- finally:
- self.cfg.ReleaseDRBDMinors(instance)
- raise
+ self.LogWarning("Device creation failed")
+ self.cfg.ReleaseDRBDMinors(instance)
+ raise
feedback_fn("adding instance %s to cluster config" % instance)
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)
"children": dev_children,
"mode": dev.mode,
"size": dev.size,
+ "name": dev.name,
+ "uuid": dev.uuid,
}
def Exec(self, feedback_fn):
return [(op, idx, params, fn()) for (op, idx, params) in mods]
+def GetItemFromContainer(identifier, kind, container):
+ """Return the item refered by the identifier.
+
+ @type identifier: string
+ @param identifier: Item index or name or UUID
+ @type kind: string
+ @param kind: One-word item description
+ @type container: list
+ @param container: Container to get the item from
+
+ """
+ # Index
+ try:
+ idx = int(identifier)
+ if idx == -1:
+ # Append
+ absidx = len(container) - 1
+ elif idx < 0:
+ raise IndexError("Not accepting negative indices other than -1")
+ elif idx > len(container):
+ raise IndexError("Got %s index %s, but there are only %s" %
+ (kind, idx, len(container)))
+ else:
+ absidx = idx
+ return (absidx, container[idx])
+ except ValueError:
+ pass
+
+ for idx, item in enumerate(container):
+ if item.uuid == identifier or item.name == identifier:
+ return (idx, item)
+
+ raise errors.OpPrereqError("Cannot find %s with identifier %s" %
+ (kind, identifier), errors.ECODE_NOENT)
+
+
#: Type description for changes as returned by L{ApplyContainerMods}'s
#: callbacks
_TApplyContModsCbChanges = \
item and private data object as added by L{PrepareContainerMods}
"""
- for (op, idx, params, private) in mods:
- if idx == -1:
- # Append
- absidx = len(container) - 1
- elif idx < 0:
- raise IndexError("Not accepting negative indices other than -1")
- elif idx > len(container):
- raise IndexError("Got %s index %s, but there are only %s" %
- (kind, idx, len(container)))
- else:
- absidx = idx
-
+ for (op, identifier, params, private) in mods:
changes = None
if op == constants.DDM_ADD:
# Calculate where item will be added
+ # When adding an item, identifier can only be an index
+ try:
+ idx = int(identifier)
+ except ValueError:
+ raise errors.OpPrereqError("Only possitive integer or -1 is accepted as"
+ " identifier for %s" % constants.DDM_ADD,
+ errors.ECODE_INVAL)
if idx == -1:
addidx = len(container)
else:
+ if idx < 0:
+ raise IndexError("Not accepting negative indices other than -1")
+ elif idx > len(container):
+ raise IndexError("Got %s index %s, but there are only %s" %
+ (kind, idx, len(container)))
addidx = idx
if create_fn is None:
container.insert(idx, item)
else:
# Retrieve existing item
- try:
- item = container[absidx]
- except IndexError:
- raise IndexError("Invalid %s index %s" % (kind, idx))
+ (absidx, item) = GetItemFromContainer(identifier, kind, container)
if op == constants.DDM_REMOVE:
assert not params
errors.ECODE_INVAL)
params[constants.IDISK_SIZE] = size
+ name = params.get(constants.IDISK_NAME, None)
+ if name is not None and name.lower() == constants.VALUE_NONE:
+ params[constants.IDISK_NAME] = None
elif op == constants.DDM_MODIFY:
if constants.IDISK_SIZE in params:
raise errors.OpPrereqError("Disk size change not possible, use"
" grow-disk", errors.ECODE_INVAL)
- if constants.IDISK_MODE not in params:
- raise errors.OpPrereqError("Disk 'mode' is the only kind of"
- " modification supported, but missing",
- errors.ECODE_NOENT)
- if len(params) > 1:
+ if len(params) > 2:
raise errors.OpPrereqError("Disk modification doesn't support"
" additional arbitrary parameters",
errors.ECODE_INVAL)
+ name = params.get(constants.IDISK_NAME, None)
+ if name is not None and name.lower() == constants.VALUE_NONE:
+ params[constants.IDISK_NAME] = None
@staticmethod
def _VerifyNicModification(op, params):
"""
if op in (constants.DDM_ADD, constants.DDM_MODIFY):
ip = params.get(constants.INIC_IP, None)
+ name = params.get(constants.INIC_NAME, None)
req_net = params.get(constants.INIC_NETWORK, None)
link = params.get(constants.NIC_LINK, None)
mode = params.get(constants.NIC_MODE, None)
+ if name is not None and name.lower() == constants.VALUE_NONE:
+ params[constants.INIC_NAME] = None
if req_net is not None:
if req_net.lower() == constants.VALUE_NONE:
params[constants.INIC_NETWORK] = None
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]):
" (%d), cannot add more" % constants.MAX_NICS,
errors.ECODE_STATE)
+ def _PrepareDiskMod(_, disk, params, __):
+ disk.name = params.get(constants.IDISK_NAME, None)
+
# Verify disk changes (operating on a copy)
- disks = instance.disks[:]
- ApplyContainerMods("disk", disks, None, self.diskmod, None, None, None)
+ disks = copy.deepcopy(instance.disks)
+ ApplyContainerMods("disk", disks, None, self.diskmod, None, _PrepareDiskMod,
+ None)
+ utils.ValidateDeviceNames("disk", disks)
if len(disks) > constants.MAX_DISKS:
raise errors.OpPrereqError("Instance has too many disks (%d), cannot add"
" more" % constants.MAX_DISKS,
nics = [nic.Copy() for nic in instance.nics]
ApplyContainerMods("NIC", nics, self._nic_chgdesc, self.nicmod,
self._CreateNewNic, self._ApplyNicMods, None)
+ # Verify that NIC names are unique and valid
+ utils.ValidateDeviceNames("NIC", nics)
self._new_nics = nics
ispec[constants.ISPEC_NIC_COUNT] = len(self._new_nics)
else:
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
# create a fake disk info for _GenerateDiskTemplate
disk_info = [{constants.IDISK_SIZE: d.size, constants.IDISK_MODE: d.mode,
- constants.IDISK_VG: d.logical_id[0]}
+ constants.IDISK_VG: d.logical_id[0],
+ constants.IDISK_NAME: d.name}
for d in instance.disks]
new_disks = _GenerateDiskTemplate(self, self.op.disk_template,
instance.name, pnode, [snode],
old_disks = _AnnotateDiskParams(instance, instance.disks, self.cfg)
new_disks = [d.children[0] for d in instance.disks]
- # copy over size and mode
+ # copy over size, mode and name
for parent, child in zip(old_disks, new_disks):
child.size = parent.size
child.mode = parent.mode
+ child.name = parent.name
# this is a DRBD disk, return its port to the pool
# NOTE: this must be done right before the call to cfg.Update!
# update instance structure
instance.disks = new_disks
instance.disk_template = constants.DT_PLAIN
+ _UpdateIvNames(0, instance.disks)
self.cfg.Update(instance, feedback_fn)
# Release locks in case removing disks takes a while
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)),
])
"""Modifies a disk.
"""
- disk.mode = params[constants.IDISK_MODE]
+ changes = []
+ mode = params.get(constants.IDISK_MODE, None)
+ if mode:
+ disk.mode = mode
+ changes.append(("disk.mode/%d" % idx, disk.mode))
- return [
- ("disk.mode/%d" % idx, disk.mode),
- ]
+ name = params.get(constants.IDISK_NAME, None)
+ disk.name = name
+ changes.append(("disk.name/%d" % idx, disk.name))
+
+ return changes
def _RemoveDisk(self, idx, root, _):
"""Removes a disk.
mac = params[constants.INIC_MAC]
ip = params.get(constants.INIC_IP, None)
net = params.get(constants.INIC_NETWORK, None)
+ name = params.get(constants.INIC_NAME, None)
net_uuid = self.cfg.LookupNetwork(net)
#TODO: not private.filled?? can a nic have no nicparams??
nicparams = private.filled
- nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, nicparams=nicparams)
+ nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, name=name,
+ nicparams=nicparams)
+ nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
return (nobj, [
("nic.%d" % idx,
"""
changes = []
- for key in [constants.INIC_MAC, constants.INIC_IP]:
+ for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NAME]:
if key in params:
changes.append(("nic.%s/%d" % (key, idx), params[key]))
setattr(nic, key, params[key])
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"