master daemon: allow skipping the voting process
[ganeti-local] / scripts / gnt-os
index 5de43d6..a0c09a7 100755 (executable)
 # 02110-1301, USA.
 
 
+# pylint: disable-msg=W0401,W0614
+# W0401: Wildcard import ganeti.cli
+# W0614: Unused import %s from wildcard import (since we need cli)
+
 import sys
 from optparse import make_option
 
 from ganeti.cli import *
 from ganeti import opcodes
-from ganeti import logger
-from ganeti import objects
 from ganeti import utils
-from ganeti import errors
-
-
-def _DiagnoseOSValid(obj):
-  """Verify whether an OS diagnose object represents a valid OS
-
-    Args:
-      obj: an diagnostic object as returned by OpDiagnoseOS
-
-    Returns:
-      bool: OS validity status
-  """
-
-  if isinstance(obj, objects.OS):
-    return True
-  elif isinstance(obj, errors.InvalidOS):
-    return False
-  else:
-    raise errors.ProgrammerError("unknown OS diagnose type: '%s'" % type(obj))
-
-
-def _DiagnoseOSName(obj):
-  """Generate a status message for an OS diagnose object.
-
-    Args:
-      obj: an diagnostic object as returned by OpDiagnoseOS
-
-    Returns:
-      string: the name of the OS in question
-  """
-
-  if _DiagnoseOSValid(obj):
-    return obj.name
-  else:
-    return obj.args[0]
-
-
-def _DiagnoseOSStatus(obj):
-  """Generate a status message for an OS diagnose object.
-
-    Args:
-      obj: an diagnostic object as returned by OpDiagnoseOS
-
-    Returns:
-      string: a description of the OS status
-  """
-
-  if _DiagnoseOSValid(obj):
-    return "valid"
-  else:
-    return obj.args[2]
-
-
-def _DiagnoseOSPath(obj):
-  """Get the path out of an OS diagnose object.
-
-    Args:
-      obj: an diagnostic object as returned by OpDiagnoseOS
-
-    Returns:
-      string: the OS path
-
-  """
-  if _DiagnoseOSValid(obj):
-    return obj.path
-  else:
-    return obj.args[1]
-
-
-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 obj in nr:
-      os_name = _DiagnoseOSName(obj)
-      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(obj)
-
-  return all_os
+from ganeti import constants
 
 
 def ListOS(opts, args):
-  """List the OSes existing on this node.
+  """List the valid OSes in the cluster.
+
+  @param opts: the command line options selected by the user
+  @type args: list
+  @param args: should be an empty list
+  @rtype: int
+  @return: the desired exit code
 
   """
-  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")
+    ToStderr("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: _DiagnoseOSValid(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]],
+                       units=None)
 
   for line in data:
-    logger.ToStdout(line)
+    ToStdout(line)
 
   return 0
 
@@ -163,44 +67,40 @@ def ListOS(opts, args):
 def DiagnoseOS(opts, args):
   """Analyse all OSes on this cluster.
 
+  @param opts: the command line options selected by the user
+  @type args: list
+  @param args: should be an empty list
+  @rtype: int
+  @return: the desired exit code
+
   """
-  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")
+    ToStderr("Can't get the OS list")
     return 1
 
-  node_data = result
-  all_os = _DiagnoseByOS(node_data)
-
-  format = "%-*s %-*s %s"
-
-  max_name = len('Name')
-  if all_os:
-    max_name = max(max_name, max([len(name) for name in all_os]))
-
-  max_node = len('Status/Node')
-  max_node = max(max_node, max([len(name) for name in node_data]))
-
-  logger.ToStdout(format % (max_name, 'Name', max_node, 'Status/Node',
-                            'Details'))
-
   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)" %
-                        (_DiagnoseOSStatus(first_os),
-                         _DiagnoseOSPath(first_os)))
-        if _DiagnoseOSValid(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"
 
@@ -213,23 +113,17 @@ def DiagnoseOS(opts, args):
       status = "partial valid"
       has_bad = True
 
-    def _OutputNodeHiddenOSStatus(dobj_list):
-      for dobj in dobj_list:
-        logger.ToStdout(format % (max_name, "", max_node, "",
-                                  "[hidden] %s" %
-                                  _DiagnoseOSStatus(dobj)))
-
     def _OutputPerNodeOSStatus(msg_map):
       map_k = utils.NiceSort(msg_map.keys())
       for node_name in map_k:
-        logger.ToStdout(format % (max_name, "", max_node,
-                                  node_name, msg_map[node_name]))
-        if node_name in all_os[os_name]:
-          _OutputNodeHiddenOSStatus(all_os[os_name][node_name])
+        ToStdout("  Node: %s, status: %s", node_name, msg_map[node_name])
+        for msg in nodes_hidden[node_name]:
+          ToStdout(msg)
 
-    logger.ToStdout(format % (max_name, os_name, max_node, status, ""))
+    ToStdout("OS: %s [global status: %s]", os_name, status)
     _OutputPerNodeOSStatus(nodes_valid)
     _OutputPerNodeOSStatus(nodes_bad)
+    ToStdout("")
 
   return int(has_bad)