return cfg.GetNdParams(node)[constants.ND_OOB_PROGRAM]
+def _IsExclusiveStorageEnabledNode(cfg, node):
+ """Whether exclusive_storage is in effect for the given node.
+
+ @type cfg: L{config.ConfigWriter}
+ @param cfg: The cluster configuration
+ @type node: L{objects.Node}
+ @param node: The node
+ @rtype: bool
+ @return: The effective value of exclusive_storage
+
+ """
+ return cfg.GetNdParams(node)[constants.ND_EXCLUSIVE_STORAGE]
+
+
+def _IsExclusiveStorageEnabledNodeName(cfg, nodename):
+ """Whether exclusive_storage is in effect for the given node.
+
+ @type cfg: L{config.ConfigWriter}
+ @param cfg: The cluster configuration
+ @type nodename: string
+ @param nodename: The node
+ @rtype: bool
+ @return: The effective value of exclusive_storage
+ @raise errors.OpPrereqError: if no node exists with the given name
+
+ """
+ ni = cfg.GetNodeInfo(nodename)
+ if ni is None:
+ raise errors.OpPrereqError("Invalid node name %s" % nodename,
+ errors.ECODE_NOENT)
+ return _IsExclusiveStorageEnabledNode(cfg, ni)
+
+
def _CopyLockList(names):
"""Makes a copy of a list of lock names.
constants.MIN_VG_SIZE)
_ErrorIf(vgstatus, constants.CV_ENODELVM, node, vgstatus)
- # check pv names
- pvlist = nresult.get(constants.NV_PVLIST, None)
- test = pvlist is None
+ # check pv names (and possibly sizes)
+ pvlist_dict = nresult.get(constants.NV_PVLIST, None)
+ test = pvlist_dict is None
_ErrorIf(test, constants.CV_ENODELVM, node, "Can't get PV list from node")
if not test:
+ pvlist = map(objects.LvmPvInfo.FromDict, pvlist_dict)
# check that ':' is not present in PV names, since it's a
# special character for lvcreate (denotes the range of PEs to
# use on the PV)
- for _, pvname, owner_vg in pvlist:
- test = ":" in pvname
+ for pv in pvlist:
+ test = ":" in pv.name
_ErrorIf(test, constants.CV_ENODELVM, node,
"Invalid character ':' in PV '%s' of VG '%s'",
- pvname, owner_vg)
+ pv.name, pv.vg_name)
def _VerifyNodeBridges(self, ninfo, nresult, bridges):
"""Check the node bridges.
# TODO: Implement support changing VG while recreating
constants.IDISK_VG,
constants.IDISK_METAVG,
+ constants.IDISK_PROVIDER,
]))
def _RunAllocator(self):
self._GoReconnect(False)
self._WaitUntilSync()
- # If the instance's disk template is `rbd' and there was a successful
- # migration, unmap the device from the source node.
- if self.instance.disk_template == constants.DT_RBD:
+ # If the instance's disk template is `rbd' or `ext' and there was a
+ # successful migration, unmap the device from the source node.
+ if self.instance.disk_template in (constants.DT_RBD, constants.DT_EXT):
disks = _ExpandCheckDisks(instance, instance.disks)
self.feedback_fn("* unmapping instance's disks from %s" % source_node)
for disk in disks:
"""
(disk,) = _AnnotateDiskParams(instance, [device], lu.cfg)
+ excl_stor = _IsExclusiveStorageEnabledNodeName(lu.cfg, node)
return _CreateBlockDevInner(lu, node, instance, disk, force_create, info,
- force_open)
+ force_open, excl_stor)
def _CreateBlockDevInner(lu, node, instance, device, force_create,
- info, force_open):
+ info, force_open, excl_stor):
"""Create a tree of block devices on a given node.
If this device type has to be created on secondaries, create it and
L{backend.BlockdevCreate} function where it specifies
whether we run on primary or not, and it affects both
the child assembly and the device own Open() execution
+ @type excl_stor: boolean
+ @param excl_stor: Whether exclusive_storage is active for the node
"""
if device.CreateOnSecondary():
if device.children:
for child in device.children:
_CreateBlockDevInner(lu, node, instance, child, force_create,
- info, force_open)
+ info, force_open, excl_stor)
if not force_create:
return
- _CreateSingleBlockDev(lu, node, instance, device, info, force_open)
+ _CreateSingleBlockDev(lu, node, instance, device, info, force_open,
+ excl_stor)
-def _CreateSingleBlockDev(lu, node, instance, device, info, force_open):
+def _CreateSingleBlockDev(lu, node, instance, device, info, force_open,
+ excl_stor):
"""Create a single block device on a given node.
This will not recurse over children of the device, so they must be
L{backend.BlockdevCreate} function where it specifies
whether we run on primary or not, and it affects both
the child assembly and the device own Open() execution
+ @type excl_stor: boolean
+ @param excl_stor: Whether exclusive_storage is active for the node
"""
lu.cfg.SetDiskID(device, node)
result = lu.rpc.call_blockdev_create(node, device, device.size,
- instance.name, force_open, info)
+ instance.name, force_open, info,
+ excl_stor)
result.Raise("Can't create block device %s on"
" node %s for instance %s" % (device, node, instance.name))
if device.physical_id is None:
_DISK_TEMPLATE_NAME_PREFIX = {
constants.DT_PLAIN: "",
constants.DT_RBD: ".rbd",
+ constants.DT_EXT: ".ext",
}
constants.DT_SHARED_FILE: constants.LD_FILE,
constants.DT_BLOCK: constants.LD_BLOCKDEV,
constants.DT_RBD: constants.LD_RBD,
+ constants.DT_EXT: constants.LD_EXT,
}
disk[constants.IDISK_ADOPT])
elif template_name == constants.DT_RBD:
logical_id_fn = lambda idx, _, disk: ("rbd", names[idx])
+ elif template_name == constants.DT_EXT:
+ def logical_id_fn(idx, _, disk):
+ provider = disk.get(constants.IDISK_PROVIDER, None)
+ if provider is None:
+ raise errors.ProgrammerError("Disk template is %s, but '%s' is"
+ " not found", constants.DT_EXT,
+ constants.IDISK_PROVIDER)
+ return (provider, names[idx])
else:
raise errors.ProgrammerError("Unknown disk template '%s'" % template_name)
dev_type = _DISK_TEMPLATE_DEVICE_TYPE[template_name]
for idx, disk in enumerate(disk_info):
+ params = {}
+ # Only for the Ext template add disk_info to params
+ if template_name == constants.DT_EXT:
+ params[constants.IDISK_PROVIDER] = disk[constants.IDISK_PROVIDER]
+ for key in disk:
+ if key not in constants.IDISK_PARAMS:
+ params[key] = disk[key]
disk_index = idx + base_index
size = disk[constants.IDISK_SIZE]
feedback_fn("* disk %s, size %s" %
logical_id=logical_id_fn(idx, disk_index, disk),
iv_name="disk/%d" % disk_index,
mode=disk[constants.IDISK_MODE],
- params={}))
+ params=params))
return disks
@param op: The instance opcode
@param default_vg: The default_vg to assume
- @return: The computer disks
+ @return: The computed disks
"""
disks = []
raise errors.OpPrereqError("Invalid disk size '%s'" % size,
errors.ECODE_INVAL)
+ ext_provider = disk.get(constants.IDISK_PROVIDER, None)
+ if ext_provider and op.disk_template != constants.DT_EXT:
+ raise errors.OpPrereqError("The '%s' option is only valid for the %s"
+ " disk template, not %s" %
+ (constants.IDISK_PROVIDER, constants.DT_EXT,
+ op.disk_template), errors.ECODE_INVAL)
+
data_vg = disk.get(constants.IDISK_VG, default_vg)
new_disk = {
constants.IDISK_SIZE: size,
constants.IDISK_MODE: mode,
constants.IDISK_VG: data_vg,
}
+
if constants.IDISK_METAVG in disk:
new_disk[constants.IDISK_METAVG] = disk[constants.IDISK_METAVG]
if constants.IDISK_ADOPT in disk:
new_disk[constants.IDISK_ADOPT] = disk[constants.IDISK_ADOPT]
+
+ # For extstorage, demand the `provider' option and add any
+ # additional parameters (ext-params) to the dict
+ if op.disk_template == constants.DT_EXT:
+ if ext_provider:
+ new_disk[constants.IDISK_PROVIDER] = ext_provider
+ for key in disk:
+ if key not in constants.IDISK_PARAMS:
+ new_disk[key] = disk[key]
+ else:
+ raise errors.OpPrereqError("Missing provider for template '%s'" %
+ constants.DT_EXT, errors.ECODE_INVAL)
+
disks.append(new_disk)
return disks
return cluster.SimpleFillBE(op.beparams)
+def _CheckOpportunisticLocking(op):
+ """Generate error if opportunistic locking is not possible.
+
+ """
+ if op.opportunistic_locking and not op.iallocator:
+ raise errors.OpPrereqError("Opportunistic locking is only available in"
+ " combination with an instance allocator",
+ errors.ECODE_INVAL)
+
+
class LUInstanceCreate(LogicalUnit):
"""Create an instance.
# check disks. parameter names and consistent adopt/no-adopt strategy
has_adopt = has_no_adopt = False
for disk in self.op.disks:
- utils.ForceDictType(disk, constants.IDISK_PARAMS_TYPES)
+ if self.op.disk_template != constants.DT_EXT:
+ utils.ForceDictType(disk, constants.IDISK_PARAMS_TYPES)
if constants.IDISK_ADOPT in disk:
has_adopt = True
else:
" template")
self.op.snode = None
+ _CheckOpportunisticLocking(self.op)
+
self._cds = _GetClusterDomainSecret()
if self.op.mode == constants.INSTANCE_IMPORT:
" or does not belong to network %s" %
(nic.ip, net),
errors.ECODE_NOTUNIQUE)
- else:
- # net is None, ip None or given
- if self.op.conflicts_check:
- _CheckForConflictingIp(self, nic.ip, self.pnode.name)
+
+ # net is None, ip None or given
+ elif self.op.conflicts_check:
+ _CheckForConflictingIp(self, nic.ip, self.pnode.name)
# mirror node verification
if self.op.disk_template in constants.DTS_INT_MIRROR:
# Any function that checks prerequisites can be placed here.
# Check if there is enough space on the RADOS cluster.
_CheckRADOSFreeSpace()
+ elif self.op.disk_template == constants.DT_EXT:
+ # FIXME: Function that checks prereqs if needed
+ pass
else:
# Check lv size requirements, if not adopting
req_sizes = _ComputeDiskSizePerVG(self.op.disk_template, self.disks)
_CheckNicsBridgesExist(self, self.nics, self.pnode.name)
+ #TODO: _CheckExtParams (remotely)
+ # Check parameters for extstorage
+
# memory check on primary node
#TODO(dynmem): use MINMEM for checking
if self.op.start:
" or set a cluster-wide default iallocator",
errors.ECODE_INVAL)
+ _CheckOpportunisticLocking(self.op)
+
dups = utils.FindDuplicates([op.instance_name for op in self.op.instances])
if dups:
raise errors.OpPrereqError("There are duplicate instance names: %s" %
new_lvs = [lv_data, lv_meta]
old_lvs = [child.Copy() for child in dev.children]
iv_names[dev.iv_name] = (dev, old_lvs, new_lvs)
+ excl_stor = _IsExclusiveStorageEnabledNodeName(self.lu.cfg, node_name)
# we pass force_create=True to force the LVM creation
for new_lv in new_lvs:
_CreateBlockDevInner(self.lu, node_name, self.instance, new_lv, True,
- _GetInstanceInfoText(self.instance), False)
+ _GetInstanceInfoText(self.instance), False,
+ excl_stor)
return iv_names
# Step: create new storage
self.lu.LogStep(3, steps_total, "Allocate new storage")
disks = _AnnotateDiskParams(self.instance, self.instance.disks, self.cfg)
+ excl_stor = _IsExclusiveStorageEnabledNodeName(self.lu.cfg, self.new_node)
for idx, dev in enumerate(disks):
self.lu.LogInfo("Adding new local storage on %s for disk/%d" %
(self.new_node, idx))
# we pass force_create=True to force LVM creation
for new_lv in dev.children:
_CreateBlockDevInner(self.lu, self.new_node, self.instance, new_lv,
- True, _GetInstanceInfoText(self.instance), False)
+ True, _GetInstanceInfoText(self.instance), False,
+ excl_stor)
# Step 4: dbrd minors and drbd setups changes
# after this, we must manually remove the drbd minors on both the
try:
_CreateSingleBlockDev(self.lu, self.new_node, self.instance,
anno_new_drbd,
- _GetInstanceInfoText(self.instance), False)
+ _GetInstanceInfoText(self.instance), False,
+ excl_stor)
except errors.GenericError:
self.cfg.ReleaseDRBDMinors(self.instance.name)
raise
if instance.disk_template not in (constants.DT_FILE,
constants.DT_SHARED_FILE,
- constants.DT_RBD):
+ constants.DT_RBD,
+ constants.DT_EXT):
# TODO: check the free disk space for file, when that feature will be
# supported
_CheckNodesFreeDiskPerVG(self, nodenames,
for (op, _, params) in mods:
assert ht.TDict(params)
- utils.ForceDictType(params, key_types)
+ # If 'key_types' is an empty dict, we assume we have an
+ # 'ext' template and thus do not ForceDictType
+ if key_types:
+ utils.ForceDictType(params, key_types)
if op == constants.DDM_REMOVE:
if params:
params[constants.IDISK_SIZE] = size
- elif op == constants.DDM_MODIFY and constants.IDISK_SIZE in params:
- raise errors.OpPrereqError("Disk size change not possible, use"
- " grow-disk", errors.ECODE_INVAL)
+ 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:
+ raise errors.OpPrereqError("Disk modification doesn't support"
+ " additional arbitrary parameters",
+ errors.ECODE_INVAL)
@staticmethod
def _VerifyNicModification(op, params):
self.op.nics = self._UpgradeDiskNicMods(
"NIC", self.op.nics, opcodes.OpInstanceSetParams.TestNicModifications)
- # Check disk modifications
- self._CheckMods("disk", self.op.disks, constants.IDISK_PARAMS_TYPES,
- self._VerifyDiskModification)
-
if self.op.disks and self.op.disk_template is not None:
raise errors.OpPrereqError("Disk template conversion and other disk"
" changes not supported at the same time",
elif new_ip.lower() == constants.NIC_IP_POOL:
raise errors.OpPrereqError("ip=pool, but no network found",
errors.ECODE_INVAL)
- else:
- # new net is None
- if self.op.conflicts_check:
- _CheckForConflictingIp(self, new_ip, pnode)
+
+ # new net is None
+ elif self.op.conflicts_check:
+ _CheckForConflictingIp(self, new_ip, pnode)
if old_ip:
if old_net:
private.params = new_params
private.filled = new_filled_params
- def CheckPrereq(self):
+ def CheckPrereq(self): # pylint: disable=R0914
"""Check prerequisites.
This only checks the instance list against the existing names.
# dictionary with instance information after the modification
ispec = {}
+ # Check disk modifications. This is done here and not in CheckArguments
+ # (as with NICs), because we need to know the instance's disk template
+ if instance.disk_template == constants.DT_EXT:
+ self._CheckMods("disk", self.op.disks, {},
+ self._VerifyDiskModification)
+ else:
+ self._CheckMods("disk", self.op.disks, constants.IDISK_PARAMS_TYPES,
+ self._VerifyDiskModification)
+
# Prepare disk/NIC modifications
self.diskmod = PrepareContainerMods(self.op.disks, None)
self.nicmod = PrepareContainerMods(self.op.nics, _InstNicModPrivate)
+ # Check the validity of the `provider' parameter
+ if instance.disk_template in constants.DT_EXT:
+ for mod in self.diskmod:
+ ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
+ if mod[0] == constants.DDM_ADD:
+ if ext_provider is None:
+ raise errors.OpPrereqError("Instance template is '%s' and parameter"
+ " '%s' missing, during disk add" %
+ (constants.DT_EXT,
+ constants.IDISK_PROVIDER),
+ errors.ECODE_NOENT)
+ elif mod[0] == constants.DDM_MODIFY:
+ if ext_provider:
+ raise errors.OpPrereqError("Parameter '%s' is invalid during disk"
+ " modification" %
+ constants.IDISK_PROVIDER,
+ errors.ECODE_INVAL)
+ else:
+ for mod in self.diskmod:
+ ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
+ if ext_provider is not None:
+ raise errors.OpPrereqError("Parameter '%s' is only valid for"
+ " instances of type '%s'" %
+ (constants.IDISK_PROVIDER,
+ constants.DT_EXT),
+ errors.ECODE_INVAL)
+
# OS change
if self.op.os_name and not self.op.force:
_CheckNodeHasOS(self, instance.primary_node, self.op.os_name,
self.diskparams)
anno_disks = rpc.AnnotateDiskParams(constants.DT_DRBD8, new_disks,
self.diskparams)
+ p_excl_stor = _IsExclusiveStorageEnabledNodeName(self.cfg, pnode)
+ s_excl_stor = _IsExclusiveStorageEnabledNodeName(self.cfg, snode)
info = _GetInstanceInfoText(instance)
feedback_fn("Creating additional volumes...")
# first, create the missing data and meta devices
for disk in anno_disks:
# unfortunately this is... not too nice
_CreateSingleBlockDev(self, pnode, instance, disk.children[1],
- info, True)
+ info, True, p_excl_stor)
for child in disk.children:
- _CreateSingleBlockDev(self, snode, instance, child, info, True)
+ _CreateSingleBlockDev(self, snode, instance, child, info, True,
+ s_excl_stor)
# at this stage, all new LVs have been created, we can rename the
# old ones
feedback_fn("Renaming original volumes...")
feedback_fn("Initializing DRBD devices...")
# all child devices are in place, we can now create the DRBD devices
for disk in anno_disks:
- for node in [pnode, snode]:
+ for (node, excl_stor) in [(pnode, p_excl_stor), (snode, s_excl_stor)]:
f_create = node == pnode
- _CreateSingleBlockDev(self, node, instance, disk, info, f_create)
+ _CreateSingleBlockDev(self, node, instance, disk, info, f_create,
+ excl_stor)
# at this point, the instance has been modified
instance.disk_template = constants.DT_DRBD8
uuid = self.cfg.LookupNetwork(self.op.network_name)
if uuid:
- raise errors.OpPrereqError("Network '%s' already defined" %
- self.op.network, errors.ECODE_EXISTS)
+ raise errors.OpPrereqError(("Network with name '%s' already exists" %
+ self.op.network_name), errors.ECODE_EXISTS)
# Check tag validity
for tag in self.op.tags:
else:
self.gateway = self.op.gateway
if self.pool.IsReserved(self.gateway):
- raise errors.OpPrereqError("%s is already reserved" %
- self.gateway, errors.ECODE_STATE)
+ raise errors.OpPrereqError("Gateway IP address '%s' is already"
+ " reserved" % self.gateway,
+ errors.ECODE_STATE)
if self.op.network_type:
if self.op.network_type == constants.VALUE_NONE:
do_instances = query.NETQ_INST in self.requested_data
do_groups = do_instances or (query.NETQ_GROUP in self.requested_data)
- network_to_groups = None
network_to_instances = None
# For NETQ_GROUP, we need to map network->[groups]
group_instances = [instance for instance in all_instances.values()
if instance.primary_node in group_nodes]
- for net_uuid in group.networks.keys():
- if net_uuid in network_to_groups:
- netparams = group.networks[net_uuid]
- mode = netparams[constants.NIC_MODE]
- link = netparams[constants.NIC_LINK]
- info = group.name + "(" + mode + ", " + link + ")"
+ for net_uuid in self.wanted:
+ netparams = group.networks.get(net_uuid, None)
+ if netparams:
+ info = (group.name, netparams[constants.NIC_MODE],
+ netparams[constants.NIC_LINK])
+
network_to_groups[net_uuid].append(info)
- if do_instances:
- for instance in group_instances:
- for nic in instance.nics:
- if nic.network == self._all_networks[net_uuid].name:
- network_to_instances[net_uuid].append(instance.name)
- break
+ if do_instances:
+ for instance in group_instances:
+ for nic in instance.nics:
+ if nic.network == self._all_networks[net_uuid].name:
+ network_to_instances[net_uuid].append(instance.name)
+ break
+ else:
+ network_to_groups = None
if query.NETQ_STATS in self.requested_data:
stats = \
self.network_uuid = self.cfg.LookupNetwork(self.network_name)
if self.network_uuid is None:
- raise errors.OpPrereqError("Network %s does not exist" %
+ raise errors.OpPrereqError("Network '%s' does not exist" %
self.network_name, errors.ECODE_NOENT)
self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
if self.group_uuid is None:
- raise errors.OpPrereqError("Group %s does not exist" %
+ raise errors.OpPrereqError("Group '%s' does not exist" %
self.group_name, errors.ECODE_NOENT)
self.needed_locks = {
self.network_uuid = self.cfg.LookupNetwork(self.network_name)
if self.network_uuid is None:
- raise errors.OpPrereqError("Network %s does not exist" %
+ raise errors.OpPrereqError("Network '%s' does not exist" %
self.network_name, errors.ECODE_NOENT)
self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
if self.group_uuid is None:
- raise errors.OpPrereqError("Group %s does not exist" %
+ raise errors.OpPrereqError("Group '%s' does not exist" %
self.group_name, errors.ECODE_NOENT)
self.needed_locks = {
def _CheckForConflictingIp(lu, ip, node):
- """In case of conflicting ip raise error.
+ """In case of conflicting IP address raise error.
@type ip: string
- @param ip: ip address
+ @param ip: IP address
@type node: string
@param node: node name
"""
(conf_net, _) = lu.cfg.CheckIPInNodeGroup(ip, node)
if conf_net is not None:
- raise errors.OpPrereqError("Conflicting IP found:"
- " %s <> %s." % (ip, conf_net),
+ raise errors.OpPrereqError(("Conflicting IP address found: '%s' != '%s'" %
+ (ip, conf_net)),
errors.ECODE_STATE)
return (None, None)