Statistics
| Branch: | Tag: | Revision:

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