| #!/usr/bin/python |
| # |
| |
| # Copyright (C) 2011 Google Inc. |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: |
| # |
| # 1. Redistributions of source code must retain the above copyright notice, |
| # this list of conditions and the following disclaimer. |
| # |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
| # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| |
| """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.GetRepeatedKeys(*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() |