| #!/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" |
| |
| #: Expected length of a rpc definition |
| _RPC_DEF_LEN = 8 |
| |
| |
| 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("") |
| sw.Write("from ganeti import rpc_defs") |
| 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 and not callable(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 _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 |
| |
| sw.Write("_CALLS = rpc_defs.CALLS[%r]", clsname) |
| sw.Write("") |
| |
| for v in calls: |
| if len(v) != _RPC_DEF_LEN: |
| raise ValueError("Procedure %s has only %d elements, expected %d" % |
| (v[0], len(v), _RPC_DEF_LEN)) |
| |
| for (name, kind, _, timeout, args, _, _, desc) in sorted(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)) |
| |
| funcargs.append("_def=_CALLS[%r]" % name) |
| |
| 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("(") |
| |
| buf.write("self._Call(_def, ") |
| if kind == _SINGLE: |
| buf.write("[node]") |
| else: |
| buf.write("node_list") |
| |
| buf.write(", [%s])" % |
| # Function arguments |
| utils.CommaJoin(map(compat.fst, args))) |
| |
| if kind == _SINGLE: |
| buf.write("[node]") |
| 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 |
| |
| dups = utils.FindDuplicates(itertools.chain(*map(lambda value: value.keys(), |
| module.CALLS.values()))) |
| if dups: |
| raise Exception("Found duplicate RPC definitions for '%s'" % |
| utils.CommaJoin(sorted(dups))) |
| |
| for (clsname, calls) in sorted(module.CALLS.items()): |
| _WriteBaseClass(sw, clsname, calls.values()) |
| |
| print buf.getvalue() |
| |
| |
| if __name__ == "__main__": |
| main() |