#!/usr/bin/env python # $Id$ # # Copyright (C) 2013 Internet Systems Consortium ("ISC") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. import os import sys import getopt import base64 import textwrap from lxml.etree import Element, SubElement, ElementTree space4 = " " * 4 space6 = " " * 6 space8 = " " * 8 star78 = "*" * 78 wiki_wrapper = textwrap.TextWrapper() conf_wrapper = textwrap.TextWrapper(initial_indent = "# ", subsequent_indent = "# ") xml6_wrapper = textwrap.TextWrapper(initial_indent = space6, subsequent_indent = space6) xml8_wrapper = textwrap.TextWrapper(initial_indent = space8, subsequent_indent = space8) class Option(object): def __init__(self, name, value, doc): self.name = name self.value = value self.doc = doc @property def width(self): return len(self.name) def to_xml(self): x = Element("option", name = self.name) if self.value is not None: x.set("value", self.value) for d in self.doc: SubElement(x, "doc").text = "\n" + xml8_wrapper.fill(d) + "\n" + space6 return x def to_wiki(self, f): f.write("\n== %s == #%s\n" % (self.name, self.name)) for d in self.doc: f.write("\n%s\n" % wiki_wrapper.fill(d)) if self.value is None: f.write("\n%s\n" % wiki_wrapper.fill("No default value.")) else: f.write("\n{{{\n#!ini\n%s = %s\n}}}\n" % (self.name, self.value)) def to_conf(self, f, width): for i, d in enumerate(self.doc): f.write("%s\n%s\n" % ("" if i == 0 else "#", conf_wrapper.fill(d))) if self.value is None: f.write("\n#%-*s = ???\n" % (width - 1, self.name)) else: f.write("\n%-*s = %s\n" % (width, self.name, self.value)) class Section(object): def __init__(self, name): self.name = name self.doc = [] self.options = [] @property def width(self): return max(o.width for o in self.options) @classmethod def from_xml(cls, elt): self = cls(name = elt.get("name")) for x in elt.iterchildren("doc"): self.doc.append(" ".join(x.text.split())) for x in elt.iterchildren("option"): self.options.append(Option(name = x.get("name"), value = x.get("value"), doc = [" ".join(d.text.split()) for d in x.iterchildren("doc")])) return self def to_xml(self): x = Element("section", name = self.name) for d in self.doc: SubElement(x, "doc").text = "\n" + xml6_wrapper.fill(d) + "\n" + space4 x.extend(o.to_xml() for o in self.options) return x def to_wiki(self, f): f.write("\n= [%s] section = #%s\n" % (self.name, self.name)) for d in self.doc: f.write("\n%s\n" % wiki_wrapper.fill(d)) for o in self.options: o.to_wiki(f) def to_conf(self, f, width): f.write("\n" + "#" * 78 + "\n\n[" + self.name + "]\n") if self.doc: f.write("\n##") for i, d in enumerate(self.doc): f.write("%s\n%s\n" % ("" if i == 0 else "#", conf_wrapper.fill(d))) f.write("##\n") for o in self.options: o.to_conf(f, width) def wiki_header(f): f.write("\n".join(( "{{{", "#!comment", "", star78, "THIS PAGE WAS GENERATED AUTOMATICALLY, DO NOT EDIT.", "", "Generated from " + ident, " by $Id$", star78, "", "}}}", ""))) if toc is not None: f.write("[[TracNav(%s)]]\n" % toc) f.write("[[PageOutline]]\n") def conf_header(f): f.write("\n".join(( "# Automatically generated. Edit as needed, but be careful of overwriting.", "#", "# Generated from " + ident, "# by $Id$", ""))) # We use clunky getopt instead of shiny argparse because ordering of # operations matters here, and most options may be repeated. No doubt # there's a way to do this with argparse, but it's not obvious that # it's worth the time it would take to figure it out. sections = [] section_map = None option_map = None ident = None toc = None try: opts, argv = getopt.getopt(sys.argv[1:], "h", ["help", "read-xml=", "write-xml=", "write-wiki=", "write-conf=", "set=", "pwgen=", "toc=", "autoconf"]) except getopt.GetoptError, e: sys.exit("%s: %s" % (sys.argv[0], e)) for o, a in opts: if o in ("-h", "--help"): sys.exit("Use the Source, Luke") elif o == "--read-xml": option_map = None root = ElementTree(file = a).getroot() ident = root.get("ident") sections.extend(Section.from_xml(x) for x in root) option_map = {} section_map = {} for section in sections: if section.name in section_map: sys.exit("Duplicate section %s" % section.name) section_map[section.name] = section for option in section.options: name = (section.name, option.name) if name in option_map: sys.exit("Duplicate option %s::%s" % name) option_map[name] = option elif o == "--set": try: name, value = a.split("=", 1) section, option = name.split("::") except ValueError: sys.exit("Couldn't parse --set specification \"%s\"" % a) name = (section, option) if name not in option_map: sys.exit("Couldn't find option %s::%s" % name) option_map[name].value = value elif o == "--pwgen": try: section, option = a.split("::") except ValueError: sys.exit("Couldn't parse --pwgen specification \"%s\"" % a) name = (section, option) if name not in option_map: sys.exit("Couldn't find option %s::%s" % name) option_map[name].value = base64.urlsafe_b64encode(os.urandom(66)) elif o == "--autoconf": try: import rpki.autoconf for option in section_map["autoconf"].options: try: option.value = getattr(rpki.autoconf, option.name) except AttributeError: pass except ImportError: sys.exit("rpki.autoconf module is not available") except KeyError: sys.exit("Couldn't find autoconf section") elif o == "--write-xml": x = Element("configuration", ident = ident) x.extend(s.to_xml() for s in sections) ElementTree(x).write(a, pretty_print = True, encoding = "us-ascii") elif o == "--write-wiki": if "%" in a: for section in sections: with open(a % section.name, "w") as f: wiki_header(f) section.to_wiki(f) else: with open(a, "w") as f: for i, section in enumerate(sections): if i == 0: wiki_header(f) else: f.write("\f\n") section.to_wiki(f) elif o == "--write-conf": with open(a, "w") as f: conf_header(f) width = max(s.width for s in sections) for section in sections: section.to_conf(f, width) elif o == "--toc": toc = a if argv: sys.exit("%s: Unexpected argument%s: %s" % (sys.argv[0], "" if len(argv) == 1 else "s", " ".join(argv)))