X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/71333cb95af39a92328db6da05a321bb331583e2..34fbc862d21452efd70338b6c8495b3af0513f29:/lib/cmdlib.py diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 038bbb6..87ef9f1 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -21,7 +21,7 @@ """Module implementing the master-side code.""" -# pylint: disable-msg=W0201,C0302 +# pylint: disable=W0201,C0302 # W0201 since most LU attributes are defined in CheckPrereq or similar # functions @@ -60,7 +60,7 @@ from ganeti import qlang from ganeti import opcodes from ganeti import ht -import ganeti.masterd.instance # pylint: disable-msg=W0611 +import ganeti.masterd.instance # pylint: disable=W0611 class ResultWithJobs: @@ -119,6 +119,8 @@ class LogicalUnit(object): self.op = op self.cfg = context.cfg self.glm = context.glm + # readability alias + self.owned_locks = context.glm.list_owned self.context = context self.rpc = rpc # Dicts used to declare locking needs to mcpu @@ -129,10 +131,10 @@ class LogicalUnit(object): # Used to force good behavior when calling helper functions self.recalculate_locks = {} # logging - self.Log = processor.Log # pylint: disable-msg=C0103 - self.LogWarning = processor.LogWarning # pylint: disable-msg=C0103 - self.LogInfo = processor.LogInfo # pylint: disable-msg=C0103 - self.LogStep = processor.LogStep # pylint: disable-msg=C0103 + self.Log = processor.Log # pylint: disable=C0103 + self.LogWarning = processor.LogWarning # pylint: disable=C0103 + self.LogInfo = processor.LogInfo # pylint: disable=C0103 + self.LogStep = processor.LogStep # pylint: disable=C0103 # support for dry-run self.dry_run_result = None # support for generic debug attribute @@ -320,7 +322,7 @@ class LogicalUnit(object): """ # API must be kept, thus we ignore the unused argument and could # be a function warnings - # pylint: disable-msg=W0613,R0201 + # pylint: disable=W0613,R0201 return lu_result def _ExpandAndLockInstance(self): @@ -374,7 +376,7 @@ class LogicalUnit(object): # future we might want to have different behaviors depending on the value # of self.recalculate_locks[locking.LEVEL_NODE] wanted_nodes = [] - locked_i = self.glm.list_owned(locking.LEVEL_INSTANCE) + locked_i = self.owned_locks(locking.LEVEL_INSTANCE) for _, instance in self.cfg.GetMultiInstanceInfo(locked_i): wanted_nodes.append(instance.primary_node) if not primary_only: @@ -388,7 +390,7 @@ class LogicalUnit(object): del self.recalculate_locks[locking.LEVEL_NODE] -class NoHooksLU(LogicalUnit): # pylint: disable-msg=W0223 +class NoHooksLU(LogicalUnit): # pylint: disable=W0223 """Simple LU which runs no hooks. This LU is intended as a parent for other LogicalUnits which will @@ -488,7 +490,7 @@ class _QueryBase: """ if self.do_locking: - names = lu.glm.list_owned(lock_level) + names = lu.owned_locks(lock_level) else: names = all_names @@ -555,6 +557,56 @@ def _ShareAll(): return dict.fromkeys(locking.LEVELS, 1) +def _CheckInstanceNodeGroups(cfg, instance_name, owned_groups): + """Checks if the owned node groups are still correct for an instance. + + @type cfg: L{config.ConfigWriter} + @param cfg: The cluster configuration + @type instance_name: string + @param instance_name: Instance name + @type owned_groups: set or frozenset + @param owned_groups: List of currently owned node groups + + """ + inst_groups = cfg.GetInstanceNodeGroups(instance_name) + + if not owned_groups.issuperset(inst_groups): + raise errors.OpPrereqError("Instance %s's node groups changed since" + " locks were acquired, current groups are" + " are '%s', owning groups '%s'; retry the" + " operation" % + (instance_name, + utils.CommaJoin(inst_groups), + utils.CommaJoin(owned_groups)), + errors.ECODE_STATE) + + return inst_groups + + +def _CheckNodeGroupInstances(cfg, group_uuid, owned_instances): + """Checks if the instances in a node group are still correct. + + @type cfg: L{config.ConfigWriter} + @param cfg: The cluster configuration + @type group_uuid: string + @param group_uuid: Node group UUID + @type owned_instances: set or frozenset + @param owned_instances: List of currently owned instances + + """ + wanted_instances = cfg.GetNodeGroupInstances(group_uuid) + if owned_instances != wanted_instances: + raise errors.OpPrereqError("Instances in node group '%s' changed since" + " locks were acquired, wanted '%s', have '%s';" + " retry the operation" % + (group_uuid, + utils.CommaJoin(wanted_instances), + utils.CommaJoin(owned_instances)), + errors.ECODE_STATE) + + return wanted_instances + + def _SupportsOob(cfg, node): """Tells if node supports OOB. @@ -665,18 +717,18 @@ def _ReleaseLocks(lu, level, names=None, keep=None): release = [] # Determine which locks to release - for name in lu.glm.list_owned(level): + for name in lu.owned_locks(level): if should_release(name): release.append(name) else: retain.append(name) - assert len(lu.glm.list_owned(level)) == (len(retain) + len(release)) + assert len(lu.owned_locks(level)) == (len(retain) + len(release)) # Release just some locks lu.glm.release(level, names=release) - assert frozenset(lu.glm.list_owned(level)) == frozenset(retain) + assert frozenset(lu.owned_locks(level)) == frozenset(retain) else: # Release everything lu.glm.release(level) @@ -705,7 +757,7 @@ def _RunPostHook(lu, node_name): try: hm.RunPhase(constants.HOOKS_PHASE_POST, nodes=[node_name]) except: - # pylint: disable-msg=W0702 + # pylint: disable=W0702 lu.LogWarning("Errors occurred running hooks on %s" % node_name) @@ -1035,7 +1087,7 @@ def _BuildInstanceHookEnvByObject(lu, instance, override=None): } if override: args.update(override) - return _BuildInstanceHookEnv(**args) # pylint: disable-msg=W0142 + return _BuildInstanceHookEnv(**args) # pylint: disable=W0142 def _AdjustCandidatePool(lu, exceptions): @@ -1201,6 +1253,29 @@ def _CheckIAllocatorOrNode(lu, iallocator_slot, node_slot): " iallocator") +def _GetDefaultIAllocator(cfg, iallocator): + """Decides on which iallocator to use. + + @type cfg: L{config.ConfigWriter} + @param cfg: Cluster configuration object + @type iallocator: string or None + @param iallocator: Iallocator specified in opcode + @rtype: string + @return: Iallocator name + + """ + if not iallocator: + # Use default iallocator + iallocator = cfg.GetDefaultIAllocator() + + if not iallocator: + raise errors.OpPrereqError("No iallocator was specified, neither in the" + " opcode nor as a cluster-wide default", + errors.ECODE_INVAL) + + return iallocator + + class LUClusterPostInit(LogicalUnit): """Logical unit for running hooks after cluster initialization. @@ -1296,7 +1371,7 @@ def _VerifyCertificate(filename): try: cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, utils.ReadFile(filename)) - except Exception, err: # pylint: disable-msg=W0703 + except Exception, err: # pylint: disable=W0703 return (LUClusterVerifyConfig.ETYPE_ERROR, "Failed to load X509 certificate %s: %s" % (filename, err)) @@ -1411,7 +1486,7 @@ class _VerifyErrors(object): if args: msg = msg % args # then format the whole message - if self.op.error_codes: # This is a mix-in. pylint: disable-msg=E1101 + if self.op.error_codes: # This is a mix-in. pylint: disable=E1101 msg = "%s:%s:%s:%s:%s" % (ltype, etxt, itype, item, msg) else: if item: @@ -1420,14 +1495,14 @@ class _VerifyErrors(object): item = "" msg = "%s: %s%s: %s" % (ltype, itype, item, msg) # and finally report it via the feedback_fn - self._feedback_fn(" - %s" % msg) # Mix-in. pylint: disable-msg=E1101 + self._feedback_fn(" - %s" % msg) # Mix-in. pylint: disable=E1101 def _ErrorIf(self, cond, *args, **kwargs): """Log an error message if the passed condition is True. """ cond = (bool(cond) - or self.op.debug_simulate_errors) # pylint: disable-msg=E1101 + or self.op.debug_simulate_errors) # pylint: disable=E1101 if cond: self._Error(*args, **kwargs) # do not mark the operation as failed for WARN cases only @@ -1435,6 +1510,47 @@ class _VerifyErrors(object): self.bad = self.bad or cond +class LUClusterVerify(NoHooksLU): + """Submits all jobs necessary to verify the cluster. + + """ + REQ_BGL = False + + def ExpandNames(self): + self.needed_locks = {} + + def Exec(self, feedback_fn): + jobs = [] + + if self.op.group_name: + groups = [self.op.group_name] + depends_fn = lambda: None + else: + groups = self.cfg.GetNodeGroupList() + + # Verify global configuration + jobs.append([opcodes.OpClusterVerifyConfig()]) + + # Always depend on global verification + depends_fn = lambda: [(-len(jobs), [])] + + jobs.extend([opcodes.OpClusterVerifyGroup(group_name=group, + depends=depends_fn())] + for group in groups) + + # Fix up all parameters + for op in itertools.chain(*jobs): # pylint: disable=W0142 + op.debug_simulate_errors = self.op.debug_simulate_errors + op.verbose = self.op.verbose + op.error_codes = self.op.error_codes + try: + op.skip_checks = self.op.skip_checks + except AttributeError: + assert not isinstance(op, opcodes.OpClusterVerifyGroup) + + return ResultWithJobs(jobs) + + class LUClusterVerifyConfig(NoHooksLU, _VerifyErrors): """Verifies the cluster config. @@ -1458,6 +1574,7 @@ class LUClusterVerifyConfig(NoHooksLU, _VerifyErrors): def ExpandNames(self): # Information can be safely retrieved as the BGL is acquired in exclusive # mode + assert locking.BGL in self.owned_locks(locking.LEVEL_CLUSTER) self.all_group_info = self.cfg.GetAllNodeGroupsInfo() self.all_node_info = self.cfg.GetAllNodesInfo() self.all_inst_info = self.cfg.GetAllInstancesInfo() @@ -1519,7 +1636,7 @@ class LUClusterVerifyConfig(NoHooksLU, _VerifyErrors): "the following instances have a non-existing primary-node:" " %s", utils.CommaJoin(no_node_instances)) - return (not self.bad, [g.name for g in self.all_group_info.values()]) + return not self.bad class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): @@ -1609,7 +1726,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): # volumes for these instances are healthy, we will need to do an # extra call to their secondaries. We ensure here those nodes will # be locked. - for inst in self.glm.list_owned(locking.LEVEL_INSTANCE): + for inst in self.owned_locks(locking.LEVEL_INSTANCE): # Important: access only the instances whose lock is owned if all_inst_info[inst].disk_template in constants.DTS_INT_MIRROR: nodes.update(all_inst_info[inst].secondary_nodes) @@ -1617,14 +1734,17 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): self.needed_locks[locking.LEVEL_NODE] = nodes def CheckPrereq(self): - group_nodes = set(self.cfg.GetNodeGroup(self.group_uuid).members) + assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP) + self.group_info = self.cfg.GetNodeGroup(self.group_uuid) + + group_nodes = set(self.group_info.members) group_instances = self.cfg.GetNodeGroupInstances(self.group_uuid) unlocked_nodes = \ - group_nodes.difference(self.glm.list_owned(locking.LEVEL_NODE)) + group_nodes.difference(self.owned_locks(locking.LEVEL_NODE)) unlocked_instances = \ - group_instances.difference(self.glm.list_owned(locking.LEVEL_INSTANCE)) + group_instances.difference(self.owned_locks(locking.LEVEL_INSTANCE)) if unlocked_nodes: raise errors.OpPrereqError("Missing lock for nodes: %s" % @@ -1658,7 +1778,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): extra_lv_nodes.add(nname) unlocked_lv_nodes = \ - extra_lv_nodes.difference(self.glm.list_owned(locking.LEVEL_NODE)) + extra_lv_nodes.difference(self.owned_locks(locking.LEVEL_NODE)) if unlocked_lv_nodes: raise errors.OpPrereqError("these nodes could be locked: %s" % @@ -1681,7 +1801,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): """ node = ninfo.name - _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 + _ErrorIf = self._ErrorIf # pylint: disable=C0103 # main result, nresult should be a non-empty dict test = not nresult or not isinstance(nresult, dict) @@ -1750,7 +1870,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): """ node = ninfo.name - _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 + _ErrorIf = self._ErrorIf # pylint: disable=C0103 ntime = nresult.get(constants.NV_TIME, None) try: @@ -1783,7 +1903,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): return node = ninfo.name - _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 + _ErrorIf = self._ErrorIf # pylint: disable=C0103 # checks vg existence and size > 20G vglist = nresult.get(constants.NV_VGLIST, None) @@ -1820,7 +1940,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): return node = ninfo.name - _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 + _ErrorIf = self._ErrorIf # pylint: disable=C0103 missing = nresult.get(constants.NV_BRIDGES, None) test = not isinstance(missing, list) @@ -1839,7 +1959,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): """ node = ninfo.name - _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 + _ErrorIf = self._ErrorIf # pylint: disable=C0103 test = constants.NV_NODELIST not in nresult _ErrorIf(test, self.ENODESSH, node, @@ -1880,7 +2000,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): available on the instance's node. """ - _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 + _ErrorIf = self._ErrorIf # pylint: disable=C0103 node_current = instanceconfig.primary_node node_vol_should = {} @@ -2080,7 +2200,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): """ node = ninfo.name - _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 + _ErrorIf = self._ErrorIf # pylint: disable=C0103 if drbd_helper: helper_result = nresult.get(constants.NV_DRBDHELPER, None) @@ -2139,7 +2259,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): """ node = ninfo.name - _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 + _ErrorIf = self._ErrorIf # pylint: disable=C0103 remote_os = nresult.get(constants.NV_OSLIST, None) test = (not isinstance(remote_os, list) or @@ -2180,7 +2300,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): """ node = ninfo.name - _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 + _ErrorIf = self._ErrorIf # pylint: disable=C0103 assert not nimg.os_fail, "Entered _VerifyNodeOS with failed OS rpc?" @@ -2250,7 +2370,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): """ node = ninfo.name - _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 + _ErrorIf = self._ErrorIf # pylint: disable=C0103 nimg.lvm_fail = True lvdata = nresult.get(constants.NV_LVLIST, "Missing LV data") @@ -2298,7 +2418,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): """ node = ninfo.name - _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 + _ErrorIf = self._ErrorIf # pylint: disable=C0103 # try to read free memory (from the hypervisor) hv_info = nresult.get(constants.NV_HVINFO, None) @@ -2340,7 +2460,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): list of tuples (success, payload) """ - _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 + _ErrorIf = self._ErrorIf # pylint: disable=C0103 node_disks = {} node_disks_devonly = {} @@ -2448,7 +2568,8 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): """Verify integrity of the node group, performing various test on nodes. """ - # This method has too many local variables. pylint: disable-msg=R0914 + # This method has too many local variables. pylint: disable=R0914 + feedback_fn("* Verifying group '%s'" % self.group_info.name) if not self.my_node_names: # empty node group @@ -2456,7 +2577,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors): return True self.bad = False - _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103 + _ErrorIf = self._ErrorIf # pylint: disable=C0103 verbose = self.op.verbose self._feedback_fn = feedback_fn @@ -2882,7 +3003,7 @@ class LUClusterVerifyDisks(NoHooksLU): } def Exec(self, feedback_fn): - group_names = self.glm.list_owned(locking.LEVEL_NODEGROUP) + group_names = self.owned_locks(locking.LEVEL_NODEGROUP) # Submit one instance of L{opcodes.OpGroupVerifyDisks} per node group return ResultWithJobs([[opcodes.OpGroupVerifyDisks(group_name=group)] @@ -2924,10 +3045,8 @@ class LUGroupVerifyDisks(NoHooksLU): # going via the node before it's locked, requiring verification # later on [group_uuid - for instance_name in - self.glm.list_owned(locking.LEVEL_INSTANCE) - for group_uuid in - self.cfg.GetInstanceNodeGroups(instance_name)]) + for instance_name in self.owned_locks(locking.LEVEL_INSTANCE) + for group_uuid in self.cfg.GetInstanceNodeGroups(instance_name)]) elif level == locking.LEVEL_NODE: # This will only lock the nodes in the group to be verified which contain @@ -2936,48 +3055,33 @@ class LUGroupVerifyDisks(NoHooksLU): self._LockInstancesNodes() # Lock all nodes in group to be verified - assert self.group_uuid in self.glm.list_owned(locking.LEVEL_NODEGROUP) + assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP) member_nodes = self.cfg.GetNodeGroup(self.group_uuid).members self.needed_locks[locking.LEVEL_NODE].extend(member_nodes) def CheckPrereq(self): - owned_instances = frozenset(self.glm.list_owned(locking.LEVEL_INSTANCE)) - owned_groups = frozenset(self.glm.list_owned(locking.LEVEL_NODEGROUP)) - owned_nodes = frozenset(self.glm.list_owned(locking.LEVEL_NODE)) + owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE)) + owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) + owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE)) assert self.group_uuid in owned_groups # Check if locked instances are still correct - wanted_instances = self.cfg.GetNodeGroupInstances(self.group_uuid) - if owned_instances != wanted_instances: - raise errors.OpPrereqError("Instances in node group %s changed since" - " locks were acquired, wanted %s, have %s;" - " retry the operation" % - (self.op.group_name, - utils.CommaJoin(wanted_instances), - utils.CommaJoin(owned_instances)), - errors.ECODE_STATE) + _CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances) # Get instance information self.instances = dict(self.cfg.GetMultiInstanceInfo(owned_instances)) # Check if node groups for locked instances are still correct for (instance_name, inst) in self.instances.items(): - assert self.group_uuid in self.cfg.GetInstanceNodeGroups(instance_name), \ - "Instance %s has no node in group %s" % (instance_name, self.group_uuid) assert owned_nodes.issuperset(inst.all_nodes), \ "Instance %s's nodes changed while we kept the lock" % instance_name - inst_groups = self.cfg.GetInstanceNodeGroups(instance_name) - if not owned_groups.issuperset(inst_groups): - raise errors.OpPrereqError("Instance %s's node groups changed since" - " locks were acquired, current groups are" - " are '%s', owning groups '%s'; retry the" - " operation" % - (instance_name, - utils.CommaJoin(inst_groups), - utils.CommaJoin(owned_groups)), - errors.ECODE_STATE) + inst_groups = _CheckInstanceNodeGroups(self.cfg, instance_name, + owned_groups) + + assert self.group_uuid in inst_groups, \ + "Instance %s has no node in group %s" % (instance_name, self.group_uuid) def Exec(self, feedback_fn): """Verify integrity of cluster disks. @@ -2997,7 +3101,7 @@ class LUGroupVerifyDisks(NoHooksLU): if inst.admin_up]) if nv_dict: - nodes = utils.NiceSort(set(self.glm.list_owned(locking.LEVEL_NODE)) & + nodes = utils.NiceSort(set(self.owned_locks(locking.LEVEL_NODE)) & set(self.cfg.GetVmCapableNodeList())) node_lvs = self.rpc.call_lv_list(nodes, []) @@ -3058,7 +3162,7 @@ class LUClusterRepairDiskSizes(NoHooksLU): """ if self.wanted_names is None: - self.wanted_names = self.glm.list_owned(locking.LEVEL_INSTANCE) + self.wanted_names = self.owned_locks(locking.LEVEL_INSTANCE) self.wanted_instances = \ map(compat.snd, self.cfg.GetMultiInstanceInfo(self.wanted_names)) @@ -3283,7 +3387,7 @@ class LUClusterSetParams(LogicalUnit): " drbd-based instances exist", errors.ECODE_INVAL) - node_list = self.glm.list_owned(locking.LEVEL_NODE) + node_list = self.owned_locks(locking.LEVEL_NODE) # if vg_name not None, checks given volume group on all nodes if self.op.vg_name: @@ -3981,6 +4085,7 @@ class LUOobCommand(NoHooksLU): raise errors.OpExecError("Check of out-of-band payload failed due to %s" % utils.CommaJoin(errs)) + class _OsQuery(_QueryBase): FIELDS = query.OS_FIELDS @@ -4239,7 +4344,7 @@ class _NodeQuery(_QueryBase): def ExpandNames(self, lu): lu.needed_locks = {} - lu.share_locks[locking.LEVEL_NODE] = 1 + lu.share_locks = _ShareAll() if self.names: self.wanted = _GetWantedNodes(lu, self.names) @@ -4250,7 +4355,7 @@ class _NodeQuery(_QueryBase): query.NQ_LIVE in self.requested_data) if self.do_locking: - # if we don't request only static fields, we need to lock the nodes + # If any non-static field is requested we need to lock the nodes lu.needed_locks[locking.LEVEL_NODE] = self.wanted def DeclareLocks(self, lu, level): @@ -4314,7 +4419,7 @@ class LUNodeQuery(NoHooksLU): """Logical unit for querying nodes. """ - # pylint: disable-msg=W0142 + # pylint: disable=W0142 REQ_BGL = False def CheckArguments(self): @@ -4354,7 +4459,7 @@ class LUNodeQueryvols(NoHooksLU): """Computes the list of nodes and their attributes. """ - nodenames = self.glm.list_owned(locking.LEVEL_NODE) + nodenames = self.owned_locks(locking.LEVEL_NODE) volumes = self.rpc.call_node_volumes(nodenames) ilist = self.cfg.GetAllInstancesInfo() @@ -4423,7 +4528,7 @@ class LUNodeQueryStorage(NoHooksLU): """Computes the list of nodes and their attributes. """ - self.nodes = self.glm.list_owned(locking.LEVEL_NODE) + self.nodes = self.owned_locks(locking.LEVEL_NODE) # Always get name to sort by if constants.SF_NAME in self.op.output_fields: @@ -4512,30 +4617,19 @@ class _InstanceQuery(_QueryBase): # via the node before it's locked, requiring verification later on lu.needed_locks[locking.LEVEL_NODEGROUP] = \ set(group_uuid - for instance_name in - lu.glm.list_owned(locking.LEVEL_INSTANCE) - for group_uuid in - lu.cfg.GetInstanceNodeGroups(instance_name)) + for instance_name in lu.owned_locks(locking.LEVEL_INSTANCE) + for group_uuid in lu.cfg.GetInstanceNodeGroups(instance_name)) elif level == locking.LEVEL_NODE: - lu._LockInstancesNodes() # pylint: disable-msg=W0212 + lu._LockInstancesNodes() # pylint: disable=W0212 @staticmethod def _CheckGroupLocks(lu): - owned_instances = frozenset(lu.glm.list_owned(locking.LEVEL_INSTANCE)) - owned_groups = frozenset(lu.glm.list_owned(locking.LEVEL_NODEGROUP)) + owned_instances = frozenset(lu.owned_locks(locking.LEVEL_INSTANCE)) + owned_groups = frozenset(lu.owned_locks(locking.LEVEL_NODEGROUP)) # Check if node groups for locked instances are still correct for instance_name in owned_instances: - inst_groups = lu.cfg.GetInstanceNodeGroups(instance_name) - if not owned_groups.issuperset(inst_groups): - raise errors.OpPrereqError("Instance %s's node groups changed since" - " locks were acquired, current groups are" - " are '%s', owning groups '%s'; retry the" - " operation" % - (instance_name, - utils.CommaJoin(inst_groups), - utils.CommaJoin(owned_groups)), - errors.ECODE_STATE) + _CheckInstanceNodeGroups(lu.cfg, instance_name, owned_groups) def _GetQueryData(self, lu): """Computes the list of instances and their attributes. @@ -4627,13 +4721,13 @@ class LUQuery(NoHooksLU): """Query for resources/items of a certain kind. """ - # pylint: disable-msg=W0142 + # pylint: disable=W0142 REQ_BGL = False def CheckArguments(self): qcls = _GetQueryImplementation(self.op.what) - self.impl = qcls(self.op.filter, self.op.fields, False) + self.impl = qcls(self.op.filter, self.op.fields, self.op.use_locking) def ExpandNames(self): self.impl.ExpandNames(self) @@ -4649,7 +4743,7 @@ class LUQueryFields(NoHooksLU): """Query for resources/items of a certain kind. """ - # pylint: disable-msg=W0142 + # pylint: disable=W0142 REQ_BGL = False def CheckArguments(self): @@ -4896,7 +4990,7 @@ class LUNodeAdd(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 # pylint: disable-msg=W0201 + new_node.drained = new_node.offline = False # pylint: disable=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 @@ -5044,7 +5138,7 @@ class LUNodeSetParams(LogicalUnit): instances_keep = [] # Build list of instances to release - locked_i = self.glm.list_owned(locking.LEVEL_INSTANCE) + locked_i = self.owned_locks(locking.LEVEL_INSTANCE) for instance_name, instance in self.cfg.GetMultiInstanceInfo(locked_i): if (instance.disk_template in constants.DTS_INT_MIRROR and self.op.node_name in instance.all_nodes): @@ -5053,7 +5147,7 @@ class LUNodeSetParams(LogicalUnit): _ReleaseLocks(self, locking.LEVEL_INSTANCE, keep=instances_keep) - assert (set(self.glm.list_owned(locking.LEVEL_INSTANCE)) == + assert (set(self.owned_locks(locking.LEVEL_INSTANCE)) == set(instances_keep)) def BuildHooksEnv(self): @@ -6456,7 +6550,7 @@ class LUInstanceQuery(NoHooksLU): """Logical unit for querying instances. """ - # pylint: disable-msg=W0142 + # pylint: disable=W0142 REQ_BGL = False def CheckArguments(self): @@ -6867,7 +6961,7 @@ class LUNodeMigrate(LogicalUnit): # running the iallocator and the actual migration, a good consistency model # will have to be found. - assert (frozenset(self.glm.list_owned(locking.LEVEL_NODE)) == + assert (frozenset(self.owned_locks(locking.LEVEL_NODE)) == frozenset([self.op.node_name])) return ResultWithJobs(jobs) @@ -7249,6 +7343,21 @@ class TLMigrateInstance(Tasklet): target_node = self.target_node source_node = self.source_node + # Check for hypervisor version mismatch and warn the user. + nodeinfo = self.rpc.call_node_info([source_node, target_node], + None, self.instance.hypervisor) + src_info = nodeinfo[source_node] + dst_info = nodeinfo[target_node] + + if ((constants.HV_NODEINFO_KEY_VERSION in src_info.payload) and + (constants.HV_NODEINFO_KEY_VERSION in dst_info.payload)): + src_version = src_info.payload[constants.HV_NODEINFO_KEY_VERSION] + dst_version = dst_info.payload[constants.HV_NODEINFO_KEY_VERSION] + if src_version != dst_version: + self.feedback_fn("* warning: hypervisor version mismatch between" + " source (%s) and target (%s) node" % + (src_version, dst_version)) + self.feedback_fn("* checking disk consistency between source and target") for dev in instance.disks: if not _CheckDiskConsistency(self.lu, dev, target_node, False): @@ -7857,7 +7966,7 @@ def _ComputeDiskSizePerVG(disk_template, disks): if disk_template not in req_size_dict: raise errors.ProgrammerError("Disk template '%s' size requirement" - " is unknown" % disk_template) + " is unknown" % disk_template) return req_size_dict[disk_template] @@ -7879,7 +7988,7 @@ def _ComputeDiskSize(disk_template, disks): if disk_template not in req_size_dict: raise errors.ProgrammerError("Disk template '%s' size requirement" - " is unknown" % disk_template) + " is unknown" % disk_template) return req_size_dict[disk_template] @@ -8159,8 +8268,8 @@ class LUInstanceCreate(LogicalUnit): self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET 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", + raise errors.OpPrereqError("Importing an instance from a path" + " requires a source node option", errors.ECODE_INVAL) else: self.op.src_node = src_node = _ExpandNodeName(self.cfg, src_node) @@ -8263,7 +8372,7 @@ class LUInstanceCreate(LogicalUnit): src_path = self.op.src_path if src_node is None: - locked_nodes = self.glm.list_owned(locking.LEVEL_NODE) + locked_nodes = self.owned_locks(locking.LEVEL_NODE) exp_list = self.rpc.call_export_list(locked_nodes) found = False for node in exp_list: @@ -8421,7 +8530,7 @@ class LUInstanceCreate(LogicalUnit): joinargs.append(self.op.instance_name) - # pylint: disable-msg=W0142 + # pylint: disable=W0142 self.instance_file_storage_dir = utils.PathJoin(*joinargs) def CheckPrereq(self): @@ -8793,7 +8902,7 @@ class LUInstanceCreate(LogicalUnit): # 'fake' LV disks with the old data, plus the new unique_id tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks] rename_to = [] - for t_dsk, a_dsk in zip (tmp_disks, self.disks): + for t_dsk, a_dsk in zip(tmp_disks, self.disks): rename_to.append(t_dsk.logical_id) t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk[constants.IDISK_ADOPT]) self.cfg.SetDiskID(t_dsk, pnode_name) @@ -8844,7 +8953,6 @@ class LUInstanceCreate(LogicalUnit): disk_abort = not _WaitForSync(self, iobj) elif iobj.disk_template in constants.DTS_INT_MIRROR: # make sure the disks are not degraded (still sync-ing is ok) - time.sleep(15) feedback_fn("* checking mirrors status") disk_abort = not _WaitForSync(self, iobj, oneshot=True) else: @@ -8861,12 +8969,33 @@ class LUInstanceCreate(LogicalUnit): if iobj.disk_template != constants.DT_DISKLESS and not self.adopt_disks: if self.op.mode == constants.INSTANCE_CREATE: if not self.op.no_install: + pause_sync = (iobj.disk_template in constants.DTS_INT_MIRROR and + not self.op.wait_for_sync) + if pause_sync: + feedback_fn("* pausing disk sync to install instance OS") + result = self.rpc.call_blockdev_pause_resume_sync(pnode_name, + iobj.disks, True) + for idx, success in enumerate(result.payload): + if not success: + logging.warn("pause-sync of instance %s for disk %d failed", + instance, idx) + feedback_fn("* running the instance OS create scripts...") # 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)) + os_add_result = \ + self.rpc.call_instance_os_add(pnode_name, iobj, False, + self.op.debug_level) + if pause_sync: + feedback_fn("* resuming disk sync") + result = self.rpc.call_blockdev_pause_resume_sync(pnode_name, + iobj.disks, False) + for idx, success in enumerate(result.payload): + if not success: + logging.warn("resume-sync of instance %s for disk %d failed", + instance, idx) + + os_add_result.Raise("Could not add os for instance %s" + " on node %s" % (instance, pnode_name)) elif self.op.mode == constants.INSTANCE_IMPORT: feedback_fn("* running the instance OS import scripts...") @@ -9071,7 +9200,7 @@ class LUInstanceReplaceDisks(LogicalUnit): # Lock member nodes of all locked groups self.needed_locks[locking.LEVEL_NODE] = [node_name - for group_uuid in self.glm.list_owned(locking.LEVEL_NODEGROUP) + for group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP) for node_name in self.cfg.GetNodeGroup(group_uuid).members] else: self._LockInstancesNodes() @@ -9111,16 +9240,9 @@ class LUInstanceReplaceDisks(LogicalUnit): assert (self.glm.is_owned(locking.LEVEL_NODEGROUP) or self.op.iallocator is None) - owned_groups = self.glm.list_owned(locking.LEVEL_NODEGROUP) + owned_groups = self.owned_locks(locking.LEVEL_NODEGROUP) if owned_groups: - groups = self.cfg.GetInstanceNodeGroups(self.op.instance_name) - if owned_groups != groups: - raise errors.OpExecError("Node groups used by instance '%s' changed" - " since lock was acquired, current list is %r," - " used to be '%s'" % - (self.op.instance_name, - utils.CommaJoin(groups), - utils.CommaJoin(owned_groups))) + _CheckInstanceNodeGroups(self.cfg, self.op.instance_name, owned_groups) return LogicalUnit.CheckPrereq(self) @@ -9209,6 +9331,9 @@ class TLReplaceDisks(Tasklet): return remote_node_name def _FindFaultyDisks(self, node_name): + """Wrapper for L{_FindFaultyInstanceDisks}. + + """ return _FindFaultyInstanceDisks(self.cfg, self.rpc, self.instance, node_name, True) @@ -9279,7 +9404,7 @@ class TLReplaceDisks(Tasklet): if remote_node is None: self.remote_node_info = None else: - assert remote_node in self.lu.glm.list_owned(locking.LEVEL_NODE), \ + assert remote_node in self.lu.owned_locks(locking.LEVEL_NODE), \ "Remote node '%s' is not locked" % remote_node self.remote_node_info = self.cfg.GetNodeInfo(remote_node) @@ -9399,13 +9524,13 @@ class TLReplaceDisks(Tasklet): if __debug__: # Verify owned locks before starting operation - owned_locks = self.lu.glm.list_owned(locking.LEVEL_NODE) - assert set(owned_locks) == set(self.node_secondary_ip), \ + owned_nodes = self.lu.owned_locks(locking.LEVEL_NODE) + assert set(owned_nodes) == set(self.node_secondary_ip), \ ("Incorrect node locks, owning %s, expected %s" % - (owned_locks, self.node_secondary_ip.keys())) + (owned_nodes, self.node_secondary_ip.keys())) - owned_locks = self.lu.glm.list_owned(locking.LEVEL_INSTANCE) - assert list(owned_locks) == [self.instance_name], \ + owned_instances = self.lu.owned_locks(locking.LEVEL_INSTANCE) + assert list(owned_instances) == [self.instance_name], \ "Instance '%s' not locked" % self.instance_name assert not self.lu.glm.is_owned(locking.LEVEL_NODEGROUP), \ @@ -9440,12 +9565,12 @@ class TLReplaceDisks(Tasklet): if __debug__: # Verify owned locks - owned_locks = self.lu.glm.list_owned(locking.LEVEL_NODE) + owned_nodes = self.lu.owned_locks(locking.LEVEL_NODE) nodes = frozenset(self.node_secondary_ip) - assert ((self.early_release and not owned_locks) or - (not self.early_release and not (set(owned_locks) - nodes))), \ + assert ((self.early_release and not owned_nodes) or + (not self.early_release and not (set(owned_nodes) - nodes))), \ ("Not owning the correct locks, early_release=%s, owned=%r," - " nodes=%r" % (self.early_release, owned_locks, nodes)) + " nodes=%r" % (self.early_release, owned_nodes, nodes)) return result @@ -9565,7 +9690,7 @@ class TLReplaceDisks(Tasklet): self.lu.LogWarning("Can't remove old LV: %s" % msg, hint="remove unused LVs manually") - def _ExecDrbd8DiskOnly(self, feedback_fn): # pylint: disable-msg=W0613 + def _ExecDrbd8DiskOnly(self, feedback_fn): # pylint: disable=W0613 """Replace a disk on the primary or secondary for DRBD 8. The algorithm for replace is quite complicated: @@ -9722,6 +9847,8 @@ class TLReplaceDisks(Tasklet): """ steps_total = 6 + pnode = self.instance.primary_node + # Step: check device activation self.lu.LogStep(1, steps_total, "Check device existence") self._CheckDisksExistence([self.instance.primary_node]) @@ -9796,10 +9923,8 @@ class TLReplaceDisks(Tasklet): " soon as possible")) self.lu.LogInfo("Detaching primary drbds from the network (=> standalone)") - result = self.rpc.call_drbd_disconnect_net([self.instance.primary_node], - self.node_secondary_ip, - self.instance.disks)\ - [self.instance.primary_node] + result = self.rpc.call_drbd_disconnect_net([pnode], self.node_secondary_ip, + self.instance.disks)[pnode] msg = result.fail_msg if msg: @@ -10004,9 +10129,9 @@ class LUNodeEvacuate(NoHooksLU): def CheckPrereq(self): # Verify locks - owned_instances = self.glm.list_owned(locking.LEVEL_INSTANCE) - owned_nodes = self.glm.list_owned(locking.LEVEL_NODE) - owned_groups = self.glm.list_owned(locking.LEVEL_NODEGROUP) + owned_instances = self.owned_locks(locking.LEVEL_INSTANCE) + owned_nodes = self.owned_locks(locking.LEVEL_NODE) + owned_groups = self.owned_locks(locking.LEVEL_NODEGROUP) assert owned_nodes == self.lock_nodes @@ -10297,7 +10422,7 @@ class LUInstanceQueryData(NoHooksLU): """ if self.wanted_names is None: assert self.op.use_locking, "Locking was not used" - self.wanted_names = self.glm.list_owned(locking.LEVEL_INSTANCE) + self.wanted_names = self.owned_locks(locking.LEVEL_INSTANCE) self.wanted_instances = \ map(compat.snd, self.cfg.GetMultiInstanceInfo(self.wanted_names)) @@ -10718,7 +10843,7 @@ class LUInstanceSetParams(LogicalUnit): if msg: # Assume the primary node is unreachable and go ahead self.warn.append("Can't get info from primary node %s: %s" % - (pnode, msg)) + (pnode, msg)) elif not isinstance(pninfo.payload.get("memory_free", None), int): self.warn.append("Node data from primary node %s doesn't contain" " free memory information" % pnode) @@ -11093,6 +11218,147 @@ class LUInstanceSetParams(LogicalUnit): } +class LUInstanceChangeGroup(LogicalUnit): + HPATH = "instance-change-group" + HTYPE = constants.HTYPE_INSTANCE + REQ_BGL = False + + def ExpandNames(self): + self.share_locks = _ShareAll() + self.needed_locks = { + locking.LEVEL_NODEGROUP: [], + locking.LEVEL_NODE: [], + } + + self._ExpandAndLockInstance() + + if self.op.target_groups: + self.req_target_uuids = map(self.cfg.LookupNodeGroup, + self.op.target_groups) + else: + self.req_target_uuids = None + + self.op.iallocator = _GetDefaultIAllocator(self.cfg, self.op.iallocator) + + def DeclareLocks(self, level): + if level == locking.LEVEL_NODEGROUP: + assert not self.needed_locks[locking.LEVEL_NODEGROUP] + + if self.req_target_uuids: + lock_groups = set(self.req_target_uuids) + + # Lock all groups used by instance optimistically; this requires going + # via the node before it's locked, requiring verification later on + instance_groups = self.cfg.GetInstanceNodeGroups(self.op.instance_name) + lock_groups.update(instance_groups) + else: + # No target groups, need to lock all of them + lock_groups = locking.ALL_SET + + self.needed_locks[locking.LEVEL_NODEGROUP] = lock_groups + + elif level == locking.LEVEL_NODE: + if self.req_target_uuids: + # Lock all nodes used by instances + self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND + self._LockInstancesNodes() + + # Lock all nodes in all potential target groups + lock_groups = (frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) - + self.cfg.GetInstanceNodeGroups(self.op.instance_name)) + member_nodes = [node_name + for group in lock_groups + for node_name in self.cfg.GetNodeGroup(group).members] + self.needed_locks[locking.LEVEL_NODE].extend(member_nodes) + else: + # Lock all nodes as all groups are potential targets + self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET + + def CheckPrereq(self): + owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE)) + owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) + owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE)) + + assert (self.req_target_uuids is None or + owned_groups.issuperset(self.req_target_uuids)) + assert owned_instances == set([self.op.instance_name]) + + # Get instance information + self.instance = self.cfg.GetInstanceInfo(self.op.instance_name) + + # Check if node groups for locked instance are still correct + assert owned_nodes.issuperset(self.instance.all_nodes), \ + ("Instance %s's nodes changed while we kept the lock" % + self.op.instance_name) + + inst_groups = _CheckInstanceNodeGroups(self.cfg, self.op.instance_name, + owned_groups) + + if self.req_target_uuids: + # User requested specific target groups + self.target_uuids = self.req_target_uuids + else: + # All groups except those used by the instance are potential targets + self.target_uuids = owned_groups - inst_groups + + conflicting_groups = self.target_uuids & inst_groups + if conflicting_groups: + raise errors.OpPrereqError("Can't use group(s) '%s' as targets, they are" + " used by the instance '%s'" % + (utils.CommaJoin(conflicting_groups), + self.op.instance_name), + errors.ECODE_INVAL) + + if not self.target_uuids: + raise errors.OpPrereqError("There are no possible target groups", + errors.ECODE_INVAL) + + def BuildHooksEnv(self): + """Build hooks env. + + """ + assert self.target_uuids + + env = { + "TARGET_GROUPS": " ".join(self.target_uuids), + } + + env.update(_BuildInstanceHookEnvByObject(self, self.instance)) + + return env + + def BuildHooksNodes(self): + """Build hooks nodes. + + """ + mn = self.cfg.GetMasterNode() + return ([mn], [mn]) + + def Exec(self, feedback_fn): + instances = list(self.owned_locks(locking.LEVEL_INSTANCE)) + + assert instances == [self.op.instance_name], "Instance not locked" + + ial = IAllocator(self.cfg, self.rpc, constants.IALLOCATOR_MODE_CHG_GROUP, + instances=instances, target_groups=list(self.target_uuids)) + + ial.Run(self.op.iallocator) + + if not ial.success: + raise errors.OpPrereqError("Can't compute solution for changing group of" + " instance '%s' using iallocator '%s': %s" % + (self.op.instance_name, self.op.iallocator, + ial.info), + errors.ECODE_NORES) + + jobs = _LoadNodeEvacResult(self, ial.result, self.op.early_release, False) + + self.LogInfo("Iallocator returned %s job(s) for changing group of" + " instance '%s'", len(jobs), self.op.instance_name) + + return ResultWithJobs(jobs) + + class LUBackupQuery(NoHooksLU): """Query the exports list @@ -11117,7 +11383,7 @@ class LUBackupQuery(NoHooksLU): that node. """ - self.nodes = self.glm.list_owned(locking.LEVEL_NODE) + self.nodes = self.owned_locks(locking.LEVEL_NODE) rpcresult = self.rpc.call_export_list(self.nodes) result = {} for node in rpcresult: @@ -11500,7 +11766,7 @@ class LUBackupRemove(NoHooksLU): fqdn_warn = True instance_name = self.op.instance_name - locked_nodes = self.glm.list_owned(locking.LEVEL_NODE) + locked_nodes = self.owned_locks(locking.LEVEL_NODE) exportlist = self.rpc.call_export_list(locked_nodes) found = False for node in exportlist: @@ -11620,12 +11886,12 @@ class LUGroupAssignNodes(NoHooksLU): """ assert self.needed_locks[locking.LEVEL_NODEGROUP] - assert (frozenset(self.glm.list_owned(locking.LEVEL_NODE)) == + assert (frozenset(self.owned_locks(locking.LEVEL_NODE)) == frozenset(self.op.nodes)) expected_locks = (set([self.group_uuid]) | self.cfg.GetNodeGroupsFromNodes(self.op.nodes)) - actual_locks = self.glm.list_owned(locking.LEVEL_NODEGROUP) + actual_locks = self.owned_locks(locking.LEVEL_NODEGROUP) if actual_locks != expected_locks: raise errors.OpExecError("Nodes changed groups since locks were acquired," " current groups are '%s', used to be '%s'" % @@ -11813,6 +12079,9 @@ class LUGroupQuery(NoHooksLU): def ExpandNames(self): self.gq.ExpandNames(self) + def DeclareLocks(self, level): + self.gq.DeclareLocks(self, level) + def Exec(self, feedback_fn): return self.gq.OldStyleQuery(self) @@ -11891,7 +12160,6 @@ class LUGroupSetParams(LogicalUnit): return result - class LUGroupRemove(LogicalUnit): HPATH = "group-remove" HTYPE = constants.HTYPE_GROUP @@ -12050,14 +12318,7 @@ class LUGroupEvacuate(LogicalUnit): utils.CommaJoin(self.req_target_uuids)), errors.ECODE_INVAL) - if not self.op.iallocator: - # Use default iallocator - self.op.iallocator = self.cfg.GetDefaultIAllocator() - - if not self.op.iallocator: - raise errors.OpPrereqError("No iallocator was specified, neither in the" - " opcode nor as a cluster-wide default", - errors.ECODE_INVAL) + self.op.iallocator = _GetDefaultIAllocator(self.cfg, self.op.iallocator) self.share_locks = _ShareAll() self.needed_locks = { @@ -12085,7 +12346,7 @@ class LUGroupEvacuate(LogicalUnit): # via the node before it's locked, requiring verification later on lock_groups.update(group_uuid for instance_name in - self.glm.list_owned(locking.LEVEL_INSTANCE) + self.owned_locks(locking.LEVEL_INSTANCE) for group_uuid in self.cfg.GetInstanceNodeGroups(instance_name)) else: @@ -12100,29 +12361,24 @@ class LUGroupEvacuate(LogicalUnit): self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND self._LockInstancesNodes() - # Lock all nodes in group to be evacuated - assert self.group_uuid in self.glm.list_owned(locking.LEVEL_NODEGROUP) - member_nodes = self.cfg.GetNodeGroup(self.group_uuid).members + # Lock all nodes in group to be evacuated and target groups + owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) + assert self.group_uuid in owned_groups + member_nodes = [node_name + for group in owned_groups + for node_name in self.cfg.GetNodeGroup(group).members] self.needed_locks[locking.LEVEL_NODE].extend(member_nodes) def CheckPrereq(self): - owned_instances = frozenset(self.glm.list_owned(locking.LEVEL_INSTANCE)) - owned_groups = frozenset(self.glm.list_owned(locking.LEVEL_NODEGROUP)) - owned_nodes = frozenset(self.glm.list_owned(locking.LEVEL_NODE)) + owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE)) + owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) + owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE)) assert owned_groups.issuperset(self.req_target_uuids) assert self.group_uuid in owned_groups # Check if locked instances are still correct - wanted_instances = self.cfg.GetNodeGroupInstances(self.group_uuid) - if owned_instances != wanted_instances: - raise errors.OpPrereqError("Instances in node group to be evacuated (%s)" - " changed since locks were acquired, wanted" - " %s, have %s; retry the operation" % - (self.group_uuid, - utils.CommaJoin(wanted_instances), - utils.CommaJoin(owned_instances)), - errors.ECODE_STATE) + _CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances) # Get instance information self.instances = dict(self.cfg.GetMultiInstanceInfo(owned_instances)) @@ -12130,21 +12386,14 @@ class LUGroupEvacuate(LogicalUnit): # Check if node groups for locked instances are still correct for instance_name in owned_instances: inst = self.instances[instance_name] - assert self.group_uuid in self.cfg.GetInstanceNodeGroups(instance_name), \ - "Instance %s has no node in group %s" % (instance_name, self.group_uuid) assert owned_nodes.issuperset(inst.all_nodes), \ "Instance %s's nodes changed while we kept the lock" % instance_name - inst_groups = self.cfg.GetInstanceNodeGroups(instance_name) - if not owned_groups.issuperset(inst_groups): - raise errors.OpPrereqError("Instance %s's node groups changed since" - " locks were acquired, current groups" - " are '%s', owning groups '%s'; retry the" - " operation" % - (instance_name, - utils.CommaJoin(inst_groups), - utils.CommaJoin(owned_groups)), - errors.ECODE_STATE) + inst_groups = _CheckInstanceNodeGroups(self.cfg, instance_name, + owned_groups) + + assert self.group_uuid in inst_groups, \ + "Instance %s has no node in group %s" % (instance_name, self.group_uuid) if self.req_target_uuids: # User requested specific target groups @@ -12155,7 +12404,8 @@ class LUGroupEvacuate(LogicalUnit): if group_uuid != self.group_uuid] if not self.target_uuids: - raise errors.OpExecError("There are no possible target groups") + raise errors.OpPrereqError("There are no possible target groups", + errors.ECODE_INVAL) def BuildHooksEnv(self): """Build hooks env. @@ -12172,14 +12422,14 @@ class LUGroupEvacuate(LogicalUnit): """ mn = self.cfg.GetMasterNode() - assert self.group_uuid in self.glm.list_owned(locking.LEVEL_NODEGROUP) + assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP) run_nodes = [mn] + self.cfg.GetNodeGroup(self.group_uuid).members return (run_nodes, run_nodes) def Exec(self, feedback_fn): - instances = list(self.glm.list_owned(locking.LEVEL_INSTANCE)) + instances = list(self.owned_locks(locking.LEVEL_INSTANCE)) assert self.group_uuid not in self.target_uuids @@ -12202,7 +12452,7 @@ class LUGroupEvacuate(LogicalUnit): return ResultWithJobs(jobs) -class TagsLU(NoHooksLU): # pylint: disable-msg=W0223 +class TagsLU(NoHooksLU): # pylint: disable=W0223 """Generic tags LU. This is an abstract class which is the parent of all the other tags LUs. @@ -12462,7 +12712,7 @@ class LUTestJqueue(NoHooksLU): # Wait for client to close try: try: - # pylint: disable-msg=E1101 + # pylint: disable=E1101 # Instance of '_socketobject' has no ... member conn.settimeout(cls._CLIENT_CONFIRM_TIMEOUT) conn.recv(1) @@ -12559,7 +12809,7 @@ class IAllocator(object): easy usage """ - # pylint: disable-msg=R0902 + # pylint: disable=R0902 # lots of instance attributes def __init__(self, cfg, rpc, mode, **kwargs): @@ -12574,7 +12824,6 @@ class IAllocator(object): self.hypervisor = None self.relocate_from = None self.name = None - self.evac_nodes = None self.instances = None self.evac_mode = None self.target_groups = [] @@ -12856,15 +13105,6 @@ class IAllocator(object): } return request - def _AddEvacuateNodes(self): - """Add evacuate nodes data to allocator structure. - - """ - request = { - "evac_nodes": self.evac_nodes - } - return request - def _AddNodeEvacuate(self): """Get data for node-evacuate requests. @@ -12906,7 +13146,7 @@ class IAllocator(object): _STRING_LIST = ht.TListOf(ht.TString) _JOB_LIST = ht.TListOf(ht.TListOf(ht.TStrictDict(True, False, { - # pylint: disable-msg=E1101 + # pylint: disable=E1101 # Class '...' has no 'OP_ID' member "OP_ID": ht.TElemOf([opcodes.OpInstanceFailover.OP_ID, opcodes.OpInstanceMigrate.OP_ID, @@ -12945,9 +13185,6 @@ class IAllocator(object): (_AddRelocateInstance, [("name", ht.TString), ("relocate_from", _STRING_LIST)], ht.TList), - constants.IALLOCATOR_MODE_MEVAC: - (_AddEvacuateNodes, [("evac_nodes", _STRING_LIST)], - ht.TListOf(ht.TAnd(ht.TIsLength(2), _STRING_LIST))), constants.IALLOCATOR_MODE_NODE_EVAC: (_AddNodeEvacuate, [ ("instances", _STRING_LIST), @@ -13006,39 +13243,25 @@ class IAllocator(object): (self._result_check, self.result), errors.ECODE_INVAL) - if self.mode in (constants.IALLOCATOR_MODE_RELOC, - constants.IALLOCATOR_MODE_MEVAC): + if self.mode == constants.IALLOCATOR_MODE_RELOC: + assert self.relocate_from is not None + assert self.required_nodes == 1 + node2group = dict((name, ndata["group"]) for (name, ndata) in self.in_data["nodes"].items()) fn = compat.partial(self._NodesToGroups, node2group, self.in_data["nodegroups"]) - if self.mode == constants.IALLOCATOR_MODE_RELOC: - assert self.relocate_from is not None - assert self.required_nodes == 1 - - request_groups = fn(self.relocate_from) - result_groups = fn(rdict["result"]) - - if result_groups != request_groups: - raise errors.OpExecError("Groups of nodes returned by iallocator (%s)" - " differ from original groups (%s)" % - (utils.CommaJoin(result_groups), - utils.CommaJoin(request_groups))) - elif self.mode == constants.IALLOCATOR_MODE_MEVAC: - request_groups = fn(self.evac_nodes) - for (instance_name, secnode) in self.result: - result_groups = fn([secnode]) - if result_groups != request_groups: - raise errors.OpExecError("Iallocator returned new secondary node" - " '%s' (group '%s') for instance '%s'" - " which is not in original group '%s'" % - (secnode, utils.CommaJoin(result_groups), - instance_name, - utils.CommaJoin(request_groups))) - else: - raise errors.ProgrammerError("Unhandled mode '%s'" % self.mode) + instance = self.cfg.GetInstanceInfo(self.name) + request_groups = fn(self.relocate_from + [instance.primary_node]) + result_groups = fn(rdict["result"] + [instance.primary_node]) + + if self.success and not set(result_groups).issubset(request_groups): + raise errors.OpExecError("Groups of nodes returned by iallocator (%s)" + " differ from original groups (%s)" % + (utils.CommaJoin(result_groups), + utils.CommaJoin(request_groups))) elif self.mode == constants.IALLOCATOR_MODE_NODE_EVAC: assert self.evac_mode in constants.IALLOCATOR_NEVAC_MODES @@ -13122,10 +13345,6 @@ class LUTestAllocator(NoHooksLU): self.op.name = fname self.relocate_from = \ list(self.cfg.GetInstanceInfo(fname).secondary_nodes) - elif self.op.mode == constants.IALLOCATOR_MODE_MEVAC: - if not hasattr(self.op, "evac_nodes"): - raise errors.OpPrereqError("Missing attribute 'evac_nodes' on" - " opcode input", errors.ECODE_INVAL) elif self.op.mode in (constants.IALLOCATOR_MODE_CHG_GROUP, constants.IALLOCATOR_MODE_NODE_EVAC): if not self.op.instances: @@ -13166,10 +13385,6 @@ class LUTestAllocator(NoHooksLU): name=self.op.name, relocate_from=list(self.relocate_from), ) - elif self.op.mode == constants.IALLOCATOR_MODE_MEVAC: - ial = IAllocator(self.cfg, self.rpc, - mode=self.op.mode, - evac_nodes=self.op.evac_nodes) elif self.op.mode == constants.IALLOCATOR_MODE_CHG_GROUP: ial = IAllocator(self.cfg, self.rpc, mode=self.op.mode,