import ganeti.masterd.instance # pylint: disable-msg=W0611
-def _SupportsOob(cfg, node):
- """Tells if node supports OOB.
-
- @type cfg: L{config.ConfigWriter}
- @param cfg: The cluster configuration
- @type node: L{objects.Node}
- @param node: The node
- @return: The OOB script if supported or an empty string otherwise
-
- """
- return cfg.GetNdParams(node)[constants.ND_OOB_PROGRAM]
-
-
class ResultWithJobs:
"""Data container for LU results with jobs.
sort_by_name=self.sort_by_name)
+def _ShareAll():
+ """Returns a dict declaring all lock levels shared.
+
+ """
+ return dict.fromkeys(locking.LEVELS, 1)
+
+
+def _SupportsOob(cfg, node):
+ """Tells if node supports OOB.
+
+ @type cfg: L{config.ConfigWriter}
+ @param cfg: The cluster configuration
+ @type node: L{objects.Node}
+ @param node: The node
+ @return: The OOB script if supported or an empty string otherwise
+
+ """
+ return cfg.GetNdParams(node)[constants.ND_OOB_PROGRAM]
+
+
def _GetWantedNodes(lu, nodes):
"""Returns list of checked and expanded node names.
assert not lu.glm.is_owned(level), "No locks should be owned"
+def _MapInstanceDisksToNodes(instances):
+ """Creates a map from (node, volume) to instance name.
+
+ @type instances: list of L{objects.Instance}
+ @rtype: dict; tuple of (node name, volume name) as key, instance name as value
+
+ """
+ return dict(((node, vol), inst.name)
+ for inst in instances
+ for (node, vols) in inst.MapLVsByNode().items()
+ for vol in vols)
+
+
def _RunPostHook(lu, node_name):
"""Runs the post-hook for an opcode on a single node.
bep = cluster.FillBE(instance)
hvp = cluster.FillHV(instance)
args = {
- 'name': instance.name,
- 'primary_node': instance.primary_node,
- 'secondary_nodes': instance.secondary_nodes,
- 'os_type': instance.os,
- 'status': instance.admin_up,
- 'memory': bep[constants.BE_MEMORY],
- '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],
- 'bep': bep,
- 'hvp': hvp,
- 'hypervisor_name': instance.hypervisor,
- 'tags': instance.tags,
+ "name": instance.name,
+ "primary_node": instance.primary_node,
+ "secondary_nodes": instance.secondary_nodes,
+ "os_type": instance.os,
+ "status": instance.admin_up,
+ "memory": bep[constants.BE_MEMORY],
+ "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],
+ "bep": bep,
+ "hvp": hvp,
+ "hypervisor_name": instance.hypervisor,
+ "tags": instance.tags,
}
if override:
args.update(override)
@param name: OS name passed by the user, to check for validity
"""
+ variant = objects.OS.GetVariant(name)
if not os_obj.supported_variants:
+ if variant:
+ raise errors.OpPrereqError("OS '%s' doesn't support variants ('%s'"
+ " passed)" % (os_obj.name, variant),
+ errors.ECODE_INVAL)
return
- variant = objects.OS.GetVariant(name)
if not variant:
raise errors.OpPrereqError("OS name must include a variant",
errors.ECODE_INVAL)
locking.LEVEL_NODE: [],
}
- self.share_locks = dict.fromkeys(locking.LEVELS, 1)
+ self.share_locks = _ShareAll()
def DeclareLocks(self, level):
if level == locking.LEVEL_NODE:
@param all_nvinfo: RPC results
"""
- node_names = frozenset(node.name for node in nodeinfo)
+ node_names = frozenset(node.name for node in nodeinfo if not node.offline)
assert master_node in node_names
assert (len(files_all | files_all_opt | files_mc | files_vm) ==
fileinfo = dict((filename, {}) for filename in file2nodefn.keys())
for node in nodeinfo:
+ if node.offline:
+ continue
+
nresult = all_nvinfo[node.name]
if nresult.fail_msg or not nresult.payload:
self._ErrorIf(test, self.ENODEHOOKS, node_name,
"Script %s failed, output:", script)
if test:
- output = self._HOOKS_INDENT_RE.sub(' ', output)
+ output = self._HOOKS_INDENT_RE.sub(" ", output)
feedback_fn("%s" % output)
lu_result = 0
REQ_BGL = False
def ExpandNames(self):
+ self.share_locks = _ShareAll()
self.needed_locks = {
- locking.LEVEL_NODE: locking.ALL_SET,
- locking.LEVEL_INSTANCE: locking.ALL_SET,
- }
- self.share_locks = dict.fromkeys(locking.LEVELS, 1)
+ locking.LEVEL_NODEGROUP: locking.ALL_SET,
+ }
+
+ def Exec(self, feedback_fn):
+ group_names = self.glm.list_owned(locking.LEVEL_NODEGROUP)
+
+ # Submit one instance of L{opcodes.OpGroupVerifyDisks} per node group
+ return ResultWithJobs([[opcodes.OpGroupVerifyDisks(group_name=group)]
+ for group in group_names])
+
+
+class LUGroupVerifyDisks(NoHooksLU):
+ """Verifies the status of all disks in a node group.
+
+ """
+ REQ_BGL = False
+
+ def ExpandNames(self):
+ # Raises errors.OpPrereqError on its own if group can't be found
+ self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
+
+ self.share_locks = _ShareAll()
+ self.needed_locks = {
+ locking.LEVEL_INSTANCE: [],
+ locking.LEVEL_NODEGROUP: [],
+ locking.LEVEL_NODE: [],
+ }
+
+ def DeclareLocks(self, level):
+ if level == locking.LEVEL_INSTANCE:
+ assert not self.needed_locks[locking.LEVEL_INSTANCE]
+
+ # Lock instances optimistically, needs verification once node and group
+ # locks have been acquired
+ self.needed_locks[locking.LEVEL_INSTANCE] = \
+ self.cfg.GetNodeGroupInstances(self.group_uuid)
+
+ elif level == locking.LEVEL_NODEGROUP:
+ assert not self.needed_locks[locking.LEVEL_NODEGROUP]
+
+ self.needed_locks[locking.LEVEL_NODEGROUP] = \
+ set([self.group_uuid] +
+ # Lock all groups used by instances optimistically; this requires
+ # going via the node before it's locked, requiring verification
+ # later on
+ [group_uuid
+ for instance_name in
+ self.glm.list_owned(locking.LEVEL_INSTANCE)
+ for group_uuid in
+ self.cfg.GetInstanceNodeGroups(instance_name)])
+
+ elif level == locking.LEVEL_NODE:
+ # This will only lock the nodes in the group to be verified which contain
+ # actual instances
+ self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
+ self._LockInstancesNodes()
+
+ # Lock all nodes in group to be verified
+ assert self.group_uuid in self.glm.list_owned(locking.LEVEL_NODEGROUP)
+ member_nodes = self.cfg.GetNodeGroup(self.group_uuid).members
+ self.needed_locks[locking.LEVEL_NODE].extend(member_nodes)
+
+ def CheckPrereq(self):
+ owned_instances = frozenset(self.glm.list_owned(locking.LEVEL_INSTANCE))
+ owned_groups = frozenset(self.glm.list_owned(locking.LEVEL_NODEGROUP))
+ owned_nodes = frozenset(self.glm.list_owned(locking.LEVEL_NODE))
+
+ assert self.group_uuid in owned_groups
+
+ # Check if locked instances are still correct
+ wanted_instances = self.cfg.GetNodeGroupInstances(self.group_uuid)
+ if owned_instances != wanted_instances:
+ raise errors.OpPrereqError("Instances in node group %s changed since"
+ " locks were acquired, wanted %s, have %s;"
+ " retry the operation" %
+ (self.op.group_name,
+ utils.CommaJoin(wanted_instances),
+ utils.CommaJoin(owned_instances)),
+ errors.ECODE_STATE)
+
+ # Get instance information
+ self.instances = dict((name, self.cfg.GetInstanceInfo(name))
+ for name in owned_instances)
+
+ # Check if node groups for locked instances are still correct
+ for (instance_name, inst) in self.instances.items():
+ assert self.group_uuid in self.cfg.GetInstanceNodeGroups(instance_name), \
+ "Instance %s has no node in group %s" % (instance_name, self.group_uuid)
+ assert owned_nodes.issuperset(inst.all_nodes), \
+ "Instance %s's nodes changed while we kept the lock" % instance_name
+
+ inst_groups = self.cfg.GetInstanceNodeGroups(instance_name)
+ if not owned_groups.issuperset(inst_groups):
+ raise errors.OpPrereqError("Instance %s's node groups changed since"
+ " locks were acquired, current groups are"
+ " are '%s', owning groups '%s'; retry the"
+ " operation" %
+ (instance_name,
+ utils.CommaJoin(inst_groups),
+ utils.CommaJoin(owned_groups)),
+ errors.ECODE_STATE)
def Exec(self, feedback_fn):
"""Verify integrity of cluster disks.
missing volumes
"""
- result = res_nodes, res_instances, res_missing = {}, [], {}
+ res_nodes = {}
+ res_instances = set()
+ res_missing = {}
- nodes = utils.NiceSort(self.cfg.GetVmCapableNodeList())
- instances = self.cfg.GetAllInstancesInfo().values()
+ nv_dict = _MapInstanceDisksToNodes([inst
+ for inst in self.instances.values()
+ if inst.admin_up])
- nv_dict = {}
- for inst in instances:
- inst_lvs = {}
- if not inst.admin_up:
- continue
- inst.MapLVsByNode(inst_lvs)
- # transform { iname: {node: [vol,],},} to {(node, vol): iname}
- for node, vol_list in inst_lvs.iteritems():
- for vol in vol_list:
- nv_dict[(node, vol)] = inst
-
- if not nv_dict:
- return result
-
- node_lvs = self.rpc.call_lv_list(nodes, [])
- for node, node_res in node_lvs.items():
- if node_res.offline:
- continue
- msg = node_res.fail_msg
- if msg:
- logging.warning("Error enumerating LVs on node %s: %s", node, msg)
- res_nodes[node] = msg
- continue
+ if nv_dict:
+ nodes = utils.NiceSort(set(self.glm.list_owned(locking.LEVEL_NODE)) &
+ set(self.cfg.GetVmCapableNodeList()))
- lvs = node_res.payload
- for lv_name, (_, _, lv_online) in lvs.items():
- inst = nv_dict.pop((node, lv_name), None)
- if (not lv_online and inst is not None
- and inst.name not in res_instances):
- res_instances.append(inst.name)
+ node_lvs = self.rpc.call_lv_list(nodes, [])
- # any leftover items in nv_dict are missing LVs, let's arrange the
- # data better
- for key, inst in nv_dict.iteritems():
- if inst.name not in res_missing:
- res_missing[inst.name] = []
- res_missing[inst.name].append(key)
+ for (node, node_res) in node_lvs.items():
+ if node_res.offline:
+ continue
- return result
+ msg = node_res.fail_msg
+ if msg:
+ logging.warning("Error enumerating LVs on node %s: %s", node, msg)
+ res_nodes[node] = msg
+ continue
+
+ for lv_name, (_, _, lv_online) in node_res.payload.items():
+ inst = nv_dict.pop((node, lv_name), None)
+ if not (lv_online or inst is None):
+ res_instances.add(inst)
+
+ # any leftover items in nv_dict are missing LVs, let's arrange the data
+ # better
+ for key, inst in nv_dict.iteritems():
+ res_missing.setdefault(inst, []).append(key)
+
+ return (res_nodes, list(res_instances), res_missing)
class LUClusterRepairDiskSizes(NoHooksLU):
locking.LEVEL_NODE: locking.ALL_SET,
locking.LEVEL_INSTANCE: locking.ALL_SET,
}
- self.share_locks = dict.fromkeys(locking.LEVELS, 1)
+ self.share_locks = _ShareAll()
def DeclareLocks(self, level):
if level == locking.LEVEL_NODE and self.wanted_names is not None:
volumes = self.rpc.call_node_volumes(nodenames)
ilist = self.cfg.GetAllInstancesInfo()
-
- vol2inst = dict(((node, vol), inst.name)
- for inst in ilist.values()
- for (node, vols) in inst.MapLVsByNode().items()
- for vol in vols)
+ vol2inst = _MapInstanceDisksToNodes(ilist.values())
output = []
for node in nodenames:
if field == "node":
val = node
elif field == "phys":
- val = vol['dev']
+ val = vol["dev"]
elif field == "vg":
- val = vol['vg']
+ val = vol["vg"]
elif field == "name":
- val = vol['name']
+ val = vol["name"]
elif field == "size":
- val = int(float(vol['size']))
+ val = int(float(vol["size"]))
elif field == "instance":
val = vol2inst.get((node, vol["vg"] + "/" + vol["name"]), "-")
else:
nodeinfo = lu.rpc.call_node_info([node], None, hypervisor_name)
nodeinfo[node].Raise("Can't get data from node %s" % node,
prereq=True, ecode=errors.ECODE_ENVIRON)
- free_mem = nodeinfo[node].payload.get('memory_free', None)
+ free_mem = nodeinfo[node].payload.get("memory_free", None)
if not isinstance(free_mem, int):
raise errors.OpPrereqError("Can't compute free memory on node %s, result"
" was '%s'" % (node, free_mem),
def ExpandNames(self):
self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name)
- self.share_locks = dict.fromkeys(locking.LEVELS, 1)
+ self.share_locks = _ShareAll()
self.needed_locks = {
locking.LEVEL_NODE: [self.op.node_name],
}
disk_images = []
for idx in range(export_disks):
- option = 'disk%d_dump' % idx
+ option = "disk%d_dump" % idx
if export_info.has_option(constants.INISECT_INS, option):
# FIXME: are the old os-es, disk sizes, etc. useful?
export_name = export_info.get(constants.INISECT_INS, option)
self.src_images = disk_images
- old_name = export_info.get(constants.INISECT_INS, 'name')
+ old_name = export_info.get(constants.INISECT_INS, "name")
try:
- exp_nic_count = export_info.getint(constants.INISECT_INS, 'nic_count')
+ exp_nic_count = export_info.getint(constants.INISECT_INS, "nic_count")
except (TypeError, ValueError), err:
raise errors.OpPrereqError("Invalid export file, nic_count is not"
" an integer: %s" % str(err),
if self.op.instance_name == old_name:
for idx, nic in enumerate(self.nics):
if nic.mac == constants.VALUE_AUTO and exp_nic_count >= idx:
- nic_mac_ini = 'nic%d_mac' % idx
+ nic_mac_ini = "nic%d_mac" % idx
nic.mac = export_info.get(constants.INISECT_INS, nic_mac_ini)
# ENDIF: self.op.mode == constants.INSTANCE_IMPORT
errors.ECODE_INVAL)
# Declare locks
- self.share_locks = dict.fromkeys(locking.LEVELS, 1)
+ self.share_locks = _ShareAll()
self.needed_locks = {
locking.LEVEL_INSTANCE: [],
locking.LEVEL_NODEGROUP: [],
self.wanted_names = None
if self.op.use_locking:
- self.share_locks = dict.fromkeys(locking.LEVELS, 1)
+ self.share_locks = _ShareAll()
if self.wanted_names is None:
self.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET
self.needed_locks[locking.LEVEL_INSTANCE] = self.wanted_names
self.needed_locks[locking.LEVEL_NODE] = []
- self.share_locks = dict.fromkeys(locking.LEVELS, 1)
self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
def DeclareLocks(self, level):
raise errors.OpPrereqError("Invalid IP address '%s'" % nic_ip,
errors.ECODE_INVAL)
- nic_bridge = nic_dict.get('bridge', None)
+ nic_bridge = nic_dict.get("bridge", None)
nic_link = nic_dict.get(constants.INIC_LINK, None)
if nic_bridge and nic_link:
raise errors.OpPrereqError("Cannot pass 'bridge' and 'link'"
" at the same time", errors.ECODE_INVAL)
elif nic_bridge and nic_bridge.lower() == constants.VALUE_NONE:
- nic_dict['bridge'] = None
+ nic_dict["bridge"] = None
elif nic_link and nic_link.lower() == constants.VALUE_NONE:
nic_dict[constants.INIC_LINK] = None
"""
args = dict()
if constants.BE_MEMORY in self.be_new:
- args['memory'] = self.be_new[constants.BE_MEMORY]
+ args["memory"] = self.be_new[constants.BE_MEMORY]
if constants.BE_VCPUS in self.be_new:
- args['vcpus'] = self.be_new[constants.BE_VCPUS]
+ args["vcpus"] = self.be_new[constants.BE_VCPUS]
# TODO: export disk changes. Note: _BuildInstanceHookEnv* don't export disk
# information at all.
if self.op.nics:
- args['nics'] = []
+ args["nics"] = []
nic_override = dict(self.op.nics)
for idx, nic in enumerate(self.instance.nics):
if idx in nic_override:
nicparams = self.cluster.SimpleFillNIC(nic.nicparams)
mode = nicparams[constants.NIC_MODE]
link = nicparams[constants.NIC_LINK]
- args['nics'].append((ip, mac, mode, link))
+ args["nics"].append((ip, mac, mode, link))
if constants.DDM_ADD in nic_override:
ip = nic_override[constants.DDM_ADD].get(constants.INIC_IP, None)
mac = nic_override[constants.DDM_ADD][constants.INIC_MAC]
nicparams = self.nic_pnew[constants.DDM_ADD]
mode = nicparams[constants.NIC_MODE]
link = nicparams[constants.NIC_LINK]
- args['nics'].append((ip, mac, mode, link))
+ args["nics"].append((ip, mac, mode, link))
elif constants.DDM_REMOVE in nic_override:
- del args['nics'][-1]
+ del args["nics"][-1]
env = _BuildInstanceHookEnvByObject(self, self.instance, override=args)
if self.op.disk_template:
# Assume the primary node is unreachable and go ahead
self.warn.append("Can't get info from primary node %s: %s" %
(pnode, msg))
- elif not isinstance(pninfo.payload.get('memory_free', None), int):
+ elif not isinstance(pninfo.payload.get("memory_free", None), int):
self.warn.append("Node data from primary node %s doesn't contain"
" free memory information" % pnode)
elif instance_info.fail_msg:
instance_info.fail_msg)
else:
if instance_info.payload:
- current_mem = int(instance_info.payload['memory'])
+ current_mem = int(instance_info.payload["memory"])
else:
# Assume instance not running
# (there is a slight race condition here, but it's not very probable,
# and we have no other way to check)
current_mem = 0
miss_mem = (be_new[constants.BE_MEMORY] - current_mem -
- pninfo.payload['memory_free'])
+ pninfo.payload["memory_free"])
if miss_mem > 0:
raise errors.OpPrereqError("This change will prevent the instance"
" from starting, due to %d MB of memory"
continue
nres.Raise("Can't get info from secondary node %s" % node,
prereq=True, ecode=errors.ECODE_STATE)
- if not isinstance(nres.payload.get('memory_free', None), int):
+ if not isinstance(nres.payload.get("memory_free", None), int):
raise errors.OpPrereqError("Secondary node %s didn't return free"
" memory information" % node,
errors.ECODE_STATE)
- elif be_new[constants.BE_MEMORY] > nres.payload['memory_free']:
+ elif be_new[constants.BE_MEMORY] > nres.payload["memory_free"]:
raise errors.OpPrereqError("This change will prevent the instance"
" from failover to its secondary node"
" %s, due to not enough memory" % node,
for key in constants.NICS_PARAMETERS
if key in nic_dict])
- if 'bridge' in nic_dict:
- update_params_dict[constants.NIC_LINK] = nic_dict['bridge']
+ if "bridge" in nic_dict:
+ update_params_dict[constants.NIC_LINK] = nic_dict["bridge"]
new_nic_params = _GetUpdatedParams(old_nic_params,
update_params_dict)
else:
nic_ip = old_nic_ip
if nic_ip is None:
- raise errors.OpPrereqError('Cannot set the nic ip to None'
- ' on a routed nic', errors.ECODE_INVAL)
+ raise errors.OpPrereqError("Cannot set the nic ip to None"
+ " on a routed nic", errors.ECODE_INVAL)
if constants.INIC_MAC in nic_dict:
nic_mac = nic_dict[constants.INIC_MAC]
if nic_mac is None:
- raise errors.OpPrereqError('Cannot set the nic mac to None',
+ raise errors.OpPrereqError("Cannot set the nic mac to None",
errors.ECODE_INVAL)
elif nic_mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
# otherwise generate the mac
" opcode nor as a cluster-wide default",
errors.ECODE_INVAL)
- self.share_locks = dict.fromkeys(locking.LEVELS, 1)
+ self.share_locks = _ShareAll()
self.needed_locks = {
locking.LEVEL_INSTANCE: [],
locking.LEVEL_NODEGROUP: [],
inst_groups = self.cfg.GetInstanceNodeGroups(instance_name)
if not owned_groups.issuperset(inst_groups):
- raise errors.OpPrereqError("Instance's node groups changed since locks"
- " were acquired, current groups are '%s',"
- " owning groups '%s'; retry the operation" %
- (utils.CommaJoin(inst_groups),
+ raise errors.OpPrereqError("Instance %s's node groups changed since"
+ " locks were acquired, current groups"
+ " are '%s', owning groups '%s'; retry the"
+ " operation" %
+ (instance_name,
+ utils.CommaJoin(inst_groups),
utils.CommaJoin(owned_groups)),
errors.ECODE_STATE)
TagsLU.ExpandNames(self)
# Share locks as this is only a read operation
- self.share_locks = dict.fromkeys(locking.LEVELS, 1)
+ self.share_locks = _ShareAll()
def Exec(self, feedback_fn):
"""Returns the tag list.
nname)
remote_info = nresult.payload
- for attr in ['memory_total', 'memory_free', 'memory_dom0',
- 'vg_size', 'vg_free', 'cpu_total']:
+ for attr in ["memory_total", "memory_free", "memory_dom0",
+ "vg_size", "vg_free", "cpu_total"]:
if attr not in remote_info:
raise errors.OpExecError("Node '%s' didn't return attribute"
" '%s'" % (nname, attr))
if iinfo.name not in node_iinfo[nname].payload:
i_used_mem = 0
else:
- i_used_mem = int(node_iinfo[nname].payload[iinfo.name]['memory'])
+ i_used_mem = int(node_iinfo[nname].payload[iinfo.name]["memory"])
i_mem_diff = beinfo[constants.BE_MEMORY] - i_used_mem
- remote_info['memory_free'] -= max(0, i_mem_diff)
+ remote_info["memory_free"] -= max(0, i_mem_diff)
if iinfo.admin_up:
i_p_up_mem += beinfo[constants.BE_MEMORY]
# compute memory used by instances
pnr_dyn = {
- "total_memory": remote_info['memory_total'],
- "reserved_memory": remote_info['memory_dom0'],
- "free_memory": remote_info['memory_free'],
- "total_disk": remote_info['vg_size'],
- "free_disk": remote_info['vg_free'],
- "total_cpus": remote_info['cpu_total'],
+ "total_memory": remote_info["memory_total"],
+ "reserved_memory": remote_info["memory_dom0"],
+ "free_memory": remote_info["memory_free"],
+ "total_disk": remote_info["vg_size"],
+ "free_disk": remote_info["vg_free"],
+ "total_cpus": remote_info["cpu_total"],
"i_pri_memory": i_p_mem,
"i_pri_up_memory": i_p_up_mem,
}