--- a/pvl/config/bind.py Sun Jul 12 02:14:08 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,187 +0,0 @@
-"""
- High-level BIND stuff
-"""
-
-from __future__ import with_statement
-
-import bind_conf as bindc
-
-import os.path, datetime
-
-DEFAULT_TTL = bindc.Interval(3600)
-
-class Settings (object) :
- """
- A set of basic settings for a zone, mostly default TTL/refresh/retry/expire/minimum settings
- """
-
- def __init__ (self, ttl, hostmaster, refresh, retry, expire, minimum) :
- self.ttl = ttl
- self.hostmaster = hostmaster
- self.refresh = refresh
- self.retry = retry
- self.expire = expire
- self.minimum = minimum
-
-class AutoSerial (object) :
- """
- Automatically generate the next serial to use by loading it from a file.
-
- The generated serials are in YYYYMMDDXX format.
- """
-
- def __init__ (self, path) :
- """
- Load the current serial
-
- @param path the path to the serial file
- """
-
- # store
- self.path = path
-
- # load it
- # XXX: locking
- serial = self.read()
-
- # current date
- today = datetime.date.today()
-
- # parse it
- if serial :
- date, code = self.parse(serial)
-
- else :
- date, code = today, 0
-
- # increment it
- date, code = self.next(date, code)
-
- # format it
- self._serial = self.build(date, code)
-
- # write it out
- self.write(self._serial)
-
- def parse (self, serial) :
- """
- Parse the given serial into a (datetime.date, code) format
- """
-
- # build it into a date
- date = datetime.date(
- year = int(serial[0:4]),
- month = int(serial[4:6]),
- day = int(serial[6:8])
- )
-
- code = int(serial[8:])
-
- return date, code
-
- def next (self, date, code) :
- """
- Return the next valid serial following the given one
- """
-
- # current date
- today = datetime.date.today()
-
- # now to increment?
- if date < today :
- # jump to today's first serial
- date = today
- code = 0
-
- else :
- # today or overflowed into the future, just increment the code
- code += 1
-
- # code overflowed into next day?
- if code > 99 :
- date += datetime.timedelta(days=1)
- code = 0
-
- # ok
- return date, code
-
- def build (self, date, code) :
- """
- Build a serial code the given date/code
- """
-
- assert 0 <= code <= 99
-
- return "%s%02d" % (date.strftime("%Y%m%d"), code)
-
- def read (self) :
- """
- Read the current serial, returning it, or None, if not found...
- """
-
- # if it doesn't exist, default
- if not os.path.exists(self.path) :
- return None
-
- # read it
- with open(self.path) as fh :
- return fh.read().strip()
-
- def write (self, serial) :
- """
- Write a new serial
- """
-
- with open(self.path, 'w') as fh :
- fh.write("%s\n" % (serial, ))
-
- def serial (self) :
- """
- Return a new, unused serial code (before __init__)
- """
-
- return self._serial
-
-class Domain (bindc.ZoneFile) :
- """
- A domain has a skeleton of stuff defined, but the rest is $INCLUDE'd from elsewhere, which is useful for
- multi-domain setups where the domains are mostly similar
- """
-
- def __init__ (self, domain, path, nameservers, mailservers, serial, settings, include=None, objs=None) :
- """
- @param domain the domain name
- @param path the path to the zone file
- @param nameservers list of nameservers as labels
- @param mailservers list of (pref, label) tuples for MX records
- @param serial the serial code to use
- @param settings the TTL/SOA settings to use
- @param include the optional zonefile to include
- @param objs the optional other objects to add to the zonefile
- """
-
- super(Domain, self).__init__(domain, path)
-
- # the default TTL
- self.add_directive(bindc.TTLDirective(settings.ttl))
-
- # the SOA record
- self.add_record(bindc.SOA(None, nameservers[0],
- settings.hostmaster, serial, settings.refresh, settings.retry, settings.expire, settings.minimum
- ))
-
- # the NS records
- for label in nameservers :
- self.add_record(bindc.NS(None, label))
-
- # the MX records
- for pref, label in mailservers :
- self.add_record(bindc.MX(None, pref, label))
-
- # include?
- if include :
- self.add_directive(bindc.IncludeDirective(include))
-
- if objs :
- for obj in objs :
- self.add_obj(obj)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/config/bind/model.py Sun Jul 12 02:14:34 2009 +0300
@@ -0,0 +1,188 @@
+"""
+ High-level BIND stuff
+"""
+
+from __future__ import with_statement
+
+from . import zone
+
+import os.path, datetime
+
+DEFAULT_TTL = zone.Interval(3600)
+
+class Settings (object) :
+ """
+ A set of basic settings for a zone, mostly default TTL/refresh/retry/expire/minimum settings
+ """
+
+ def __init__ (self, ttl, hostmaster, refresh, retry, expire, minimum) :
+ self.ttl = ttl
+ self.hostmaster = hostmaster
+ self.refresh = refresh
+ self.retry = retry
+ self.expire = expire
+ self.minimum = minimum
+
+class AutoSerial (object) :
+ """
+ Automatically generate the next serial to use by loading it from a file.
+
+ The generated serials are in YYYYMMDDXX format.
+ """
+
+ def __init__ (self, path) :
+ """
+ Load the current serial
+
+ @param path the path to the serial file
+ """
+
+ # store
+ self.path = path
+
+ # load it
+ # XXX: locking
+ serial = self.read()
+
+ # current date
+ today = datetime.date.today()
+
+ # parse it
+ if serial :
+ date, code = self.parse(serial)
+
+ else :
+ date, code = today, 0
+
+ # increment it
+ date, code = self.next(date, code)
+
+ # format it
+ self._serial = self.build(date, code)
+
+ # write it out
+ self.write(self._serial)
+
+ def parse (self, serial) :
+ """
+ Parse the given serial into a (datetime.date, code) format
+ """
+
+ # build it into a date
+ date = datetime.date(
+ year = int(serial[0:4]),
+ month = int(serial[4:6]),
+ day = int(serial[6:8])
+ )
+
+ code = int(serial[8:])
+
+ return date, code
+
+ def next (self, date, code) :
+ """
+ Return the next valid serial following the given one
+ """
+
+ # current date
+ today = datetime.date.today()
+
+ # now to increment?
+ if date < today :
+ # jump to today's first serial
+ date = today
+ code = 0
+
+ else :
+ # today or overflowed into the future, just increment the code
+ code += 1
+
+ # code overflowed into next day?
+ if code > 99 :
+ date += datetime.timedelta(days=1)
+ code = 0
+
+ # ok
+ return date, code
+
+ def build (self, date, code) :
+ """
+ Build a serial code the given date/code
+ """
+
+ assert 0 <= code <= 99
+
+ return "%s%02d" % (date.strftime("%Y%m%d"), code)
+
+ def read (self) :
+ """
+ Read the current serial, returning it, or None, if not found...
+ """
+
+ # if it doesn't exist, default
+ if not os.path.exists(self.path) :
+ return None
+
+ # read it
+ with open(self.path) as fh :
+ return fh.read().strip()
+
+ def write (self, serial) :
+ """
+ Write a new serial
+ """
+
+ with open(self.path, 'w') as fh :
+ fh.write("%s\n" % (serial, ))
+
+ def serial (self) :
+ """
+ Return a new, unused serial code (before __init__)
+ """
+
+ return self._serial
+
+class Domain (zone.Zone) :
+ """
+ A domain has a skeleton of stuff defined, but the rest is $INCLUDE'd from elsewhere, which is useful for
+ multi-domain setups where the domains are mostly similar
+ """
+
+ def __init__ (self, domain, path, nameservers, mailservers, serial, settings, include=None, objs=None) :
+ """
+ @param domain the domain name
+ @param path the path to the zone file
+ @param nameservers list of nameservers as labels
+ @param mailservers list of (pref, label) tuples for MX records
+ @param serial the serial code to use
+ @param settings the TTL/SOA settings to use
+ @param include the optional zonefile to include
+ @param objs the optional other objects to add to the zonefile
+ """
+
+ super(Domain, self).__init__(domain, path)
+
+ # the default TTL
+ self.add_directive(zone.TTLDirective(settings.ttl))
+
+ # the SOA record
+ self.add_record(zone.SOA(None, nameservers[0],
+ settings.hostmaster, serial, settings.refresh, settings.retry, settings.expire, settings.minimum
+ ))
+
+ # the NS records
+ for label in nameservers :
+ self.add_record(zone.NS(None, label))
+
+ # the MX records
+ for pref, label in mailservers :
+ self.add_record(zone.MX(None, pref, label))
+
+ # include?
+ if include :
+ self.add_directive(zone.IncludeDirective(include))
+
+ if objs :
+ for obj in objs :
+ self.add_obj(obj)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/config/bind/zone.py Sun Jul 12 02:14:34 2009 +0300
@@ -0,0 +1,355 @@
+"""
+ Configuration file output for the ISC DNS server: BIND
+"""
+
+from .. import file
+
+class Item (file.Item) :
+ """
+ Item that knows how to format comments
+ """
+
+ COMMENT_PREFIX = ';'
+
+class Zone (Item, file.Contents) :
+ """
+ A zone containing a bunch of directives, resource records and comments
+ """
+
+ def __init__ (self, ttl=None, comment=None) :
+ """
+ @param name the name of the zonefile, for status stuff
+ @param path the path to the zonefile
+ @param ttl default TTL to use
+ """
+
+ Item.__init__(self, [comment])
+ file.Contents.__init__(self)
+
+ if ttl :
+ self.add(TTLDirective(ttl))
+
+class Comment (Item) :
+ """
+ A comment, is, well, a comment :)
+ """
+
+ def __init__ (self, comment) :
+ """
+ @param comment the comment string
+ """
+
+ Item.__init__(self, [comment])
+
+ # only need to iterate over our comments
+ iter_lines = Item.iter_comments
+
+class Label (object) :
+ """
+ A label, as used in a ResourceRecord, either as the label, or the rdata for various resource types
+
+ You can also use strs, this just implements a __str__ method
+ """
+
+ def __init__ (self, label) :
+ """
+ @param label the literal label to use
+ """
+
+ self.label = label
+
+ def __str__ (self) :
+ return self.label
+
+class Origin (Label) :
+ """
+ A label that represents the zone's origin
+ """
+
+ def __init__ (self) :
+ pass
+
+ def __str__ (self) :
+ return '@'
+
+class FQDN (Label) :
+ """
+ A label that represents the given external domain (i.e. this adds the . to the end that people always forget).
+ """
+
+ def __init__ (self, fqdn) :
+ self.fqdn = fqdn
+
+ def __str__ (self) :
+ return "%s." % (self.fqdn, )
+
+class Interval (object) :
+ """
+ A time interval suitable for use in SOA records
+ """
+
+ def __init__ (self, s=None, m=None, h=None, d=None) :
+ """
+ @param s seconds
+ @param m minutes
+ @param h hours
+ @param d days
+ """
+
+ self.s = s
+ self.m = m
+ self.h = h
+ self.d = d
+
+ def __str__ (self) :
+ """
+ If only seconds were given, just return those directly, otherwise, apply units
+ """
+
+ if self.s and not self.m and not self.h and not self.d :
+ return str(self.s)
+
+ else :
+ return "%s%s%s%s" % (
+ "%ds" % self.s if self.s else '',
+ "%dm" % self.m if self.m else '',
+ "%dh" % self.h if self.h else '',
+ "%dd" % self.d if self.d else ''
+ )
+
+class ResourceRecord (Item) :
+ """
+ A generic resource record for a BIND zone file
+ """
+
+ def __init__ (self, label, type, rdata, cls='IN', ttl=None, **kwargs) :
+ """
+ @param label the "name" of this record, or None to referr to the previous record's label
+ @param type the type as a string ('A', 'TXT', etc.)
+ @param rdata the rdata, as a raw string
+ @param cls the class, e.g. 'IN'
+ @param ttl the time-to-live value in seconds, or None to omit it
+ """
+
+ super(ResourceRecord, self).__init__(**kwargs)
+
+ self.label = label
+ self.type = type
+ self.rdata = rdata
+ self.cls = cls
+ self.ttl = ttl
+
+ def iter_lines (self) :
+ """
+ Just format the lines, eh
+ """
+
+ # prefix comments
+ for line in self.iter_comments() :
+ yield line
+
+ # then format the line
+ # XXX: TTL?
+ yield "%-30s %-4s%-4s %-8s %s" % (self.label if self.label is not None else '', str(self.ttl) if self.ttl else '', self.cls, self.type, self.rdata)
+
+class SOA (ResourceRecord) :
+ """
+ "Identifies the start of a zone of authority", must be the first record
+ """
+
+ def __init__ (self, label, primary_ns, hostmaster, serial, refresh, retry, expire, minimum, **kwargs) :
+ """
+ @param label the "name" of the zone, usually ORIGIN
+ @param primary_ns the address of the primary NS server
+ @param hostmaster the mailbox of the zone's hostmaster
+ @param serial the serial number of the zone as an integer
+ @param refresh time interval between zone refreshes in seconds
+ @param retry time interval between retrys for failed refreshes
+ @param expire time interval before zone data can no longer be considered authorative
+ @param minimum minimum TTL for RRs in this zone
+ """
+
+ super(SOA, self).__init__(label, 'SOA', "%s %s ( %s %s %s %s %s )" % (
+ primary_ns, hostmaster, serial, refresh, retry, expire, minimum
+ ), **kwargs)
+
+class A (ResourceRecord) :
+ """
+ An IPv4 forward address
+ """
+
+ def __init__ (self, label, addr, **kwargs) :
+ """
+ @param label the "name" of the address
+ @param addr the IPv4 target address
+ """
+
+ assert(addr.is_v4())
+
+ super(A, self).__init__(label, 'A', addr, **kwargs)
+
+class AAAA (ResourceRecord) :
+ """
+ An IPv6 forward address
+ """
+
+ def __init__ (self, label, addr, **kwargs) :
+ """
+ @param label the "name" of the address
+ @param addr the IPv6 target address
+ """
+
+ assert(addr.is_v6())
+
+ super(AAAA, self).__init__(label, 'AAAA', addr.strCompressed(), **kwargs)
+
+class CNAME (ResourceRecord) :
+ """
+ A canonical-name alias
+ """
+
+ def __init__ (self, label, target, **kwargs) :
+ """
+ @param label the "name" of the alias
+ @param target the alias target
+ """
+
+ super(CNAME, self).__init__(label, 'CNAME', target, **kwargs)
+
+class TXT (ResourceRecord) :
+ """
+ A human-readable information record
+ """
+
+ def __init__ (self, label, text, **kwargs) :
+ """
+ @param label the "name" of the text record
+ @param text the text data, shouldn't contain any quotes...
+ """
+
+ # XXX: escaping?
+ super(TXT, self).__init__(label, 'TXT', '"%s"' % text, **kwargs)
+
+class MX (ResourceRecord) :
+ """
+ A mail-exchange definition
+ """
+
+ def __init__ (self, label, pref, exchange, **kwargs) :
+ """
+ @param label the "name" of the domain to handle mail for
+ @param pref the numerical preference for this exchange
+ @param exchange the domain name of the mail exchange (SMTP server)
+ """
+
+ super(MX, self).__init__(label, 'MX', "%d %s" % (pref, exchange), **kwargs)
+
+class NS (ResourceRecord) :
+ """
+ An authorative name server
+ """
+
+ def __init__ (self, label, nsname, **kwargs) :
+ """
+ @param label the "name" of the domain to have a nameserver for
+ @param nsname the name of the nameserver
+ """
+
+ super(NS, self).__init__(label, 'NS', nsname)
+
+class PTR (ResourceRecord) :
+ """
+ An IPv4/IPv6 reverse address
+ """
+
+ def __init__ (self, addr, name, **kwargs) :
+ """
+ @param addr the addr.IP to map via in-addr.arpa
+ @param name the name to map the address to
+ """
+
+ # XXX: quick hack, this gives an absolute name
+ label = addr.reverseName()
+
+ super(PTR, self).__init__(label, 'PTR', name)
+
+class Directive (Item) :
+ """
+ Special directives that can be used in zone files to control behaviour
+ """
+
+ def __init__ (self, name, *args, **kwargs) :
+ """
+ @param name the $NAME bit
+ @param args optional list of space-seprated arguments, Nones are ignored
+ """
+
+ super(Directive, self).__init__(**kwargs)
+
+ self.name = name
+ self.args = [arg for arg in args if arg is not None]
+
+ def iter_lines (self) :
+ # prefix comments
+ for line in self.iter_comments() :
+ yield line
+
+ # then format it
+ yield "$%s%s" % (self.name, (' ' + ' '.join(str(arg) for arg in self.args)) if self.args else '')
+
+class OriginDirective (Directive) :
+ """
+ Set the origin used to resolve the zone's labels.
+
+ Note that the origin label is not absolute by default - use FQDN for that
+ """
+
+ def __init__ (self, origin, **kwargs) :
+ """
+ @param origin the origin label
+ """
+
+ super(OriginDirective, self).__init__('ORIGIN', origin, **kwargs)
+
+class TTLDirective (Directive) :
+ """
+ Set the TTL used for records by default
+ """
+
+ def __init__ (self, ttl, **kwargs) :
+ """
+ @param ttl the new ttl to use
+ """
+
+ super(TTLDirective, self).__init__('TTL', ttl, **kwargs)
+
+class IncludeDirective (Directive) :
+ """
+ Include another zoen file, optionally with a different origin
+ """
+
+ def __init__ (self, filename, origin=None, **kwargs) :
+ """
+ @param filename the zone file to include
+ @param origin the optional origin to process the zonefile with
+ """
+
+ super(IncludeDirective, self).__init__('INCLUDE', filename, origin, **kwargs)
+
+class GenerateDirective (Directive) :
+ """
+ Generate a bunch of numbered records using an expression for the label and rdata.
+
+ At the simplest, any "$" in the expression is replaced with the value of the iterator.
+ """
+
+ def __init__ (self, range, lhs, type, rhs, ttl=None, cls=None, **kwargs) :
+ """
+ @param range (start, stop, step) tuple
+ @param lhs expression to generate the label
+ @param type the resource record type
+ @param rhs expression to generate the rdata
+ """
+
+ super(GenerateDirective, self).__init__('GENERATE', '%d-%d' % range, lhs, ttl, cls, type, rhs, **kwargs)
+
--- a/pvl/config/bind_conf.py Sun Jul 12 02:14:08 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,395 +0,0 @@
-"""
- Configuration file output for the ISC DNS server: BIND
-"""
-
-import conf
-
-class Object (conf.ConfObject) :
- """
- Our own version of ConfObject that knows how to format comments
- """
-
- def _fmt_comments (self) :
- """
- Format comments using the standard format:
- ";" <comment>
- """
-
- for comment in self.comments :
- if comment is not None :
- yield "; %s" % (comment, )
-
-class ZoneFile (Object, conf.File) :
- """
- A zone file containing a bunch of directives, resource records and comments
- """
-
- def __init__ (self, name, path, ttl=None, comment=None) :
- """
- @param name the name of the zonefile, for status stuff
- @param path the path to the zonefile
- @param ttl default TTL to use
- @param comment optional comments
- """
-
- Object.__init__(self, comment)
- conf.File.__init__(self, name, path)
-
- # init
- self.objects = []
-
- if ttl :
- self.add_directive(TTLDirective(ttl))
-
- def add_obj (self, obj) :
- """
- Add an Object onto the list of things to include in this zone file
- """
-
- self.objects.append(obj)
-
- # various aliases...
- add_comment = add_record = add_directive = add_obj
-
- def fmt_lines (self) :
- """
- Just format all our objects
- """
-
- # prefix comments
- for line in self._fmt_comments() :
- yield line
-
- # and then all objects
- for obj in self.objects :
- # ...and their lines
- for line in obj.fmt_lines() :
- yield line
-
-class Comment (Object) :
- """
- A comment, is, well, a comment :)
- """
-
- def __init__ (self, comment) :
- """
- @param comment the comment string
- """
-
- Object.__init__(self, [comment])
-
- def fmt_lines (self) :
- """
- Yield a single line with the comment
- """
-
- return self._fmt_comments()
-
-class Label (object) :
- """
- A label, as used in a ResourceRecord, either as the label, or the rdata for various resource types
-
- You can also use strs, this just implements a __str__ method
- """
-
- def __init__ (self, label) :
- """
- @param label the literal label to use
- """
-
- self.label = label
-
- def __str__ (self) :
- return self.label
-
-class Origin (Label) :
- """
- A label that represents the zone's origin
- """
-
- def __init__ (self) :
- pass
-
- def __str__ (self) :
- return '@'
-
-class FQDN (Label) :
- """
- A label that represents the given external domain (i.e. this adds the . to the end that people always forget).
- """
-
- def __init__ (self, fqdn) :
- self.fqdn = fqdn
-
- def __str__ (self) :
- return "%s." % (self.fqdn, )
-
-class Interval (object) :
- """
- A time interval suitable for use in SOA records
- """
-
- def __init__ (self, s=None, m=None, h=None, d=None) :
- """
- @param s seconds
- @param m minutes
- @param h hours
- @param d days
- """
-
- self.s = s
- self.m = m
- self.h = h
- self.d = d
-
- def __str__ (self) :
- """
- If only seconds were given, just return those directly, otherwise, apply units
- """
-
- if self.s and not self.m and not self.h and not self.d :
- return str(self.s)
-
- else :
- return "%s%s%s%s" % (
- "%ds" % self.s if self.s else '',
- "%dm" % self.m if self.m else '',
- "%dh" % self.h if self.h else '',
- "%dd" % self.d if self.d else ''
- )
-
-class ResourceRecord (Object) :
- """
- A generic resource record for a BIND zone file
- """
-
- def __init__ (self, label, type, rdata, cls='IN', ttl=None, **kwargs) :
- """
- @param label the "name" of this record, or None to referr to the previous record's label
- @param type the type as a string ('A', 'TXT', etc.)
- @param rdata the rdata, as a raw string
- @param cls the class, e.g. 'IN'
- @param ttl the time-to-live value in seconds, or None to omit it
- """
-
- super(ResourceRecord, self).__init__(**kwargs)
-
- self.label = label
- self.type = type
- self.rdata = rdata
- self.cls = cls
- self.ttl = ttl
-
- def fmt_lines (self) :
- """
- Just format the lines, eh
- """
-
- # prefix comments
- for line in self._fmt_comments() :
- yield line
-
- # then format the line
- # XXX: TTL?
- yield "%-30s %-4s%-4s %-8s %s" % (self.label if self.label is not None else '', str(self.ttl) if self.ttl else '', self.cls, self.type, self.rdata)
-
-class SOA (ResourceRecord) :
- """
- "Identifies the start of a zone of authority", must be the first record
- """
-
- def __init__ (self, label, primary_ns, hostmaster, serial, refresh, retry, expire, minimum, **kwargs) :
- """
- @param label the "name" of the zone, usually ORIGIN
- @param primary_ns the address of the primary NS server
- @param hostmaster the mailbox of the zone's hostmaster
- @param serial the serial number of the zone as an integer
- @param refresh time interval between zone refreshes in seconds
- @param retry time interval between retrys for failed refreshes
- @param expire time interval before zone data can no longer be considered authorative
- @param minimum minimum TTL for RRs in this zone
- """
-
- super(SOA, self).__init__(label, 'SOA', "%s %s ( %s %s %s %s %s )" % (
- primary_ns, hostmaster, serial, refresh, retry, expire, minimum
- ), **kwargs)
-
-class A (ResourceRecord) :
- """
- An IPv4 forward address
- """
-
- def __init__ (self, label, addr, **kwargs) :
- """
- @param label the "name" of the address
- @param addr the IPv4 target address
- """
-
- assert(addr.is_v4())
-
- super(A, self).__init__(label, 'A', addr, **kwargs)
-
-class AAAA (ResourceRecord) :
- """
- An IPv6 forward address
- """
-
- def __init__ (self, label, addr, **kwargs) :
- """
- @param label the "name" of the address
- @param addr the IPv6 target address
- """
-
- assert(addr.is_v6())
-
- super(AAAA, self).__init__(label, 'AAAA', addr.strCompressed(), **kwargs)
-
-class CNAME (ResourceRecord) :
- """
- A canonical-name alias
- """
-
- def __init__ (self, label, target, **kwargs) :
- """
- @param label the "name" of the alias
- @param target the alias target
- """
-
- super(CNAME, self).__init__(label, 'CNAME', target, **kwargs)
-
-class TXT (ResourceRecord) :
- """
- A human-readable information record
- """
-
- def __init__ (self, label, text, **kwargs) :
- """
- @param label the "name" of the text record
- @param text the text data, shouldn't contain any quotes...
- """
-
- super(TXT, self).__init__(label, 'TXT', '"%s"' % text, **kwargs)
-
-class MX (ResourceRecord) :
- """
- A mail-exchange definition
- """
-
- def __init__ (self, label, pref, exchange, **kwargs) :
- """
- @param label the "name" of the domain to handle mail for
- @param pref the numerical preference for this exchange
- @param exchange the domain name of the mail exchange (SMTP server)
- """
-
- super(MX, self).__init__(label, 'MX', "%d %s" % (pref, exchange), **kwargs)
-
-class NS (ResourceRecord) :
- """
- An authorative name server
- """
-
- def __init__ (self, label, nsname, **kwargs) :
- """
- @param label the "name" of the domain to have a nameserver for
- @param nsname the name of the nameserver
- """
-
- super(NS, self).__init__(label, 'NS', nsname)
-
-class PTR (ResourceRecord) :
- """
- An IPv4/IPv6 reverse address
- """
-
- def __init__ (self, addr, name, **kwargs) :
- """
- @param addr the addr.IP to map via in-addr.arpa
- @param name the name to map the address to
- """
-
- # XXX: quick hack, this gives an absolute name
- label = addr.reverseName()
-
- super(PTR, self).__init__(label, 'PTR', name)
-
-class Directive (Object) :
- """
- Special directives that can be used in zone files to control behaviour
- """
-
- def __init__ (self, name, *args, **kwargs) :
- """
- @param name the $NAME bit
- @param args optional list of space-seprated arguments, Nones are ignored
- """
-
- super(Directive, self).__init__(**kwargs)
-
- self.name = name
- self.args = [arg for arg in args if arg is not None]
-
- def fmt_lines (self) :
- # prefix comments
- for line in self._fmt_comments() :
- yield line
-
- # then format it
- yield "$%s%s" % (self.name, (' ' + ' '.join(str(arg) for arg in self.args)) if self.args else '')
-
-class OriginDirective (Directive) :
- """
- Set the origin used to resolve the zone's labels.
-
- Note that the origin label is not absolute by default - use FQDN for that
- """
-
- def __init__ (self, origin, **kwargs) :
- """
- @param origin the origin label
- """
-
- super(OriginDirective, self).__init__('ORIGIN', origin, **kwargs)
-
-class TTLDirective (Directive) :
- """
- Set the TTL used for records by default
- """
-
- def __init__ (self, ttl, **kwargs) :
- """
- @param ttl the new ttl to use
- """
-
- super(TTLDirective, self).__init__('TTL', ttl, **kwargs)
-
-class IncludeDirective (Directive) :
- """
- Include another zoen file, optionally with a different origin
- """
-
- def __init__ (self, filename, origin=None, **kwargs) :
- """
- @param filename the zone file to include
- @param origin the optional origin to process the zonefile with
- """
-
- super(IncludeDirective, self).__init__('INCLUDE', filename, origin, **kwargs)
-
-class GenerateDirective (Directive) :
- """
- Generate a bunch of numbered records using an expression for the label and rdata.
-
- At the simplest, any "$" in the expression is replaced with the value of the iterator.
- """
-
- def __init__ (self, range, lhs, type, rhs, ttl=None, cls=None, **kwargs) :
- """
- @param range (start, stop, step) tuple
- @param lhs expression to generate the label
- @param type the resource record type
- @param rhs expression to generate the rdata
- """
-
- super(GenerateDirective, self).__init__('GENERATE', '%d-%d' % range, lhs, ttl, cls, type, rhs, **kwargs)
-
--- a/pvl/config/dhcp.py Sun Jul 12 02:14:08 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-"""
- Higher-level DHCP config structure model
-"""
-
-import dhcp_conf as dhcpc
-
-class Config (dhcpc.ConfFile) :
- """
- A full configuration file
- """
-
- def __init__ (self, name=dhcpc.ConfFile.DEFAULT_NAME, path=dhcpc.ConfFile.DEFAULT_PATH,
- settings=None, options=None, shared_network=False, subnets=None, hosts=None, comment=None
- ) :
- """
- Create a full configuration file for the given settings:
-
- settings: a { name: value } mappping of general settings to set
- options: a { opt_name: opt_value } mapping of options to set
- shared_network: define the subnets as a shared network of the given name
- subnets: an iterable of Subnet's to define
- hosts: an iterable of Host's to define
-
- """
-
- dhcpc.ConfFile.__init__(self, name, path, comment=comment)
-
- # define global settings
- if settings :
- self.add_params(dhcpc.Parameter(setting, value) for setting, value in settings.iteritems())
-
- # define global options
- if options :
- self.add_params(dhcpc.Option(option, value) for option, value in options.iteritems())
-
- # the shared-network section, or a series of subnets
- if shared_network :
- self.add_decl(dhcpc.SharedNetwork(shared_network, decls=subnets))
-
- elif subnets :
- self.add_decls(subnets)
-
- # hosts section
- if hosts :
- self.add_decls(hosts)
-
-class Subnet (dhcpc.Subnet) :
- """
- A subnet declaration with a router, and optionally a dynamic address pool, and allow/deny unknown clients
- """
-
- def __init__ (self, subnet, router_idx=1, range=None, unknown_clients=None, comment=None) :
- """
- @param subnet the addr.IP representing the subnet
- @param router_idx the subnet[index] of the default gateway
- @param range optional (from_idx, to_idx) to define a dhcp pool
- @param unknown_clients optional 'allow'/'deny' to set a policy for unknown clients
- """
-
- # validate
- if unknown_clients :
- assert unknown_clients in ('allow', 'deny')
-
- super(Subnet, self).__init__(subnet, params=[
- dhcpc.Option("routers", subnet[router_idx]),
- dhcpc.Parameter("range", subnet[range[0]], subnet[range[1]]) if range else None,
- dhcpc.Parameter(unknown_clients, "unknown-clients") if unknown_clients else None,
- ], comment=comment)
-
-
-class Host (dhcpc.Host) :
- """
- A host declaration with a hardware address and a IP address
- """
-
- def __init__ (self, hostname, mac_addr, ip_addr, comment=None) :
- super(Host, self).__init__(hostname, params=[
- dhcpc.Parameter("hardware ethernet", mac_addr),
- dhcpc.Parameter("fixed-address", ip_addr)
- ], comment=comment)
-
--- a/pvl/config/dhcp_conf.py Sun Jul 12 02:14:08 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,341 +0,0 @@
-"""
- Configuration file output for the ISC DHCP server
-"""
-
-import conf
-
-import itertools
-
-class Object (conf.ConfObject) :
- """
- Our version of ConfObject
- """
-
- def _fmt_comments (self) :
- """
- Format our comment lines
- """
-
- for comment in self.comments :
- if comment is not None :
- yield "# %s" % (comment, )
-
-
-class Comment (Object) :
- """
- A comment, is, well, a comment :)
- """
-
- def __init__ (self, comment) :
- """
- @param comment the comment string
- """
-
- Object.__init__(self, [comment])
-
- def fmt_lines (self) :
- """
- Yield a single line with the comment
- """
-
- return self._fmt_comments()
-
-class _Section (Object) :
- """
- Base implementation of Section, but doesn't format comments in output (inheriting class can define how that happens)
- """
-
- def __init__ (self, params=None, decls=None, comment=None) :
- """
- If params/decls are given, those are the used as the initial contents of this section
-
- If a comment is given, then it will be formatted before the section's stuff
- """
-
- Object.__init__(self, [comment])
-
- self.params = params or []
- self.decls = decls or []
-
- def add_param (self, param) :
- """
- Add the given Parameter to the end of this section's params
- """
-
- self.params.append(param)
-
- def add_params (self, params) :
- for param in params :
- self.add_param(param)
-
- def add_decl (self, decl) :
- """
- Add the given Declaration to the end of this section's decls
- """
-
- self.decls.append(decl)
-
- def add_decls (self, decls) :
- for decl in decls :
- self.add_decl(decl)
-
- def fmt_lines (self) :
- """
- Format all of our params and decls, in that order
- """
-
- # then output each content line
- for stmt in itertools.chain(self.params, self.decls) :
- # skip Nones
- if stmt is None :
- continue
-
- for line in stmt.fmt_lines() :
- yield line
-
-class Section (_Section) :
- """
- A section holds a list of params and a list of decls, plus some comments at the beginning of the section
- """
-
- def fmt_lines (self) :
- """
- Format all of our comments, and then super
- """
-
- # comments
- for line in self._fmt_comments() :
- yield line
-
- # section stuff
- for line in _Section.fmt_lines(self) :
- yield line
-
-class ConfFile (Section, conf.File) :
- DEFAULT_NAME = "dhcpd.conf"
- DEFAULT_PATH = "/etc/dhcp3/dhcpd.conf"
-
- def __init__ (self, name=DEFAULT_NAME, path=DEFAULT_PATH, params=None, decls=None, comment=None) :
- """
- Initialize the dhcpd config file, but don't open it yet.
- """
-
- conf.File.__init__(self, name, path)
- Section.__init__(self, params, decls, comment)
-
-class Statement (Object) :
- """
- A statement is a single line in the config file
- """
-
- def __init__ (self, name, *args, **kwargs) :
- """
- Arguments given as None will be ignored.
-
- A comment can be given as a keyword argument
- """
-
- if kwargs : assert len(kwargs) == 1 and 'comment' in kwargs
-
- Object.__init__(self, [kwargs.get('comment')])
-
- self.name = name
- self.args = [arg for arg in args if arg is not None]
-
- def _fmt_arg (self, arg) :
- """
- Formats a arg for use in output, the following types are supported:
-
- list/tuple/iter: results in a comma-and-space separated list of formatted values
- unicode: results in an encoded str
- str: results in the string itself, quoted if needed
- other: attempt to convert to a str, and then format that
- """
-
- # format lists specially
- # XXX: iterators?
- if isinstance(arg, (list, tuple)) :
- # recurse as a comma-and-space separated list
- return ', '.join(self._fmt_arg(a) for a in arg)
-
- elif isinstance(arg, Literal) :
- # use what it specifies
- return arg.fmt_arg()
-
- elif isinstance(arg, unicode) :
- # recurse with the str version
- # XXX: what encoding to use?
- return self._fmt_arg(arg.encode('utf8'))
-
- elif isinstance(arg, str) :
- # XXX: quoting
- return arg
-
- else :
- # try and use it as a string
- return self._fmt_arg(str(arg))
-
- def _fmt_data (self) :
- """
- Formats the statement name/params as a single line, ignoring None
- """
-
- return "%s%s" % (self.name, (' ' + ' '.join(self._fmt_arg(a) for a in self.args)) if self.args else '')
-
-class Literal (Statement) :
- """
- A literal is something that goes into the config file as-is, with no formatting or escaping applied.
- """
-
- def __init__ (self, literal) :
- self.literal = literal
-
- def fmt_arg (self) :
- return self.literal
-
- def fmt_lines (self) :
- yield self.literal
-
-class Parameter (Statement) :
- """
- A parameter is a single statement that configures the behaviour of something.
-
- Parameters have a name, and optionally, a number of arguments, and are formatted as statements terminated with
- a semicolon. For convenience, params/decls that are None are ignored.
-
- The parameter will be formatted like this:
- <name> [ <arg> [ ... ] ] ";"
- """
-
- def fmt_lines (self) :
- """
- Yields a single ;-terminated line
- """
-
- # comments
- for line in self._fmt_comments() :
- yield line
-
- # the line
- yield "%s;" % self._fmt_data()
-
-class Declaration (_Section, Statement) :
- """
- A declaration begins like a statement (with name and args), but then contains a curly-braces-delimited block
- that acts like a Section.
-
- <name> [ <args> [ ... ] ] {
- [ <Section> ]
- }
-
- """
-
- def __init__ (self, name, args=[], params=None, decls=None, comment=None) :
- """
- The name/args will be formatted as in Statement, but params should be an iterable of Parameters, and decls
- an iterable of Declarations.
- """
-
- # init the statement bit
- _Section.__init__(self, params, decls)
- Statement.__init__(self, name, *args, **dict(comment=comment))
-
- def fmt_lines (self) :
- """
- Yields a header line, a series of indented body lines, and the footer line
- """
-
- # comments
- for line in self._fmt_comments() :
- yield line
-
- # the header to open the block
- yield "%s {" % self._fmt_data()
-
- # then output the section stuff, indented
- for line in _Section.fmt_lines(self) :
- yield "\t%s" % line
-
- # and then close the block
- yield "}"
-
-class SharedNetwork (Declaration) :
- """
- A shared-network declaration is used to define a set of subnets that share the same physical network,
- optionally with some shared params.
-
- shared-network <name> {
- [ parameters ]
- [ declarations ]
- }
- """
-
- def __init__ (self, name, *args, **kwargs) :
- """
- @param name the name of the shared-subnet
- """
-
- super(SharedNetwork, self).__init__("shared-network", [name], *args, **kwargs)
-
-class Subnet (Declaration) :
- """
- A subnet is used to provide the information about a subnet required to identify whether or not an IP address is
- on that subnet, and may also be used to specify parameters/declarations for that subnet.
-
- subnet <subnet-number> netmask <netmask> {
- [ parameters ]
- [ declarations ]
- }
- """
-
- def __init__ (self, network, *args, **kwargs) :
- """
- @param network the addr.Network for the subnet
- """
-
- super(Subnet, self).__init__("subnet", [network.net(), "netmask", network.netmask()], *args, **kwargs)
-
-class Group (Declaration) :
- """
- A group is simply used to apply a set of parameters to a set of declarations.
-
- group {
- [ parameters ]
- [ declarations ]
- }
- """
-
- def __init__ (self, *args, **kwargs) :
- super(Group, self).__init__("group", [], *args, **kwargs)
-
-
-class Host (Declaration) :
- """
- A host is used to match a request against specific host, and then apply settings for that host.
-
- The "hostname" is the DHCP name to identify the host.
-
- If no dhcp-client-identifier option is specified in the parameters, then the host is matched using the
- "hardware" parameter.
-
- host <hostname> {
- [ parameters ]
- [ declarations ]
- }
- """
-
- def __init__ (self, hostname, *args, **kwargs) :
- super(Host, self).__init__("host", [hostname], *args, **kwargs)
-
-class Option (Parameter) :
- """
- A generic 'option' parameter for a dhcpd.conf file
- """
-
- def __init__ (self, name, *args, **kwargs) :
- """
- Formatted as a Satement with a name of "option <name>".
- """
-
- super(Option, self).__init__("option %s" % name, *args, **kwargs)
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/config/dhcpd/conf.py Sun Jul 12 02:14:34 2009 +0300
@@ -0,0 +1,341 @@
+"""
+ Configuration file output for the ISC DHCP server
+"""
+
+import conf
+
+import itertools
+
+class Object (conf.ConfObject) :
+ """
+ Our version of ConfObject
+ """
+
+ def _fmt_comments (self) :
+ """
+ Format our comment lines
+ """
+
+ for comment in self.comments :
+ if comment is not None :
+ yield "# %s" % (comment, )
+
+
+class Comment (Object) :
+ """
+ A comment, is, well, a comment :)
+ """
+
+ def __init__ (self, comment) :
+ """
+ @param comment the comment string
+ """
+
+ Object.__init__(self, [comment])
+
+ def fmt_lines (self) :
+ """
+ Yield a single line with the comment
+ """
+
+ return self._fmt_comments()
+
+class _Section (Object) :
+ """
+ Base implementation of Section, but doesn't format comments in output (inheriting class can define how that happens)
+ """
+
+ def __init__ (self, params=None, decls=None, comment=None) :
+ """
+ If params/decls are given, those are the used as the initial contents of this section
+
+ If a comment is given, then it will be formatted before the section's stuff
+ """
+
+ Object.__init__(self, [comment])
+
+ self.params = params or []
+ self.decls = decls or []
+
+ def add_param (self, param) :
+ """
+ Add the given Parameter to the end of this section's params
+ """
+
+ self.params.append(param)
+
+ def add_params (self, params) :
+ for param in params :
+ self.add_param(param)
+
+ def add_decl (self, decl) :
+ """
+ Add the given Declaration to the end of this section's decls
+ """
+
+ self.decls.append(decl)
+
+ def add_decls (self, decls) :
+ for decl in decls :
+ self.add_decl(decl)
+
+ def fmt_lines (self) :
+ """
+ Format all of our params and decls, in that order
+ """
+
+ # then output each content line
+ for stmt in itertools.chain(self.params, self.decls) :
+ # skip Nones
+ if stmt is None :
+ continue
+
+ for line in stmt.fmt_lines() :
+ yield line
+
+class Section (_Section) :
+ """
+ A section holds a list of params and a list of decls, plus some comments at the beginning of the section
+ """
+
+ def fmt_lines (self) :
+ """
+ Format all of our comments, and then super
+ """
+
+ # comments
+ for line in self._fmt_comments() :
+ yield line
+
+ # section stuff
+ for line in _Section.fmt_lines(self) :
+ yield line
+
+class ConfFile (Section, conf.File) :
+ DEFAULT_NAME = "dhcpd.conf"
+ DEFAULT_PATH = "/etc/dhcp3/dhcpd.conf"
+
+ def __init__ (self, name=DEFAULT_NAME, path=DEFAULT_PATH, params=None, decls=None, comment=None) :
+ """
+ Initialize the dhcpd config file, but don't open it yet.
+ """
+
+ conf.File.__init__(self, name, path)
+ Section.__init__(self, params, decls, comment)
+
+class Statement (Object) :
+ """
+ A statement is a single line in the config file
+ """
+
+ def __init__ (self, name, *args, **kwargs) :
+ """
+ Arguments given as None will be ignored.
+
+ A comment can be given as a keyword argument
+ """
+
+ if kwargs : assert len(kwargs) == 1 and 'comment' in kwargs
+
+ Object.__init__(self, [kwargs.get('comment')])
+
+ self.name = name
+ self.args = [arg for arg in args if arg is not None]
+
+ def _fmt_arg (self, arg) :
+ """
+ Formats a arg for use in output, the following types are supported:
+
+ list/tuple/iter: results in a comma-and-space separated list of formatted values
+ unicode: results in an encoded str
+ str: results in the string itself, quoted if needed
+ other: attempt to convert to a str, and then format that
+ """
+
+ # format lists specially
+ # XXX: iterators?
+ if isinstance(arg, (list, tuple)) :
+ # recurse as a comma-and-space separated list
+ return ', '.join(self._fmt_arg(a) for a in arg)
+
+ elif isinstance(arg, Literal) :
+ # use what it specifies
+ return arg.fmt_arg()
+
+ elif isinstance(arg, unicode) :
+ # recurse with the str version
+ # XXX: what encoding to use?
+ return self._fmt_arg(arg.encode('utf8'))
+
+ elif isinstance(arg, str) :
+ # XXX: quoting
+ return arg
+
+ else :
+ # try and use it as a string
+ return self._fmt_arg(str(arg))
+
+ def _fmt_data (self) :
+ """
+ Formats the statement name/params as a single line, ignoring None
+ """
+
+ return "%s%s" % (self.name, (' ' + ' '.join(self._fmt_arg(a) for a in self.args)) if self.args else '')
+
+class Literal (Statement) :
+ """
+ A literal is something that goes into the config file as-is, with no formatting or escaping applied.
+ """
+
+ def __init__ (self, literal) :
+ self.literal = literal
+
+ def fmt_arg (self) :
+ return self.literal
+
+ def fmt_lines (self) :
+ yield self.literal
+
+class Parameter (Statement) :
+ """
+ A parameter is a single statement that configures the behaviour of something.
+
+ Parameters have a name, and optionally, a number of arguments, and are formatted as statements terminated with
+ a semicolon. For convenience, params/decls that are None are ignored.
+
+ The parameter will be formatted like this:
+ <name> [ <arg> [ ... ] ] ";"
+ """
+
+ def fmt_lines (self) :
+ """
+ Yields a single ;-terminated line
+ """
+
+ # comments
+ for line in self._fmt_comments() :
+ yield line
+
+ # the line
+ yield "%s;" % self._fmt_data()
+
+class Declaration (_Section, Statement) :
+ """
+ A declaration begins like a statement (with name and args), but then contains a curly-braces-delimited block
+ that acts like a Section.
+
+ <name> [ <args> [ ... ] ] {
+ [ <Section> ]
+ }
+
+ """
+
+ def __init__ (self, name, args=[], params=None, decls=None, comment=None) :
+ """
+ The name/args will be formatted as in Statement, but params should be an iterable of Parameters, and decls
+ an iterable of Declarations.
+ """
+
+ # init the statement bit
+ _Section.__init__(self, params, decls)
+ Statement.__init__(self, name, *args, **dict(comment=comment))
+
+ def fmt_lines (self) :
+ """
+ Yields a header line, a series of indented body lines, and the footer line
+ """
+
+ # comments
+ for line in self._fmt_comments() :
+ yield line
+
+ # the header to open the block
+ yield "%s {" % self._fmt_data()
+
+ # then output the section stuff, indented
+ for line in _Section.fmt_lines(self) :
+ yield "\t%s" % line
+
+ # and then close the block
+ yield "}"
+
+class SharedNetwork (Declaration) :
+ """
+ A shared-network declaration is used to define a set of subnets that share the same physical network,
+ optionally with some shared params.
+
+ shared-network <name> {
+ [ parameters ]
+ [ declarations ]
+ }
+ """
+
+ def __init__ (self, name, *args, **kwargs) :
+ """
+ @param name the name of the shared-subnet
+ """
+
+ super(SharedNetwork, self).__init__("shared-network", [name], *args, **kwargs)
+
+class Subnet (Declaration) :
+ """
+ A subnet is used to provide the information about a subnet required to identify whether or not an IP address is
+ on that subnet, and may also be used to specify parameters/declarations for that subnet.
+
+ subnet <subnet-number> netmask <netmask> {
+ [ parameters ]
+ [ declarations ]
+ }
+ """
+
+ def __init__ (self, network, *args, **kwargs) :
+ """
+ @param network the addr.Network for the subnet
+ """
+
+ super(Subnet, self).__init__("subnet", [network.net(), "netmask", network.netmask()], *args, **kwargs)
+
+class Group (Declaration) :
+ """
+ A group is simply used to apply a set of parameters to a set of declarations.
+
+ group {
+ [ parameters ]
+ [ declarations ]
+ }
+ """
+
+ def __init__ (self, *args, **kwargs) :
+ super(Group, self).__init__("group", [], *args, **kwargs)
+
+
+class Host (Declaration) :
+ """
+ A host is used to match a request against specific host, and then apply settings for that host.
+
+ The "hostname" is the DHCP name to identify the host.
+
+ If no dhcp-client-identifier option is specified in the parameters, then the host is matched using the
+ "hardware" parameter.
+
+ host <hostname> {
+ [ parameters ]
+ [ declarations ]
+ }
+ """
+
+ def __init__ (self, hostname, *args, **kwargs) :
+ super(Host, self).__init__("host", [hostname], *args, **kwargs)
+
+class Option (Parameter) :
+ """
+ A generic 'option' parameter for a dhcpd.conf file
+ """
+
+ def __init__ (self, name, *args, **kwargs) :
+ """
+ Formatted as a Satement with a name of "option <name>".
+ """
+
+ super(Option, self).__init__("option %s" % name, *args, **kwargs)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/config/dhcpd/model.py Sun Jul 12 02:14:34 2009 +0300
@@ -0,0 +1,81 @@
+"""
+ Higher-level DHCP config structure model
+"""
+
+import dhcp_conf as dhcpc
+
+class Config (dhcpc.ConfFile) :
+ """
+ A full configuration file
+ """
+
+ def __init__ (self, name=dhcpc.ConfFile.DEFAULT_NAME, path=dhcpc.ConfFile.DEFAULT_PATH,
+ settings=None, options=None, shared_network=False, subnets=None, hosts=None, comment=None
+ ) :
+ """
+ Create a full configuration file for the given settings:
+
+ settings: a { name: value } mappping of general settings to set
+ options: a { opt_name: opt_value } mapping of options to set
+ shared_network: define the subnets as a shared network of the given name
+ subnets: an iterable of Subnet's to define
+ hosts: an iterable of Host's to define
+
+ """
+
+ dhcpc.ConfFile.__init__(self, name, path, comment=comment)
+
+ # define global settings
+ if settings :
+ self.add_params(dhcpc.Parameter(setting, value) for setting, value in settings.iteritems())
+
+ # define global options
+ if options :
+ self.add_params(dhcpc.Option(option, value) for option, value in options.iteritems())
+
+ # the shared-network section, or a series of subnets
+ if shared_network :
+ self.add_decl(dhcpc.SharedNetwork(shared_network, decls=subnets))
+
+ elif subnets :
+ self.add_decls(subnets)
+
+ # hosts section
+ if hosts :
+ self.add_decls(hosts)
+
+class Subnet (dhcpc.Subnet) :
+ """
+ A subnet declaration with a router, and optionally a dynamic address pool, and allow/deny unknown clients
+ """
+
+ def __init__ (self, subnet, router_idx=1, range=None, unknown_clients=None, comment=None) :
+ """
+ @param subnet the addr.IP representing the subnet
+ @param router_idx the subnet[index] of the default gateway
+ @param range optional (from_idx, to_idx) to define a dhcp pool
+ @param unknown_clients optional 'allow'/'deny' to set a policy for unknown clients
+ """
+
+ # validate
+ if unknown_clients :
+ assert unknown_clients in ('allow', 'deny')
+
+ super(Subnet, self).__init__(subnet, params=[
+ dhcpc.Option("routers", subnet[router_idx]),
+ dhcpc.Parameter("range", subnet[range[0]], subnet[range[1]]) if range else None,
+ dhcpc.Parameter(unknown_clients, "unknown-clients") if unknown_clients else None,
+ ], comment=comment)
+
+
+class Host (dhcpc.Host) :
+ """
+ A host declaration with a hardware address and a IP address
+ """
+
+ def __init__ (self, hostname, mac_addr, ip_addr, comment=None) :
+ super(Host, self).__init__(hostname, params=[
+ dhcpc.Parameter("hardware ethernet", mac_addr),
+ dhcpc.Parameter("fixed-address", ip_addr)
+ ], comment=comment)
+
--- a/test/bind.py Sun Jul 12 02:14:08 2009 +0300
+++ b/test/bind.py Sun Jul 12 02:14:34 2009 +0300
@@ -3,8 +3,9 @@
Test bind_conf
"""
-import bind_conf as bindc
-import test_conf, addr
+from pvl.config.bind import zone
+from pvl.config import addr
+from . import conf
import unittest