1 #!/usr/bin/env python |
|
2 # vim: set ft=python : |
|
3 |
|
4 """ |
|
5 Process zonefiles with template expansions. |
|
6 """ |
|
7 |
|
8 __version__ = '0.0.1-dev' |
|
9 |
|
10 import optparse |
|
11 import codecs |
|
12 import os.path |
|
13 from datetime import datetime |
|
14 import logging |
|
15 |
|
16 log = logging.getLogger() |
|
17 |
|
18 # command-line options, global state |
|
19 options = None |
|
20 |
|
21 def parse_options (argv) : |
|
22 """ |
|
23 Parse command-line arguments. |
|
24 """ |
|
25 |
|
26 parser = optparse.OptionParser( |
|
27 prog = argv[0], |
|
28 usage = '%prog: [options]', |
|
29 version = __version__, |
|
30 |
|
31 # module docstring |
|
32 description = __doc__, |
|
33 ) |
|
34 |
|
35 # logging |
|
36 general = optparse.OptionGroup(parser, "General Options") |
|
37 |
|
38 general.add_option('-q', '--quiet', dest='loglevel', action='store_const', const=logging.ERROR, help="Less output") |
|
39 general.add_option('-v', '--verbose', dest='loglevel', action='store_const', const=logging.INFO, help="More output") |
|
40 general.add_option('-D', '--debug', dest='loglevel', action='store_const', const=logging.DEBUG, help="Even more output") |
|
41 |
|
42 parser.add_option_group(general) |
|
43 |
|
44 parser.add_option('-c', '--input-charset', metavar='CHARSET', default='utf-8', |
|
45 help="Encoding used for input files") |
|
46 |
|
47 parser.add_option('-o', '--output', metavar='FILE', default='-', |
|
48 help="Write to output file; default stdout") |
|
49 |
|
50 parser.add_option('--output-charset', metavar='CHARSET', default='utf-8', |
|
51 help="Encoding used for output files") |
|
52 |
|
53 parser.add_option('--expand', metavar='NAME=VALUE', action='append', |
|
54 help="Expand given template variable in zone") |
|
55 |
|
56 parser.add_option('--serial', metavar='FILE', |
|
57 help="Read/expand serial from given .serial file") |
|
58 |
|
59 # defaults |
|
60 parser.set_defaults( |
|
61 loglevel = logging.WARN, |
|
62 expand = [], |
|
63 ) |
|
64 |
|
65 # parse |
|
66 options, args = parser.parse_args(argv[1:]) |
|
67 |
|
68 # configure |
|
69 logging.basicConfig( |
|
70 format = '%(processName)s: %(name)s: %(levelname)s %(funcName)s : %(message)s', |
|
71 level = options.loglevel, |
|
72 ) |
|
73 |
|
74 return options, args |
|
75 |
|
76 def process_file (file, expansions) : |
|
77 """ |
|
78 Process file, expanding lines. |
|
79 """ |
|
80 |
|
81 for line in file : |
|
82 line = line.format(**expansions) |
|
83 |
|
84 yield line |
|
85 |
|
86 def write_lines (file, lines, suffix='\n') : |
|
87 for line in lines : |
|
88 file.write(line + suffix) |
|
89 |
|
90 def open_file (path, mode, charset) : |
|
91 """ |
|
92 Open unicode-enabled file from path, with - using stdio. |
|
93 """ |
|
94 |
|
95 if path == '-' : |
|
96 # use stdin/out based on mode |
|
97 stream, func = { |
|
98 'r': (sys.stdin, codecs.getreader), |
|
99 'w': (sys.stdout, codecs.getwriter), |
|
100 }[mode[0]] |
|
101 |
|
102 # wrap |
|
103 return func(charset)(stream) |
|
104 |
|
105 else : |
|
106 # open |
|
107 return codecs.open(path, mode, charset) |
|
108 |
|
109 def process_serial (path) : |
|
110 """ |
|
111 Use serial number from given file. |
|
112 |
|
113 Returns the new serial as a string. |
|
114 """ |
|
115 |
|
116 if not os.path.exists(path) : |
|
117 raise Exception("Given --serial does not exist: %s" % path) |
|
118 |
|
119 return open(path).read().strip() |
|
120 |
|
121 def parse_expand (expand) : |
|
122 """ |
|
123 Parse an --expand foo=bar to (key, value) |
|
124 """ |
|
125 |
|
126 key, value = expand.split('=', 1) |
|
127 |
|
128 return key, value |
|
129 |
|
130 def main (argv) : |
|
131 global options |
|
132 |
|
133 options, args = parse_options(argv) |
|
134 |
|
135 # expands |
|
136 expand = dict(parse_expand(expand) for expand in options.expand) |
|
137 |
|
138 # serial? |
|
139 if options.serial : |
|
140 serial = process_serial(options.serial) |
|
141 |
|
142 expand['serial'] = serial |
|
143 |
|
144 # input |
|
145 if args : |
|
146 # open files |
|
147 input_files = [open_file(path, 'r', options.input_charset) for path in args] |
|
148 |
|
149 else : |
|
150 # default to stdout |
|
151 input_files = [open_file('-', 'r', options.input_charset)] |
|
152 |
|
153 # process |
|
154 lines = [] |
|
155 |
|
156 for file in input_files : |
|
157 log.info("Reading zone: %s", file) |
|
158 |
|
159 lines += list(process_file(file, expand)) |
|
160 |
|
161 # output |
|
162 output = open_file(options.output, 'w', options.output_charset) |
|
163 write_lines(output, lines, suffix='') |
|
164 |
|
165 return 0 |
|
166 |
|
167 if __name__ == '__main__': |
|
168 import sys |
|
169 |
|
170 sys.exit(main(sys.argv)) |
|