1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Sphinx extension for building opcode documentation.
23
24 """
25
26 from cStringIO import StringIO
27
28 import docutils.statemachine
29 import docutils.nodes
30 import docutils.utils
31
32 import sphinx.errors
33 import sphinx.util.compat
34
35 s_compat = sphinx.util.compat
36
37 from ganeti import constants
38 from ganeti import compat
39 from ganeti import errors
40 from ganeti import utils
41 from ganeti import opcodes
42 from ganeti import ht
43 from ganeti import rapi
44 from ganeti import luxi
45
46 import ganeti.rapi.rlib2
47
48
59
60
61 COMMON_PARAM_NAMES = _GetCommonParamNames()
62
63
64 EVAL_NS = dict(compat=compat, constants=constants, utils=utils, errors=errors,
65 rlib2=rapi.rlib2, luxi=luxi)
66
67
68 CV_ECODES_DOC = "ecodes"
69
70
71 CV_ECODES_DOC_LIST = [(name, doc) for (_, name, doc) in constants.CV_ALL_ECODES]
72 DOCUMENTED_CONSTANTS = {
73 CV_ECODES_DOC: CV_ECODES_DOC_LIST,
74 }
75
76
79
80
82 """Split simple option list.
83
84 @type text: string
85 @param text: Options, e.g. "foo, bar, baz"
86
87 """
88 return [i.strip(",").strip() for i in text.split()]
89
90
92 """Parse simple assignment option.
93
94 @type text: string
95 @param text: Assignments, e.g. "foo=bar, hello=world"
96 @rtype: dict
97
98 """
99 result = {}
100
101 for part in _SplitOption(text):
102 if "=" not in part:
103 raise OpcodeError("Invalid option format, missing equal sign")
104
105 (name, value) = part.split("=", 1)
106
107 result[name.strip()] = value.strip()
108
109 return result
110
111
113 """Build opcode parameter documentation.
114
115 @type op_id: string
116 @param op_id: Opcode ID
117
118 """
119 op_cls = opcodes.OP_MAPPING[op_id]
120
121 params_with_alias = \
122 utils.NiceSort([(alias.get(name, name), name, default, test, doc)
123 for (name, default, test, doc) in op_cls.GetAllParams()],
124 key=compat.fst)
125
126 for (rapi_name, name, default, test, doc) in params_with_alias:
127
128 if (name in COMMON_PARAM_NAMES and
129 (not include or name not in include)):
130 continue
131 if exclude is not None and name in exclude:
132 continue
133 if include is not None and name not in include:
134 continue
135
136 has_default = default is not ht.NoDefault
137 has_test = not (test is None or test is ht.NoType)
138
139 buf = StringIO()
140 buf.write("``%s``" % rapi_name)
141 if has_default or has_test:
142 buf.write(" (")
143 if has_default:
144 buf.write("defaults to ``%s``" % default)
145 if has_test:
146 buf.write(", ")
147 if has_test:
148 buf.write("must be ``%s``" % test)
149 buf.write(")")
150 yield buf.getvalue()
151
152
153 for line in doc.splitlines():
154 yield " %s" % line
155
156
158 """Build opcode result documentation.
159
160 @type op_id: string
161 @param op_id: Opcode ID
162
163 """
164 op_cls = opcodes.OP_MAPPING[op_id]
165
166 result_fn = getattr(op_cls, "OP_RESULT", None)
167
168 if not result_fn:
169 raise OpcodeError("Opcode '%s' has no result description" % op_id)
170
171 return "``%s``" % result_fn
172
173
175 """Custom directive for opcode parameters.
176
177 See also <http://docutils.sourceforge.net/docs/howto/rst-directives.html>.
178
179 """
180 has_content = False
181 required_arguments = 1
182 optional_arguments = 0
183 final_argument_whitespace = False
184 option_spec = dict(include=_SplitOption, exclude=_SplitOption,
185 alias=_ParseAlias)
186
188 op_id = self.arguments[0]
189 include = self.options.get("include", None)
190 exclude = self.options.get("exclude", None)
191 alias = self.options.get("alias", {})
192
193 tab_width = 2
194 path = op_id
195 include_text = "\n".join(_BuildOpcodeParams(op_id, include, exclude, alias))
196
197
198 include_lines = docutils.statemachine.string2lines(include_text, tab_width,
199 convert_whitespace=1)
200 self.state_machine.insert_input(include_lines, path)
201
202 return []
203
204
206 """Custom directive for opcode result.
207
208 See also <http://docutils.sourceforge.net/docs/howto/rst-directives.html>.
209
210 """
211 has_content = False
212 required_arguments = 1
213 optional_arguments = 0
214 final_argument_whitespace = False
215
217 op_id = self.arguments[0]
218
219 tab_width = 2
220 path = op_id
221 include_text = _BuildOpcodeResult(op_id)
222
223
224 include_lines = docutils.statemachine.string2lines(include_text, tab_width,
225 convert_whitespace=1)
226 self.state_machine.insert_input(include_lines, path)
227
228 return []
229
230
231 -def PythonEvalRole(role, rawtext, text, lineno, inliner,
232 options={}, content=[]):
233 """Custom role to evaluate Python expressions.
234
235 The expression's result is included as a literal.
236
237 """
238
239
240
241
242
243 code = docutils.utils.unescape(text, restore_backslashes=True)
244
245 try:
246 result = eval(code, EVAL_NS)
247 except Exception, err:
248 msg = inliner.reporter.error("Failed to evaluate %r: %s" % (code, err),
249 line=lineno)
250 return ([inliner.problematic(rawtext, rawtext, msg)], [msg])
251
252 node = docutils.nodes.literal("", unicode(result), **options)
253
254 return ([node], [])
255
256
258 """Custom directive for writing assertions.
259
260 The content must be a valid Python expression. If its result does not
261 evaluate to C{True}, the assertion fails.
262
263 """
264 has_content = True
265 required_arguments = 0
266 optional_arguments = 0
267 final_argument_whitespace = False
268
270
271 if hasattr(self, "assert_has_content"):
272 self.assert_has_content()
273 else:
274 assert self.content
275
276 code = "\n".join(self.content)
277
278 try:
279 result = eval(code, EVAL_NS)
280 except Exception, err:
281 raise self.error("Failed to evaluate %r: %s" % (code, err))
282
283 if not result:
284 raise self.error("Assertion failed: %s" % (code, ))
285
286 return []
287
288
290 """Build query fields documentation.
291
292 @type fields: dict (field name as key, field details as value)
293
294 """
295 defs = [(fdef.name, fdef.doc)
296 for (_, (fdef, _, _, _)) in utils.NiceSort(fields.items(),
297 key=compat.fst)]
298 return BuildValuesDoc(defs)
299
300
302 """Builds documentation for a list of values
303
304 @type values: list of tuples in the form (value, documentation)
305
306 """
307 for name, doc in values:
308 assert len(doc.splitlines()) == 1
309 yield "``%s``" % name
310 yield " %s" % doc
311
312
313
314
315
324