aboutsummaryrefslogtreecommitdiff
path: root/docs/repository-engine-objects.dot
blob: 24abc8dd2a6098226cc248947d9f74dec9f560df (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// $URL$
// $Id$
//
// {arrowhead,arrowtail} shapes indicate database object relationships:
//   1-  none
//   m-  crow
//
// Color code:
//   Blue:  visible in left-right protocol
//   Green: created on the fly

digraph rpki_engine_objects {
	rotate=90;	size="11,8.5";	splines=true;	ratio=fill;
	node		[ shape=record ];

	// Objects visible in left-to-right protocol
	node		[ color=blue ];
	self		[ label="Self|{Preferences}" ];
	parent		[ label="Parent|{URI|TA|SIA Base}" ];
	repo		[ label="Repository|{URI|TA}" ];
	child		[ label="Child|{TA}" ];
	biz_sign	[ label="Business\nSigning Context|{Keypair|CertChain}" ];
	route_origin	[ label="Route\nOrigin|{AS Number}" ];

	// Objects which left-right protocol sees as part of other
	// objects but which SQL needs to be separate for
	// normalization.

	addr_set	[ label="Address\nPrefix", color=purple ];

	// Objects created on the fly by the RPKI engine
	node		[ color=green ];
	ca		[ label="CA|{Last CRL #|Next CRL Date|Last Issued Serial #|Last Manifest #|Next Manifest Date|SIA URI}" ];
	ca_detail	[ label="CA Detail|{CA Private Key Handle|CA Public Key|Latest CA Certificate|Manifest EE Private Key Handle|Manifest EE Public Key|Latest Manifest EE Certificate|Latest Manifest|Latest CRL}" ];

	// Some question whether these objects need to be in database
	// per se or are just properties hanging on some other object
	// like ca or ca_detail.  For manifests, we need last serial,
	// same as for CRL.
	roa		[ label="ROA|{EE Certificate|ROA}" ];

	// This one is a table of everything we have ever issued to
	// this child, not to be confused with what's -currently-
	// issued to this child.  Some question whether this hangs off
	// ca or ca_detail, but we -think- hanging off of ca_detail is
	// correct because certificates are issued by a particular
	// keypair.

	child_cert	[ label="Child CA Certificate" ];

	// One-many mappings
	edge [ color=blue, arrowtail=none, arrowhead=crow ];
	self -> biz_sign;
	biz_sign -> child;
	biz_sign -> parent;
	biz_sign -> repo;
	self -> child;
	self -> parent;
	repo -> parent;
	self -> route_origin;

	route_origin -> addr_set [ color=purple, arrowtail=none, arrowhead=crow ];

	// This is many-many because each child is an entity, each CA
	// can have multiple children, and each child can hold certs
	// from multiple CAs (thanks, RobL).
	//
	ca -> child	[ color=green, arrowtail=crow, arrowhead=crow ];

	// One-many mappings
	edge [ color=green, arrowtail=none, arrowhead=crow ];
	ca -> ca_detail;
	child -> child_cert;
	parent -> ca;
	ca_detail -> child_cert;
	ca_detail -> roa;

	// One-one mapping -- separate object to highlight dynamic nature
	edge [ color=green, arrowtail=none, arrowhead=none, style=solid ];
	route_origin -> roa;

}

// Local Variables:
// compile-command: "dot -Tps2 repository-engine-objects.dot | ps2pdf - repository-engine-objects.pdf"
// End:
ss="o">= base + "/wiki/doc/RPKI/TOC", help = "table of contents URL") parser.add_argument("-d", "--directory", default = ".", help = "output directory") parser.add_argument("-p", "--pdf_file", default = "manual.pdf", help = "output PDF file") parser.add_argument("-r", "--html2textrc", default = os.path.join(os.path.dirname(sys.argv[0]), "html2textrc"), help = "html2textrc rules file") args = parser.parse_args() urls = str(xsl_get_toc(lxml.etree.parse(urllib.urlopen(args.toc)).getroot(), basename = repr(args.base_url))).splitlines() assert all(urlparse.urlparse(url).path.startswith("/wiki/") for url in urls) htmldoc = subprocess.Popen( ("htmldoc", "--book", "--title", "--outfile", args.pdf_file, "--format", "pdf", "--firstpage", "p1", "--size", "Universal", "--no-duplex", "--fontsize", "11.0", "--fontspacing", "1.1", "--headfootsize", "11.0", "--headingfont", "Helvetica", "--bodyfont", "Times", "--headfootfont", "Helvetica-Oblique", "-"), stdin = subprocess.PIPE) lxml.etree.ElementTree(xml_title).write(htmldoc.stdin) png_fns = [] for url in urls: path = urlparse.urlparse(url).path page = xsl_get_page(lxml.etree.parse(urllib.urlopen(url)).getroot(), basename = repr(args.base_url), path = repr(path)) for img in page.xpath("//img | //object | //embed"): attr = "data" if img.tag == "object" else "src" img_url = img.get(attr) if img_url.endswith(".svg"): #sys.stderr.write("Converting %s to PNG\n" % img_url) png_fd, png_fn = tempfile.mkstemp(suffix = ".png") subprocess.Popen(("svg2png", "-h", "700", "-w", "600", "-", "-"), stdout = png_fd, stdin = subprocess.PIPE).communicate(urllib.urlopen(img_url).read()) os.close(png_fd) img.set(attr, png_fn) png_fns.append(png_fn) page.write(htmldoc.stdin) html2text = subprocess.Popen(("html2text", "-rcfile", args.html2textrc, "-nobs", "-ascii"), stdin = subprocess.PIPE, stdout = subprocess.PIPE) page.write(html2text.stdin) html2text.stdin.close() lines = html2text.stdout.readlines() html2text.stdout.close() html2text.wait() while lines and lines[0].isspace(): del lines[0] fn = os.path.join(args.directory, path[len("/wiki/"):].replace("/", ".")) f = open(fn, "w") want_blank = False for line in lines: blank = line.isspace() if want_blank and not blank: f.write("\n") if not blank: f.write(line) want_blank = blank f.close() sys.stderr.write("Wrote %s\n" % fn) htmldoc.stdin.close() htmldoc.wait() sys.stderr.write("Wrote %s\n" % args.pdf_file) for png_fn in png_fns: os.unlink(png_fn) # HTMLDOC title page. At some point we might want to generate this # dynamically as an ElementTree, but static content will do for the # moment. xml_title = lxml.etree.HTML('''\ <html> <head> <meta name="author" content="http://rpki.net"> <title>RPKI Tools Manual</title> </head> <body> </body> </html> ''') # XSL transform to extract list of Wiki page URLs from the TOC Wiki page xsl_get_toc = lxml.etree.XSLT(lxml.etree.XML('''\ <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="text" encoding="us-ascii"/> <xsl:param name="basename"/> <xsl:template match="/"> <xsl:for-each select="//div[@id = 'wikipage']/ul//a"> <xsl:value-of select="concat($basename, @href, '&#10;')"/> </xsl:for-each> </xsl:template> </xsl:transform> ''')) # XSL transform to extract useful content of a Wiki page. # Django generates weird HTML for ordered lists: it sometimes breaks # up a single ordered list into multiple adjacent <ol/> elements, # using the @start attribute to try to make the result look like a # single ordered list. This looks OK in Firefox but confuses the # bejesus out of both html2text and htmldoc. In some cases this is # probably unavoidable, but most of the uses of this I've seen look # gratuitous, and are probably the result of code modulararity issues # in Django. # # So we try to clean this up, by merging adjacent <ol/> elements where # we can. The merge incantation is an adaptation of: # # http://stackoverflow.com/questions/1806123/merging-adjacent-nodes-of-same-type-xslt-1-0 # # There may be a more efficient way to do this, but I don't think # we care, and this seems to work. # # Original author's explanation: # # The rather convoluted XPath expression for selecting the following # sibling aaa nodes which are merged with the current one: # # following-sibling::aaa[ # following 'aaa' siblings # not(preceding-sibling::*[ # if they are not preceded by # not(self::aaa) and # a non-'aaa' node # not(following-sibling::aaa = current()) # after the current node # ]) # ] xsl_get_page = lxml.etree.XSLT(lxml.etree.XML('''\ <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" encoding="us-ascii" omit-xml-declaration="yes" /> <xsl:param name="basename"/> <xsl:param name="path"/> <xsl:template match="/"> <xsl:message><xsl:value-of select="concat('Got path: ', $path)"/></xsl:message> <xsl:variable name="id"> <xsl:call-template name="path-to-id"> <xsl:with-param name="p" select="$path"/> </xsl:call-template> </xsl:variable> <xsl:message><xsl:value-of select="concat('Got id: ', $id)"/></xsl:message> <xsl:comment>NEW PAGE</xsl:comment> <html> <body> <div id="{$id}"> <xsl:apply-templates select="//div[@id = 'wikipage']/*"/> </div> </body> </html> </xsl:template> <xsl:template match="//div[contains(@class, 'wiki-toc')]"/> <xsl:template match="//span[@class = 'icon' and not(*)]"/> <xsl:template match="a[contains(@class, 'wiki') and starts-with(@href, '/wiki/')]"> <xsl:variable name="href"> <xsl:call-template name="path-to-id"> <xsl:with-param name="p" select="@href"/> </xsl:call-template> </xsl:variable> <a href="#{$href}"> <xsl:apply-templates select="@*[name() != 'href']"/> <xsl:apply-templates/> </a> </xsl:template> <xsl:template match="a[starts-with(@href, '/attachment/wiki/')]"> <a href="{concat($basename, @href)}"> <xsl:apply-templates select="@*[name() != 'href']"/> <xsl:apply-templates/> </a> </xsl:template> <xsl:template match="img[starts-with(@src, '/raw-attachment/wiki/')]"> <img src="{concat($basename, @src)}"> <xsl:apply-templates select="@*[name() != 'src']"/> <xsl:apply-templates/> </img> </xsl:template> <xsl:template match="object[starts-with(@data, '/raw-attachment/wiki/') or starts-with(@data, '/graphviz/')]"> <object data="{concat($basename, @data)}"> <xsl:apply-templates select="@*[name() != 'data']"/> <xsl:apply-templates/> </object> </xsl:template> <xsl:template match="embed[starts-with(@src, '/raw-attachment/wiki/') or starts-with(@src, '/graphviz/')]"> <embed src="{concat($basename, @src)}"> <xsl:apply-templates select="@*[name() != 'src']"/> <xsl:apply-templates/> </embed> </xsl:template> <xsl:template match="text()[contains(., '&#8203;')]"> <xsl:call-template name="remove-zero-width-spaces"> <xsl:with-param name="s" select="."/> </xsl:call-template> </xsl:template> <xsl:template match="@*|node()"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates/> </xsl:copy> </xsl:template> <xsl:template name="path-to-id"> <xsl:param name="p"/> <xsl:text>_</xsl:text> <xsl:call-template name="replace"> <xsl:with-param name="s" select="$p"/> <xsl:with-param name="old">/</xsl:with-param> <xsl:with-param name="new">.</xsl:with-param> </xsl:call-template> </xsl:template> <xsl:template name="remove-zero-width-spaces"> <xsl:param name="s"/> <xsl:call-template name="replace"> <xsl:with-param name="s" select="$s"/> <xsl:with-param name="old">&#8203;</xsl:with-param> <xsl:with-param name="new"/> </xsl:call-template> </xsl:template> <xsl:template name="replace"> <xsl:param name="s"/> <xsl:param name="old"/> <xsl:param name="new"/> <xsl:choose> <xsl:when test="contains($s, $old)"> <xsl:call-template name="replace"> <xsl:with-param name="s" select="concat(substring-before($s, $old), $new, substring-after($s, $old))"/> <xsl:with-param name="old" select="$old"/> <xsl:with-param name="new" select="$new"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$s"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="ol"> <xsl:if test="not(preceding-sibling::*[1]/self::ol)"> <xsl:variable name="following" select="following-sibling::ol[ not(preceding-sibling::*[ not(self::ol) and not(following-sibling::ol = current()) ]) ]"/> <xsl:copy> <xsl:apply-templates select="$following/@*[name() != 'start']"/> <xsl:apply-templates select="@*"/> <xsl:apply-templates select="node()"/> <xsl:apply-templates select="$following/node()"/> </xsl:copy> </xsl:if> </xsl:template> </xsl:transform> ''')) # All the files we want to parse are HTML, so make HTML the default # parser. In theory the HTML produced by Trac is XHTML thus should # parse correctly (in fact, better) as XML, but in practice this seems # not to work properly at the moment, while parsing as HTML does. # Haven't bothered to figure out why, life is too short. # # If you're reading this comment because this script stopped working # after a Trac upgrade, try commenting out this line to see whether # things have changed and Trac's HTML now parses better as XML. lxml.etree.set_default_parser(lxml.etree.HTMLParser()) # Run the main program. main()