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)