X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/db366d9a2c1de320b8da678b4d90be187b174c38..cfda0e48eb3238187dbb74bb56d54ca87747684c:/lib/cmdlib.py?ds=sidebyside diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 7add29c..fa8716e 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -496,15 +496,6 @@ class _QueryBase: # Return expanded names return self.wanted - @classmethod - def FieldsQuery(cls, fields): - """Returns list of available fields. - - @return: List of L{objects.QueryFieldDefinition} - - """ - return query.QueryFields(cls.FIELDS, fields) - def ExpandNames(self, lu): """Expand names for this query. @@ -615,6 +606,18 @@ def _GetUpdatedParams(old_params, update_dict, return params_copy +def _RunPostHook(lu, node_name): + """Runs the post-hook for an opcode on a single node. + + """ + hm = lu.proc.hmclass(lu.rpc.call_hooks_runner, lu) + try: + hm.RunPhase(constants.HOOKS_PHASE_POST, nodes=[node_name]) + except: + # pylint: disable-msg=W0702 + lu.LogWarning("Errors occurred running hooks on %s" % node_name) + + def _CheckOutputFields(static, dynamic, selected): """Checks whether all selected fields are valid. @@ -1159,12 +1162,7 @@ class LUClusterDestroy(LogicalUnit): master = self.cfg.GetMasterNode() # Run post hooks on master node before it's removed - hm = self.proc.hmclass(self.rpc.call_hooks_runner, self) - try: - hm.RunPhase(constants.HOOKS_PHASE_POST, [master]) - except: - # pylint: disable-msg=W0702 - self.LogWarning("Errors occurred running hooks on %s" % master) + _RunPostHook(self, master) result = self.rpc.call_node_stop_master(master, False) result.Raise("Could not disable the master role") @@ -2323,7 +2321,7 @@ class LUClusterVerify(LogicalUnit): utils.CommaJoin(inst_config.secondary_nodes), code=self.ETYPE_WARNING) - if inst_config.disk_template in constants.DTS_NET_MIRROR: + if inst_config.disk_template in constants.DTS_INT_MIRROR: pnode = inst_config.primary_node instance_nodes = utils.NiceSort(inst_config.all_nodes) instance_groups = {} @@ -3249,6 +3247,8 @@ class LUOobCommand(NoHooksLU): self.nodes = [] self.master_node = self.cfg.GetMasterNode() + assert self.op.power_delay >= 0.0 + if self.op.node_names: if self.op.command in self._SKIP_MASTER: if self.master_node in self.op.node_names: @@ -3312,7 +3312,7 @@ class LUOobCommand(NoHooksLU): master_node = self.master_node ret = [] - for node in self.nodes: + for idx, node in enumerate(self.nodes): node_entry = [(constants.RS_NORMAL, node.name)] ret.append(node_entry) @@ -3366,6 +3366,10 @@ class LUOobCommand(NoHooksLU): node_entry.append((constants.RS_NORMAL, result.payload)) + if (self.op.command == constants.OOB_POWER_ON and + idx < len(self.nodes) - 1): + time.sleep(self.op.power_delay) + return ret def _CheckPayload(self, result): @@ -3404,37 +3408,28 @@ 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 - -class LUOsDiagnose(NoHooksLU): - """Logical unit for OS diagnose/query. - - """ - REQ_BGL = False - _HID = "hidden" - _BLK = "blacklisted" - _VLD = "valid" - _FIELDS_STATIC = utils.FieldSet() - _FIELDS_DYNAMIC = utils.FieldSet("name", _VLD, "node_status", "variants", - "parameters", "api_versions", _HID, _BLK) - - def CheckArguments(self): - if self.op.names: - raise errors.OpPrereqError("Selective OS query not supported", - errors.ECODE_INVAL) - - _CheckOutputFields(static=self._FIELDS_STATIC, - dynamic=self._FIELDS_DYNAMIC, - selected=self.op.output_fields) - - def ExpandNames(self): - # Lock all nodes, in shared mode + def ExpandNames(self, lu): + # Lock all nodes in shared mode # Temporary removal of locks, should be reverted later # TODO: reintroduce locks when they are lighter-weight - self.needed_locks = {} + lu.needed_locks = {} #self.share_locks[locking.LEVEL_NODE] = 1 #self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET + # The following variables interact with _QueryBase._GetNames + if self.names: + self.wanted = self.names + else: + self.wanted = locking.ALL_SET + + self.do_locking = self.use_locking + + def DeclareLocks(self, lu, level): + pass + @staticmethod def _DiagnoseByOS(rlist): """Remaps a per-node return list into an a per-os per-node dictionary @@ -3475,71 +3470,100 @@ class LUOsDiagnose(NoHooksLU): variants, params, api_versions)) return all_os - def Exec(self, feedback_fn): - """Compute the list of OSes. + def _GetQueryData(self, lu): + """Computes the list of nodes and their attributes. """ + # Locking is not used + assert not (lu.acquired_locks or self.do_locking or self.use_locking) + valid_nodes = [node.name - for node in self.cfg.GetAllNodesInfo().values() + for node in lu.cfg.GetAllNodesInfo().values() if not node.offline and node.vm_capable] - node_data = self.rpc.call_os_diagnose(valid_nodes) - pol = self._DiagnoseByOS(node_data) - output = [] - cluster = self.cfg.GetClusterInfo() + pol = self._DiagnoseByOS(lu.rpc.call_os_diagnose(valid_nodes)) + cluster = lu.cfg.GetClusterInfo() + + data = {} + + for (os_name, os_data) in pol.items(): + info = query.OsInfo(name=os_name, valid=True, node_status=os_data, + hidden=(os_name in cluster.hidden_os), + blacklisted=(os_name in cluster.blacklisted_os)) + + variants = set() + parameters = set() + api_versions = set() - for os_name in utils.NiceSort(pol.keys()): - os_data = pol[os_name] - row = [] - valid = True - (variants, params, api_versions) = null_state = (set(), set(), set()) for idx, osl in enumerate(os_data.values()): - valid = bool(valid and osl and osl[0][1]) - if not valid: - (variants, params, api_versions) = null_state + info.valid = bool(info.valid and osl and osl[0][1]) + if not info.valid: break - node_variants, node_params, node_api = osl[0][3:6] - if idx == 0: # first entry - variants = set(node_variants) - params = set(node_params) - api_versions = set(node_api) - else: # keep consistency + + (node_variants, node_params, node_api) = osl[0][3:6] + if idx == 0: + # First entry + variants.update(node_variants) + parameters.update(node_params) + api_versions.update(node_api) + else: + # Filter out inconsistent values variants.intersection_update(node_variants) - params.intersection_update(node_params) + parameters.intersection_update(node_params) api_versions.intersection_update(node_api) - is_hid = os_name in cluster.hidden_os - is_blk = os_name in cluster.blacklisted_os - if ((self._HID not in self.op.output_fields and is_hid) or - (self._BLK not in self.op.output_fields and is_blk) or - (self._VLD not in self.op.output_fields and not valid)): - continue + info.variants = list(variants) + info.parameters = list(parameters) + info.api_versions = list(api_versions) - for field in self.op.output_fields: - if field == "name": - val = os_name - elif field == self._VLD: - val = valid - elif field == "node_status": - # this is just a copy of the dict - val = {} - for node_name, nos_list in os_data.items(): - val[node_name] = nos_list - elif field == "variants": - val = utils.NiceSort(list(variants)) - elif field == "parameters": - val = list(params) - elif field == "api_versions": - val = list(api_versions) - elif field == self._HID: - val = is_hid - elif field == self._BLK: - val = is_blk - else: - raise errors.ParameterError(field) - row.append(val) - output.append(row) + data[os_name] = info - return output + # Prepare data in requested order + return [data[name] for name in self._GetNames(lu, pol.keys(), None) + if name in data] + + +class LUOsDiagnose(NoHooksLU): + """Logical unit for OS diagnose/query. + + """ + REQ_BGL = False + + @staticmethod + def _BuildFilter(fields, names): + """Builds a filter for querying OSes. + + """ + name_filter = qlang.MakeSimpleFilter("name", names) + + # Legacy behaviour: Hide hidden, blacklisted or invalid OSes if the + # respective field is not requested + status_filter = [[qlang.OP_NOT, [qlang.OP_TRUE, fname]] + for fname in ["hidden", "blacklisted"] + if fname not in fields] + if "valid" not in fields: + status_filter.append([qlang.OP_TRUE, "valid"]) + + if status_filter: + status_filter.insert(0, qlang.OP_AND) + else: + status_filter = None + + if name_filter and status_filter: + return [qlang.OP_AND, name_filter, status_filter] + elif name_filter: + return name_filter + else: + return status_filter + + def CheckArguments(self): + self.oq = _OsQuery(self._BuildFilter(self.op.output_fields, self.op.names), + self.op.output_fields, False) + + def ExpandNames(self): + self.oq.ExpandNames(self) + + def Exec(self, feedback_fn): + return self.oq.OldStyleQuery(self) class LUNodeRemove(LogicalUnit): @@ -3615,12 +3639,7 @@ class LUNodeRemove(LogicalUnit): self.context.RemoveNode(node.name) # Run post hooks on the node before it's removed - hm = self.proc.hmclass(self.rpc.call_hooks_runner, self) - try: - hm.RunPhase(constants.HOOKS_PHASE_POST, [node.name]) - except: - # pylint: disable-msg=W0702 - self.LogWarning("Errors occurred running hooks on %s" % node.name) + _RunPostHook(self, node.name) result = self.rpc.call_node_leave_cluster(node.name, modify_ssh_setup) msg = result.fail_msg @@ -4018,7 +4037,7 @@ class LUQueryFields(NoHooksLU): self.needed_locks = {} def Exec(self, feedback_fn): - return self.qcls.FieldsQuery(self.op.fields) + return query.QueryFields(self.qcls.FIELDS, self.op.fields) class LUNodeModifyStorage(NoHooksLU): @@ -4095,9 +4114,12 @@ class LUNodeAdd(LogicalUnit): "MASTER_CAPABLE": str(self.op.master_capable), "VM_CAPABLE": str(self.op.vm_capable), } - nodes_0 = self.cfg.GetNodeList() - nodes_1 = nodes_0 + [self.op.node_name, ] - return env, nodes_0, nodes_1 + + # Exclude added node + pre_nodes = list(set(self.cfg.GetNodeList()) - set([self.op.node_name])) + post_nodes = pre_nodes + [self.op.node_name, ] + + return (env, pre_nodes, post_nodes) def CheckPrereq(self): """Check prerequisites. @@ -4394,7 +4416,7 @@ class LUNodeSetParams(LogicalUnit): if self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET: for instance_name in self.acquired_locks[locking.LEVEL_INSTANCE]: instance = self.context.cfg.GetInstanceInfo(instance_name) - i_mirrored = instance.disk_template in constants.DTS_NET_MIRROR + i_mirrored = instance.disk_template in constants.DTS_INT_MIRROR if i_mirrored and self.op.node_name in instance.all_nodes: instances_keep.append(instance_name) self.affected_instances.append(instance) @@ -5710,7 +5732,6 @@ class LUInstanceFailover(LogicalUnit): """ self.iallocator = getattr(self.op, "iallocator", None) self.target_node = getattr(self.op, "target_node", None) - _CheckIAllocatorOrNode(self, "iallocator", "target_node") def ExpandNames(self): self._ExpandAndLockInstance() @@ -5749,7 +5770,7 @@ class LUInstanceFailover(LogicalUnit): "NEW_PRIMARY": self.op.target_node, } - if instance.disk_template in constants.DTS_NET_MIRROR: + if instance.disk_template in constants.DTS_INT_MIRROR: env["OLD_SECONDARY"] = instance.secondary_nodes[0] env["NEW_SECONDARY"] = source_node else: @@ -5778,6 +5799,7 @@ class LUInstanceFailover(LogicalUnit): errors.ECODE_STATE) if instance.disk_template in constants.DTS_EXT_MIRROR: + _CheckIAllocatorOrNode(self, "iallocator", "target_node") if self.op.iallocator: self._RunAllocator() # Release all unnecessary node locks @@ -5799,6 +5821,13 @@ class LUInstanceFailover(LogicalUnit): instance.disk_template) target_node = secondary_nodes[0] + if self.op.iallocator or (self.op.target_node and + self.op.target_node != target_node): + raise errors.OpPrereqError("Instances with disk template %s cannot" + " be failed over to arbitrary nodes" + " (neither an iallocator nor a target" + " node can be passed)" % + instance.disk_template, errors.ECODE_INVAL) _CheckNodeOnline(self, target_node) _CheckNodeNotDrained(self, target_node) @@ -5928,9 +5957,6 @@ class LUInstanceMigrate(LogicalUnit): HTYPE = constants.HTYPE_INSTANCE REQ_BGL = False - def CheckArguments(self): - _CheckIAllocatorOrNode(self, "iallocator", "target_node") - def ExpandNames(self): self._ExpandAndLockInstance() @@ -5975,7 +6001,7 @@ class LUInstanceMigrate(LogicalUnit): "NEW_PRIMARY": target_node, }) - if instance.disk_template in constants.DTS_NET_MIRROR: + if instance.disk_template in constants.DTS_INT_MIRROR: env["OLD_SECONDARY"] = target_node env["NEW_SECONDARY"] = source_node else: @@ -6266,9 +6292,7 @@ class TLMigrateInstance(Tasklet): errors.ECODE_STATE) if instance.disk_template in constants.DTS_EXT_MIRROR: - if [self.iallocator, self.target_node].count(None) != 1: - raise errors.OpPrereqError("Do not specify both, iallocator and" - " target node", errors.ECODE_INVAL) + _CheckIAllocatorOrNode(self.lu, "iallocator", "target_node") if self.iallocator: self._RunAllocator() @@ -6292,6 +6316,13 @@ class TLMigrateInstance(Tasklet): " %s disk template" % instance.disk_template) target_node = secondary_nodes[0] + if self.lu.op.iallocator or (self.lu.op.target_node and + self.lu.op.target_node != target_node): + raise errors.OpPrereqError("Instances with disk template %s cannot" + " be migrated over to arbitrary nodes" + " (neither an iallocator nor a target" + " node can be passed)" % + instance.disk_template, errors.ECODE_INVAL) i_be = self.cfg.GetClusterInfo().FillBE(instance) @@ -6474,7 +6505,7 @@ class TLMigrateInstance(Tasklet): " primary node (%s)" % source_node) demoted_node = target_node - if instance.disk_template in constants.DTS_NET_MIRROR: + if instance.disk_template in constants.DTS_INT_MIRROR: self._EnsureSecondary(demoted_node) try: self._WaitUntilSync() @@ -6634,7 +6665,7 @@ class TLMigrateInstance(Tasklet): self.source_node = self.instance.primary_node # FIXME: if we implement migrate-to-any in DRBD, this needs fixing - if self.instance.disk_template in constants.DTS_NET_MIRROR: + if self.instance.disk_template in constants.DTS_INT_MIRROR: self.target_node = self.instance.secondary_nodes[0] # Otherwise self.target_node has been populated either # directly, or through an iallocator. @@ -7244,7 +7275,7 @@ class LUInstanceCreate(LogicalUnit): _CheckIAllocatorOrNode(self, "iallocator", "pnode") if self.op.pnode is not None: - if self.op.disk_template in constants.DTS_NET_MIRROR: + if self.op.disk_template in constants.DTS_INT_MIRROR: if self.op.snode is None: raise errors.OpPrereqError("The networked disk templates need" " a mirror node", errors.ECODE_INVAL) @@ -7794,7 +7825,7 @@ class LUInstanceCreate(LogicalUnit): self.secondaries = [] # mirror node verification - if self.op.disk_template in constants.DTS_NET_MIRROR: + if self.op.disk_template in constants.DTS_INT_MIRROR: if self.op.snode == pnode.name: raise errors.OpPrereqError("The secondary node cannot be the" " primary node.", errors.ECODE_INVAL) @@ -8001,7 +8032,7 @@ class LUInstanceCreate(LogicalUnit): if self.op.wait_for_sync: disk_abort = not _WaitForSync(self, iobj) - elif iobj.disk_template in constants.DTS_NET_MIRROR: + 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") @@ -8323,6 +8354,30 @@ class TLReplaceDisks(Tasklet): return _FindFaultyInstanceDisks(self.cfg, self.rpc, self.instance, node_name, True) + def _CheckDisksActivated(self, instance): + """Checks if the instance disks are activated. + + @param instance: The instance to check disks + @return: True if they are activated, False otherwise + + """ + nodes = instance.all_nodes + + for idx, dev in enumerate(instance.disks): + for node in nodes: + self.lu.LogInfo("Checking disk/%d on %s", idx, node) + self.cfg.SetDiskID(dev, node) + + result = self.rpc.call_blockdev_find(node, dev) + + if result.offline: + continue + elif result.fail_msg or not result.payload: + return False + + return True + + def CheckPrereq(self): """Check prerequisites. @@ -8386,6 +8441,10 @@ class TLReplaceDisks(Tasklet): errors.ECODE_INVAL) if self.mode == constants.REPLACE_DISK_AUTO: + if not self._CheckDisksActivated(instance): + raise errors.OpPrereqError("Please run activate-disks on instance %s" + " first" % self.instance_name, + errors.ECODE_STATE) faulty_primary = self._FindFaultyDisks(instance.primary_node) faulty_secondary = self._FindFaultyDisks(secondary_node) @@ -9326,7 +9385,7 @@ class LUInstanceSetParams(LogicalUnit): errors.ECODE_INVAL) if (self.op.disk_template and - self.op.disk_template in constants.DTS_NET_MIRROR and + self.op.disk_template in constants.DTS_INT_MIRROR and self.op.remote_node is None): raise errors.OpPrereqError("Changing the disk template to a mirrored" " one requires specifying a secondary node", @@ -9486,7 +9545,7 @@ class LUInstanceSetParams(LogicalUnit): self.op.disk_template), errors.ECODE_INVAL) _CheckInstanceDown(self, instance, "cannot change disk template") - if self.op.disk_template in constants.DTS_NET_MIRROR: + if self.op.disk_template in constants.DTS_INT_MIRROR: if self.op.remote_node == pnode: raise errors.OpPrereqError("Given new secondary node %s is the same" " as the primary node of the instance" % @@ -10475,7 +10534,7 @@ class LUGroupAssignNodes(NoHooksLU): In particular, it returns information about newly split instances, and instances that were already split, and remain so after the change. - Only instances whose disk template is listed in constants.DTS_NET_MIRROR are + Only instances whose disk template is listed in constants.DTS_INT_MIRROR are considered. @type changes: list of (node_name, new_group_uuid) pairs. @@ -10498,7 +10557,7 @@ class LUGroupAssignNodes(NoHooksLU): return [instance.primary_node] + list(instance.secondary_nodes) for inst in instance_data.values(): - if inst.disk_template not in constants.DTS_NET_MIRROR: + if inst.disk_template not in constants.DTS_INT_MIRROR: continue instance_nodes = InstanceNodes(inst) @@ -11405,7 +11464,7 @@ class IAllocator(object): """ disk_space = _ComputeDiskSize(self.disk_template, self.disks) - if self.disk_template in constants.DTS_NET_MIRROR: + if self.disk_template in constants.DTS_INT_MIRROR: self.required_nodes = 2 else: self.required_nodes = 1 @@ -11442,7 +11501,7 @@ class IAllocator(object): raise errors.OpPrereqError("Can't relocate non-mirrored instances", errors.ECODE_INVAL) - if instance.disk_template in constants.DTS_NET_MIRROR and \ + if instance.disk_template in constants.DTS_INT_MIRROR and \ len(instance.secondary_nodes) != 1: raise errors.OpPrereqError("Instance has not exactly one secondary node", errors.ECODE_STATE) @@ -11628,13 +11687,16 @@ _QUERY_IMPL = { constants.QR_INSTANCE: _InstanceQuery, constants.QR_NODE: _NodeQuery, constants.QR_GROUP: _GroupQuery, + constants.QR_OS: _OsQuery, } +assert set(_QUERY_IMPL.keys()) == constants.QR_VIA_OP + def _GetQueryImplementation(name): """Returns the implemtnation for a query type. - @param name: Query type, must be one of L{constants.QR_OP_QUERY} + @param name: Query type, must be one of L{constants.QR_VIA_OP} """ try: