Statistics
| Branch: | Tag: | Revision:

root / autotools / build-rpc @ a09f9847

History | View | Annotate | Download (4.9 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

    
56

    
57
def _WrapCode(line):
58
  """Wraps Python code.
59

    
60
  """
61
  return textwrap.wrap(line, width=70, expand_tabs=False,
62
                       fix_sentence_endings=False, break_long_words=False,
63
                       replace_whitespace=True,
64
                       subsequent_indent=utils.ShellWriter.INDENT_STR)
65

    
66

    
67
def _WriteDocstring(sw, name, timeout, kind, args, desc):
68
  """Writes a docstring for an RPC wrapper.
69

    
70
  """
71
  sw.Write("\"\"\"Wrapper for RPC call '%s'", name)
72
  sw.Write("")
73
  if desc:
74
    sw.Write(desc)
75
    sw.Write("")
76

    
77
  note = ["This is a %s call" % kind]
78
  if timeout:
79
    note.append(" with a timeout of %s" % utils.FormatSeconds(timeout))
80
  sw.Write("@note: %s", "".join(note))
81

    
82
  if kind == _SINGLE:
83
    sw.Write("@type node: string")
84
    sw.Write("@param node: Node name")
85
  else:
86
    sw.Write("@type node_list: list of string")
87
    sw.Write("@param node_list: List of node names")
88

    
89
  if args:
90
    for (argname, _, argtext) in args:
91
      if argtext:
92
        docline = "@param %s: %s" % (argname, argtext)
93
        for line in _WrapCode(docline):
94
          sw.Write(line)
95
  sw.Write("")
96
  sw.Write("\"\"\"")
97

    
98

    
99
def _WriteBaseClass(sw, clsname, calls):
100
  """Write RPC wrapper class.
101

    
102
  """
103
  sw.Write("")
104
  sw.Write("class %s(object):", clsname)
105
  sw.IncIndent()
106
  try:
107
    sw.Write("# E1101: Non-existent members")
108
    sw.Write("# R0904: Too many public methods")
109
    sw.Write("# pylint: disable=E1101,R0904")
110

    
111
    if not calls:
112
      sw.Write("pass")
113
      return
114

    
115
    for (name, kind, timeout, args, postproc, desc) in calls:
116
      funcargs = ["self"]
117

    
118
      if kind == _SINGLE:
119
        funcargs.append("node")
120
      elif kind == _MULTI:
121
        funcargs.append("node_list")
122
      else:
123
        raise Exception("Unknown kind '%s'" % kind)
124

    
125
      funcargs.extend(map(compat.fst, args))
126

    
127
      assert "read_timeout" not in funcargs
128
      funcargs.append("read_timeout=%s" % timeout)
129

    
130
      funcdef = "def call_%s(%s):" % (name, utils.CommaJoin(funcargs))
131
      for line in _WrapCode(funcdef):
132
        sw.Write(line)
133

    
134
      sw.IncIndent()
135
      try:
136
        _WriteDocstring(sw, name, timeout, kind, args, desc)
137

    
138
        buf = StringIO()
139
        buf.write("return ")
140

    
141
        # In case line gets too long and is wrapped in a bad spot
142
        buf.write("( ")
143

    
144
        if postproc:
145
          buf.write("%s(" % postproc)
146
        buf.write("self._Call(")
147
        if kind == _SINGLE:
148
          buf.write("[node]")
149
        else:
150
          buf.write("node_list")
151

    
152
        buf.write(", \"%s\", read_timeout, [%s], [%s])" %
153
                  (name,
154
                   # Argument definitions
155
                   utils.CommaJoin(map(compat.snd, args)),
156
                   # Function arguments
157
                   utils.CommaJoin(map(compat.fst, args))))
158

    
159
        if kind == _SINGLE:
160
          buf.write("[node]")
161
        if postproc:
162
          buf.write(")")
163
        buf.write(")")
164

    
165
        for line in _WrapCode(buf.getvalue()):
166
          sw.Write(line)
167
      finally:
168
        sw.DecIndent()
169
      sw.Write("")
170
  finally:
171
    sw.DecIndent()
172

    
173

    
174
def main():
175
  """Main function.
176

    
177
  """
178
  buf = StringIO()
179
  sw = utils.ShellWriter(buf)
180

    
181
  _WritePreamble(sw)
182

    
183
  for filename in sys.argv[1:]:
184
    sw.Write("# Definitions from '%s'", filename)
185

    
186
    module = build.LoadModule(filename)
187

    
188
    # Call types are re-defined in definitions file to avoid imports. Verify
189
    # here to ensure they're equal to local constants.
190
    assert module.SINGLE == _SINGLE
191
    assert module.MULTI == _MULTI
192

    
193
    for (clsname, calls) in module.CALLS.items():
194
      _WriteBaseClass(sw, clsname, calls.values())
195

    
196
  print buf.getvalue()
197

    
198

    
199
if __name__ == "__main__":
200
  main()