]> git.proxmox.com Git - mirror_ovs.git/blame - ovsdb/ovsdb-doc.in
vswitchd: Remove trailing whitespace
[mirror_ovs.git] / ovsdb / ovsdb-doc.in
CommitLineData
89365653
BP
1#! @PYTHON@
2
3from datetime import date
4import getopt
5import os
6import re
7import sys
8import xml.dom.minidom
9
99155935
BP
10import ovs.json
11from ovs.db import error
12import ovs.db.schema
89365653
BP
13
14argv0 = sys.argv[0]
15
fcbaf28c 16def textToNroff(s, font=r'\fR'):
89365653
BP
17 def escape(match):
18 c = match.group(0)
fcbaf28c
BP
19 if c == '-':
20 if font == r'\fB':
21 return r'\-'
22 else:
23 return '-'
89365653
BP
24 if c == '\\':
25 return r'\e'
26 elif c == '"':
27 return r'\(dq'
28 elif c == "'":
29 return r'\(cq'
30 else:
99155935 31 raise error.Error("bad escape")
89365653 32
fcbaf28c
BP
33 # Escape - \ " ' as needed by nroff.
34 s = re.sub('([-"\'\\\\])', escape, s)
89365653
BP
35 if s.startswith('.'):
36 s = '\\' + s
37 return s
38
39def escapeNroffLiteral(s):
fcbaf28c 40 return r'\fB%s\fR' % textToNroff(s, r'\fB')
89365653
BP
41
42def inlineXmlToNroff(node, font):
43 if node.nodeType == node.TEXT_NODE:
fcbaf28c 44 return textToNroff(node.data, font)
89365653 45 elif node.nodeType == node.ELEMENT_NODE:
c9423856 46 if node.tagName in ['code', 'em', 'option']:
89365653
BP
47 s = r'\fB'
48 for child in node.childNodes:
49 s += inlineXmlToNroff(child, r'\fB')
50 return s + font
51 elif node.tagName == 'ref':
52 s = r'\fB'
53 if node.hasAttribute('column'):
54 s += node.attributes['column'].nodeValue
55 elif node.hasAttribute('table'):
56 s += node.attributes['table'].nodeValue
57 elif node.hasAttribute('group'):
58 s += node.attributes['group'].nodeValue
59 else:
99155935 60 raise error.Error("'ref' lacks column and table attributes")
89365653
BP
61 return s + font
62 elif node.tagName == 'var':
63 s = r'\fI'
64 for child in node.childNodes:
65 s += inlineXmlToNroff(child, r'\fI')
66 return s + font
67 else:
99155935 68 raise error.Error("element <%s> unknown or invalid here" % node.tagName)
89365653 69 else:
99155935 70 raise error.Error("unknown node %s in inline xml" % node)
89365653
BP
71
72def blockXmlToNroff(nodes, para='.PP'):
73 s = ''
74 for node in nodes:
75 if node.nodeType == node.TEXT_NODE:
76 s += textToNroff(node.data)
77 s = s.lstrip()
78 elif node.nodeType == node.ELEMENT_NODE:
c9423856 79 if node.tagName in ['ul', 'ol']:
89365653
BP
80 if s != "":
81 s += "\n"
82 s += ".RS\n"
c9423856 83 i = 0
89365653
BP
84 for liNode in node.childNodes:
85 if (liNode.nodeType == node.ELEMENT_NODE
86 and liNode.tagName == 'li'):
c9423856
BP
87 i += 1
88 if node.tagName == 'ul':
89 s += ".IP \\bu\n"
90 else:
91 s += ".IP %d. .25in\n" % i
92 s += blockXmlToNroff(liNode.childNodes, ".IP")
89365653
BP
93 elif (liNode.nodeType != node.TEXT_NODE
94 or not liNode.data.isspace()):
c9423856 95 raise error.Error("<%s> element may only have <li> children" % node.tagName)
89365653
BP
96 s += ".RE\n"
97 elif node.tagName == 'dl':
98 if s != "":
99 s += "\n"
100 s += ".RS\n"
101 prev = "dd"
102 for liNode in node.childNodes:
103 if (liNode.nodeType == node.ELEMENT_NODE
104 and liNode.tagName == 'dt'):
105 if prev == 'dd':
106 s += '.TP\n'
107 else:
108 s += '.TQ\n'
109 prev = 'dt'
110 elif (liNode.nodeType == node.ELEMENT_NODE
111 and liNode.tagName == 'dd'):
112 if prev == 'dd':
113 s += '.IP\n'
114 prev = 'dd'
115 elif (liNode.nodeType != node.TEXT_NODE
116 or not liNode.data.isspace()):
99155935 117 raise error.Error("<dl> element may only have <dt> and <dd> children")
89365653
BP
118 s += blockXmlToNroff(liNode.childNodes, ".IP")
119 s += ".RE\n"
120 elif node.tagName == 'p':
121 if s != "":
122 if not s.endswith("\n"):
123 s += "\n"
124 s += para + "\n"
125 s += blockXmlToNroff(node.childNodes, para)
126 else:
127 s += inlineXmlToNroff(node, r'\fR')
128 else:
99155935 129 raise error.Error("unknown node %s in block xml" % node)
89365653
BP
130 if s != "" and not s.endswith('\n'):
131 s += '\n'
132 return s
133
134def typeAndConstraintsToNroff(column):
135 type = column.type.toEnglish(escapeNroffLiteral)
136 constraints = column.type.constraintsToEnglish(escapeNroffLiteral)
137 if constraints:
138 type += ", " + constraints
139 return type
140
141def columnToNroff(columnName, column, node):
142 type = typeAndConstraintsToNroff(column)
143 s = '.IP "\\fB%s\\fR: %s"\n' % (columnName, type)
144 s += blockXmlToNroff(node.childNodes, '.IP') + "\n"
145 return s
146
147def columnGroupToNroff(table, groupXml):
148 introNodes = []
149 columnNodes = []
150 for node in groupXml.childNodes:
151 if (node.nodeType == node.ELEMENT_NODE
152 and node.tagName in ('column', 'group')):
153 columnNodes += [node]
154 else:
155 introNodes += [node]
156
157 summary = []
158 intro = blockXmlToNroff(introNodes)
159 body = ''
160 for node in columnNodes:
161 if node.tagName == 'column':
162 columnName = node.attributes['name'].nodeValue
163 column = table.columns[columnName]
164 body += columnToNroff(columnName, column, node)
165 summary += [('column', columnName, column)]
166 elif node.tagName == 'group':
167 title = node.attributes["title"].nodeValue
168 subSummary, subIntro, subBody = columnGroupToNroff(table, node)
169 summary += [('group', title, subSummary)]
170 body += '.ST "%s:"\n' % textToNroff(title)
171 body += subIntro + subBody
172 else:
99155935 173 raise error.Error("unknown element %s in <table>" % node.tagName)
89365653
BP
174 return summary, intro, body
175
176def tableSummaryToNroff(summary, level=0):
177 s = ""
178 for type, name, arg in summary:
179 if type == 'column':
180
181 s += "%s\\fB%s\\fR\tT{\n%s\nT}\n" % (
182 r'\ \ ' * level, name, typeAndConstraintsToNroff(arg))
183 else:
184 if s != "":
185 s += "_\n"
186 s += """.T&
187li | s
188l | l.
189%s%s
190_
191""" % (r'\ \ ' * level, name)
192 s += tableSummaryToNroff(arg, level + 1)
193 return s
194
195def tableToNroff(schema, tableXml):
196 tableName = tableXml.attributes['name'].nodeValue
197 table = schema.tables[tableName]
198
199 s = """.bp
200.SS "%s Table"
201""" % tableName
202 summary, intro, body = columnGroupToNroff(table, tableXml)
203 s += intro
204
205 s += r"""
206.sp
207.ce 1
208\fB%s\fR Table Columns:
209.TS
210center box;
211l | l.
212Column Type
213=
214""" % tableName
215 s += tableSummaryToNroff(summary)
216 s += ".TE\n"
217
218 s += body
219 return s
220
f8d739a9 221def docsToNroff(schemaFile, xmlFile, erFile, title=None):
99155935 222 schema = ovs.db.schema.DbSchema.from_json(ovs.json.from_file(schemaFile))
89365653 223 doc = xml.dom.minidom.parse(xmlFile).documentElement
f8d739a9 224
89365653
BP
225 schemaDate = os.stat(schemaFile).st_mtime
226 xmlDate = os.stat(xmlFile).st_mtime
227 d = date.fromtimestamp(max(schemaDate, xmlDate))
228
229 if title == None:
230 title = schema.name
231
f8d739a9
BP
232 # Putting '\" pt as the first line tells "man" that the manpage
233 # needs to be preprocessed by "pic" and "tbl".
234 s = r''''\" pt
235.TH %s 5 "%s" "Open vSwitch" "Open vSwitch Manual"
89365653
BP
236.\" -*- nroff -*-
237.de TQ
238. br
239. ns
240. TP "\\$1"
241..
242.de ST
243. PP
244. RS -0.15in
245. I "\\$1"
246. RE
247..
248''' % (title, d.strftime("%B %Y"))
249
250 s += '.SH "%s DATABASE"\n' % schema.name
251
252 tables = ""
253 introNodes = []
254 tableNodes = []
255 summary = []
256 for dbNode in doc.childNodes:
257 if (dbNode.nodeType == dbNode.ELEMENT_NODE
258 and dbNode.tagName == "table"):
259 tableNodes += [dbNode]
260
261 name = dbNode.attributes['name'].nodeValue
262 if dbNode.hasAttribute("title"):
263 title = dbNode.attributes['title'].nodeValue
264 else:
265 title = name + " configuration."
266 summary += [(name, title)]
267 else:
268 introNodes += [dbNode]
269
270 s += blockXmlToNroff(introNodes) + "\n"
271 tableSummary = r"""
272.sp
273.ce 1
274\fB%s\fR Database Tables:
275.TS
276center box;
277l | l
278lb | l.
279Table Purpose
280=
281""" % schema.name
282 for name, title in summary:
283 tableSummary += "%s\t%s\n" % (name, textToNroff(title))
284 tableSummary += '.TE\n'
285 s += tableSummary
f8d739a9
BP
286
287 if erFile:
288 s += """
289.sp 1
290.SH "TABLE RELATIONSHIPS"
291.PP
292The following diagram shows the relationship among tables in the
293database. Each node represents a table. Each edge leads from the
294table that contains it and points to the table that its value
295represents. Edges are labeled with their column names.
296.RS -1in
297"""
298 erStream = open(erFile, "r")
299 for line in erStream:
300 s += line + '\n'
301 erStream.close()
302 s += ".RE\n"
303
89365653
BP
304 for node in tableNodes:
305 s += tableToNroff(schema, node) + "\n"
306 return s
307
308def usage():
309 print """\
310%(argv0)s: ovsdb schema documentation generator
311Prints documentation for an OVSDB schema as an nroff-formatted manpage.
312usage: %(argv0)s [OPTIONS] SCHEMA XML
313where SCHEMA is an OVSDB schema in JSON format
314 and XML is OVSDB documentation in XML format.
315
316The following options are also available:
f8d739a9 317 --er-diagram=DIAGRAM.PIC include E-R diagram from DIAGRAM.PIC
89365653
BP
318 --title=TITLE use TITLE as title instead of schema name
319 -h, --help display this help message
320 -V, --version display version information\
321""" % {'argv0': argv0}
322 sys.exit(0)
323
324if __name__ == "__main__":
325 try:
326 try:
327 options, args = getopt.gnu_getopt(sys.argv[1:], 'hV',
f8d739a9
BP
328 ['er-diagram=', 'title=',
329 'help', 'version'])
89365653
BP
330 except getopt.GetoptError, geo:
331 sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
332 sys.exit(1)
333
f8d739a9 334 er_diagram = None
89365653
BP
335 title = None
336 for key, value in options:
f8d739a9
BP
337 if key == '--er-diagram':
338 er_diagram = value
339 elif key == '--title':
89365653
BP
340 title = value
341 elif key in ['-h', '--help']:
342 usage()
343 elif key in ['-V', '--version']:
344 print "ovsdb-doc (Open vSwitch) @VERSION@"
345 else:
346 sys.exit(0)
347
348 if len(args) != 2:
349 sys.stderr.write("%s: exactly 2 non-option arguments required "
350 "(use --help for help)\n" % argv0)
351 sys.exit(1)
352
353 # XXX we should warn about undocumented tables or columns
f8d739a9 354 s = docsToNroff(args[0], args[1], er_diagram)
89365653
BP
355 for line in s.split("\n"):
356 line = line.strip()
357 if len(line):
358 print line
359
99155935 360 except error.Error, e:
89365653
BP
361 sys.stderr.write("%s: %s\n" % (argv0, e.msg))
362 sys.exit(1)
363
364# Local variables:
365# mode: python
366# End: