Statistics
| Branch: | Tag: | Revision:

root / autotools / build-rpc @ e37f47d3

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()