X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/3e53a60b8753182702e988b38e0afc00005ba178..5627f3753b4793a77be266d75a3aa9dcf0d3feab:/lib/cmdlib.py diff --git a/lib/cmdlib.py b/lib/cmdlib.py index c2187eb..afffbfb 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -21,7 +21,10 @@ """Module implementing the master-side code.""" -# pylint: disable-msg=W0613,W0201 +# pylint: disable-msg=W0201 + +# W0201 since most LU attributes are defined in CheckPrereq or similar +# functions import os import os.path @@ -87,11 +90,15 @@ class LogicalUnit(object): self.recalculate_locks = {} self.__ssh = None # logging - self.LogWarning = processor.LogWarning - self.LogInfo = processor.LogInfo - self.LogStep = processor.LogStep + self.LogWarning = processor.LogWarning # pylint: disable-msg=C0103 + self.LogInfo = processor.LogInfo # pylint: disable-msg=C0103 + self.LogStep = processor.LogStep # pylint: disable-msg=C0103 # support for dry-run self.dry_run_result = None + # support for generic debug attribute + if (not hasattr(self.op, "debug_level") or + not isinstance(self.op.debug_level, int)): + self.op.debug_level = 0 # Tasklets self.tasklets = None @@ -100,7 +107,7 @@ class LogicalUnit(object): attr_val = getattr(op, attr_name, None) if attr_val is None: raise errors.OpPrereqError("Required parameter '%s' missing" % - attr_name) + attr_name, errors.ECODE_INVAL) self.CheckArguments() @@ -277,6 +284,9 @@ class LogicalUnit(object): and hook results """ + # API must be kept, thus we ignore the unused argument and could + # be a function warnings + # pylint: disable-msg=W0613,R0201 return lu_result def _ExpandAndLockInstance(self): @@ -294,12 +304,9 @@ class LogicalUnit(object): else: assert locking.LEVEL_INSTANCE not in self.needed_locks, \ "_ExpandAndLockInstance called with instance-level locks set" - expanded_name = self.cfg.ExpandInstanceName(self.op.instance_name) - if expanded_name is None: - raise errors.OpPrereqError("Instance '%s' not known" % - self.op.instance_name) - self.needed_locks[locking.LEVEL_INSTANCE] = expanded_name - self.op.instance_name = expanded_name + self.op.instance_name = _ExpandInstanceName(self.cfg, + self.op.instance_name) + self.needed_locks[locking.LEVEL_INSTANCE] = self.op.instance_name def _LockInstancesNodes(self, primary_only=False): """Helper function to declare instances' nodes for locking. @@ -347,7 +354,7 @@ class LogicalUnit(object): del self.recalculate_locks[locking.LEVEL_NODE] -class NoHooksLU(LogicalUnit): +class NoHooksLU(LogicalUnit): # pylint: disable-msg=W0223 """Simple LU which runs no hooks. This LU is intended as a parent for other LogicalUnits which will @@ -357,6 +364,14 @@ class NoHooksLU(LogicalUnit): HPATH = None HTYPE = None + def BuildHooksEnv(self): + """Empty BuildHooksEnv for NoHooksLu. + + This just raises an error. + + """ + assert False, "BuildHooksEnv called for NoHooksLUs" + class Tasklet: """Tasklet base class. @@ -413,23 +428,18 @@ def _GetWantedNodes(lu, nodes): @param nodes: list of node names or None for all nodes @rtype: list @return: the list of nodes, sorted - @raise errors.OpProgrammerError: if the nodes parameter is wrong type + @raise errors.ProgrammerError: if the nodes parameter is wrong type """ if not isinstance(nodes, list): - raise errors.OpPrereqError("Invalid argument type 'nodes'") + raise errors.OpPrereqError("Invalid argument type 'nodes'", + errors.ECODE_INVAL) if not nodes: raise errors.ProgrammerError("_GetWantedNodes should only be called with a" " non-empty list of nodes whose name is to be expanded.") - wanted = [] - for name in nodes: - node = lu.cfg.ExpandNodeName(name) - if node is None: - raise errors.OpPrereqError("No such node name '%s'" % name) - wanted.append(node) - + wanted = [_ExpandNodeName(lu.cfg, name) for name in nodes] return utils.NiceSort(wanted) @@ -447,17 +457,11 @@ def _GetWantedInstances(lu, instances): """ if not isinstance(instances, list): - raise errors.OpPrereqError("Invalid argument type 'instances'") + raise errors.OpPrereqError("Invalid argument type 'instances'", + errors.ECODE_INVAL) if instances: - wanted = [] - - for name in instances: - instance = lu.cfg.ExpandInstanceName(name) - if instance is None: - raise errors.OpPrereqError("No such instance name '%s'" % name) - wanted.append(instance) - + wanted = [_ExpandInstanceName(lu.cfg, name) for name in instances] else: wanted = utils.NiceSort(lu.cfg.GetInstanceList()) return wanted @@ -479,7 +483,7 @@ def _CheckOutputFields(static, dynamic, selected): delta = f.NonMatching(selected) if delta: raise errors.OpPrereqError("Unknown output fields selected: %s" - % ",".join(delta)) + % ",".join(delta), errors.ECODE_INVAL) def _CheckBooleanOpField(op, name): @@ -492,10 +496,25 @@ def _CheckBooleanOpField(op, name): val = getattr(op, name, None) if not (val is None or isinstance(val, bool)): raise errors.OpPrereqError("Invalid boolean parameter '%s' (%s)" % - (name, str(val))) + (name, str(val)), errors.ECODE_INVAL) setattr(op, name, val) +def _CheckGlobalHvParams(params): + """Validates that given hypervisor params are not global ones. + + This will ensure that instances don't get customised versions of + global params. + + """ + used_globals = constants.HVC_GLOBALS.intersection(params) + if used_globals: + msg = ("The following hypervisor parameters are global and cannot" + " be customized at instance level, please modify them at" + " cluster level: %s" % utils.CommaJoin(used_globals)) + raise errors.OpPrereqError(msg, errors.ECODE_INVAL) + + def _CheckNodeOnline(lu, node): """Ensure that a given node is online. @@ -505,7 +524,8 @@ def _CheckNodeOnline(lu, node): """ if lu.cfg.GetNodeInfo(node).offline: - raise errors.OpPrereqError("Can't use offline node %s" % node) + raise errors.OpPrereqError("Can't use offline node %s" % node, + errors.ECODE_INVAL) def _CheckNodeNotDrained(lu, node): @@ -517,7 +537,35 @@ def _CheckNodeNotDrained(lu, node): """ if lu.cfg.GetNodeInfo(node).drained: - raise errors.OpPrereqError("Can't use drained node %s" % node) + raise errors.OpPrereqError("Can't use drained node %s" % node, + errors.ECODE_INVAL) + + +def _ExpandItemName(fn, name, kind): + """Expand an item name. + + @param fn: the function to use for expansion + @param name: requested item name + @param kind: text description ('Node' or 'Instance') + @return: the resolved (full) name + @raise errors.OpPrereqError: if the item is not found + + """ + full_name = fn(name) + if full_name is None: + raise errors.OpPrereqError("%s '%s' not known" % (kind, name), + errors.ECODE_NOENT) + return full_name + + +def _ExpandNodeName(cfg, name): + """Wrapper over L{_ExpandItemName} for nodes.""" + return _ExpandItemName(cfg.ExpandNodeName, name, "Node") + + +def _ExpandInstanceName(cfg, name): + """Wrapper over L{_ExpandItemName} for instance.""" + return _ExpandItemName(cfg.ExpandInstanceName, name, "Instance") def _BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status, @@ -667,7 +715,7 @@ def _BuildInstanceHookEnvByObject(lu, instance, override=None): } if override: args.update(override) - return _BuildInstanceHookEnv(**args) + return _BuildInstanceHookEnv(**args) # pylint: disable-msg=W0142 def _AdjustCandidatePool(lu, exceptions): @@ -677,7 +725,7 @@ def _AdjustCandidatePool(lu, exceptions): mod_list = lu.cfg.MaintainCandidatePool(exceptions) if mod_list: lu.LogInfo("Promoted nodes to master candidate role: %s", - ", ".join(node.name for node in mod_list)) + utils.CommaJoin(node.name for node in mod_list)) for name in mod_list: lu.context.ReaddNode(name) mc_now, mc_max, _ = lu.cfg.GetMasterCandidateStats(exceptions) @@ -710,7 +758,7 @@ def _CheckNicsBridgesExist(lu, target_nics, target_node, if brlist: result = lu.rpc.call_bridges_exist(target_node, brlist) result.Raise("Error checking bridges on destination node '%s'" % - target_node, prereq=True) + target_node, prereq=True, ecode=errors.ECODE_ENVIRON) def _CheckInstanceBridgesExist(lu, instance, node=None): @@ -736,10 +784,11 @@ def _CheckOSVariant(os_obj, name): try: variant = name.split("+", 1)[1] except IndexError: - raise errors.OpPrereqError("OS name must include a variant") + raise errors.OpPrereqError("OS name must include a variant", + errors.ECODE_INVAL) if variant not in os_obj.supported_variants: - raise errors.OpPrereqError("Unsupported OS variant") + raise errors.OpPrereqError("Unsupported OS variant", errors.ECODE_INVAL) def _GetNodeInstancesInner(cfg, fn): @@ -790,7 +839,7 @@ def _FindFaultyInstanceDisks(cfg, rpc, instance, node_name, prereq): result = rpc.call_blockdev_getmirrorstatus(node_name, instance.disks) result.Raise("Failed to get disk status from node %s" % node_name, - prereq=prereq) + prereq=prereq, ecode=errors.ECODE_ENVIRON) for idx, bdev_status in enumerate(result.payload): if bdev_status and bdev_status.ldisk_status == constants.LDS_FAULTY: @@ -856,11 +905,13 @@ class LUDestroyCluster(LogicalUnit): nodelist = self.cfg.GetNodeList() if len(nodelist) != 1 or nodelist[0] != master: raise errors.OpPrereqError("There are still %d node(s) in" - " this cluster." % (len(nodelist) - 1)) + " this cluster." % (len(nodelist) - 1), + errors.ECODE_INVAL) instancelist = self.cfg.GetInstanceList() if instancelist: raise errors.OpPrereqError("There are still %d instance(s) in" - " this cluster." % len(instancelist)) + " this cluster." % len(instancelist), + errors.ECODE_INVAL) def Exec(self, feedback_fn): """Destroys the cluster. @@ -874,6 +925,7 @@ class LUDestroyCluster(LogicalUnit): try: hm.RunPhase(constants.HOOKS_PHASE_POST, [master]) except: + # pylint: disable-msg=W0702 self.LogWarning("Errors occurred running hooks on %s" % master) result = self.rpc.call_node_stop_master(master, False) @@ -920,6 +972,7 @@ class LUVerifyCluster(LogicalUnit): ENODESSH = (TNODE, "ENODESSH") ENODEVERSION = (TNODE, "ENODEVERSION") ENODESETUP = (TNODE, "ENODESETUP") + ENODETIME = (TNODE, "ENODETIME") ETYPE_FIELD = "code" ETYPE_ERROR = "ERROR" @@ -993,7 +1046,7 @@ class LUVerifyCluster(LogicalUnit): """ node = nodeinfo.name - _ErrorIf = self._ErrorIf + _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 # main result, node_result should be a non-empty dict test = not node_result or not isinstance(node_result, dict) @@ -1118,6 +1171,20 @@ class LUVerifyCluster(LogicalUnit): _ErrorIf(test, self.ENODESETUP, node, "node setup error: %s", "; ".join(test)) + # check pv names + if vg_name is not None: + pvlist = node_result.get(constants.NV_PVLIST, None) + test = pvlist is None + _ErrorIf(test, self.ENODELVM, node, "Can't get PV list from node") + if not test: + # 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 + _ErrorIf(test, self.ENODELVM, node, "Invalid character ':' in PV" + " '%s' of VG '%s'", pvname, owner_vg) + def _VerifyInstance(self, instance, instanceconfig, node_vol_is, node_instance, n_offline): """Verify an instance. @@ -1126,7 +1193,7 @@ class LUVerifyCluster(LogicalUnit): available on the instance's node. """ - _ErrorIf = self._ErrorIf + _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 node_current = instanceconfig.primary_node node_vol_should = {} @@ -1217,7 +1284,8 @@ class LUVerifyCluster(LogicalUnit): """ self.skip_set = frozenset(self.op.skip_checks) if not constants.VERIFY_OPTIONAL_CHECKS.issuperset(self.skip_set): - raise errors.OpPrereqError("Invalid checks to be skipped specified") + raise errors.OpPrereqError("Invalid checks to be skipped specified", + errors.ECODE_INVAL) def BuildHooksEnv(self): """Build hooks env. @@ -1240,7 +1308,7 @@ class LUVerifyCluster(LogicalUnit): """ self.bad = False - _ErrorIf = self._ErrorIf + _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 verbose = self.op.verbose self._feedback_fn = feedback_fn feedback_fn("* Verifying global settings") @@ -1287,13 +1355,23 @@ class LUVerifyCluster(LogicalUnit): constants.NV_VERSION: None, constants.NV_HVINFO: self.cfg.GetHypervisorType(), constants.NV_NODESETUP: None, + constants.NV_TIME: None, } + if vg_name is not None: node_verify_param[constants.NV_VGLIST] = None node_verify_param[constants.NV_LVLIST] = vg_name + node_verify_param[constants.NV_PVLIST] = [vg_name] node_verify_param[constants.NV_DRBDLIST] = None + + # Due to the way our RPC system works, exact response times cannot be + # guaranteed (e.g. a broken node could run into a timeout). By keeping the + # time before and after executing the request, we can at least have a time + # window. + nvinfo_starttime = time.time() all_nvinfo = self.rpc.call_node_verify(nodelist, node_verify_param, self.cfg.GetClusterName()) + nvinfo_endtime = time.time() cluster = self.cfg.GetClusterInfo() master_node = self.cfg.GetMasterNode() @@ -1340,6 +1418,7 @@ class LUVerifyCluster(LogicalUnit): else: instance = instanceinfo[instance] node_drbd[minor] = (instance.name, instance.admin_up) + self._VerifyNode(node_i, file_names, local_checksums, nresult, master_files, node_drbd, vg_name) @@ -1373,6 +1452,27 @@ class LUVerifyCluster(LogicalUnit): if test: continue + # Node time + ntime = nresult.get(constants.NV_TIME, None) + try: + ntime_merged = utils.MergeTime(ntime) + except (ValueError, TypeError): + _ErrorIf(test, self.ENODETIME, node, "Node returned invalid time") + + if ntime_merged < (nvinfo_starttime - constants.NODE_MAX_CLOCK_SKEW): + ntime_diff = abs(nvinfo_starttime - ntime_merged) + elif ntime_merged > (nvinfo_endtime + constants.NODE_MAX_CLOCK_SKEW): + ntime_diff = abs(ntime_merged - nvinfo_endtime) + else: + ntime_diff = None + + _ErrorIf(ntime_diff is not None, self.ENODETIME, node, + "Node time diverges by at least %0.1fs from master node time", + ntime_diff) + + if ntime_diff is not None: + continue + try: node_info[node] = { "mfree": int(nodeinfo['memory_free']), @@ -1458,7 +1558,7 @@ class LUVerifyCluster(LogicalUnit): # warn that the instance lives on offline nodes _ErrorIf(inst_nodes_offline, self.EINSTANCEBADNODE, instance, "instance lives on offline node(s) %s", - ", ".join(inst_nodes_offline)) + utils.CommaJoin(inst_nodes_offline)) feedback_fn("* Verifying orphan volumes") self._VerifyOrphanVolumes(node_vol_should, node_volume) @@ -1511,13 +1611,13 @@ class LUVerifyCluster(LogicalUnit): assert hooks_results, "invalid result from hooks" for node_name in hooks_results: - show_node_header = True res = hooks_results[node_name] msg = res.fail_msg test = msg and not res.offline self._ErrorIf(test, self.ENODEHOOKS, node_name, "Communication failure in hooks execution: %s", msg) - if test: + if res.offline or msg: + # No need to investigate payload if node is offline or gave an error. # override manually lu_result here as _ErrorIf only # overrides self.bad lu_result = 1 @@ -1601,7 +1701,7 @@ class LUVerifyDisks(NoHooksLU): continue lvs = node_res.payload - for lv_name, (_, lv_inactive, lv_online) in lvs.items(): + 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): @@ -1626,14 +1726,13 @@ class LURepairDiskSizes(NoHooksLU): def ExpandNames(self): if not isinstance(self.op.instances, list): - raise errors.OpPrereqError("Invalid argument type 'instances'") + raise errors.OpPrereqError("Invalid argument type 'instances'", + errors.ECODE_INVAL) if self.op.instances: self.wanted_names = [] for name in self.op.instances: - full_name = self.cfg.ExpandInstanceName(name) - if full_name is None: - raise errors.OpPrereqError("Instance '%s' not known" % name) + full_name = _ExpandInstanceName(self.cfg, name) self.wanted_names.append(full_name) self.needed_locks = { locking.LEVEL_NODE: [], @@ -1755,13 +1854,14 @@ class LURenameCluster(LogicalUnit): "NEW_NAME": self.op.name, } mn = self.cfg.GetMasterNode() - return env, [mn], [mn] + all_nodes = self.cfg.GetNodeList() + return env, [mn], all_nodes def CheckPrereq(self): """Verify that the passed name is a valid one. """ - hostname = utils.HostInfo(self.op.name) + hostname = utils.GetHostInfo(self.op.name) new_name = hostname.name self.ip = new_ip = hostname.ip @@ -1769,12 +1869,13 @@ class LURenameCluster(LogicalUnit): old_ip = self.cfg.GetMasterIP() if new_name == old_name and new_ip == old_ip: raise errors.OpPrereqError("Neither the name nor the IP address of the" - " cluster has changed") + " cluster has changed", + errors.ECODE_INVAL) if new_ip != old_ip: if utils.TcpPing(new_ip, constants.DEFAULT_NODED_PORT): raise errors.OpPrereqError("The given cluster IP address (%s) is" " reachable on the network. Aborting." % - new_ip) + new_ip, errors.ECODE_NOTUNIQUE) self.op.name = new_name @@ -1856,9 +1957,10 @@ class LUSetClusterParams(LogicalUnit): self.op.candidate_pool_size = int(self.op.candidate_pool_size) except (ValueError, TypeError), err: raise errors.OpPrereqError("Invalid candidate_pool_size value: %s" % - str(err)) + str(err), errors.ECODE_INVAL) if self.op.candidate_pool_size < 1: - raise errors.OpPrereqError("At least one master candidate needed") + raise errors.OpPrereqError("At least one master candidate needed", + errors.ECODE_INVAL) def ExpandNames(self): # FIXME: in the future maybe other cluster params won't require checking on @@ -1892,7 +1994,8 @@ class LUSetClusterParams(LogicalUnit): for disk in inst.disks: if _RecursiveCheckIfLVMBased(disk): raise errors.OpPrereqError("Cannot disable lvm storage while" - " lvm-based instances exist") + " lvm-based instances exist", + errors.ECODE_INVAL) node_list = self.acquired_locks[locking.LEVEL_NODE] @@ -1911,7 +2014,7 @@ class LUSetClusterParams(LogicalUnit): constants.MIN_VG_SIZE) if vgstatus: raise errors.OpPrereqError("Error on node '%s': %s" % - (node, vgstatus)) + (node, vgstatus), errors.ECODE_ENVIRON) self.cluster = cluster = self.cfg.GetClusterInfo() # validate params changes @@ -1925,12 +2028,36 @@ class LUSetClusterParams(LogicalUnit): self.new_nicparams = objects.FillDict( cluster.nicparams[constants.PP_DEFAULT], self.op.nicparams) objects.NIC.CheckParameterSyntax(self.new_nicparams) + nic_errors = [] + + # check all instances for consistency + for instance in self.cfg.GetAllInstancesInfo().values(): + for nic_idx, nic in enumerate(instance.nics): + params_copy = copy.deepcopy(nic.nicparams) + params_filled = objects.FillDict(self.new_nicparams, params_copy) + + # check parameter syntax + try: + objects.NIC.CheckParameterSyntax(params_filled) + except errors.ConfigurationError, err: + nic_errors.append("Instance %s, nic/%d: %s" % + (instance.name, nic_idx, err)) + + # if we're moving instances to routed, check that they have an ip + target_mode = params_filled[constants.NIC_MODE] + if target_mode == constants.NIC_MODE_ROUTED and not nic.ip: + nic_errors.append("Instance %s, nic/%d: routed nick with no ip" % + (instance.name, nic_idx)) + if nic_errors: + raise errors.OpPrereqError("Cannot apply the change, errors:\n%s" % + "\n".join(nic_errors)) # hypervisor list/parameters self.new_hvparams = objects.FillDict(cluster.hvparams, {}) if self.op.hvparams: if not isinstance(self.op.hvparams, dict): - raise errors.OpPrereqError("Invalid 'hvparams' parameter on input") + raise errors.OpPrereqError("Invalid 'hvparams' parameter on input", + errors.ECODE_INVAL) for hv_name, hv_dict in self.op.hvparams.items(): if hv_name not in self.new_hvparams: self.new_hvparams[hv_name] = hv_dict @@ -1941,11 +2068,14 @@ class LUSetClusterParams(LogicalUnit): self.hv_list = self.op.enabled_hypervisors if not self.hv_list: raise errors.OpPrereqError("Enabled hypervisors list must contain at" - " least one member") + " least one member", + errors.ECODE_INVAL) invalid_hvs = set(self.hv_list) - constants.HYPER_TYPES if invalid_hvs: raise errors.OpPrereqError("Enabled hypervisors contains invalid" - " entries: %s" % " ,".join(invalid_hvs)) + " entries: %s" % + utils.CommaJoin(invalid_hvs), + errors.ECODE_INVAL) else: self.hv_list = cluster.enabled_hypervisors @@ -2063,7 +2193,7 @@ class LURedistributeConfig(NoHooksLU): _RedistributeAncillaryFiles(self) -def _WaitForSync(lu, instance, oneshot=False, unlock=False): +def _WaitForSync(lu, instance, oneshot=False): """Sleep and poll for an instance's disk to sync. """ @@ -2078,6 +2208,8 @@ def _WaitForSync(lu, instance, oneshot=False, unlock=False): for dev in instance.disks: lu.cfg.SetDiskID(dev, node) + # TODO: Convert to utils.Retry + retries = 0 degr_retries = 10 # in seconds, as we sleep 1 second each time while True: @@ -2181,7 +2313,8 @@ class LUDiagnoseOS(NoHooksLU): def ExpandNames(self): if self.op.names: - raise errors.OpPrereqError("Selective OS query not supported") + raise errors.OpPrereqError("Selective OS query not supported", + errors.ECODE_INVAL) _CheckOutputFields(static=self._FIELDS_STATIC, dynamic=self._FIELDS_DYNAMIC, @@ -2200,10 +2333,9 @@ class LUDiagnoseOS(NoHooksLU): """ @staticmethod - def _DiagnoseByOS(node_list, rlist): + def _DiagnoseByOS(rlist): """Remaps a per-node return list into an a per-os per-node dictionary - @param node_list: a list with the names of all nodes @param rlist: a map with node names as keys and OS objects as values @rtype: dict @@ -2241,7 +2373,7 @@ class LUDiagnoseOS(NoHooksLU): """ valid_nodes = [node for node in self.cfg.GetOnlineNodeList()] node_data = self.rpc.call_os_diagnose(valid_nodes) - pol = self._DiagnoseByOS(valid_nodes, node_data) + pol = self._DiagnoseByOS(node_data) output = [] calc_valid = self._FIELDS_NEEDVALID.intersection(self.op.output_fields) calc_variants = "variants" in self.op.output_fields @@ -2303,8 +2435,11 @@ class LURemoveNode(LogicalUnit): "NODE_NAME": self.op.node_name, } all_nodes = self.cfg.GetNodeList() - if self.op.node_name in all_nodes: + try: all_nodes.remove(self.op.node_name) + except ValueError: + logging.warning("Node %s which is about to be removed not found" + " in the all nodes list", self.op.node_name) return env, all_nodes, all_nodes def CheckPrereq(self): @@ -2318,22 +2453,24 @@ class LURemoveNode(LogicalUnit): Any errors are signaled by raising errors.OpPrereqError. """ - node = self.cfg.GetNodeInfo(self.cfg.ExpandNodeName(self.op.node_name)) - if node is None: - raise errors.OpPrereqError, ("Node '%s' is unknown." % self.op.node_name) + self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name) + node = self.cfg.GetNodeInfo(self.op.node_name) + assert node is not None instance_list = self.cfg.GetInstanceList() masternode = self.cfg.GetMasterNode() if node.name == masternode: raise errors.OpPrereqError("Node is the master node," - " you need to failover first.") + " you need to failover first.", + errors.ECODE_INVAL) for instance_name in instance_list: instance = self.cfg.GetInstanceInfo(instance_name) if node.name in instance.all_nodes: raise errors.OpPrereqError("Instance %s is still running on the node," - " please remove first." % instance_name) + " please remove first." % instance_name, + errors.ECODE_INVAL) self.op.node_name = node.name self.node = node @@ -2354,8 +2491,9 @@ class LURemoveNode(LogicalUnit): # Run post hooks on the node before it's removed hm = self.proc.hmclass(self.rpc.call_hooks_runner, self) try: - h_results = hm.RunPhase(constants.HOOKS_PHASE_POST, [node.name]) + hm.RunPhase(constants.HOOKS_PHASE_POST, [node.name]) except: + # pylint: disable-msg=W0702 self.LogWarning("Errors occurred running hooks on %s" % node.name) result = self.rpc.call_node_leave_cluster(node.name, modify_ssh_setup) @@ -2369,6 +2507,7 @@ class LUQueryNodes(NoHooksLU): """Logical unit for querying nodes. """ + # pylint: disable-msg=W0142 _OP_REQP = ["output_fields", "names", "use_locking"] REQ_BGL = False @@ -2469,10 +2608,9 @@ class LUQueryNodes(NoHooksLU): inst_fields = frozenset(("pinst_cnt", "pinst_list", "sinst_cnt", "sinst_list")) if inst_fields & frozenset(self.op.output_fields): - instancelist = self.cfg.GetInstanceList() + inst_data = self.cfg.GetAllInstancesInfo() - for instance_name in instancelist: - inst = self.cfg.GetInstanceInfo(instance_name) + for inst in inst_data.values(): if inst.primary_node in node_to_primary: node_to_primary[inst.primary_node].add(inst.name) for secnode in inst.secondary_nodes: @@ -2624,7 +2762,8 @@ class LUQueryNodeStorage(NoHooksLU): storage_type = self.op.storage_type if storage_type not in constants.VALID_STORAGE_TYPES: - raise errors.OpPrereqError("Unknown storage type: %s" % storage_type) + raise errors.OpPrereqError("Unknown storage type: %s" % storage_type, + errors.ECODE_INVAL) _CheckOutputFields(static=self._FIELDS_STATIC, dynamic=utils.FieldSet(*constants.VALID_STORAGE_FIELDS), @@ -2716,15 +2855,12 @@ class LUModifyNodeStorage(NoHooksLU): REQ_BGL = False def CheckArguments(self): - node_name = self.cfg.ExpandNodeName(self.op.node_name) - if node_name is None: - raise errors.OpPrereqError("Invalid node name '%s'" % self.op.node_name) - - self.op.node_name = node_name + self.opnode_name = _ExpandNodeName(self.cfg, self.op.node_name) storage_type = self.op.storage_type if storage_type not in constants.VALID_STORAGE_TYPES: - raise errors.OpPrereqError("Unknown storage type: %s" % storage_type) + raise errors.OpPrereqError("Unknown storage type: %s" % storage_type, + errors.ECODE_INVAL) def ExpandNames(self): self.needed_locks = { @@ -2741,13 +2877,15 @@ class LUModifyNodeStorage(NoHooksLU): modifiable = constants.MODIFIABLE_STORAGE_FIELDS[storage_type] except KeyError: raise errors.OpPrereqError("Storage units of type '%s' can not be" - " modified" % storage_type) + " modified" % storage_type, + errors.ECODE_INVAL) diff = set(self.op.changes.keys()) - modifiable if diff: raise errors.OpPrereqError("The following fields can not be modified for" " storage units of type '%s': %r" % - (storage_type, list(diff))) + (storage_type, list(diff)), + errors.ECODE_INVAL) def Exec(self, feedback_fn): """Computes the list of nodes and their attributes. @@ -2799,7 +2937,7 @@ class LUAddNode(LogicalUnit): node_name = self.op.node_name cfg = self.cfg - dns_data = utils.HostInfo(node_name) + dns_data = utils.GetHostInfo(node_name) node = dns_data.name primary_ip = self.op.primary_ip = dns_data.ip @@ -2807,15 +2945,17 @@ class LUAddNode(LogicalUnit): if secondary_ip is None: secondary_ip = primary_ip if not utils.IsValidIP(secondary_ip): - raise errors.OpPrereqError("Invalid secondary IP given") + raise errors.OpPrereqError("Invalid secondary IP given", + errors.ECODE_INVAL) self.op.secondary_ip = secondary_ip node_list = cfg.GetNodeList() if not self.op.readd and node in node_list: raise errors.OpPrereqError("Node %s is already in the configuration" % - node) + node, errors.ECODE_EXISTS) elif self.op.readd and node not in node_list: - raise errors.OpPrereqError("Node %s is not in the configuration" % node) + raise errors.OpPrereqError("Node %s is not in the configuration" % node, + errors.ECODE_NOENT) for existing_node_name in node_list: existing_node = cfg.GetNodeInfo(existing_node_name) @@ -2824,7 +2964,8 @@ class LUAddNode(LogicalUnit): if (existing_node.primary_ip != primary_ip or existing_node.secondary_ip != secondary_ip): raise errors.OpPrereqError("Readded node doesn't have the same IP" - " address configuration as before") + " address configuration as before", + errors.ECODE_INVAL) continue if (existing_node.primary_ip == primary_ip or @@ -2832,7 +2973,8 @@ class LUAddNode(LogicalUnit): existing_node.primary_ip == secondary_ip or existing_node.secondary_ip == secondary_ip): raise errors.OpPrereqError("New node ip address(es) conflict with" - " existing node %s" % existing_node.name) + " existing node %s" % existing_node.name, + errors.ECODE_NOTUNIQUE) # check that the type of the node (single versus dual homed) is the # same as for the master @@ -2842,21 +2984,25 @@ class LUAddNode(LogicalUnit): if master_singlehomed != newbie_singlehomed: if master_singlehomed: raise errors.OpPrereqError("The master has no private ip but the" - " new node has one") + " new node has one", + errors.ECODE_INVAL) else: raise errors.OpPrereqError("The master has a private ip but the" - " new node doesn't have one") + " new node doesn't have one", + errors.ECODE_INVAL) # checks reachability if not utils.TcpPing(primary_ip, constants.DEFAULT_NODED_PORT): - raise errors.OpPrereqError("Node not reachable by ping") + raise errors.OpPrereqError("Node not reachable by ping", + errors.ECODE_ENVIRON) if not newbie_singlehomed: # check reachability from my secondary ip to newbie's secondary ip if not utils.TcpPing(secondary_ip, constants.DEFAULT_NODED_PORT, source=myself.secondary_ip): raise errors.OpPrereqError("Node secondary ip not reachable by TCP" - " based ping to noded port") + " based ping to noded port", + errors.ECODE_ENVIRON) if self.op.readd: exceptions = [node] @@ -2887,7 +3033,7 @@ class LUAddNode(LogicalUnit): # later in the procedure; this also means that if the re-add # fails, we are left with a non-offlined, broken node if self.op.readd: - new_node.drained = new_node.offline = False + new_node.drained = new_node.offline = False # pylint: disable-msg=W0201 self.LogInfo("Readding a node, the offline/drained flags were reset") # if we demote the node, we do cleanup later in the procedure new_node.master_candidate = self.master_candidate @@ -2932,7 +3078,7 @@ class LUAddNode(LogicalUnit): result = self.rpc.call_node_has_ip_address(new_node.name, new_node.secondary_ip) result.Raise("Failure checking secondary ip on node %s" % new_node.name, - prereq=True) + prereq=True, ecode=errors.ECODE_ENVIRON) if not result.payload: raise errors.OpExecError("Node claims it doesn't have the secondary ip" " you gave (%s). Please fix and re-run this" @@ -2970,7 +3116,7 @@ class LUAddNode(LogicalUnit): " candidate status: %s" % msg) else: _RedistributeAncillaryFiles(self, additional_nodes=[node]) - self.context.AddNode(new_node) + self.context.AddNode(new_node, self.proc.GetECId()) class LUSetNodeParams(LogicalUnit): @@ -2983,19 +3129,18 @@ class LUSetNodeParams(LogicalUnit): REQ_BGL = False def CheckArguments(self): - node_name = self.cfg.ExpandNodeName(self.op.node_name) - if node_name is None: - raise errors.OpPrereqError("Invalid node name '%s'" % self.op.node_name) - self.op.node_name = node_name + self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name) _CheckBooleanOpField(self.op, 'master_candidate') _CheckBooleanOpField(self.op, 'offline') _CheckBooleanOpField(self.op, 'drained') all_mods = [self.op.offline, self.op.master_candidate, self.op.drained] if all_mods.count(None) == 3: - raise errors.OpPrereqError("Please pass at least one modification") + raise errors.OpPrereqError("Please pass at least one modification", + errors.ECODE_INVAL) if all_mods.count(True) > 1: raise errors.OpPrereqError("Can't set the node into more than one" - " state at the same time") + " state at the same time", + errors.ECODE_INVAL) def ExpandNames(self): self.needed_locks = {locking.LEVEL_NODE: self.op.node_name} @@ -3030,7 +3175,8 @@ class LUSetNodeParams(LogicalUnit): # we can't change the master's node flags if self.op.node_name == self.cfg.GetMasterNode(): raise errors.OpPrereqError("The master role can be changed" - " only via masterfailover") + " only via masterfailover", + errors.ECODE_INVAL) # Boolean value that tells us whether we're offlining or draining the node offline_or_drain = self.op.offline == True or self.op.drained == True @@ -3050,17 +3196,18 @@ class LUSetNodeParams(LogicalUnit): if self.op.force and offline_or_drain and mc_should == mc_max: self.LogWarning(msg) else: - raise errors.OpPrereqError(msg) + raise errors.OpPrereqError(msg, errors.ECODE_INVAL) if (self.op.master_candidate == True and ((node.offline and not self.op.offline == False) or (node.drained and not self.op.drained == False))): raise errors.OpPrereqError("Node '%s' is offline or drained, can't set" - " to master_candidate" % node.name) + " to master_candidate" % node.name, + errors.ECODE_INVAL) # If we're being deofflined/drained, we'll MC ourself if needed if (deoffline_or_drain and not offline_or_drain and not - self.op.master_candidate == True): + self.op.master_candidate == True and not node.master_candidate): self.op.master_candidate = _DecideSelfPromotion(self) if self.op.master_candidate: self.LogInfo("Autopromoting node to master candidate") @@ -3131,13 +3278,11 @@ class LUPowercycleNode(NoHooksLU): REQ_BGL = False def CheckArguments(self): - node_name = self.cfg.ExpandNodeName(self.op.node_name) - if node_name is None: - raise errors.OpPrereqError("Invalid node name '%s'" % self.op.node_name) - self.op.node_name = node_name - if node_name == self.cfg.GetMasterNode() and not self.op.force: + self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name) + if self.op.node_name == self.cfg.GetMasterNode() and not self.op.force: raise errors.OpPrereqError("The node is the master and the force" - " parameter was not set") + " parameter was not set", + errors.ECODE_INVAL) def ExpandNames(self): """Locking for PowercycleNode. @@ -3497,15 +3642,18 @@ def _CheckNodeFreeMemory(lu, node, reason, requested, hypervisor_name): """ nodeinfo = lu.rpc.call_node_info([node], lu.cfg.GetVGName(), hypervisor_name) - nodeinfo[node].Raise("Can't get data from node %s" % node, prereq=True) + 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) if not isinstance(free_mem, int): raise errors.OpPrereqError("Can't compute free memory on node %s, result" - " was '%s'" % (node, free_mem)) + " was '%s'" % (node, free_mem), + errors.ECODE_ENVIRON) if requested > free_mem: raise errors.OpPrereqError("Not enough memory on node %s for %s:" " needed %s MiB, available %s MiB" % - (node, reason, requested, free_mem)) + (node, reason, requested, free_mem), + errors.ECODE_NORES) class LUStartupInstance(LogicalUnit): @@ -3548,7 +3696,8 @@ class LUStartupInstance(LogicalUnit): if self.beparams: if not isinstance(self.beparams, dict): raise errors.OpPrereqError("Invalid beparams passed: %s, expected" - " dict" % (type(self.beparams), )) + " dict" % (type(self.beparams), ), + errors.ECODE_INVAL) # fill the beparams dict utils.ForceDictType(self.beparams, constants.BES_PARAMETER_TYPES) self.op.beparams = self.beparams @@ -3558,7 +3707,8 @@ class LUStartupInstance(LogicalUnit): if self.hvparams: if not isinstance(self.hvparams, dict): raise errors.OpPrereqError("Invalid hvparams passed: %s, expected" - " dict" % (type(self.hvparams), )) + " dict" % (type(self.hvparams), ), + errors.ECODE_INVAL) # check hypervisor parameter syntax (locally) cluster = self.cfg.GetClusterInfo() @@ -3581,7 +3731,7 @@ class LUStartupInstance(LogicalUnit): instance.name, instance.hypervisor) remote_info.Raise("Error checking node %s" % instance.primary_node, - prereq=True) + prereq=True, ecode=errors.ECODE_ENVIRON) if not remote_info.payload: # not running already _CheckNodeFreeMemory(self, instance.primary_node, "starting instance %s" % instance.name, @@ -3790,32 +3940,32 @@ class LUReinstallInstance(LogicalUnit): if instance.disk_template == constants.DT_DISKLESS: raise errors.OpPrereqError("Instance '%s' has no disks" % - self.op.instance_name) + self.op.instance_name, + errors.ECODE_INVAL) if instance.admin_up: raise errors.OpPrereqError("Instance '%s' is marked to be up" % - self.op.instance_name) + self.op.instance_name, + errors.ECODE_STATE) remote_info = self.rpc.call_instance_info(instance.primary_node, instance.name, instance.hypervisor) remote_info.Raise("Error checking node %s" % instance.primary_node, - prereq=True) + prereq=True, ecode=errors.ECODE_ENVIRON) if remote_info.payload: raise errors.OpPrereqError("Instance '%s' is running on the node %s" % (self.op.instance_name, - instance.primary_node)) + instance.primary_node), + errors.ECODE_STATE) self.op.os_type = getattr(self.op, "os_type", None) self.op.force_variant = getattr(self.op, "force_variant", False) if self.op.os_type is not None: # OS verification - pnode = self.cfg.GetNodeInfo( - self.cfg.ExpandNodeName(instance.primary_node)) - if pnode is None: - raise errors.OpPrereqError("Primary node '%s' is unknown" % - self.op.pnode) - result = self.rpc.call_os_get(pnode.name, self.op.os_type) + pnode = _ExpandNodeName(self.cfg, instance.primary_node) + result = self.rpc.call_os_get(pnode, self.op.os_type) result.Raise("OS '%s' not in supported OS list for primary node %s" % - (self.op.os_type, pnode.name), prereq=True) + (self.op.os_type, pnode), + prereq=True, ecode=errors.ECODE_INVAL) if not self.op.force_variant: _CheckOSVariant(result.payload, self.op.os_type) @@ -3835,7 +3985,9 @@ class LUReinstallInstance(LogicalUnit): _StartInstanceDisks(self, inst, None) try: feedback_fn("Running the instance OS create scripts...") - result = self.rpc.call_instance_os_add(inst.primary_node, inst, True) + # FIXME: pass debug option from opcode to backend + result = self.rpc.call_instance_os_add(inst.primary_node, inst, True, + self.op.debug_level) result.Raise("Could not install OS for instance %s on node %s" % (inst.name, inst.primary_node)) finally: @@ -3856,12 +4008,12 @@ class LURecreateInstanceDisks(LogicalUnit): """ if not isinstance(self.op.disks, list): - raise errors.OpPrereqError("Invalid disks parameter") + raise errors.OpPrereqError("Invalid disks parameter", errors.ECODE_INVAL) for item in self.op.disks: if (not isinstance(item, int) or item < 0): raise errors.OpPrereqError("Invalid disk specification '%s'" % - str(item)) + str(item), errors.ECODE_INVAL) def ExpandNames(self): self._ExpandAndLockInstance() @@ -3889,26 +4041,27 @@ class LURecreateInstanceDisks(LogicalUnit): if instance.disk_template == constants.DT_DISKLESS: raise errors.OpPrereqError("Instance '%s' has no disks" % - self.op.instance_name) + self.op.instance_name, errors.ECODE_INVAL) if instance.admin_up: raise errors.OpPrereqError("Instance '%s' is marked to be up" % - self.op.instance_name) + self.op.instance_name, errors.ECODE_STATE) remote_info = self.rpc.call_instance_info(instance.primary_node, instance.name, instance.hypervisor) remote_info.Raise("Error checking node %s" % instance.primary_node, - prereq=True) + prereq=True, ecode=errors.ECODE_ENVIRON) if remote_info.payload: raise errors.OpPrereqError("Instance '%s' is running on the node %s" % (self.op.instance_name, - instance.primary_node)) + instance.primary_node), errors.ECODE_STATE) if not self.op.disks: self.op.disks = range(len(instance.disks)) else: for idx in self.op.disks: if idx >= len(instance.disks): - raise errors.OpPrereqError("Invalid disk index passed '%s'" % idx) + raise errors.OpPrereqError("Invalid disk index passed '%s'" % idx, + errors.ECODE_INVAL) self.instance = instance @@ -3917,7 +4070,7 @@ class LURecreateInstanceDisks(LogicalUnit): """ to_skip = [] - for idx, disk in enumerate(self.instance.disks): + for idx, _ in enumerate(self.instance.disks): if idx not in self.op.disks: # disk idx has not been passed in to_skip.append(idx) continue @@ -3950,40 +4103,40 @@ class LURenameInstance(LogicalUnit): This checks that the instance is in the cluster and is not running. """ - instance = self.cfg.GetInstanceInfo( - self.cfg.ExpandInstanceName(self.op.instance_name)) - if instance is None: - raise errors.OpPrereqError("Instance '%s' not known" % - self.op.instance_name) + self.op.instance_name = _ExpandInstanceName(self.cfg, + self.op.instance_name) + instance = self.cfg.GetInstanceInfo(self.op.instance_name) + assert instance is not None _CheckNodeOnline(self, instance.primary_node) if instance.admin_up: raise errors.OpPrereqError("Instance '%s' is marked to be up" % - self.op.instance_name) + self.op.instance_name, errors.ECODE_STATE) remote_info = self.rpc.call_instance_info(instance.primary_node, instance.name, instance.hypervisor) remote_info.Raise("Error checking node %s" % instance.primary_node, - prereq=True) + prereq=True, ecode=errors.ECODE_ENVIRON) if remote_info.payload: raise errors.OpPrereqError("Instance '%s' is running on the node %s" % (self.op.instance_name, - instance.primary_node)) + instance.primary_node), errors.ECODE_STATE) self.instance = instance # new name verification - name_info = utils.HostInfo(self.op.new_name) + name_info = utils.GetHostInfo(self.op.new_name) self.op.new_name = new_name = name_info.name instance_list = self.cfg.GetInstanceList() if new_name in instance_list: raise errors.OpPrereqError("Instance '%s' is already in the cluster" % - new_name) + new_name, errors.ECODE_EXISTS) if not getattr(self.op, "ignore_ip", False): if utils.TcpPing(name_info.ip, constants.DEFAULT_NODED_PORT): raise errors.OpPrereqError("IP %s of instance %s already in use" % - (name_info.ip, new_name)) + (name_info.ip, new_name), + errors.ECODE_NOTUNIQUE) def Exec(self, feedback_fn): @@ -4017,7 +4170,7 @@ class LURenameInstance(LogicalUnit): _StartInstanceDisks(self, inst, None) try: result = self.rpc.call_instance_run_rename(inst.primary_node, inst, - old_name) + old_name, self.op.debug_level) msg = result.fail_msg if msg: msg = ("Could not run OS rename script for instance %s on node %s" @@ -4062,7 +4215,8 @@ class LURemoveInstance(LogicalUnit): env = _BuildInstanceHookEnvByObject(self, self.instance) env["SHUTDOWN_TIMEOUT"] = self.shutdown_timeout nl = [self.cfg.GetMasterNode()] - return env, nl, nl + nl_post = list(self.instance.all_nodes) + nl + return env, nl, nl_post def CheckPrereq(self): """Check prerequisites. @@ -4111,6 +4265,7 @@ class LUQueryInstances(NoHooksLU): """Logical unit for querying instances. """ + # pylint: disable-msg=W0142 _OP_REQP = ["output_fields", "names", "use_locking"] REQ_BGL = False _SIMPLE_FIELDS = ["name", "os", "network_port", "hypervisor", @@ -4130,7 +4285,8 @@ class LUQueryInstances(NoHooksLU): "hvparams", ] + _SIMPLE_FIELDS + ["hv/%s" % name - for name in constants.HVS_PARAMETERS] + + for name in constants.HVS_PARAMETERS + if name not in constants.HVC_GLOBALS] + ["be/%s" % name for name in constants.BES_PARAMETERS]) _FIELDS_DYNAMIC = utils.FieldSet("oper_state", "oper_ram", "status") @@ -4171,6 +4327,8 @@ class LUQueryInstances(NoHooksLU): """Computes the list of nodes and their attributes. """ + # pylint: disable-msg=R0912 + # way too many branches here all_info = self.cfg.GetAllInstancesInfo() if self.wanted == locking.ALL_SET: # caller didn't specify instance names, so ordering is not important @@ -4225,7 +4383,7 @@ class LUQueryInstances(NoHooksLU): cluster = self.cfg.GetClusterInfo() for instance in instance_list: iout = [] - i_hv = cluster.FillHV(instance) + i_hv = cluster.FillHV(instance, skip_globals=True) i_be = cluster.FillBE(instance) i_nicp = [objects.FillDict(cluster.nicparams[constants.PP_DEFAULT], nic.nicparams) for nic in instance.nics] @@ -4312,7 +4470,8 @@ class LUQueryInstances(NoHooksLU): elif field == "hvparams": val = i_hv elif (field.startswith(HVPREFIX) and - field[len(HVPREFIX):] in constants.HVS_PARAMETERS): + field[len(HVPREFIX):] in constants.HVS_PARAMETERS and + field[len(HVPREFIX):] not in constants.HVC_GLOBALS): val = i_hv.get(field[len(HVPREFIX):], None) elif field == "beparams": val = i_be @@ -4416,13 +4575,22 @@ class LUFailoverInstance(LogicalUnit): This runs on master, primary and secondary nodes of the instance. """ + instance = self.instance + source_node = instance.primary_node + target_node = instance.secondary_nodes[0] env = { "IGNORE_CONSISTENCY": self.op.ignore_consistency, "SHUTDOWN_TIMEOUT": self.shutdown_timeout, + "OLD_PRIMARY": source_node, + "OLD_SECONDARY": target_node, + "NEW_PRIMARY": target_node, + "NEW_SECONDARY": source_node, } - env.update(_BuildInstanceHookEnvByObject(self, self.instance)) - nl = [self.cfg.GetMasterNode()] + list(self.instance.secondary_nodes) - return env, nl, nl + env.update(_BuildInstanceHookEnvByObject(self, instance)) + nl = [self.cfg.GetMasterNode()] + list(instance.secondary_nodes) + nl_post = list(nl) + nl_post.append(source_node) + return env, nl, nl_post def CheckPrereq(self): """Check prerequisites. @@ -4437,7 +4605,8 @@ class LUFailoverInstance(LogicalUnit): bep = self.cfg.GetClusterInfo().FillBE(instance) if instance.disk_template not in constants.DTS_NET_MIRROR: raise errors.OpPrereqError("Instance's disk layout is not" - " network mirrored, cannot failover.") + " network mirrored, cannot failover.", + errors.ECODE_STATE) secondary_nodes = instance.secondary_nodes if not secondary_nodes: @@ -4563,11 +4732,21 @@ class LUMigrateInstance(LogicalUnit): """ instance = self._migrater.instance + source_node = instance.primary_node + target_node = instance.secondary_nodes[0] env = _BuildInstanceHookEnvByObject(self, instance) env["MIGRATE_LIVE"] = self.op.live env["MIGRATE_CLEANUP"] = self.op.cleanup + env.update({ + "OLD_PRIMARY": source_node, + "OLD_SECONDARY": target_node, + "NEW_PRIMARY": target_node, + "NEW_SECONDARY": source_node, + }) nl = [self.cfg.GetMasterNode()] + list(instance.secondary_nodes) - return env, nl, nl + nl_post = list(nl) + nl_post.append(source_node) + return env, nl, nl_post class LUMoveInstance(LogicalUnit): @@ -4588,10 +4767,7 @@ class LUMoveInstance(LogicalUnit): def ExpandNames(self): self._ExpandAndLockInstance() - target_node = self.cfg.ExpandNodeName(self.op.target_node) - if target_node is None: - raise errors.OpPrereqError("Node '%s' not known" % - self.op.target_node) + target_node = _ExpandNodeName(self.cfg, self.op.target_node) self.op.target_node = target_node self.needed_locks[locking.LEVEL_NODE] = [target_node] self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND @@ -4633,14 +4809,15 @@ class LUMoveInstance(LogicalUnit): if target_node == instance.primary_node: raise errors.OpPrereqError("Instance %s is already on the node %s" % - (instance.name, target_node)) + (instance.name, target_node), + errors.ECODE_STATE) bep = self.cfg.GetClusterInfo().FillBE(instance) for idx, dsk in enumerate(instance.disks): if dsk.dev_type not in (constants.LD_LV, constants.LD_FILE): raise errors.OpPrereqError("Instance disk %d has a complex layout," - " cannot copy") + " cannot copy" % idx, errors.ECODE_STATE) _CheckNodeOnline(self, target_node) _CheckNodeNotDrained(self, target_node) @@ -4764,9 +4941,7 @@ class LUMigrateNode(LogicalUnit): REQ_BGL = False def ExpandNames(self): - self.op.node_name = self.cfg.ExpandNodeName(self.op.node_name) - if self.op.node_name is None: - raise errors.OpPrereqError("Node '%s' not known" % self.op.node_name) + self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name) self.needed_locks = { locking.LEVEL_NODE: [self.op.node_name], @@ -4826,15 +5001,13 @@ class TLMigrateInstance(Tasklet): This checks that the instance is in the cluster. """ - instance = self.cfg.GetInstanceInfo( - self.cfg.ExpandInstanceName(self.instance_name)) - if instance is None: - raise errors.OpPrereqError("Instance '%s' not known" % - self.instance_name) + instance_name = _ExpandInstanceName(self.lu.cfg, self.instance_name) + instance = self.cfg.GetInstanceInfo(instance_name) + assert instance is not None if instance.disk_template != constants.DT_DRBD8: raise errors.OpPrereqError("Instance's disk layout is not" - " drbd8, cannot migrate.") + " drbd8, cannot migrate.", errors.ECODE_STATE) secondary_nodes = instance.secondary_nodes if not secondary_nodes: @@ -4856,7 +5029,8 @@ class TLMigrateInstance(Tasklet): _CheckNodeNotDrained(self, target_node) result = self.rpc.call_instance_migratable(instance.primary_node, instance) - result.Raise("Can't migrate, please use failover", prereq=True) + result.Raise("Can't migrate, please use failover", + prereq=True, ecode=errors.ECODE_STATE) self.instance = instance @@ -5018,8 +5192,8 @@ class TLMigrateInstance(Tasklet): False) abort_msg = abort_result.fail_msg if abort_msg: - logging.error("Aborting migration failed on target node %s: %s" % - (target_node, abort_msg)) + logging.error("Aborting migration failed on target node %s: %s", + target_node, abort_msg) # Don't raise an exception here, as we stil have to try to revert the # disk status, even if this step failed. @@ -5073,6 +5247,7 @@ class TLMigrateInstance(Tasklet): if msg: logging.error("Instance pre-migration failed, trying to revert" " disk status: %s", msg) + self.feedback_fn("Pre-migration failed, aborting") self._AbortMigration() self._RevertDiskStatus() raise errors.OpExecError("Could not pre-migrate instance %s: %s" % @@ -5087,6 +5262,7 @@ class TLMigrateInstance(Tasklet): if msg: logging.error("Instance migration failed, trying to revert" " disk status: %s", msg) + self.feedback_fn("Migration failed, aborting") self._AbortMigration() self._RevertDiskStatus() raise errors.OpExecError("Could not migrate instance %s: %s" % @@ -5104,7 +5280,7 @@ class TLMigrateInstance(Tasklet): msg = result.fail_msg if msg: logging.error("Instance migration succeeded, but finalization failed:" - " %s" % msg) + " %s", msg) raise errors.OpExecError("Could not finalize instance migration: %s" % msg) @@ -5218,7 +5394,7 @@ def _GenerateUniqueNames(lu, exts): """ results = [] for val in exts: - new_id = lu.cfg.GenerateUniqueID() + new_id = lu.cfg.GenerateUniqueID(lu.proc.GetECId()) results.append("%s%s" % (new_id, val)) return results @@ -5230,7 +5406,7 @@ def _GenerateDRBD8Branch(lu, primary, secondary, size, names, iv_name, """ port = lu.cfg.AllocatePort() vgname = lu.cfg.GetVGName() - shared_secret = lu.cfg.GenerateDRBDSecret() + shared_secret = lu.cfg.GenerateDRBDSecret(lu.proc.GetECId()) dev_data = objects.Disk(dev_type=constants.LD_LV, size=size, logical_id=(vgname, names[0])) dev_meta = objects.Disk(dev_type=constants.LD_LV, size=128, @@ -5470,14 +5646,18 @@ class LUCreateInstance(LogicalUnit): "hvparams", "beparams"] REQ_BGL = False - def _ExpandNode(self, node): - """Expands and checks one node name. + def CheckArguments(self): + """Check arguments. """ - node_full = self.cfg.ExpandNodeName(node) - if node_full is None: - raise errors.OpPrereqError("Unknown node %s" % node) - return node_full + # do not require name_check to ease forward/backward compatibility + # for tools + if not hasattr(self.op, "name_check"): + self.op.name_check = True + if self.op.ip_check and not self.op.name_check: + # TODO: make the ip check more flexible and not depend on the name check + raise errors.OpPrereqError("Cannot do ip checks without a name check", + errors.ECODE_INVAL) def ExpandNames(self): """ExpandNames for CreateInstance. @@ -5498,11 +5678,12 @@ class LUCreateInstance(LogicalUnit): if self.op.mode not in (constants.INSTANCE_CREATE, constants.INSTANCE_IMPORT): raise errors.OpPrereqError("Invalid instance creation mode '%s'" % - self.op.mode) + self.op.mode, errors.ECODE_INVAL) # disk template and mirror node verification if self.op.disk_template not in constants.DISK_TEMPLATES: - raise errors.OpPrereqError("Invalid disk template name") + raise errors.OpPrereqError("Invalid disk template name", + errors.ECODE_INVAL) if self.op.hypervisor is None: self.op.hypervisor = self.cfg.GetHypervisorType() @@ -5512,7 +5693,8 @@ class LUCreateInstance(LogicalUnit): if self.op.hypervisor not in enabled_hvs: raise errors.OpPrereqError("Selected hypervisor (%s) not enabled in the" " cluster (%s)" % (self.op.hypervisor, - ",".join(enabled_hvs))) + ",".join(enabled_hvs)), + errors.ECODE_STATE) # check hypervisor parameter syntax (locally) utils.ForceDictType(self.op.hvparams, constants.HVS_PARAMETER_TYPES) @@ -5521,6 +5703,8 @@ class LUCreateInstance(LogicalUnit): hv_type = hypervisor.GetHypervisor(self.op.hypervisor) hv_type.CheckParameterSyntax(filled_hvp) self.hv_full = filled_hvp + # check that we don't specify global parameters on an instance + _CheckGlobalHvParams(self.op.hvparams) # fill and remember the beparams dict utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES) @@ -5530,14 +5714,20 @@ class LUCreateInstance(LogicalUnit): #### instance parameters check # instance name verification - hostname1 = utils.HostInfo(self.op.instance_name) - self.op.instance_name = instance_name = hostname1.name + if self.op.name_check: + hostname1 = utils.GetHostInfo(self.op.instance_name) + self.op.instance_name = instance_name = hostname1.name + # used in CheckPrereq for ip ping check + self.check_ip = hostname1.ip + else: + instance_name = self.op.instance_name + self.check_ip = None # this is just a preventive check, but someone might still add this # instance in the meantime, and creation will fail at lock-add time if instance_name in self.cfg.GetInstanceList(): raise errors.OpPrereqError("Instance '%s' is already in the cluster" % - instance_name) + instance_name, errors.ECODE_EXISTS) self.add_locks[locking.LEVEL_INSTANCE] = instance_name @@ -5560,37 +5750,44 @@ class LUCreateInstance(LogicalUnit): if ip is None or ip.lower() == constants.VALUE_NONE: nic_ip = None elif ip.lower() == constants.VALUE_AUTO: + if not self.op.name_check: + raise errors.OpPrereqError("IP address set to auto but name checks" + " have been skipped. Aborting.", + errors.ECODE_INVAL) nic_ip = hostname1.ip else: if not utils.IsValidIP(ip): raise errors.OpPrereqError("Given IP address '%s' doesn't look" - " like a valid IP" % ip) + " like a valid IP" % ip, + errors.ECODE_INVAL) nic_ip = ip - # TODO: check the ip for uniqueness !! + # TODO: check the ip address for uniqueness if nic_mode == constants.NIC_MODE_ROUTED and not nic_ip: - raise errors.OpPrereqError("Routed nic mode requires an ip address") + raise errors.OpPrereqError("Routed nic mode requires an ip address", + errors.ECODE_INVAL) # MAC address verification mac = nic.get("mac", constants.VALUE_AUTO) if mac not in (constants.VALUE_AUTO, constants.VALUE_GENERATE): - if not utils.IsValidMac(mac.lower()): - raise errors.OpPrereqError("Invalid MAC address specified: %s" % - mac) - else: - # or validate/reserve the current one - if self.cfg.IsMacInUse(mac): - raise errors.OpPrereqError("MAC address %s already in use" - " in cluster" % mac) + mac = utils.NormalizeAndValidateMac(mac) + + try: + self.cfg.ReserveMAC(mac, self.proc.GetECId()) + except errors.ReservationError: + raise errors.OpPrereqError("MAC address %s already in use" + " in cluster" % mac, + errors.ECODE_NOTUNIQUE) # bridge verification bridge = nic.get("bridge", None) link = nic.get("link", None) if bridge and link: raise errors.OpPrereqError("Cannot pass 'bridge' and 'link'" - " at the same time") + " at the same time", errors.ECODE_INVAL) elif bridge and nic_mode == constants.NIC_MODE_ROUTED: - raise errors.OpPrereqError("Cannot pass 'bridge' on a routed nic") + raise errors.OpPrereqError("Cannot pass 'bridge' on a routed nic", + errors.ECODE_INVAL) elif bridge: link = bridge @@ -5611,40 +5808,40 @@ class LUCreateInstance(LogicalUnit): mode = disk.get("mode", constants.DISK_RDWR) if mode not in constants.DISK_ACCESS_SET: raise errors.OpPrereqError("Invalid disk access mode '%s'" % - mode) + mode, errors.ECODE_INVAL) size = disk.get("size", None) if size is None: - raise errors.OpPrereqError("Missing disk size") + raise errors.OpPrereqError("Missing disk size", errors.ECODE_INVAL) try: size = int(size) - except ValueError: - raise errors.OpPrereqError("Invalid disk size '%s'" % size) + except (TypeError, ValueError): + raise errors.OpPrereqError("Invalid disk size '%s'" % size, + errors.ECODE_INVAL) self.disks.append({"size": size, "mode": mode}) - # used in CheckPrereq for ip ping check - self.check_ip = hostname1.ip - # file storage checks if (self.op.file_driver and not self.op.file_driver in constants.FILE_DRIVER): raise errors.OpPrereqError("Invalid file driver name '%s'" % - self.op.file_driver) + self.op.file_driver, errors.ECODE_INVAL) if self.op.file_storage_dir and os.path.isabs(self.op.file_storage_dir): - raise errors.OpPrereqError("File storage directory path not absolute") + raise errors.OpPrereqError("File storage directory path not absolute", + errors.ECODE_INVAL) ### Node/iallocator related checks if [self.op.iallocator, self.op.pnode].count(None) != 1: raise errors.OpPrereqError("One and only one of iallocator and primary" - " node must be given") + " node must be given", + errors.ECODE_INVAL) if self.op.iallocator: self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET else: - self.op.pnode = self._ExpandNode(self.op.pnode) + self.op.pnode = _ExpandNodeName(self.cfg, self.op.pnode) nodelist = [self.op.pnode] if self.op.snode is not None: - self.op.snode = self._ExpandNode(self.op.snode) + self.op.snode = _ExpandNodeName(self.cfg, self.op.snode) nodelist.append(self.op.snode) self.needed_locks[locking.LEVEL_NODE] = nodelist @@ -5661,9 +5858,10 @@ class LUCreateInstance(LogicalUnit): self.op.src_node = None if os.path.isabs(src_path): raise errors.OpPrereqError("Importing an instance from an absolute" - " path requires a source node option.") + " path requires a source node option.", + errors.ECODE_INVAL) else: - self.op.src_node = src_node = self._ExpandNode(src_node) + self.op.src_node = src_node = _ExpandNodeName(self.cfg, src_node) if self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET: self.needed_locks[locking.LEVEL_NODE].append(src_node) if not os.path.isabs(src_path): @@ -5677,7 +5875,8 @@ class LUCreateInstance(LogicalUnit): else: # INSTANCE_CREATE if getattr(self.op, "os_type", None) is None: - raise errors.OpPrereqError("No guest OS specified") + raise errors.OpPrereqError("No guest OS specified", + errors.ECODE_INVAL) self.op.force_variant = getattr(self.op, "force_variant", False) def _RunAllocator(self): @@ -5702,17 +5901,18 @@ class LUCreateInstance(LogicalUnit): if not ial.success: raise errors.OpPrereqError("Can't compute nodes using" - " iallocator '%s': %s" % (self.op.iallocator, - ial.info)) + " iallocator '%s': %s" % + (self.op.iallocator, ial.info), + errors.ECODE_NORES) if len(ial.nodes) != ial.required_nodes: raise errors.OpPrereqError("iallocator '%s' returned invalid number" " of nodes (%s), required %s" % (self.op.iallocator, len(ial.nodes), - ial.required_nodes)) + ial.required_nodes), errors.ECODE_FAULT) self.op.pnode = ial.nodes[0] self.LogInfo("Selected nodes for instance %s via iallocator %s: %s", self.op.instance_name, self.op.iallocator, - ", ".join(ial.nodes)) + utils.CommaJoin(ial.nodes)) if ial.required_nodes == 2: self.op.snode = ial.nodes[1] @@ -5758,7 +5958,7 @@ class LUCreateInstance(LogicalUnit): if (not self.cfg.GetVGName() and self.op.disk_template not in constants.DTS_NOT_LVM): raise errors.OpPrereqError("Cluster does not support lvm-based" - " instances") + " instances", errors.ECODE_STATE) if self.op.mode == constants.INSTANCE_IMPORT: src_node = self.op.src_node @@ -5779,7 +5979,7 @@ class LUCreateInstance(LogicalUnit): break if not found: raise errors.OpPrereqError("No export found for relative path %s" % - src_path) + src_path, errors.ECODE_INVAL) _CheckNodeOnline(self, src_node) result = self.rpc.call_export_info(src_node, src_path) @@ -5787,12 +5987,14 @@ class LUCreateInstance(LogicalUnit): export_info = objects.SerializableConfigParser.Loads(str(result.payload)) if not export_info.has_section(constants.INISECT_EXP): - raise errors.ProgrammerError("Corrupted export config") + raise errors.ProgrammerError("Corrupted export config", + errors.ECODE_ENVIRON) ei_version = export_info.get(constants.INISECT_EXP, 'version') if (int(ei_version) != constants.EXPORT_VERSION): raise errors.OpPrereqError("Wrong export version %s (wanted %d)" % - (ei_version, constants.EXPORT_VERSION)) + (ei_version, constants.EXPORT_VERSION), + errors.ECODE_ENVIRON) # Check that the new instance doesn't have less disks than the export instance_disks = len(self.disks) @@ -5800,7 +6002,8 @@ class LUCreateInstance(LogicalUnit): if instance_disks < export_disks: raise errors.OpPrereqError("Not enough disks to import." " (instance: %d, export: %d)" % - (instance_disks, export_disks)) + (instance_disks, export_disks), + errors.ECODE_INVAL) self.op.os_type = export_info.get(constants.INISECT_EXP, 'os') disk_images = [] @@ -5826,15 +6029,13 @@ class LUCreateInstance(LogicalUnit): nic.mac = export_info.get(constants.INISECT_INS, nic_mac_ini) # ENDIF: self.op.mode == constants.INSTANCE_IMPORT - # ip ping checks (we use the same ip that was resolved in ExpandNames) - if self.op.start and not self.op.ip_check: - raise errors.OpPrereqError("Cannot ignore IP address conflicts when" - " adding an instance in start mode") + # ip ping checks (we use the same ip that was resolved in ExpandNames) if self.op.ip_check: if utils.TcpPing(self.check_ip, constants.DEFAULT_NODED_PORT): raise errors.OpPrereqError("IP %s of instance %s already in use" % - (self.check_ip, self.op.instance_name)) + (self.check_ip, self.op.instance_name), + errors.ECODE_NOTUNIQUE) #### mac address generation # By generating here the mac address both the allocator and the hooks get @@ -5846,7 +6047,7 @@ class LUCreateInstance(LogicalUnit): # creation job will fail. for nic in self.nics: if nic.mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE): - nic.mac = self.cfg.GenerateMAC() + nic.mac = self.cfg.GenerateMAC(self.proc.GetECId()) #### allocator run @@ -5861,10 +6062,10 @@ class LUCreateInstance(LogicalUnit): "Cannot retrieve locked node %s" % self.op.pnode if pnode.offline: raise errors.OpPrereqError("Cannot use offline primary node '%s'" % - pnode.name) + pnode.name, errors.ECODE_STATE) if pnode.drained: raise errors.OpPrereqError("Cannot use drained primary node '%s'" % - pnode.name) + pnode.name, errors.ECODE_STATE) self.secondaries = [] @@ -5872,10 +6073,10 @@ class LUCreateInstance(LogicalUnit): if self.op.disk_template in constants.DTS_NET_MIRROR: if self.op.snode is None: raise errors.OpPrereqError("The networked disk templates need" - " a mirror node") + " a mirror node", errors.ECODE_INVAL) if self.op.snode == pnode.name: - raise errors.OpPrereqError("The secondary node cannot be" - " the primary node.") + raise errors.OpPrereqError("The secondary node cannot be the" + " primary node.", errors.ECODE_INVAL) _CheckNodeOnline(self, self.op.snode) _CheckNodeNotDrained(self, self.op.snode) self.secondaries.append(self.op.snode) @@ -5896,18 +6097,20 @@ class LUCreateInstance(LogicalUnit): vg_free = info.get('vg_free', None) if not isinstance(vg_free, int): raise errors.OpPrereqError("Can't compute free disk space on" - " node %s" % node) + " node %s" % node, errors.ECODE_ENVIRON) if req_size > vg_free: raise errors.OpPrereqError("Not enough disk space on target node %s." " %d MB available, %d MB required" % - (node, vg_free, req_size)) + (node, vg_free, req_size), + errors.ECODE_NORES) _CheckHVParams(self, nodenames, self.op.hypervisor, self.op.hvparams) # os verification result = self.rpc.call_os_get(pnode.name, self.op.os_type) result.Raise("OS '%s' not in supported os list for primary node %s" % - (self.op.os_type, pnode.name), prereq=True) + (self.op.os_type, pnode.name), + prereq=True, ecode=errors.ECODE_INVAL) if not self.op.force_variant: _CheckOSVariant(result.payload, self.op.os_type) @@ -5983,7 +6186,8 @@ class LUCreateInstance(LogicalUnit): feedback_fn("adding instance %s to cluster config" % instance) - self.cfg.AddInstance(iobj) + self.cfg.AddInstance(iobj, self.proc.GetECId()) + # Declare that we don't want to remove the instance lock anymore, as we've # added the instance to the config del self.remove_locks[locking.LEVEL_INSTANCE] @@ -6022,7 +6226,9 @@ class LUCreateInstance(LogicalUnit): if iobj.disk_template != constants.DT_DISKLESS: if self.op.mode == constants.INSTANCE_CREATE: feedback_fn("* running the instance OS create scripts...") - result = self.rpc.call_instance_os_add(pnode_name, iobj, False) + # FIXME: pass debug option from opcode to backend + result = self.rpc.call_instance_os_add(pnode_name, iobj, False, + self.op.debug_level) result.Raise("Could not add os for instance %s" " on node %s" % (instance, pnode_name)) @@ -6031,9 +6237,11 @@ class LUCreateInstance(LogicalUnit): src_node = self.op.src_node src_images = self.src_images cluster_name = self.cfg.GetClusterName() + # FIXME: pass debug option from opcode to backend import_result = self.rpc.call_instance_os_import(pnode_name, iobj, src_node, src_images, - cluster_name) + cluster_name, + self.op.debug_level) msg = import_result.fail_msg if msg: self.LogWarning("Error while importing the disk images for instance" @@ -6121,6 +6329,8 @@ class LUReplaceDisks(LogicalUnit): self.op.remote_node = None if not hasattr(self.op, "iallocator"): self.op.iallocator = None + if not hasattr(self.op, "early_release"): + self.op.early_release = False TLReplaceDisks.CheckArguments(self.op.mode, self.op.remote_node, self.op.iallocator) @@ -6132,11 +6342,7 @@ class LUReplaceDisks(LogicalUnit): self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET elif self.op.remote_node is not None: - remote_node = self.cfg.ExpandNodeName(self.op.remote_node) - if remote_node is None: - raise errors.OpPrereqError("Node '%s' not known" % - self.op.remote_node) - + remote_node = _ExpandNodeName(self.cfg, self.op.remote_node) self.op.remote_node = remote_node # Warning: do not remove the locking of the new secondary here @@ -6152,7 +6358,7 @@ class LUReplaceDisks(LogicalUnit): self.replacer = TLReplaceDisks(self, self.op.instance_name, self.op.mode, self.op.iallocator, self.op.remote_node, - self.op.disks) + self.op.disks, False, self.op.early_release) self.tasklets = [self.replacer] @@ -6199,15 +6405,15 @@ class LUEvacuateNode(LogicalUnit): self.op.remote_node = None if not hasattr(self.op, "iallocator"): self.op.iallocator = None + if not hasattr(self.op, "early_release"): + self.op.early_release = False TLReplaceDisks.CheckArguments(constants.REPLACE_DISK_CHG, self.op.remote_node, self.op.iallocator) def ExpandNames(self): - self.op.node_name = self.cfg.ExpandNodeName(self.op.node_name) - if self.op.node_name is None: - raise errors.OpPrereqError("Node '%s' not known" % self.op.node_name) + self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name) self.needed_locks = {} @@ -6216,22 +6422,17 @@ class LUEvacuateNode(LogicalUnit): self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET elif self.op.remote_node is not None: - remote_node = self.cfg.ExpandNodeName(self.op.remote_node) - if remote_node is None: - raise errors.OpPrereqError("Node '%s' not known" % - self.op.remote_node) - - self.op.remote_node = remote_node + self.op.remote_node = _ExpandNodeName(self.cfg, self.op.remote_node) # Warning: do not remove the locking of the new secondary here # unless DRBD8.AddChildren is changed to work in parallel; # currently it doesn't since parallel invocations of # FindUnusedMinor will conflict - self.needed_locks[locking.LEVEL_NODE] = [remote_node] + self.needed_locks[locking.LEVEL_NODE] = [self.op.remote_node] self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND else: - raise errors.OpPrereqError("Invalid parameters") + raise errors.OpPrereqError("Invalid parameters", errors.ECODE_INVAL) # Create tasklets for replacing disks for all secondary instances on this # node @@ -6243,7 +6444,8 @@ class LUEvacuateNode(LogicalUnit): names.append(inst.name) replacer = TLReplaceDisks(self, inst.name, constants.REPLACE_DISK_CHG, - self.op.iallocator, self.op.remote_node, []) + self.op.iallocator, self.op.remote_node, [], + True, self.op.early_release) tasklets.append(replacer) self.tasklets = tasklets @@ -6285,7 +6487,7 @@ class TLReplaceDisks(Tasklet): """ def __init__(self, lu, instance_name, mode, iallocator_name, remote_node, - disks): + disks, delay_iallocator, early_release): """Initializes this class. """ @@ -6297,6 +6499,8 @@ class TLReplaceDisks(Tasklet): self.iallocator_name = iallocator_name self.remote_node = remote_node self.disks = disks + self.delay_iallocator = delay_iallocator + self.early_release = early_release # Runtime data self.instance = None @@ -6316,17 +6520,17 @@ class TLReplaceDisks(Tasklet): if remote_node is None and iallocator is None: raise errors.OpPrereqError("When changing the secondary either an" " iallocator script must be used or the" - " new node given") + " new node given", errors.ECODE_INVAL) if remote_node is not None and iallocator is not None: raise errors.OpPrereqError("Give either the iallocator or the new" - " secondary, not both") + " secondary, not both", errors.ECODE_INVAL) elif remote_node is not None or iallocator is not None: # Not replacing the secondary raise errors.OpPrereqError("The iallocator and new node options can" " only be used when changing the" - " secondary node") + " secondary node", errors.ECODE_INVAL) @staticmethod def _RunAllocator(lu, iallocator_name, instance_name, relocate_from): @@ -6342,12 +6546,15 @@ class TLReplaceDisks(Tasklet): if not ial.success: raise errors.OpPrereqError("Can't compute nodes using iallocator '%s':" - " %s" % (iallocator_name, ial.info)) + " %s" % (iallocator_name, ial.info), + errors.ECODE_NORES) if len(ial.nodes) != ial.required_nodes: raise errors.OpPrereqError("iallocator '%s' returned invalid number" " of nodes (%s), required %s" % - (len(ial.nodes), ial.required_nodes)) + (iallocator_name, + len(ial.nodes), ial.required_nodes), + errors.ECODE_FAULT) remote_node_name = ial.nodes[0] @@ -6372,13 +6579,27 @@ class TLReplaceDisks(Tasklet): if instance.disk_template != constants.DT_DRBD8: raise errors.OpPrereqError("Can only run replace disks for DRBD8-based" - " instances") + " instances", errors.ECODE_INVAL) if len(instance.secondary_nodes) != 1: raise errors.OpPrereqError("The instance has a strange layout," " expected one secondary but found %d" % - len(instance.secondary_nodes)) + len(instance.secondary_nodes), + errors.ECODE_FAULT) + + if not self.delay_iallocator: + self._CheckPrereq2() + def _CheckPrereq2(self): + """Check prerequisites, second part. + + This function should always be part of CheckPrereq. It was separated and is + now called from Exec because during node evacuation iallocator was only + called with an unmodified cluster model, not taking planned changes into + account. + + """ + instance = self.instance secondary_node = instance.secondary_nodes[0] if self.iallocator_name is None: @@ -6396,15 +6617,17 @@ class TLReplaceDisks(Tasklet): if remote_node == self.instance.primary_node: raise errors.OpPrereqError("The specified node is the primary node of" - " the instance.") + " the instance.", errors.ECODE_INVAL) if remote_node == secondary_node: raise errors.OpPrereqError("The specified node is already the" - " secondary node of the instance.") + " secondary node of the instance.", + errors.ECODE_INVAL) if self.disks and self.mode in (constants.REPLACE_DISK_AUTO, constants.REPLACE_DISK_CHG): - raise errors.OpPrereqError("Cannot specify disks to be replaced") + raise errors.OpPrereqError("Cannot specify disks to be replaced", + errors.ECODE_INVAL) if self.mode == constants.REPLACE_DISK_AUTO: faulty_primary = self._FindFaultyDisks(instance.primary_node) @@ -6413,7 +6636,8 @@ class TLReplaceDisks(Tasklet): if faulty_primary and faulty_secondary: raise errors.OpPrereqError("Instance %s has faulty disks on more than" " one node and can not be repaired" - " automatically" % self.instance_name) + " automatically" % self.instance_name, + errors.ECODE_STATE) if faulty_primary: self.disks = faulty_primary @@ -6449,6 +6673,14 @@ class TLReplaceDisks(Tasklet): _CheckNodeNotDrained(self.lu, remote_node) + old_node_info = self.cfg.GetNodeInfo(secondary_node) + assert old_node_info is not None + if old_node_info.offline and not self.early_release: + # doesn't make sense to delay the release + self.early_release = True + self.lu.LogInfo("Old secondary %s is offline, automatically enabling" + " early-release mode", secondary_node) + else: raise errors.ProgrammerError("Unhandled disk replace mode (%s)" % self.mode) @@ -6479,12 +6711,15 @@ class TLReplaceDisks(Tasklet): This dispatches the disk replacement to the appropriate handler. """ + if self.delay_iallocator: + self._CheckPrereq2() + if not self.disks: feedback_fn("No disks need replacement") return feedback_fn("Replacing disk(s) %s for %s" % - (", ".join([str(i) for i in self.disks]), self.instance.name)) + (utils.CommaJoin(self.disks), self.instance.name)) activate_disks = (not self.instance.admin_up) @@ -6502,7 +6737,8 @@ class TLReplaceDisks(Tasklet): return fn(feedback_fn) finally: - # Deactivate the instance disks if we're replacing them on a down instance + # Deactivate the instance disks if we're replacing them on a + # down instance if activate_disks: _SafeShutdownInstanceDisks(self.lu, self.instance) @@ -6588,7 +6824,7 @@ class TLReplaceDisks(Tasklet): return iv_names def _CheckDevices(self, node_name, iv_names): - for name, (dev, old_lvs, new_lvs) in iv_names.iteritems(): + for name, (dev, _, _) in iv_names.iteritems(): self.cfg.SetDiskID(dev, node_name) result = self.rpc.call_blockdev_find(node_name, dev) @@ -6604,7 +6840,7 @@ class TLReplaceDisks(Tasklet): raise errors.OpExecError("DRBD device %s is degraded!" % name) def _RemoveOldStorage(self, node_name, iv_names): - for name, (dev, old_lvs, _) in iv_names.iteritems(): + for name, (_, old_lvs, _) in iv_names.iteritems(): self.lu.LogInfo("Remove logical volumes for %s" % name) for lv in old_lvs: @@ -6615,6 +6851,10 @@ class TLReplaceDisks(Tasklet): self.lu.LogWarning("Can't remove old LV: %s" % msg, hint="remove unused LVs manually") + def _ReleaseNodeLock(self, node_name): + """Releases the lock for a given node.""" + self.lu.context.glm.release(locking.LEVEL_NODE, node_name) + def _ExecDrbd8DiskOnly(self, feedback_fn): """Replace a disk on the primary or secondary for DRBD 8. @@ -6725,18 +6965,30 @@ class TLReplaceDisks(Tasklet): self.cfg.Update(self.instance, feedback_fn) + cstep = 5 + if self.early_release: + self.lu.LogStep(cstep, steps_total, "Removing old storage") + cstep += 1 + self._RemoveOldStorage(self.target_node, iv_names) + # WARNING: we release both node locks here, do not do other RPCs + # than WaitForSync to the primary node + self._ReleaseNodeLock([self.target_node, self.other_node]) + # Wait for sync # This can fail as the old devices are degraded and _WaitForSync # does a combined result over all disks, so we don't check its return value - self.lu.LogStep(5, steps_total, "Sync devices") - _WaitForSync(self.lu, self.instance, unlock=True) + self.lu.LogStep(cstep, steps_total, "Sync devices") + cstep += 1 + _WaitForSync(self.lu, self.instance) # Check all devices manually self._CheckDevices(self.instance.primary_node, iv_names) # Step: remove old storage - self.lu.LogStep(6, steps_total, "Removing old storage") - self._RemoveOldStorage(self.target_node, iv_names) + if not self.early_release: + self.lu.LogStep(cstep, steps_total, "Removing old storage") + cstep += 1 + self._RemoveOldStorage(self.target_node, iv_names) def _ExecDrbd8Secondary(self, feedback_fn): """Replace the secondary node for DRBD 8. @@ -6785,7 +7037,7 @@ class TLReplaceDisks(Tasklet): minors = self.cfg.AllocateDRBDMinor([self.new_node for dev in self.instance.disks], self.instance.name) - logging.debug("Allocated minors %r" % (minors,)) + logging.debug("Allocated minors %r", minors) iv_names = {} for idx, (dev, new_minor) in enumerate(zip(self.instance.disks, minors)): @@ -6799,6 +7051,7 @@ class TLReplaceDisks(Tasklet): if self.instance.primary_node == o_node1: p_minor = o_minor1 else: + assert self.instance.primary_node == o_node2, "Three-node instance?" p_minor = o_minor2 new_alone_id = (self.instance.primary_node, self.new_node, None, @@ -6869,19 +7122,31 @@ class TLReplaceDisks(Tasklet): to_node, msg, hint=("please do a gnt-instance info to see the" " status of disks")) + cstep = 5 + if self.early_release: + self.lu.LogStep(cstep, steps_total, "Removing old storage") + cstep += 1 + self._RemoveOldStorage(self.target_node, iv_names) + # WARNING: we release all node locks here, do not do other RPCs + # than WaitForSync to the primary node + self._ReleaseNodeLock([self.instance.primary_node, + self.target_node, + self.new_node]) # Wait for sync # This can fail as the old devices are degraded and _WaitForSync # does a combined result over all disks, so we don't check its return value - self.lu.LogStep(5, steps_total, "Sync devices") - _WaitForSync(self.lu, self.instance, unlock=True) + self.lu.LogStep(cstep, steps_total, "Sync devices") + cstep += 1 + _WaitForSync(self.lu, self.instance) # Check all devices manually self._CheckDevices(self.instance.primary_node, iv_names) # Step: remove old storage - self.lu.LogStep(6, steps_total, "Removing old storage") - self._RemoveOldStorage(self.target_node, iv_names) + if not self.early_release: + self.lu.LogStep(cstep, steps_total, "Removing old storage") + self._RemoveOldStorage(self.target_node, iv_names) class LURepairNodeStorage(NoHooksLU): @@ -6892,11 +7157,7 @@ class LURepairNodeStorage(NoHooksLU): REQ_BGL = False def CheckArguments(self): - node_name = self.cfg.ExpandNodeName(self.op.node_name) - if node_name is None: - raise errors.OpPrereqError("Invalid node name '%s'" % self.op.node_name) - - self.op.node_name = node_name + self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name) def ExpandNames(self): self.needed_locks = { @@ -6904,10 +7165,18 @@ class LURepairNodeStorage(NoHooksLU): } def _CheckFaultyDisks(self, instance, node_name): - if _FindFaultyInstanceDisks(self.cfg, self.rpc, instance, - node_name, True): - raise errors.OpPrereqError("Instance '%s' has faulty disks on" - " node '%s'" % (instance.name, node_name)) + """Ensure faulty disks abort the opcode or at least warn.""" + try: + if _FindFaultyInstanceDisks(self.cfg, self.rpc, instance, + node_name, True): + raise errors.OpPrereqError("Instance '%s' has faulty disks on" + " node '%s'" % (instance.name, node_name), + errors.ECODE_STATE) + except errors.OpPrereqError, err: + if self.op.ignore_consistency: + self.proc.LogWarning(str(err.args[0])) + else: + raise def CheckPrereq(self): """Check prerequisites. @@ -6918,10 +7187,13 @@ class LURepairNodeStorage(NoHooksLU): if (constants.SO_FIX_CONSISTENCY not in constants.VALID_STORAGE_OPERATIONS.get(storage_type, [])): raise errors.OpPrereqError("Storage units of type '%s' can not be" - " repaired" % storage_type) + " repaired" % storage_type, + errors.ECODE_INVAL) # Check whether any instance on this node has faulty disks for inst in _GetNodeInstances(self.cfg, self.op.node_name): + if not inst.admin_up: + continue check_nodes = set(inst.all_nodes) check_nodes.discard(self.op.node_name) for inst_node_name in check_nodes: @@ -6969,10 +7241,7 @@ class LUGrowDisk(LogicalUnit): "AMOUNT": self.op.amount, } env.update(_BuildInstanceHookEnvByObject(self, self.instance)) - nl = [ - self.cfg.GetMasterNode(), - self.instance.primary_node, - ] + nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes) return env, nl, nl def CheckPrereq(self): @@ -6993,7 +7262,7 @@ class LUGrowDisk(LogicalUnit): if instance.disk_template not in (constants.DT_PLAIN, constants.DT_DRBD8): raise errors.OpPrereqError("Instance's disk layout does not support" - " growing.") + " growing.", errors.ECODE_INVAL) self.disk = instance.FindDisk(self.op.disk) @@ -7005,11 +7274,12 @@ class LUGrowDisk(LogicalUnit): vg_free = info.payload.get('vg_free', None) if not isinstance(vg_free, int): raise errors.OpPrereqError("Can't compute free disk space on" - " node %s" % node) + " node %s" % node, errors.ECODE_ENVIRON) if self.op.amount > vg_free: raise errors.OpPrereqError("Not enough disk space on target node %s:" " %d MiB available, %d MiB required" % - (node, vg_free, self.op.amount)) + (node, vg_free, self.op.amount), + errors.ECODE_NORES) def Exec(self, feedback_fn): """Execute disk grow. @@ -7021,6 +7291,14 @@ class LUGrowDisk(LogicalUnit): self.cfg.SetDiskID(disk, node) result = self.rpc.call_blockdev_grow(node, disk, self.op.amount) result.Raise("Grow request failed to node %s" % node) + + # TODO: Rewrite code to work properly + # DRBD goes into sync mode for a short amount of time after executing the + # "resize" command. DRBD 8.x below version 8.0.13 contains a bug whereby + # calling "resize" in sync mode fails. Sleeping for a short amount of + # time is a work-around. + time.sleep(5) + disk.RecordGrow(self.op.amount) self.cfg.Update(instance, feedback_fn) if self.op.wait_for_sync: @@ -7042,14 +7320,13 @@ class LUQueryInstanceData(NoHooksLU): self.share_locks = dict.fromkeys(locking.LEVELS, 1) if not isinstance(self.op.instances, list): - raise errors.OpPrereqError("Invalid argument type 'instances'") + raise errors.OpPrereqError("Invalid argument type 'instances'", + errors.ECODE_INVAL) if self.op.instances: self.wanted_names = [] for name in self.op.instances: - full_name = self.cfg.ExpandInstanceName(name) - if full_name is None: - raise errors.OpPrereqError("Instance '%s' not known" % name) + full_name = _ExpandInstanceName(self.cfg, name) self.wanted_names.append(full_name) self.needed_locks[locking.LEVEL_INSTANCE] = self.wanted_names else: @@ -7174,7 +7451,7 @@ class LUQueryInstanceData(NoHooksLU): "hypervisor": instance.hypervisor, "network_port": instance.network_port, "hv_instance": instance.hvparams, - "hv_actual": cluster.FillHV(instance), + "hv_actual": cluster.FillHV(instance, skip_globals=True), "be_instance": instance.beparams, "be_actual": cluster.FillBE(instance), "serial_no": instance.serial_no, @@ -7209,7 +7486,10 @@ class LUSetInstanceParams(LogicalUnit): self.op.force = getattr(self.op, "force", False) if not (self.op.nics or self.op.disks or self.op.hvparams or self.op.beparams): - raise errors.OpPrereqError("No changes submitted") + raise errors.OpPrereqError("No changes submitted", errors.ECODE_INVAL) + + if self.op.hvparams: + _CheckGlobalHvParams(self.op.hvparams) # Disk validation disk_addremove = 0 @@ -7221,33 +7501,35 @@ class LUSetInstanceParams(LogicalUnit): disk_addremove += 1 else: if not isinstance(disk_op, int): - raise errors.OpPrereqError("Invalid disk index") + raise errors.OpPrereqError("Invalid disk index", errors.ECODE_INVAL) if not isinstance(disk_dict, dict): msg = "Invalid disk value: expected dict, got '%s'" % disk_dict - raise errors.OpPrereqError(msg) + raise errors.OpPrereqError(msg, errors.ECODE_INVAL) if disk_op == constants.DDM_ADD: mode = disk_dict.setdefault('mode', constants.DISK_RDWR) if mode not in constants.DISK_ACCESS_SET: - raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode) + raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode, + errors.ECODE_INVAL) size = disk_dict.get('size', None) if size is None: - raise errors.OpPrereqError("Required disk parameter size missing") + raise errors.OpPrereqError("Required disk parameter size missing", + errors.ECODE_INVAL) try: size = int(size) - except ValueError, err: + except (TypeError, ValueError), err: raise errors.OpPrereqError("Invalid disk size parameter: %s" % - str(err)) + str(err), errors.ECODE_INVAL) disk_dict['size'] = size else: # modification of disk if 'size' in disk_dict: raise errors.OpPrereqError("Disk size change not possible, use" - " grow-disk") + " grow-disk", errors.ECODE_INVAL) if disk_addremove > 1: raise errors.OpPrereqError("Only one disk add or remove operation" - " supported at a time") + " supported at a time", errors.ECODE_INVAL) # NIC validation nic_addremove = 0 @@ -7259,10 +7541,10 @@ class LUSetInstanceParams(LogicalUnit): nic_addremove += 1 else: if not isinstance(nic_op, int): - raise errors.OpPrereqError("Invalid nic index") + raise errors.OpPrereqError("Invalid nic index", errors.ECODE_INVAL) if not isinstance(nic_dict, dict): msg = "Invalid nic value: expected dict, got '%s'" % nic_dict - raise errors.OpPrereqError(msg) + raise errors.OpPrereqError(msg, errors.ECODE_INVAL) # nic_dict should be a dict nic_ip = nic_dict.get('ip', None) @@ -7271,13 +7553,14 @@ class LUSetInstanceParams(LogicalUnit): nic_dict['ip'] = None else: if not utils.IsValidIP(nic_ip): - raise errors.OpPrereqError("Invalid IP address '%s'" % nic_ip) + raise errors.OpPrereqError("Invalid IP address '%s'" % nic_ip, + errors.ECODE_INVAL) nic_bridge = nic_dict.get('bridge', None) nic_link = nic_dict.get('link', None) if nic_bridge and nic_link: raise errors.OpPrereqError("Cannot pass 'bridge' and 'link'" - " at the same time") + " at the same time", errors.ECODE_INVAL) elif nic_bridge and nic_bridge.lower() == constants.VALUE_NONE: nic_dict['bridge'] = None elif nic_link and nic_link.lower() == constants.VALUE_NONE: @@ -7291,15 +7574,16 @@ class LUSetInstanceParams(LogicalUnit): if 'mac' in nic_dict: nic_mac = nic_dict['mac'] if nic_mac not in (constants.VALUE_AUTO, constants.VALUE_GENERATE): - if not utils.IsValidMac(nic_mac): - raise errors.OpPrereqError("Invalid MAC address %s" % nic_mac) + nic_mac = utils.NormalizeAndValidateMac(nic_mac) + if nic_op != constants.DDM_ADD and nic_mac == constants.VALUE_AUTO: raise errors.OpPrereqError("'auto' is not a valid MAC address when" - " modifying an existing nic") + " modifying an existing nic", + errors.ECODE_INVAL) if nic_addremove > 1: raise errors.OpPrereqError("Only one NIC add or remove operation" - " supported at a time") + " supported at a time", errors.ECODE_INVAL) def ExpandNames(self): self._ExpandAndLockInstance() @@ -7361,7 +7645,8 @@ class LUSetInstanceParams(LogicalUnit): nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes) return env, nl, nl - def _GetUpdatedParams(self, old_params, update_dict, + @staticmethod + def _GetUpdatedParams(old_params, update_dict, default_values, parameter_types): """Return the new params dict for the given params. @@ -7472,7 +7757,8 @@ class LUSetInstanceParams(LogicalUnit): if miss_mem > 0: raise errors.OpPrereqError("This change will prevent the instance" " from starting, due to %d MB of memory" - " missing on its primary node" % miss_mem) + " missing on its primary node" % miss_mem, + errors.ECODE_NORES) if be_new[constants.BE_AUTO_BALANCE]: for node, nres in nodeinfo.items(): @@ -7495,14 +7781,20 @@ class LUSetInstanceParams(LogicalUnit): for nic_op, nic_dict in self.op.nics: if nic_op == constants.DDM_REMOVE: if not instance.nics: - raise errors.OpPrereqError("Instance has no NICs, cannot remove") + raise errors.OpPrereqError("Instance has no NICs, cannot remove", + errors.ECODE_INVAL) continue if nic_op != constants.DDM_ADD: # an existing nic + if not instance.nics: + raise errors.OpPrereqError("Invalid NIC index %s, instance has" + " no NICs" % nic_op, + errors.ECODE_INVAL) if nic_op < 0 or nic_op >= len(instance.nics): raise errors.OpPrereqError("Invalid NIC index %s, valid values" " are 0 to %d" % - (nic_op, len(instance.nics))) + (nic_op, len(instance.nics) - 1), + errors.ECODE_INVAL) old_nic_params = instance.nics[nic_op].nicparams old_nic_ip = instance.nics[nic_op].ip else: @@ -7533,7 +7825,7 @@ class LUSetInstanceParams(LogicalUnit): if self.force: self.warn.append(msg) else: - raise errors.OpPrereqError(msg) + raise errors.OpPrereqError(msg, errors.ECODE_ENVIRON) if new_nic_mode == constants.NIC_MODE_ROUTED: if 'ip' in nic_dict: nic_ip = nic_dict['ip'] @@ -7541,49 +7833,57 @@ class LUSetInstanceParams(LogicalUnit): nic_ip = old_nic_ip if nic_ip is None: raise errors.OpPrereqError('Cannot set the nic ip to None' - ' on a routed nic') + ' on a routed nic', errors.ECODE_INVAL) if 'mac' in nic_dict: nic_mac = nic_dict['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 - nic_dict['mac'] = self.cfg.GenerateMAC() + nic_dict['mac'] = self.cfg.GenerateMAC(self.proc.GetECId()) else: # or validate/reserve the current one - if self.cfg.IsMacInUse(nic_mac): + try: + self.cfg.ReserveMAC(nic_mac, self.proc.GetECId()) + except errors.ReservationError: raise errors.OpPrereqError("MAC address %s already in use" - " in cluster" % nic_mac) + " in cluster" % nic_mac, + errors.ECODE_NOTUNIQUE) # DISK processing if self.op.disks and instance.disk_template == constants.DT_DISKLESS: raise errors.OpPrereqError("Disk operations not supported for" - " diskless instances") - for disk_op, disk_dict in self.op.disks: + " diskless instances", + errors.ECODE_INVAL) + for disk_op, _ in self.op.disks: if disk_op == constants.DDM_REMOVE: if len(instance.disks) == 1: raise errors.OpPrereqError("Cannot remove the last disk of" - " an instance") + " an instance", + errors.ECODE_INVAL) ins_l = self.rpc.call_instance_list([pnode], [instance.hypervisor]) ins_l = ins_l[pnode] msg = ins_l.fail_msg if msg: raise errors.OpPrereqError("Can't contact node %s: %s" % - (pnode, msg)) + (pnode, msg), errors.ECODE_ENVIRON) if instance.name in ins_l.payload: raise errors.OpPrereqError("Instance is running, can't remove" - " disks.") + " disks.", errors.ECODE_STATE) if (disk_op == constants.DDM_ADD and len(instance.nics) >= constants.MAX_DISKS): raise errors.OpPrereqError("Instance has too many disks (%d), cannot" - " add more" % constants.MAX_DISKS) + " add more" % constants.MAX_DISKS, + errors.ECODE_STATE) if disk_op not in (constants.DDM_ADD, constants.DDM_REMOVE): # an existing disk if disk_op < 0 or disk_op >= len(instance.disks): raise errors.OpPrereqError("Invalid disk index %s, valid values" " are 0 to %d" % - (disk_op, len(instance.disks))) + (disk_op, len(instance.disks)), + errors.ECODE_INVAL) return @@ -7600,7 +7900,6 @@ class LUSetInstanceParams(LogicalUnit): result = [] instance = self.instance - cluster = self.cluster # disk changes for disk_op, disk_dict in self.op.disks: if disk_op == constants.DDM_REMOVE: @@ -7675,8 +7974,8 @@ class LUSetInstanceParams(LogicalUnit): for key in 'mac', 'ip': if key in nic_dict: setattr(instance.nics[nic_op], key, nic_dict[key]) - if nic_op in self.nic_pnew: - instance.nics[nic_op].nicparams = self.nic_pnew[nic_op] + if nic_op in self.nic_pinst: + instance.nics[nic_op].nicparams = self.nic_pinst[nic_op] for key, val in nic_dict.iteritems(): result.append(("nic.%s/%d" % (key, nic_op), val)) @@ -7799,12 +8098,10 @@ class LUExportInstance(LogicalUnit): "Cannot retrieve locked instance %s" % self.op.instance_name _CheckNodeOnline(self, self.instance.primary_node) - self.dst_node = self.cfg.GetNodeInfo( - self.cfg.ExpandNodeName(self.op.target_node)) + self.op.target_node = _ExpandNodeName(self.cfg, self.op.target_node) + self.dst_node = self.cfg.GetNodeInfo(self.op.target_node) + assert self.dst_node is not None - if self.dst_node is None: - # This is wrong node name, not a non-locked node - raise errors.OpPrereqError("Wrong node name %s" % self.op.target_node) _CheckNodeOnline(self, self.dst_node.name) _CheckNodeNotDrained(self, self.dst_node.name) @@ -7812,7 +8109,7 @@ class LUExportInstance(LogicalUnit): for disk in self.instance.disks: if disk.dev_type == constants.LD_FILE: raise errors.OpPrereqError("Export not supported for instances with" - " file-based disks") + " file-based disks", errors.ECODE_INVAL) def Exec(self, feedback_fn): """Export an instance to an image in the cluster. @@ -7885,8 +8182,10 @@ class LUExportInstance(LogicalUnit): feedback_fn("Exporting snapshot %s from %s to %s" % (idx, src_node, dst_node.name)) if dev: + # FIXME: pass debug from opcode to backend result = self.rpc.call_snapshot_export(src_node, dev, dst_node.name, - instance, cluster_name, idx) + instance, cluster_name, + idx, self.op.debug_level) msg = result.fail_msg if msg: self.LogWarning("Could not export disk/%s from node %s to" @@ -7990,7 +8289,7 @@ class LURemoveExport(NoHooksLU): " Domain Name.") -class TagsLU(NoHooksLU): +class TagsLU(NoHooksLU): # pylint: disable-msg=W0223 """Generic tags LU. This is an abstract class which is the parent of all the other tags LUs. @@ -8000,19 +8299,11 @@ class TagsLU(NoHooksLU): def ExpandNames(self): self.needed_locks = {} if self.op.kind == constants.TAG_NODE: - name = self.cfg.ExpandNodeName(self.op.name) - if name is None: - raise errors.OpPrereqError("Invalid node name (%s)" % - (self.op.name,)) - self.op.name = name - self.needed_locks[locking.LEVEL_NODE] = name + self.op.name = _ExpandNodeName(self.cfg, self.op.name) + self.needed_locks[locking.LEVEL_NODE] = self.op.name elif self.op.kind == constants.TAG_INSTANCE: - name = self.cfg.ExpandInstanceName(self.op.name) - if name is None: - raise errors.OpPrereqError("Invalid instance name (%s)" % - (self.op.name,)) - self.op.name = name - self.needed_locks[locking.LEVEL_INSTANCE] = name + self.op.name = _ExpandInstanceName(self.cfg, self.op.name) + self.needed_locks[locking.LEVEL_INSTANCE] = self.op.name def CheckPrereq(self): """Check prerequisites. @@ -8026,7 +8317,7 @@ class TagsLU(NoHooksLU): self.target = self.cfg.GetInstanceInfo(self.op.name) else: raise errors.OpPrereqError("Wrong tag type requested (%s)" % - str(self.op.kind)) + str(self.op.kind), errors.ECODE_INVAL) class LUGetTags(TagsLU): @@ -8063,7 +8354,7 @@ class LUSearchTags(NoHooksLU): self.re = re.compile(self.op.pattern) except re.error, err: raise errors.OpPrereqError("Invalid search pattern '%s': %s" % - (self.op.pattern, err)) + (self.op.pattern, err), errors.ECODE_INVAL) def Exec(self, feedback_fn): """Returns the tag list. @@ -8109,12 +8400,7 @@ class LUAddTags(TagsLU): self.target.AddTag(tag) except errors.TagError, err: raise errors.OpExecError("Error while setting tag: %s" % str(err)) - try: - self.cfg.Update(self.target, feedback_fn) - except errors.ConfigurationError: - raise errors.OpRetryError("There has been a modification to the" - " config file and the operation has been" - " aborted. Please retry.") + self.cfg.Update(self.target, feedback_fn) class LUDelTags(TagsLU): @@ -8140,7 +8426,7 @@ class LUDelTags(TagsLU): diff_names = ["'%s'" % tag for tag in diff_tags] diff_names.sort() raise errors.OpPrereqError("Tag(s) %s not found" % - (",".join(diff_names))) + (",".join(diff_names)), errors.ECODE_NOENT) def Exec(self, feedback_fn): """Remove the tag from the object. @@ -8148,12 +8434,7 @@ class LUDelTags(TagsLU): """ for tag in self.op.tags: self.target.RemoveTag(tag) - try: - self.cfg.Update(self.target, feedback_fn) - except errors.ConfigurationError: - raise errors.OpRetryError("There has been a modification to the" - " config file and the operation has been" - " aborted. Please retry.") + self.cfg.Update(self.target, feedback_fn) class LUTestDelay(NoHooksLU): @@ -8211,6 +8492,8 @@ class IAllocator(object): easy usage """ + # pylint: disable-msg=R0902 + # lots of instance attributes _ALLO_KEYS = [ "mem_size", "disks", "disk_template", "os", "tags", "nics", "vcpus", "hypervisor", @@ -8429,10 +8712,12 @@ class IAllocator(object): " IAllocator" % self.name) if instance.disk_template not in constants.DTS_NET_MIRROR: - raise errors.OpPrereqError("Can't relocate non-mirrored instances") + raise errors.OpPrereqError("Can't relocate non-mirrored instances", + errors.ECODE_INVAL) if len(instance.secondary_nodes) != 1: - raise errors.OpPrereqError("Instance has not exactly one secondary node") + raise errors.OpPrereqError("Instance has not exactly one secondary node", + errors.ECODE_STATE) self.required_nodes = 1 disk_sizes = [{'size': disk.size} for disk in instance.disks] @@ -8520,51 +8805,52 @@ class LUTestAllocator(NoHooksLU): "os", "tags", "nics", "vcpus"]: if not hasattr(self.op, attr): raise errors.OpPrereqError("Missing attribute '%s' on opcode input" % - attr) + attr, errors.ECODE_INVAL) iname = self.cfg.ExpandInstanceName(self.op.name) if iname is not None: raise errors.OpPrereqError("Instance '%s' already in the cluster" % - iname) + iname, errors.ECODE_EXISTS) if not isinstance(self.op.nics, list): - raise errors.OpPrereqError("Invalid parameter 'nics'") + raise errors.OpPrereqError("Invalid parameter 'nics'", + errors.ECODE_INVAL) for row in self.op.nics: if (not isinstance(row, dict) or "mac" not in row or "ip" not in row or "bridge" not in row): - raise errors.OpPrereqError("Invalid contents of the" - " 'nics' parameter") + raise errors.OpPrereqError("Invalid contents of the 'nics'" + " parameter", errors.ECODE_INVAL) if not isinstance(self.op.disks, list): - raise errors.OpPrereqError("Invalid parameter 'disks'") + raise errors.OpPrereqError("Invalid parameter 'disks'", + errors.ECODE_INVAL) for row in self.op.disks: if (not isinstance(row, dict) or "size" not in row or not isinstance(row["size"], int) or "mode" not in row or row["mode"] not in ['r', 'w']): - raise errors.OpPrereqError("Invalid contents of the" - " 'disks' parameter") + raise errors.OpPrereqError("Invalid contents of the 'disks'" + " parameter", errors.ECODE_INVAL) if not hasattr(self.op, "hypervisor") or self.op.hypervisor is None: self.op.hypervisor = self.cfg.GetHypervisorType() elif self.op.mode == constants.IALLOCATOR_MODE_RELOC: if not hasattr(self.op, "name"): - raise errors.OpPrereqError("Missing attribute 'name' on opcode input") - fname = self.cfg.ExpandInstanceName(self.op.name) - if fname is None: - raise errors.OpPrereqError("Instance '%s' not found for relocation" % - self.op.name) + raise errors.OpPrereqError("Missing attribute 'name' on opcode input", + errors.ECODE_INVAL) + fname = _ExpandInstanceName(self.cfg, self.op.name) self.op.name = fname self.relocate_from = self.cfg.GetInstanceInfo(fname).secondary_nodes else: raise errors.OpPrereqError("Invalid test allocator mode '%s'" % - self.op.mode) + self.op.mode, errors.ECODE_INVAL) if self.op.direction == constants.IALLOCATOR_DIR_OUT: if not hasattr(self.op, "allocator") or self.op.allocator is None: - raise errors.OpPrereqError("Missing allocator name") + raise errors.OpPrereqError("Missing allocator name", + errors.ECODE_INVAL) elif self.op.direction != constants.IALLOCATOR_DIR_IN: raise errors.OpPrereqError("Wrong allocator test '%s'" % - self.op.direction) + self.op.direction, errors.ECODE_INVAL) def Exec(self, feedback_fn): """Run the allocator test.