Add option to ignore offline node on instance start/stop
[ganeti-local] / scripts / gnt-os
index 4d566b1..f74213a 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 #
 
-# Copyright (C) 2006, 2007 Google Inc.
+# Copyright (C) 2006, 2007, 2010 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 # 02110-1301, USA.
 
+"""OS scripts related commands"""
 
-# pylint: disable-msg=W0401,W0614
+# pylint: disable-msg=W0401,W0613,W0614,C0103
 # W0401: Wildcard import ganeti.cli
+# W0613: Unused argument, since all functions follow the same API
 # W0614: Unused import %s from wildcard import (since we need cli)
+# C0103: Invalid name gnt-os
 
 import sys
 
 from ganeti.cli import *
+from ganeti import constants
 from ganeti import opcodes
 from ganeti import utils
-from ganeti import constants
 
 
 def ListOS(opts, args):
@@ -41,8 +44,8 @@ def ListOS(opts, args):
   @return: the desired exit code
 
   """
-  op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
-  result = SubmitOpCode(op)
+  op = opcodes.OpDiagnoseOS(output_fields=["name", "variants"], names=[])
+  result = SubmitOpCode(op, opts=opts)
 
   if not result:
     ToStderr("Can't get the OS list")
@@ -53,9 +56,12 @@ def ListOS(opts, args):
   else:
     headers = None
 
+  os_names = []
+  for (name, variants) in result:
+    os_names.extend([[n] for n in CalculateOSNames(name, variants)])
+
   data = GenerateTable(separator=None, headers=headers, fields=["name"],
-                       data=[[row[0]] for row in result if row[1]],
-                       units=None)
+                       data=os_names, units=None)
 
   for line in data:
     ToStdout(line)
@@ -63,6 +69,58 @@ def ListOS(opts, args):
   return 0
 
 
+def ShowOSInfo(opts, args):
+  """List detailed information about 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(output_fields=["name", "valid", "variants",
+                                           "parameters", "api_versions",
+                                           "blacklisted", "hidden"],
+                            names=[])
+  result = SubmitOpCode(op, opts=opts)
+
+  if not result:
+    ToStderr("Can't get the OS list")
+    return 1
+
+  do_filter = bool(args)
+
+  for (name, valid, variants, parameters, api_versions, blk, hid) in result:
+    if do_filter:
+      if name not in args:
+        continue
+      else:
+        args.remove(name)
+    ToStdout("%s:", name)
+    ToStdout("  - valid: %s", valid)
+    ToStdout("  - hidden: %s", hid)
+    ToStdout("  - blacklisted: %s", blk)
+    if valid:
+      ToStdout("  - API versions:")
+      for version in sorted(api_versions):
+        ToStdout("    - %s", version)
+      ToStdout("  - variants:")
+      for vname in variants:
+        ToStdout("    - %s", vname)
+      ToStdout("  - parameters:")
+      for pname, pdesc in parameters:
+        ToStdout("    - %s: %s", pname, pdesc)
+    ToStdout("")
+
+  if args:
+    for name in args:
+      ToStdout("%s: ", name)
+      ToStdout("")
+
+  return 0
+
+
 def _OsStatus(status, diagnose):
   """Beautifier function for OS status.
 
@@ -79,6 +137,7 @@ def _OsStatus(status, diagnose):
   else:
     return "invalid - %s" % diagnose
 
+
 def DiagnoseOS(opts, args):
   """Analyse all OSes on this cluster.
 
@@ -89,9 +148,10 @@ def DiagnoseOS(opts, args):
   @return: the desired exit code
 
   """
-  op = opcodes.OpDiagnoseOS(output_fields=["name", "valid", "node_status"],
-                            names=[])
-  result = SubmitOpCode(op)
+  op = opcodes.OpDiagnoseOS(output_fields=["name", "valid", "variants",
+                                           "node_status", "hidden",
+                                           "blacklisted"], names=[])
+  result = SubmitOpCode(op, opts=opts)
 
   if not result:
     ToStderr("Can't get the OS list")
@@ -99,22 +159,39 @@ def DiagnoseOS(opts, args):
 
   has_bad = False
 
-  for os_name, os_valid, node_data in result:
+  for os_name, _, os_variants, node_data, hid, blk in result:
     nodes_valid = {}
     nodes_bad = {}
     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_path, first_os_status, first_os_msg = node_info.pop(0)
-        first_os_msg = ("%s (path: %s)" % (_OsStatus(first_os_status,
-                                                     first_os_msg),
-                                           first_os_path))
-        if first_os_status:
-          nodes_valid[node_name] = first_os_msg
+        (fo_path, fo_status, fo_msg, fo_variants,
+         fo_params, fo_api) = node_info.pop(0)
+        fo_msg = "%s (path: %s)" % (_OsStatus(fo_status, fo_msg), fo_path)
+        if fo_api:
+          max_os_api = max(fo_api)
+          fo_msg += " [API versions: %s]" % utils.CommaJoin(fo_api)
         else:
-          nodes_bad[node_name] = first_os_msg
-        for hpath, hstatus, hmsg in node_info:
+          max_os_api = 0
+          fo_msg += " [no API versions declared]"
+
+        if max_os_api >= constants.OS_API_V15:
+          if fo_variants:
+            fo_msg += " [variants: %s]" % utils.CommaJoin(fo_variants)
+          else:
+            fo_msg += " [no variants]"
+        if max_os_api >= constants.OS_API_V20:
+          if fo_params:
+            fo_msg += (" [parameters: %s]" %
+                       utils.CommaJoin([v[0] for v in fo_params]))
+          else:
+            fo_msg += " [no parameters]"
+        if fo_status:
+          nodes_valid[node_name] = fo_msg
+        else:
+          nodes_bad[node_name] = fo_msg
+        for hpath, hstatus, hmsg, _, _, _ in node_info:
           nodes_hidden[node_name].append("    [hidden] path: %s, status: %s" %
                                          (hpath, _OsStatus(hstatus, hmsg)))
       else:
@@ -136,7 +213,14 @@ def DiagnoseOS(opts, args):
         for msg in nodes_hidden[node_name]:
           ToStdout(msg)
 
-    ToStdout("OS: %s [global status: %s]", os_name, status)
+    st_msg = "OS: %s [global status: %s]" % (os_name, status)
+    if hid:
+      st_msg += " [hidden]"
+    if blk:
+      st_msg += " [blacklisted]"
+    ToStdout(st_msg)
+    if os_variants:
+      ToStdout("  Variants: [%s]" % utils.CommaJoin(os_variants))
     _OutputPerNodeOSStatus(nodes_valid)
     _OutputPerNodeOSStatus(nodes_bad)
     ToStdout("")
@@ -144,12 +228,74 @@ def DiagnoseOS(opts, args):
   return int(has_bad)
 
 
+def ModifyOS(opts, args):
+  """Modify OS parameters for one OS.
+
+  @param opts: the command line options selected by the user
+  @type args: list
+  @param args: should be a list with one entry
+  @rtype: int
+  @return: the desired exit code
+
+  """
+  os = args[0]
+
+  if opts.hvparams:
+    os_hvp = {os: dict(opts.hvparams)}
+  else:
+    os_hvp = None
+
+  if opts.osparams:
+    osp = {os: opts.osparams}
+  else:
+    osp = None
+
+  if opts.hidden is not None:
+    if opts.hidden:
+      ohid = [(constants.DDM_ADD, os)]
+    else:
+      ohid = [(constants.DDM_REMOVE, os)]
+  else:
+    ohid = None
+
+  if opts.blacklisted is not None:
+    if opts.blacklisted:
+      oblk = [(constants.DDM_ADD, os)]
+    else:
+      oblk = [(constants.DDM_REMOVE, os)]
+  else:
+    oblk = None
+
+  if not (os_hvp or osp or ohid or oblk):
+    ToStderr("At least one of OS parameters or hypervisor parameters"
+             " must be passed")
+    return 1
+
+  op = opcodes.OpSetClusterParams(os_hvp=os_hvp,
+                                  osparams=osp,
+                                  hidden_os=ohid,
+                                  blacklisted_os=oblk)
+  SubmitOpCode(op, opts=opts)
+
+  return 0
+
+
 commands = {
   'list': (
-    ListOS, ARGS_NONE, [DEBUG_OPT, NOHDR_OPT],
-    "", "Lists all valid OSes on the master"),
+    ListOS, ARGS_NONE, [NOHDR_OPT, PRIORITY_OPT],
+    "", "Lists all valid operating systems on the cluster"),
   'diagnose': (
-    DiagnoseOS, ARGS_NONE, [DEBUG_OPT], "", "Diagnose all OSes"),
+    DiagnoseOS, ARGS_NONE, [PRIORITY_OPT],
+    "", "Diagnose all operating systems"),
+  'info': (
+    ShowOSInfo, [ArgOs()], [PRIORITY_OPT],
+    "", "Show detailed information about "
+    "operating systems"),
+  'modify': (
+    ModifyOS, ARGS_ONE_OS,
+    [HVLIST_OPT, OSPARAMS_OPT, DRY_RUN_OPT, PRIORITY_OPT,
+     HID_OS_OPT, BLK_OS_OPT],
+    "", "Modify the OS parameters"),
   }
 
 if __name__ == '__main__':