aboutsummaryrefslogtreecommitdiff
path: root/zc
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2023-01-18 15:23:39 -0500
committerRob Austein <sra@hactrn.net>2023-01-18 15:23:39 -0500
commitdf853d56f4f65e5798a41e58a6821da3908f0861 (patch)
treeb5dc004c24e744c571adc91a86c496dfc36f2175 /zc
parent7d6eb4f759fcec4da6d014e10ab82430a8c13c05 (diff)
Add $INCLUDE support
This doesn't support the origin stacking defined in RFC 1034 because: 1. Doing so would require us to maintain a real $INCLUDE stack instead of just chaining iterators; and 2. The expected use case is including automatically-generated snippets in zones that are being maintained with zc, so there's no real need for origin fiddling anyway because whatever automation is generating the snippets can just generate FQDNs if necessary. If really needed, we could fix this, but, YAGNI.
Diffstat (limited to 'zc')
-rwxr-xr-xzc66
1 files changed, 42 insertions, 24 deletions
diff --git a/zc b/zc
index 7bc932d..f8ab312 100755
--- a/zc
+++ b/zc
@@ -32,6 +32,7 @@ from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter, \
RawDescriptionHelpFormatter, FileType
from socket import inet_ntop, inet_pton, AF_INET, AF_INET6
from collections import OrderedDict
+from itertools import chain
import dns.reversename
import dns.rdataclass
@@ -152,24 +153,28 @@ class ZoneGen(object):
+ $MAP <boolean>
+ $RANGE <start-addr> <stop-addr> [<offset> [<multiplier> [<mapaddr>]]]
+ $REVERSE_ZONE <zone-name> [<zone-name> ...]
+ + $INCLUDE <file-name>
- At present $INCLUDE and $GENERATE are not supported: we don't really need the former,
- and $RANGE is (intended as) a replacement for the latter.
+ At present $GENERATE is not supported: $RANGE is (intended as) a replacement.
"""
- def __init__(self, input, filename, now, reverse):
+ def __init__(self, input, now, reverse, opener):
self.input = input
- self.filename = filename
self.now = now
+ self.opener = opener
self.lines = []
self.origin = None
self.cur_origin = None
self.map = OrderedDict()
self.map_enable = False
self.reverse = []
- logger.info("Compiling zone %s", filename)
+ last_filename = None
try:
- for self.lineno, self.line in enumerate(input, 1):
+ while True:
+ self.lineno, self.line, self.filename = next(self.input)
+ if self.filename != last_filename:
+ logger.info("Compiling %s", self.filename)
+ last_filename = self.filename
self.line = self.line.rstrip()
part = self.line.partition(";")
token = part[0].split()
@@ -191,6 +196,8 @@ class ZoneGen(object):
self.rr(name, addr, comment)
if self.map_enable:
self.map_rr(name, addr, comment)
+ except StopIteration:
+ pass
except Exception as e:
logger.error("{self.filename}:{self.lineno}: {e!s}: {self.line}\n".format(self = self, e = e))
sys.exit(1)
@@ -251,8 +258,8 @@ class ZoneGen(object):
def handle_MAP(self, cmd):
self.map_enable = self.get_mapping_state(cmd)
- def handle_INCLUDE(self, name):
- raise NotImplementedError("Not implemented")
+ def handle_INCLUDE(self, filename):
+ self.input = chain(self.opener(filename), self.input)
def handle_GENERATE(self, name, *args):
raise NotImplementedError("Not implemented (try $RANGE)")
@@ -296,7 +303,7 @@ class ZoneGen(object):
z.find_rdataset(rname, PTR, create = True).add(rdata, ttl)
break
else:
- logger.warn("%29s (%-16s %s) does not match any given reverse zone", rname, addr, name)
+ logger.warning("%29s (%-16s %s) does not match any given reverse zone", rname, addr, name)
class ZoneHerd(object):
@@ -307,13 +314,13 @@ class ZoneHerd(object):
a confirmation dance when running as git {pre,post}-receive hooks
"""
- def __init__(self, inputs, outdir, tempword = "RENMWO"):
+ def __init__(self, inputs, outdir, opener, tempword = "RENMWO"):
self.names = OrderedDict()
atexit.register(self.cleanup)
now = int(time.time())
reverse = OrderedDict()
- forward = [ZoneGen(lines, name, now, reverse) for lines, name in inputs]
+ forward = [ZoneGen(input, now, reverse, opener) for input in inputs]
header = ";; Generated by zc at {time}, do not edit by hand\n\n".format(
time = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(now)))
@@ -368,13 +375,17 @@ class GitView(object):
self.commit = newsha
break
if self.commit is not None:
- tree = self.repo.commit(self.commit).tree
- self.jcfg = json.loads(tree["config.json"].data_stream.read())
+ self.tree = self.repo.commit(self.commit).tree
+ self.jcfg = json.load(self.tree["config.json"].data_stream)
log_level = self.jcfg.get("log-level", "warning").strip()
self.stderr_logger.setLevel(log_levels[log_level])
- self.zone_blobs = [tree[name] for name in self.jcfg["zones"]]
+ self.zone_inputs = [self.opener(name) for name in self.jcfg["zones"]]
self.log_user_hook_commit()
+ def opener(self, name):
+ for lineno, line in enumerate(self.tree[name].data_stream.read().decode().splitlines(), 1):
+ yield lineno, line, name
+
def configure_logging(self):
self.stderr_logger = logging.StreamHandler()
self.stderr_logger.setLevel(logging.WARNING)
@@ -465,7 +476,14 @@ def cli_main():
logging.basicConfig(format = "%(message)s", level = log_levels[args.log_level])
- herd = ZoneHerd(((input, input.name) for input in args.input), args.output_directory)
+ def opener(f):
+ if isinstance(f, str):
+ f = open(f, "r")
+ with f:
+ for lineno, line in enumerate(f, 1):
+ yield lineno, line, f.name
+
+ herd = ZoneHerd((opener(input) for input in args.input), args.output_directory, opener)
herd.finish()
@@ -507,9 +525,7 @@ def pre_receive_main():
if not stat.S_ISFIFO(os.fstat(fifo).st_mode):
raise RuntimeError("{} is not a FIFO!".format(gv.fifo_name))
- herd = ZoneHerd(((blob.data_stream.read().splitlines(), blob.name) for blob in gv.zone_blobs),
- gv.outdir,
- gv.commit)
+ herd = ZoneHerd(gv.zone_inputs, gv.outdir, gv.opener, gv.commit)
logging.getLogger().removeHandler(gv.stderr_logger)
@@ -524,7 +540,7 @@ def pre_receive_main():
t = time.time()
if not select.select([fifo], [], [], remaining)[0]:
break # Timeout
- chunk = os.read(fifo, 1024)
+ chunk = os.read(fifo, 1024).decode()
if chunk == "":
break # EOF
confirmation += chunk
@@ -533,11 +549,12 @@ def pre_receive_main():
herd.finish() # Success
if gv.postcmd:
logger.info("Running post-command %r", gv.postcmd)
- proc = subprocess.Popen(gv.postcmd, stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
- for line in proc.stdout.read().splitlines():
- logger.info(">> %s", line)
- proc.stdout.close()
- proc.wait()
+ with subprocess.Popen(gv.postcmd,
+ stdout = subprocess.PIPE,
+ stderr = subprocess.STDOUT,
+ text = True, errors = "backslashreplace") as proc:
+ for line in proc.stdout:
+ logger.info(">> %s", line.rstrip())
break
remaining -= time.time() - t
@@ -559,6 +576,7 @@ def post_receive_main():
gv = GitView()
if gv.commit is not None:
with open(gv.fifo_name, "w") as f:
+ logger.debug("Commit: %s", gv.commit)
f.write(gv.commit + "\n")
except Exception as e:
logger.error("%s", e)