#!/usr/bin/python
# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
import os;
import pywbem;
import optparse
import re
import cgi
import sys
class HtmlExporter(object):
def __init__(self, cliconn):
self.classcache = {}
self.cliconn = cliconn
# original list of classes
self.classes = []
# set of all classes to print, i.e. incl. all parents
self.queue = set()
self.file = None
def escape(self, string):
re_string = re.compile(r'(?P[<&>])|(?P^[ \t]+)|(?P\r\n|\r|\n)|(?P(^|\s)((http|ftp)://.*?))(\s|$)', re.S|re.M|re.I)
re_nl = re.compile(r"\\n")
def do_sub(m):
c = m.groupdict()
if c['htmlchars']:
return cgi.escape(c['htmlchars'])
if c['lineend']:
return ' '
elif c['space']:
t = m.group().replace('\t', ' '*tabstop)
t = t.replace(' ', ' ')
return t
elif c['space'] == '\t':
return ' '*tabstop;
else:
url = m.group('protocal')
if url.startswith(' '):
prefix = ' '
url = url[1:]
else:
prefix = ''
last = m.groups()[-1]
if last in ['\n', '\r', '\r\n']:
last = ' '
return '%s%s%s' % (prefix, url, url, last)
string2 = re.sub(re_string, do_sub, string)
return re.sub(re_nl, " ", string2)
def load_class(self, classname):
if classname in self.classcache:
return self.classcache[classname]
c = self.cliconn.GetClass(classname)
self.classcache[classname] = c
return c
def source_class(self, c, param):
"""
Find a class where a CIMMethod or CIMProperty is first defined.
Start at class c and inspect all parents.
"""
while True:
if not c.superclass:
# we're at the top class
return c.classname
if isinstance(param, pywbem.CIMMethod):
if not c.methods.has_key(param.name):
break
else:
if not c.properties.has_key(param.name):
break
parent = c
c = self.load_class(c.superclass)
return parent.classname
def display_type(self, param):
"""
Return displayable type of given parameter.
It adds [] if it's array and class name of referenced classes.
"""
url=None
if param.reference_class:
ptype = param.reference_class
url=param.reference_class + ".html"
else:
ptype = param.type
if param.is_array:
ptype = ptype + "[]"
if url:
ptype = "%s" % (url, ptype)
return ptype
def print_parameters(self, params):
"""
Print table of method parametes.
"""
if not params:
print >>self.file, "None"
return
print >>self.file, "
"
for p in params.values():
direction = set()
if p.qualifiers.has_key("Out"):
direction.add("OUT")
if p.qualifiers.has_key("In"):
direction.add("IN")
if not direction:
direction.add("IN")
direction = "/".join(sorted(direction))
print >>self.file, "
"
def print_qualifiers(self, qualifiers):
"""
Print contenf of table of qualifiers.
Only Deprecated, Description, Values and ValueMap are recognized.
"""
deprecated = ""
for q in sorted(qualifiers):
if q.name == "Deprecated":
deprecated = "
DEPRECATED
"
if q.name == "Description":
print >>self.file, "
" % (c.classname, c.superclass, c.superclass)
description = c.qualifiers.get("Description", None)
if not description:
description = parent.qualifiers.get("Description", None)
if description:
print >>self.file, "
%s
" % (self.escape(description.value))
self.print_keys(c)
local_props = []
inherited_props = []
for name in sorted(c.properties.keys()):
if parent and parent.properties.has_key(name):
inherited_props.append(c.properties[name])
if not self.compare_properties(c.properties[name], parent.properties[name]):
# the property was overridden
local_props.append(c.properties[name])
else:
local_props.append(c.properties[name])
local_methods = []
inherited_methods = []
for name in sorted(c.methods.keys()):
if parent and parent.methods.has_key(name):
inherited_methods.append(c.methods[name])
else:
local_methods.append(c.methods[name])
print >>self.file, "
Local properties:
"
if local_props:
print >>self.file, "
Name
Type
Qualifiers
"
for prop in local_props:
if prop in inherited_props:
style="i"
else:
style="b"
print >>self.file, "
"
else:
print >>self.file, "None"
def print_file(self, filename):
f = open(filename, "r")
data = f.read()
self.file.write(data)
f.close()
def print_page(self, c, header, footer):
print "exporting ", c.classname
self.file = open(c.classname + ".html", "w")
print >>self.file, "%s" % (c.classname)
print >>self.file, ""
print >>self.file, ""
if header:
self.print_file(header)
self.print_class(c)
if footer:
self.print_file(footer)
print >>self.file, ""
self.file.close()
def add_class(self, classname):
self.classes.append(classname)
print "adding", classname
self.queue.add(classname)
# add all parents
c = self.load_class(classname)
parentname = c.superclass
while parentname:
if parentname in self.queue:
break
print "adding", parentname
self.queue.add(parentname)
c = self.load_class(parentname)
parentname = c.superclass
def export(self, header=None, footer=None):
"""
Print everything, i.e. index.html + all classes + all their parents.
"""
# remove duplicate classes from the queue (and sort them)
self.queue = sorted(self.queue)
self.print_index(header, footer)
while self.queue:
c = self.queue.pop()
self.print_page(self.load_class(c), header, footer)
def _do_print_tree(self, name, subtree, level):
print >>self.file, "
"
if level:
print >>self.file, " |"*level + "--- "
if name in self.classes:
printname = "%s" % (name)
else:
printname = "%s" % (name)
print >>self.file, "%s" % (name, printname)
print >>self.file, "
"
for n in subtree.keys():
self._do_print_tree(n, subtree[n], level+1)
def print_tree(self):
"""
Create inheritance tree of classes.
"""
# hash classname -> list of hash of (direct) sublasses
subclasses = {}
# hash classname -> nr. of its parents
parents = {}
# initialize the hash
for cname in self.queue:
subclasses[cname] = {}
parents[cname] = 0
# fill the hash
for cname in self.queue:
c = self.load_class(cname)
if c.superclass:
subclasses[c.superclass][cname] = subclasses[cname]
parents[cname] += 1
print >>self.file, "
"
# print all top classes
for cname in self.queue:
if parents[cname] == 0:
self._do_print_tree(cname, subclasses[cname], 0)
print >>self.file, "
"
def print_index(self, header, footer):
print "exporting index"
self.file = open("index.html", "w")
print >>self.file, "Index"
print >>self.file, ""
print >>self.file, ""
if header:
self.print_file(header)
self.print_tree()
if footer:
self.print_file(footer)
print >>self.file, ""
self.file.close()
description = """
Generate HTML documentation for given CIM classes. The tool connects to specified CIMOM
and reads class definition from there. It generates separate HTML file for each class
specified on command line and for all it's parents.
The tool also generates index.html with inheritance tree.
All generated HTML pages will optionally contain header and/or footer. Both must be valid
HTML snippets and will be inserted just after or before .
"""
parser = optparse.OptionParser(usage="usage: %prog [options] classname [classname ...]", description=description)
parser.add_option('-u', '--url', action='store', dest='addr', default='https://localhost:5989', help='URL of CIM server, default: https://localhost:5989')
parser.add_option('-U', '--user', action='store', dest='user', default=None, help='CIM user name')
parser.add_option('-P', '--password', action='store', dest='password', default=None, help='CIM password')
parser.add_option('-H', '--header', action='store', dest='header', default=None, help='File with HTML page header')
parser.add_option('-F', '--footer', action='store', dest='footer', default=None, help='File with HTML page footer')
(options, args) = parser.parse_args()
cliconn = pywbem.WBEMConnection(options.addr, (options.user, options.password))
exporter = HtmlExporter(cliconn)
for c in args:
exporter.add_class(c)
exporter.export(options.header, options.footer)