A new script, “autotools/build-rpc”, will be used to generate code for
RPC client wrappers. This is done because “lib/rpc.py” contains lots and
lots of boilerplate code. Forthcoming patches will start converting
RPC wrappers.
---
 .gitignore                   |    1 +
 Makefile.am                  |   10 ++-
 autotools/build-rpc          |  204 ++++++++++++++++++++++++++++++++++++++++++
 lib/build/rpc_definitions.py |   40 ++++++++
 4 files changed, 254 insertions(+), 1 deletions(-)
 create mode 100755 autotools/build-rpc
 create mode 100644 lib/build/rpc_definitions.py

diff --git a/.gitignore b/.gitignore
index cd88112..1b82e7c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -72,6 +72,7 @@
 # lib
 /lib/_autoconf.py
 /lib/_vcsversion.py
+/lib/_generated_rpc.py
 
 # man
 /man/*.[0-9]
diff --git a/Makefile.am b/Makefile.am
index 077b210..147ab6c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -28,6 +28,7 @@ CHECK_IMPORTS = $(top_srcdir)/autotools/check-imports
 DOCPP = $(top_srcdir)/autotools/docpp
 REPLACE_VARS_SED = autotools/replace_vars.sed
 CONVERT_CONSTANTS = $(top_srcdir)/autotools/convert-constants
+BUILD_RPC = $(top_srcdir)/autotools/build-rpc
 
 # Note: these are automake-specific variables, and must be named after
 # the directory + 'dir' suffix
@@ -144,6 +145,7 @@ CLEANFILES = \
        doc/examples/ganeti.cron \
        doc/examples/gnt-config-backup \
        doc/examples/hooks/ipsec \
+       lib/_generated_rpc.py \
        $(man_MANS) \
        $(manhtml) \
        tools/kvm-ifup \
@@ -164,13 +166,15 @@ BUILT_SOURCES = \
 
 BUILT_PYTHON_SOURCES = \
        lib/_autoconf.py \
-       lib/_vcsversion.py
+       lib/_vcsversion.py \
+       lib/_generated_rpc.py
 
 nodist_pkgpython_PYTHON = \
        $(BUILT_PYTHON_SOURCES)
 
 noinst_PYTHON = \
        lib/build/__init__.py \
+       lib/build/rpc_definitions.py \
        lib/build/sphinx_ext.py
 
 pkgpython_PYTHON = \
@@ -555,6 +559,7 @@ EXTRA_DIST = \
        epydoc.conf.in \
        pylintrc \
        autotools/build-bash-completion \
+       autotools/build-rpc \
        autotools/check-python-code \
        autotools/check-imports \
        autotools/check-man \
@@ -1015,6 +1020,9 @@ lib/_vcsversion.py: Makefile vcs-version | lib/.dir
          echo "VCS_VERSION = '$$VCSVER'"; \
        } > $@
 
+lib/_generated_rpc.py: lib/build/rpc_definitions.py $(BUILD_RPC) | lib/.dir
+       PYTHONPATH=. $(RUN_IN_TEMPDIR) $(CURDIR)/$(BUILD_RPC) $< > $@
+
 $(REPLACE_VARS_SED): Makefile
        set -e; \
        { echo 's#@PREFIX@#$(prefix)#g'; \
diff --git a/autotools/build-rpc b/autotools/build-rpc
new file mode 100755
index 0000000..96007c5
--- /dev/null
+++ b/autotools/build-rpc
@@ -0,0 +1,204 @@
+#!/usr/bin/python
+#
+
+# Copyright (C) 2011 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+
+"""Script to generate RPC code.
+
+"""
+
+# pylint: disable=C0103
+# [C0103] Invalid name
+
+import sys
+import re
+import itertools
+import textwrap
+from cStringIO import StringIO
+
+from ganeti import utils
+from ganeti import compat
+from ganeti import build
+
+
+_SINGLE = "single-node"
+_MULTI = "multi-node"
+
+
+def _WritePreamble(sw):
+  """Writes a preamble for the RPC wrapper output.
+
+  """
+  sw.Write("# This code is automatically generated at build time.")
+  sw.Write("# Do not modify manually.")
+  sw.Write("")
+  sw.Write("\"\"\"Automatically generated RPC client wrappers.")
+  sw.Write("")
+  sw.Write("\"\"\"")
+  sw.Write("")
+
+
+def _WrapCode(line):
+  """Wraps Python code.
+
+  """
+  return textwrap.wrap(line, width=70, expand_tabs=False,
+                       fix_sentence_endings=False, break_long_words=False,
+                       replace_whitespace=True,
+                       subsequent_indent=utils.ShellWriter.INDENT_STR)
+
+
+def _WriteDocstring(sw, name, timeout, kind, args, desc):
+  """Writes a docstring for an RPC wrapper.
+
+  """
+  sw.Write("\"\"\"Wrapper for RPC call '%s'", name)
+  sw.Write("")
+  if desc:
+    sw.Write(desc)
+    sw.Write("")
+
+  note = ["This is a %s call" % kind]
+  if timeout:
+    note.append(" with a timeout of %s" % utils.FormatSeconds(timeout))
+  sw.Write("@note: %s", "".join(note))
+
+  if kind == _SINGLE:
+    sw.Write("@type node: string")
+    sw.Write("@param node: Node name")
+  else:
+    sw.Write("@type node_list: list of string")
+    sw.Write("@param node_list: List of node names")
+
+  if args:
+    for (argname, _, argtext) in args:
+      if argtext:
+        docline = "@param %s: %s" % (argname, argtext)
+        for line in _WrapCode(docline):
+          sw.Write(line)
+  sw.Write("")
+  sw.Write("\"\"\"")
+
+
+def _MakeArgument((argname, wrapper, _)):
+  """Format argument for function call.
+
+  """
+  if wrapper:
+    return wrapper % argname
+  else:
+    return argname
+
+
+def _WriteBaseClass(sw, clsname, calls):
+  """Write RPC wrapper class.
+
+  """
+  sw.Write("")
+  sw.Write("class %s(object):", clsname)
+  sw.IncIndent()
+  try:
+    sw.Write("# E1101: Non-existent members")
+    sw.Write("# R0904: Too many public methods")
+    sw.Write("# pylint: disable=E1101,R0904")
+
+    if not calls:
+      sw.Write("pass")
+      return
+
+    for (name, kind, timeout, args, postproc, desc) in calls:
+      funcargs = ["self"]
+
+      if kind == _SINGLE:
+        funcargs.append("node")
+      elif kind == _MULTI:
+        funcargs.append("node_list")
+      else:
+        raise Exception("Unknown kind '%s'" % kind)
+
+      funcargs.extend(map(compat.fst, args))
+
+      assert "read_timeout" not in funcargs
+      funcargs.append("read_timeout=%s" % timeout)
+
+      funcdef = "def call_%s(%s):" % (name, utils.CommaJoin(funcargs))
+      for line in _WrapCode(funcdef):
+        sw.Write(line)
+
+      sw.IncIndent()
+      try:
+        _WriteDocstring(sw, name, timeout, kind, args, desc)
+
+        buf = StringIO()
+        buf.write("return ")
+
+        # In case line gets too long and is wrapped in a bad spot
+        buf.write("( ")
+
+        if postproc:
+          buf.write("%s(" % postproc)
+        buf.write("self._Call(")
+        if kind == _SINGLE:
+          buf.write("[node]")
+        else:
+          buf.write("node_list")
+        buf.write(", \"%s\", read_timeout, [%s])" %
+                  (name, utils.CommaJoin(map(_MakeArgument, args))))
+        if kind == _SINGLE:
+          buf.write("[node]")
+        if postproc:
+          buf.write(")")
+        buf.write(")")
+
+        for line in _WrapCode(buf.getvalue()):
+          sw.Write(line)
+      finally:
+        sw.DecIndent()
+      sw.Write("")
+  finally:
+    sw.DecIndent()
+
+
+def main():
+  """Main function.
+
+  """
+  buf = StringIO()
+  sw = utils.ShellWriter(buf)
+
+  _WritePreamble(sw)
+
+  for filename in sys.argv[1:]:
+    sw.Write("# Definitions from '%s'", filename)
+
+    module = build.LoadModule(filename)
+
+    # Call types are re-defined in definitions file to avoid imports. Verify
+    # here to ensure they're equal to local constants.
+    assert module.SINGLE == _SINGLE
+    assert module.MULTI == _MULTI
+
+    for (clsname, calls) in module.CALLS.items():
+      _WriteBaseClass(sw, clsname, calls)
+
+  print buf.getvalue()
+
+
+if __name__ == "__main__":
+  main()
diff --git a/lib/build/rpc_definitions.py b/lib/build/rpc_definitions.py
new file mode 100644
index 0000000..e2bc9f5
--- /dev/null
+++ b/lib/build/rpc_definitions.py
@@ -0,0 +1,40 @@
+#
+#
+
+# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+"""RPC definitions for communication between master and node daemons.
+
+"""
+
+
+# Various time constants for the timeout table
+TMO_URGENT = 60 # one minute
+TMO_FAST = 5 * 60 # five minutes
+TMO_NORMAL = 15 * 60 # 15 minutes
+TMO_SLOW = 3600 # one hour
+TMO_4HRS = 4 * 3600
+TMO_1DAY = 86400
+
+SINGLE = "single-node"
+MULTI = "multi-node"
+
+CALLS = {
+  "RpcClientDefault": [
+    ],
+  }
-- 
1.7.6

Reply via email to