3 from datetime import date
11 from ovs.db import error
16 def textToNroff(s, font=r'\fR'):
31 raise error.Error("bad escape")
33 # Escape - \ " ' as needed by nroff.
34 s = re.sub('([-"\'\\\\])', escape, s)
39 def escapeNroffLiteral(s):
40 return r'\fB%s\fR' % textToNroff(s, r'\fB')
42 def inlineXmlToNroff(node, font):
43 if node.nodeType == node.TEXT_NODE:
44 return textToNroff(node.data, font)
45 elif node.nodeType == node.ELEMENT_NODE:
46 if node.tagName in ['code', 'em', 'option']:
48 for child in node.childNodes:
49 s += inlineXmlToNroff(child, r'\fB')
51 elif node.tagName == 'ref':
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
60 raise error.Error("'ref' lacks column and table attributes")
62 elif node.tagName == 'var':
64 for child in node.childNodes:
65 s += inlineXmlToNroff(child, r'\fI')
68 raise error.Error("element <%s> unknown or invalid here" % node.tagName)
70 raise error.Error("unknown node %s in inline xml" % node)
72 def blockXmlToNroff(nodes, para='.PP'):
75 if node.nodeType == node.TEXT_NODE:
76 s += textToNroff(node.data)
78 elif node.nodeType == node.ELEMENT_NODE:
79 if node.tagName in ['ul', 'ol']:
84 for liNode in node.childNodes:
85 if (liNode.nodeType == node.ELEMENT_NODE
86 and liNode.tagName == 'li'):
88 if node.tagName == 'ul':
91 s += ".IP %d. .25in\n" % i
92 s += blockXmlToNroff(liNode.childNodes, ".IP")
93 elif (liNode.nodeType != node.TEXT_NODE
94 or not liNode.data.isspace()):
95 raise error.Error("<%s> element may only have <li> children" % node.tagName)
97 elif node.tagName == 'dl':
102 for liNode in node.childNodes:
103 if (liNode.nodeType == node.ELEMENT_NODE
104 and liNode.tagName == 'dt'):
110 elif (liNode.nodeType == node.ELEMENT_NODE
111 and liNode.tagName == 'dd'):
115 elif (liNode.nodeType != node.TEXT_NODE
116 or not liNode.data.isspace()):
117 raise error.Error("<dl> element may only have <dt> and <dd> children")
118 s += blockXmlToNroff(liNode.childNodes, ".IP")
120 elif node.tagName == 'p':
122 if not s.endswith("\n"):
125 s += blockXmlToNroff(node.childNodes, para)
127 s += inlineXmlToNroff(node, r'\fR')
129 raise error.Error("unknown node %s in block xml" % node)
130 if s != "" and not s.endswith('\n'):
134 def typeAndConstraintsToNroff(column):
135 type = column.type.toEnglish(escapeNroffLiteral)
136 constraints = column.type.constraintsToEnglish(escapeNroffLiteral)
138 type += ", " + constraints
141 def 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"
147 def columnGroupToNroff(table, groupXml):
150 for node in groupXml.childNodes:
151 if (node.nodeType == node.ELEMENT_NODE
152 and node.tagName in ('column', 'group')):
153 columnNodes += [node]
158 intro = blockXmlToNroff(introNodes)
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
173 raise error.Error("unknown element %s in <table>" % node.tagName)
174 return summary, intro, body
176 def tableSummaryToNroff(summary, level=0):
178 for type, name, arg in summary:
181 s += "%s\\fB%s\\fR\tT{\n%s\nT}\n" % (
182 r'\ \ ' * level, name, typeAndConstraintsToNroff(arg))
191 """ % (r'\ \ ' * level, name)
192 s += tableSummaryToNroff(arg, level + 1)
195 def tableToNroff(schema, tableXml):
196 tableName = tableXml.attributes['name'].nodeValue
197 table = schema.tables[tableName]
202 summary, intro, body = columnGroupToNroff(table, tableXml)
208 \fB%s\fR Table Columns:
215 s += tableSummaryToNroff(summary)
221 def docsToNroff(schemaFile, xmlFile, erFile, title=None):
222 schema = ovs.db.schema.DbSchema.from_json(ovs.json.from_file(schemaFile))
223 doc = xml.dom.minidom.parse(xmlFile).documentElement
225 schemaDate = os.stat(schemaFile).st_mtime
226 xmlDate = os.stat(xmlFile).st_mtime
227 d = date.fromtimestamp(max(schemaDate, xmlDate))
232 # Putting '\" pt as the first line tells "man" that the manpage
233 # needs to be preprocessed by "pic" and "tbl".
235 .TH %s 5 "%s" "Open vSwitch" "Open vSwitch Manual"
248 ''' % (title, d.strftime("%B %Y"))
250 s += '.SH "%s DATABASE"\n' % schema.name
256 for dbNode in doc.childNodes:
257 if (dbNode.nodeType == dbNode.ELEMENT_NODE
258 and dbNode.tagName == "table"):
259 tableNodes += [dbNode]
261 name = dbNode.attributes['name'].nodeValue
262 if dbNode.hasAttribute("title"):
263 title = dbNode.attributes['title'].nodeValue
265 title = name + " configuration."
266 summary += [(name, title)]
268 introNodes += [dbNode]
270 s += blockXmlToNroff(introNodes) + "\n"
274 \fB%s\fR Database Tables:
282 for name, title in summary:
283 tableSummary += "%s\t%s\n" % (name, textToNroff(title))
284 tableSummary += '.TE\n'
290 .SH "TABLE RELATIONSHIPS"
292 The following diagram shows the relationship among tables in the
293 database. Each node represents a table. Tables that are part of the
294 ``root set'' are shown with double borders. Each edge leads from the
295 table that contains it and points to the table that its value
296 represents. Edges are labeled with their column names. Thick lines
297 represent strong references; thin lines represent weak references.
300 erStream = open(erFile, "r")
301 for line in erStream:
306 for node in tableNodes:
307 s += tableToNroff(schema, node) + "\n"
312 %(argv0)s: ovsdb schema documentation generator
313 Prints documentation for an OVSDB schema as an nroff-formatted manpage.
314 usage: %(argv0)s [OPTIONS] SCHEMA XML
315 where SCHEMA is an OVSDB schema in JSON format
316 and XML is OVSDB documentation in XML format.
318 The following options are also available:
319 --er-diagram=DIAGRAM.PIC include E-R diagram from DIAGRAM.PIC
320 --title=TITLE use TITLE as title instead of schema name
321 -h, --help display this help message
322 -V, --version display version information\
323 """ % {'argv0': argv0}
326 if __name__ == "__main__":
329 options, args = getopt.gnu_getopt(sys.argv[1:], 'hV',
330 ['er-diagram=', 'title=',
332 except getopt.GetoptError, geo:
333 sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
338 for key, value in options:
339 if key == '--er-diagram':
341 elif key == '--title':
343 elif key in ['-h', '--help']:
345 elif key in ['-V', '--version']:
346 print "ovsdb-doc (Open vSwitch) @VERSION@"
351 sys.stderr.write("%s: exactly 2 non-option arguments required "
352 "(use --help for help)\n" % argv0)
355 # XXX we should warn about undocumented tables or columns
356 s = docsToNroff(args[0], args[1], er_diagram)
357 for line in s.split("\n"):
362 except error.Error, e:
363 sys.stderr.write("%s: %s\n" % (argv0, e.msg))