root / autotools / build-rpc @ f68cc544
History | View | Annotate | Download (4.8 kB)
1 |
#!/usr/bin/python |
---|---|
2 |
# |
3 |
|
4 |
# Copyright (C) 2011 Google Inc. |
5 |
# |
6 |
# This program is free software; you can redistribute it and/or modify |
7 |
# it under the terms of the GNU General Public License as published by |
8 |
# the Free Software Foundation; either version 2 of the License, or |
9 |
# (at your option) any later version. |
10 |
# |
11 |
# This program is distributed in the hope that it will be useful, but |
12 |
# WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 |
# General Public License for more details. |
15 |
# |
16 |
# You should have received a copy of the GNU General Public License |
17 |
# along with this program; if not, write to the Free Software |
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
19 |
# 02110-1301, USA. |
20 |
|
21 |
|
22 |
"""Script to generate RPC code. |
23 |
|
24 |
""" |
25 |
|
26 |
# pylint: disable=C0103 |
27 |
# [C0103] Invalid name |
28 |
|
29 |
import sys |
30 |
import re |
31 |
import itertools |
32 |
import textwrap |
33 |
from cStringIO import StringIO |
34 |
|
35 |
from ganeti import utils |
36 |
from ganeti import compat |
37 |
from ganeti import build |
38 |
|
39 |
|
40 |
_SINGLE = "single-node" |
41 |
_MULTI = "multi-node" |
42 |
|
43 |
|
44 |
def _WritePreamble(sw): |
45 |
"""Writes a preamble for the RPC wrapper output. |
46 |
|
47 |
""" |
48 |
sw.Write("# This code is automatically generated at build time.") |
49 |
sw.Write("# Do not modify manually.") |
50 |
sw.Write("") |
51 |
sw.Write("\"\"\"Automatically generated RPC client wrappers.") |
52 |
sw.Write("") |
53 |
sw.Write("\"\"\"") |
54 |
sw.Write("") |
55 |
sw.Write("from ganeti import rpc_defs") |
56 |
sw.Write("") |
57 |
|
58 |
|
59 |
def _WrapCode(line): |
60 |
"""Wraps Python code. |
61 |
|
62 |
""" |
63 |
return textwrap.wrap(line, width=70, expand_tabs=False, |
64 |
fix_sentence_endings=False, break_long_words=False, |
65 |
replace_whitespace=True, |
66 |
subsequent_indent=utils.ShellWriter.INDENT_STR) |
67 |
|
68 |
|
69 |
def _WriteDocstring(sw, name, timeout, kind, args, desc): |
70 |
"""Writes a docstring for an RPC wrapper. |
71 |
|
72 |
""" |
73 |
sw.Write("\"\"\"Wrapper for RPC call '%s'", name) |
74 |
sw.Write("") |
75 |
if desc: |
76 |
sw.Write(desc) |
77 |
sw.Write("") |
78 |
|
79 |
note = ["This is a %s call" % kind] |
80 |
if timeout and not callable(timeout): |
81 |
note.append(" with a timeout of %s" % utils.FormatSeconds(timeout)) |
82 |
sw.Write("@note: %s", "".join(note)) |
83 |
|
84 |
if kind == _SINGLE: |
85 |
sw.Write("@type node: string") |
86 |
sw.Write("@param node: Node name") |
87 |
else: |
88 |
sw.Write("@type node_list: list of string") |
89 |
sw.Write("@param node_list: List of node names") |
90 |
|
91 |
if args: |
92 |
for (argname, _, argtext) in args: |
93 |
if argtext: |
94 |
docline = "@param %s: %s" % (argname, argtext) |
95 |
for line in _WrapCode(docline): |
96 |
sw.Write(line) |
97 |
sw.Write("") |
98 |
sw.Write("\"\"\"") |
99 |
|
100 |
|
101 |
def _WriteBaseClass(sw, clsname, calls): |
102 |
"""Write RPC wrapper class. |
103 |
|
104 |
""" |
105 |
sw.Write("") |
106 |
sw.Write("class %s(object):", clsname) |
107 |
sw.IncIndent() |
108 |
try: |
109 |
sw.Write("# E1101: Non-existent members") |
110 |
sw.Write("# R0904: Too many public methods") |
111 |
sw.Write("# pylint: disable=E1101,R0904") |
112 |
|
113 |
if not calls: |
114 |
sw.Write("pass") |
115 |
return |
116 |
|
117 |
sw.Write("_CALLS = rpc_defs.CALLS[%r]", clsname) |
118 |
sw.Write("") |
119 |
|
120 |
for (name, kind, timeout, args, _, desc) in calls: |
121 |
funcargs = ["self"] |
122 |
|
123 |
if kind == _SINGLE: |
124 |
funcargs.append("node") |
125 |
elif kind == _MULTI: |
126 |
funcargs.append("node_list") |
127 |
else: |
128 |
raise Exception("Unknown kind '%s'" % kind) |
129 |
|
130 |
funcargs.extend(map(compat.fst, args)) |
131 |
|
132 |
funcargs.append("_def=_CALLS[%r]" % name) |
133 |
|
134 |
funcdef = "def call_%s(%s):" % (name, utils.CommaJoin(funcargs)) |
135 |
for line in _WrapCode(funcdef): |
136 |
sw.Write(line) |
137 |
|
138 |
sw.IncIndent() |
139 |
try: |
140 |
_WriteDocstring(sw, name, timeout, kind, args, desc) |
141 |
|
142 |
buf = StringIO() |
143 |
buf.write("return ") |
144 |
|
145 |
# In case line gets too long and is wrapped in a bad spot |
146 |
buf.write("( ") |
147 |
|
148 |
buf.write("self._Call(_def, ") |
149 |
if kind == _SINGLE: |
150 |
buf.write("[node]") |
151 |
else: |
152 |
buf.write("node_list") |
153 |
|
154 |
buf.write(", [%s])" % |
155 |
# Function arguments |
156 |
utils.CommaJoin(map(compat.fst, args))) |
157 |
|
158 |
if kind == _SINGLE: |
159 |
buf.write("[node]") |
160 |
buf.write(")") |
161 |
|
162 |
for line in _WrapCode(buf.getvalue()): |
163 |
sw.Write(line) |
164 |
finally: |
165 |
sw.DecIndent() |
166 |
sw.Write("") |
167 |
finally: |
168 |
sw.DecIndent() |
169 |
|
170 |
|
171 |
def main(): |
172 |
"""Main function. |
173 |
|
174 |
""" |
175 |
buf = StringIO() |
176 |
sw = utils.ShellWriter(buf) |
177 |
|
178 |
_WritePreamble(sw) |
179 |
|
180 |
for filename in sys.argv[1:]: |
181 |
sw.Write("# Definitions from '%s'", filename) |
182 |
|
183 |
module = build.LoadModule(filename) |
184 |
|
185 |
# Call types are re-defined in definitions file to avoid imports. Verify |
186 |
# here to ensure they're equal to local constants. |
187 |
assert module.SINGLE == _SINGLE |
188 |
assert module.MULTI == _MULTI |
189 |
|
190 |
for (clsname, calls) in module.CALLS.items(): |
191 |
_WriteBaseClass(sw, clsname, calls.values()) |
192 |
|
193 |
print buf.getvalue() |
194 |
|
195 |
|
196 |
if __name__ == "__main__": |
197 |
main() |