root / autotools / build-rpc @ 4edaf707
History | View | Annotate | Download (5.3 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 |
#: Expected length of a rpc definition |
44 |
_RPC_DEF_LEN = 8 |
45 |
|
46 |
|
47 |
def _WritePreamble(sw): |
48 |
"""Writes a preamble for the RPC wrapper output. |
49 |
|
50 |
""" |
51 |
sw.Write("# This code is automatically generated at build time.") |
52 |
sw.Write("# Do not modify manually.") |
53 |
sw.Write("") |
54 |
sw.Write("\"\"\"Automatically generated RPC client wrappers.") |
55 |
sw.Write("") |
56 |
sw.Write("\"\"\"") |
57 |
sw.Write("") |
58 |
sw.Write("from ganeti import rpc_defs") |
59 |
sw.Write("") |
60 |
|
61 |
|
62 |
def _WrapCode(line): |
63 |
"""Wraps Python code. |
64 |
|
65 |
""" |
66 |
return textwrap.wrap(line, width=70, expand_tabs=False, |
67 |
fix_sentence_endings=False, break_long_words=False, |
68 |
replace_whitespace=True, |
69 |
subsequent_indent=utils.ShellWriter.INDENT_STR) |
70 |
|
71 |
|
72 |
def _WriteDocstring(sw, name, timeout, kind, args, desc): |
73 |
"""Writes a docstring for an RPC wrapper. |
74 |
|
75 |
""" |
76 |
sw.Write("\"\"\"Wrapper for RPC call '%s'", name) |
77 |
sw.Write("") |
78 |
if desc: |
79 |
sw.Write(desc) |
80 |
sw.Write("") |
81 |
|
82 |
note = ["This is a %s call" % kind] |
83 |
if timeout and not callable(timeout): |
84 |
note.append(" with a timeout of %s" % utils.FormatSeconds(timeout)) |
85 |
sw.Write("@note: %s", "".join(note)) |
86 |
|
87 |
if kind == _SINGLE: |
88 |
sw.Write("@type node: string") |
89 |
sw.Write("@param node: Node name") |
90 |
else: |
91 |
sw.Write("@type node_list: list of string") |
92 |
sw.Write("@param node_list: List of node names") |
93 |
|
94 |
if args: |
95 |
for (argname, _, argtext) in args: |
96 |
if argtext: |
97 |
docline = "@param %s: %s" % (argname, argtext) |
98 |
for line in _WrapCode(docline): |
99 |
sw.Write(line) |
100 |
sw.Write("") |
101 |
sw.Write("\"\"\"") |
102 |
|
103 |
|
104 |
def _WriteBaseClass(sw, clsname, calls): |
105 |
"""Write RPC wrapper class. |
106 |
|
107 |
""" |
108 |
sw.Write("") |
109 |
sw.Write("class %s(object):", clsname) |
110 |
sw.IncIndent() |
111 |
try: |
112 |
sw.Write("# E1101: Non-existent members") |
113 |
sw.Write("# R0904: Too many public methods") |
114 |
sw.Write("# pylint: disable=E1101,R0904") |
115 |
|
116 |
if not calls: |
117 |
sw.Write("pass") |
118 |
return |
119 |
|
120 |
sw.Write("_CALLS = rpc_defs.CALLS[%r]", clsname) |
121 |
sw.Write("") |
122 |
|
123 |
for v in calls: |
124 |
if len(v) != _RPC_DEF_LEN: |
125 |
raise ValueError("Procedure %s has only %d elements, expected %d" % |
126 |
(v[0], len(v), _RPC_DEF_LEN)) |
127 |
|
128 |
for (name, kind, _, timeout, args, _, _, desc) in sorted(calls): |
129 |
funcargs = ["self"] |
130 |
|
131 |
if kind == _SINGLE: |
132 |
funcargs.append("node") |
133 |
elif kind == _MULTI: |
134 |
funcargs.append("node_list") |
135 |
else: |
136 |
raise Exception("Unknown kind '%s'" % kind) |
137 |
|
138 |
funcargs.extend(map(compat.fst, args)) |
139 |
|
140 |
funcargs.append("_def=_CALLS[%r]" % name) |
141 |
|
142 |
funcdef = "def call_%s(%s):" % (name, utils.CommaJoin(funcargs)) |
143 |
for line in _WrapCode(funcdef): |
144 |
sw.Write(line) |
145 |
|
146 |
sw.IncIndent() |
147 |
try: |
148 |
_WriteDocstring(sw, name, timeout, kind, args, desc) |
149 |
|
150 |
buf = StringIO() |
151 |
buf.write("return ") |
152 |
|
153 |
# In case line gets too long and is wrapped in a bad spot |
154 |
buf.write("(") |
155 |
|
156 |
buf.write("self._Call(_def, ") |
157 |
if kind == _SINGLE: |
158 |
buf.write("[node]") |
159 |
else: |
160 |
buf.write("node_list") |
161 |
|
162 |
buf.write(", [%s])" % |
163 |
# Function arguments |
164 |
utils.CommaJoin(map(compat.fst, args))) |
165 |
|
166 |
if kind == _SINGLE: |
167 |
buf.write("[node]") |
168 |
buf.write(")") |
169 |
|
170 |
for line in _WrapCode(buf.getvalue()): |
171 |
sw.Write(line) |
172 |
finally: |
173 |
sw.DecIndent() |
174 |
sw.Write("") |
175 |
finally: |
176 |
sw.DecIndent() |
177 |
|
178 |
|
179 |
def main(): |
180 |
"""Main function. |
181 |
|
182 |
""" |
183 |
buf = StringIO() |
184 |
sw = utils.ShellWriter(buf) |
185 |
|
186 |
_WritePreamble(sw) |
187 |
|
188 |
for filename in sys.argv[1:]: |
189 |
sw.Write("# Definitions from '%s'", filename) |
190 |
|
191 |
module = build.LoadModule(filename) |
192 |
|
193 |
# Call types are re-defined in definitions file to avoid imports. Verify |
194 |
# here to ensure they're equal to local constants. |
195 |
assert module.SINGLE == _SINGLE |
196 |
assert module.MULTI == _MULTI |
197 |
|
198 |
dups = utils.FindDuplicates(itertools.chain(*map(lambda value: value.keys(), |
199 |
module.CALLS.values()))) |
200 |
if dups: |
201 |
raise Exception("Found duplicate RPC definitions for '%s'" % |
202 |
utils.CommaJoin(sorted(dups))) |
203 |
|
204 |
for (clsname, calls) in sorted(module.CALLS.items()): |
205 |
_WriteBaseClass(sw, clsname, calls.values()) |
206 |
|
207 |
print buf.getvalue() |
208 |
|
209 |
|
210 |
if __name__ == "__main__": |
211 |
main() |