diff --git a/lib/cmdlib.py b/lib/cmdlib.py index f8654d0e5aa662d66657eeab49e47c0f201d9bd7..dbeef5d0542fd6e6a72edb2b10916158d2724874 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -1163,7 +1163,7 @@ class LUDiagnoseOS(NoHooksLU): """Logical unit for OS diagnose/query. """ - _OP_REQP = [] + _OP_REQP = ["output_fields", "names"] def CheckPrereq(self): """Check prerequisites. @@ -1171,7 +1171,44 @@ class LUDiagnoseOS(NoHooksLU): This always succeeds, since this is a pure query LU. """ - return + if self.op.names: + raise errors.OpPrereqError("Selective OS query not supported") + + self.dynamic_fields = frozenset(["name", "valid", "node_status"]) + _CheckOutputFields(static=[], + dynamic=self.dynamic_fields, + selected=self.op.output_fields) + + @staticmethod + def _DiagnoseByOS(node_list, rlist): + """Remaps a per-node return list into an a per-os per-node dictionary + + Args: + node_list: a list with the names of all nodes + rlist: a map with node names as keys and OS objects as values + + Returns: + map: a map with osnames as keys and as value another map, with + nodes as + keys and list of OS objects as values + e.g. {"debian-etch": {"node1": [<object>,...], + "node2": [<object>,]} + } + + """ + all_os = {} + for node_name, nr in rlist.iteritems(): + if not nr: + continue + for os in nr: + if os.name not in all_os: + # build a list of nodes for this os containing empty lists + # for each node in node_list + all_os[os.name] = {} + for nname in node_list: + all_os[os.name][nname] = [] + all_os[os.name][node_name].append(os) + return all_os def Exec(self, feedback_fn): """Compute the list of OSes. @@ -1181,7 +1218,25 @@ class LUDiagnoseOS(NoHooksLU): node_data = rpc.call_os_diagnose(node_list) if node_data == False: raise errors.OpExecError("Can't gather the list of OSes") - return node_data + pol = self._DiagnoseByOS(node_list, node_data) + output = [] + for os_name, os_data in pol.iteritems(): + row = [] + for field in self.op.output_fields: + if field == "name": + val = os_name + elif field == "valid": + val = utils.all([osl and osl[0] for osl in os_data.values()]) + elif field == "node_status": + val = {} + for node_name, nos_list in os_data.iteritems(): + val[node_name] = [(v.status, v.path) for v in nos_list] + else: + raise errors.ParameterError(field) + row.append(val) + output.append(row) + + return output class LURemoveNode(LogicalUnit): diff --git a/lib/opcodes.py b/lib/opcodes.py index a5d8dedfea446d4089958d7610690a1d877c1207..3d7a3b01e651ce4063ae279c5977288bccad565d 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -379,7 +379,7 @@ class OpSetInstanceParams(OpCode): class OpDiagnoseOS(OpCode): """Compute the list of guest operating systems.""" OP_ID = "OP_OS_DIAGNOSE" - __slots__ = [] + __slots__ = ["output_fields", "names"] # Exports opcodes diff --git a/scripts/gnt-os b/scripts/gnt-os index 5918769eb55925f6ea6657a61679406f96cb16d0..e4041c728d9b2d9d203aca30edcaa2ffbae3ddb3 100755 --- a/scripts/gnt-os +++ b/scripts/gnt-os @@ -28,64 +28,27 @@ from ganeti import logger from ganeti import objects from ganeti import utils from ganeti import errors - - -def _DiagnoseByOS(rlist): - """Remap an OpDiagnoseOS() return list into an a per-os per-node dictionary - - Args: - rlist: a map with nodes as keys and diagnoseobjects as values - - Returns: - map: a map with osnames as keys and as value another map, with nodes as - keys and diagnoseobjects as values - e.g. {"debian-etch": {"node1": <object>, "node2": <object>}} - - """ - all_os = {} - for node_name, nr in rlist.iteritems(): - if not nr: - continue - for os in nr: - if os.name not in all_os: - all_os[os.name] = {} - if node_name not in all_os[os.name]: - all_os[os.name][node_name] = [] - all_os[os.name][node_name].append(os) - - return all_os +from ganeti import constants def ListOS(opts, args): """List the OSes existing on this node. """ - op = opcodes.OpDiagnoseOS() + op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[]) result = SubmitOpCode(op) if not result: logger.ToStdout("Can't get the OS list") return 1 - node_data = result - num_nodes = len(node_data) - all_os = _DiagnoseByOS(node_data) - - valid_os = [] - for os_name, os_node_data in all_os.iteritems(): - if len(os_node_data) != num_nodes: - continue - - if utils.all(os_node_data.values(), lambda l: l[0]): - valid_os.append(os_name) - if not opts.no_headers: headers = {"name": "Name"} else: headers = None data = GenerateTable(separator=None, headers=headers, fields=["name"], - data=[[os] for os in valid_os]) + data=[[row[0]] for row in result if row[1]]) for line in data: logger.ToStdout(line) @@ -97,30 +60,33 @@ def DiagnoseOS(opts, args): """Analyse all OSes on this cluster. """ - op = opcodes.OpDiagnoseOS() + op = opcodes.OpDiagnoseOS(output_fields=["name", "valid", "node_status"], + names=[]) result = SubmitOpCode(op) if not result: logger.ToStdout("Can't get the OS list") return 1 - node_data = result - all_os = _DiagnoseByOS(node_data) - has_bad = False - for os_name in all_os: + for os_name, os_valid, node_data in result: nodes_valid = {} nodes_bad = {} - for node_name in node_data: - if node_name in all_os[os_name]: - first_os = all_os[os_name][node_name].pop(0) + nodes_hidden = {} + for node_name, node_info in node_data.iteritems(): + nodes_hidden[node_name] = [] + if node_info: # at least one entry in the per-node list + first_os_status, first_os_path = node_info.pop(0) first_os_msg = ("%s (path: %s)" % - (first_os.status, first_os.path)) - if first_os: + (first_os_status, first_os_path)) + if first_os_status == constants.OS_VALID_STATUS: nodes_valid[node_name] = first_os_msg else: nodes_bad[node_name] = first_os_msg + for hstatus, hpath in node_info: + nodes_hidden[node_name].append(" [hidden] path: %s, status: %s" % + (hpath, hstatus)) else: nodes_bad[node_name] = "OS not found" @@ -133,18 +99,13 @@ def DiagnoseOS(opts, args): status = "partial valid" has_bad = True - def _OutputNodeHiddenOSStatus(dobj_list): - for dobj in dobj_list: - logger.ToStdout(" [hidden] path: %s, status: %s" % - (dobj.path, dobj.status)) - def _OutputPerNodeOSStatus(msg_map): map_k = utils.NiceSort(msg_map.keys()) for node_name in map_k: logger.ToStdout(" Node: %s, status: %s" % (node_name, msg_map[node_name])) - if node_name in all_os[os_name]: - _OutputNodeHiddenOSStatus(all_os[os_name][node_name]) + for msg in nodes_hidden[node_name]: + logger.ToStdout(msg) logger.ToStdout("OS: %s [global status: %s]" % (os_name, status)) _OutputPerNodeOSStatus(nodes_valid) diff --git a/tools/burnin b/tools/burnin index 590cd15e102ba88a10552f4c4c510a7c949c56c3..0ecf11fd22b649888a6c02cd6e1ab6fa0c293c96 100755 --- a/tools/burnin +++ b/tools/burnin @@ -167,22 +167,15 @@ class Burner(object): sys.exit(err_code) self.nodes = [data[0] for data in result] - result = self.ExecOp(opcodes.OpDiagnoseOS()) + result = self.ExecOp(opcodes.OpDiagnoseOS(output_fields=["name", "valid"], + names=[])) if not result: Log("Can't get the OS list") sys.exit(1) # filter non-valid OS-es - oses = {} - for node_name in result: - oses[node_name] = [obj for obj in result[node_name] if obj] - - fnode = oses.keys()[0] - os_set = set([os_inst.name for os_inst in oses[fnode]]) - del oses[fnode] - for node in oses: - os_set &= set([os_inst.name for os_inst in oses[node]]) + os_set = [val[0] for val in result if val[1]] if self.opts.os not in os_set: Log("OS '%s' not found" % self.opts.os)