Export ndparams in RAPI node query
[ganeti-local] / lib / build / sphinx_ext.py
index 1570b02..6967a84 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2011 Google Inc.
+# Copyright (C) 2011, 2012 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
@@ -23,7 +23,6 @@
 
 """
 
-import operator
 from cStringIO import StringIO
 
 import docutils.statemachine
@@ -33,18 +32,45 @@ import docutils.utils
 import sphinx.errors
 import sphinx.util.compat
 
+s_compat = sphinx.util.compat
+
 from ganeti import constants
 from ganeti import compat
 from ganeti import errors
 from ganeti import utils
 from ganeti import opcodes
 from ganeti import ht
+from ganeti import rapi
+
+import ganeti.rapi.rlib2 # pylint: disable=W0611
+
+
+def _GetCommonParamNames():
+  """Builds a list of parameters common to all opcodes.
+
+  """
+  names = set(map(compat.fst, opcodes.OpCode.OP_PARAMS))
 
+  # The "depends" attribute should be listed
+  names.remove(opcodes.DEPEND_ATTR)
 
-COMMON_PARAM_NAMES = map(operator.itemgetter(0), opcodes.OpCode.OP_PARAMS)
+  return names
+
+
+COMMON_PARAM_NAMES = _GetCommonParamNames()
 
 #: Namespace for evaluating expressions
-EVAL_NS = dict(compat=compat, constants=constants, utils=utils, errors=errors)
+EVAL_NS = dict(compat=compat, constants=constants, utils=utils, errors=errors,
+               rlib2=rapi.rlib2)
+
+# Constants documentation for man pages
+CV_ECODES_DOC = "ecodes"
+# We don't care about the leak of variables _, name and doc here.
+# pylint: disable=W0621
+CV_ECODES_DOC_LIST = [(name, doc) for (_, name, doc) in constants.CV_ALL_ECODES]
+DOCUMENTED_CONSTANTS = {
+  CV_ECODES_DOC: CV_ECODES_DOC_LIST,
+  }
 
 
 class OpcodeError(sphinx.errors.SphinxError):
@@ -94,7 +120,7 @@ def _BuildOpcodeParams(op_id, include, exclude, alias):
   params_with_alias = \
     utils.NiceSort([(alias.get(name, name), name, default, test, doc)
                     for (name, default, test, doc) in op_cls.GetAllParams()],
-                   key=operator.itemgetter(0))
+                   key=compat.fst)
 
   for (rapi_name, name, default, test, doc) in params_with_alias:
     # Hide common parameters if not explicitely included
@@ -127,7 +153,24 @@ def _BuildOpcodeParams(op_id, include, exclude, alias):
       yield "  %s" % line
 
 
-class OpcodeParams(sphinx.util.compat.Directive):
+def _BuildOpcodeResult(op_id):
+  """Build opcode result documentation.
+
+  @type op_id: string
+  @param op_id: Opcode ID
+
+  """
+  op_cls = opcodes.OP_MAPPING[op_id]
+
+  result_fn = getattr(op_cls, "OP_RESULT", None)
+
+  if not result_fn:
+    raise OpcodeError("Opcode '%s' has no result description" % op_id)
+
+  return "``%s``" % result_fn
+
+
+class OpcodeParams(s_compat.Directive):
   """Custom directive for opcode parameters.
 
   See also <http://docutils.sourceforge.net/docs/howto/rst-directives.html>.
@@ -158,6 +201,32 @@ class OpcodeParams(sphinx.util.compat.Directive):
     return []
 
 
+class OpcodeResult(s_compat.Directive):
+  """Custom directive for opcode result.
+
+  See also <http://docutils.sourceforge.net/docs/howto/rst-directives.html>.
+
+  """
+  has_content = False
+  required_arguments = 1
+  optional_arguments = 0
+  final_argument_whitespace = False
+
+  def run(self):
+    op_id = self.arguments[0]
+
+    tab_width = 2
+    path = op_id
+    include_text = _BuildOpcodeResult(op_id)
+
+    # Inject into state machine
+    include_lines = docutils.statemachine.string2lines(include_text, tab_width,
+                                                       convert_whitespace=1)
+    self.state_machine.insert_input(include_lines, path)
+
+    return []
+
+
 def PythonEvalRole(role, rawtext, text, lineno, inliner,
                    options={}, content=[]):
   """Custom role to evaluate Python expressions.
@@ -165,7 +234,7 @@ def PythonEvalRole(role, rawtext, text, lineno, inliner,
   The expression's result is included as a literal.
 
   """
-  # pylint: disable-msg=W0102,W0613,W0142
+  # pylint: disable=W0102,W0613,W0142
   # W0102: Dangerous default value as argument
   # W0142: Used * or ** magic
   # W0613: Unused argument
@@ -174,7 +243,7 @@ def PythonEvalRole(role, rawtext, text, lineno, inliner,
 
   try:
     result = eval(code, EVAL_NS)
-  except Exception, err: # pylint: disable-msg=W0703
+  except Exception, err: # pylint: disable=W0703
     msg = inliner.reporter.error("Failed to evaluate %r: %s" % (code, err),
                                  line=lineno)
     return ([inliner.problematic(rawtext, rawtext, msg)], [msg])
@@ -184,7 +253,7 @@ def PythonEvalRole(role, rawtext, text, lineno, inliner,
   return ([node], [])
 
 
-class PythonAssert(sphinx.util.compat.Directive):
+class PythonAssert(s_compat.Directive):
   """Custom directive for writing assertions.
 
   The content must be a valid Python expression. If its result does not
@@ -222,11 +291,22 @@ def BuildQueryFields(fields):
   @type fields: dict (field name as key, field details as value)
 
   """
-  for (_, (fdef, _, _)) in utils.NiceSort(fields.items(),
-                                          key=operator.itemgetter(0)):
-    assert len(fdef.doc.splitlines()) == 1
-    yield "``%s``" % fdef.name
-    yield "  %s" % fdef.doc
+  defs = [(fdef.name, fdef.doc)
+           for (_, (fdef, _, _, _)) in utils.NiceSort(fields.items(),
+                                                      key=compat.fst)]
+  return BuildValuesDoc(defs)
+
+
+def BuildValuesDoc(values):
+  """Builds documentation for a list of values
+
+  @type values: list of tuples in the form (value, documentation)
+
+  """
+  for name, doc in values:
+    assert len(doc.splitlines()) == 1
+    yield "``%s``" % name
+    yield "  %s" % doc
 
 
 # TODO: Implement Sphinx directive for query fields
@@ -237,5 +317,6 @@ def setup(app):
 
   """
   app.add_directive("opcode_params", OpcodeParams)
+  app.add_directive("opcode_result", OpcodeResult)
   app.add_directive("pyassert", PythonAssert)
   app.add_role("pyeval", PythonEvalRole)