terom@0: """ terom@0: Configuration file output for the ISC DHCP server terom@0: """ terom@0: terom@0: import conf terom@0: terom@0: import itertools terom@0: terom@0: class ConfDHCP (conf.File) : terom@0: def __init__ (self, name="dhcpd.conf", path="/etc/dhcp3/dhcpd.conf") : terom@0: """ terom@0: Initialize the dhcpd config file, but don't open it yet terom@0: terom@0: @see conf.ConfFile.__init__ terom@0: """ terom@0: terom@0: super(ConfDHCP, self).__init__(name, path) terom@0: terom@0: class Statement (conf.Object) : terom@0: """ terom@0: A statement is a single line in the config file terom@0: """ terom@0: terom@0: def __init__ (self, name, *args) : terom@0: """ terom@0: The statement will be formatted like this: terom@0: [ [ ... ] ] ";" terom@0: """ terom@0: terom@0: self.name = name terom@0: self.args = args terom@0: terom@0: def _fmt_arg (self, arg) : terom@0: """ terom@0: Formats a arg for use in output, the following types are supported: terom@0: terom@0: list/tuple/iter: results in a comma-and-space separated list of formatted values terom@0: unicode: results in an encoded str terom@0: str: results in the string itself, quoted if needed terom@0: other: attempt to convert to a str, and then format that terom@0: """ terom@0: terom@0: # format lists specially terom@0: # XXX: iterators? terom@0: if isinstance(arg, (list, tuple)) : terom@0: # recurse as a comma-and-space separated list terom@0: return ', '.join(self._fmt_arg(a) for a in arg) terom@0: terom@0: elif isinstance(arg, Literal) : terom@0: # use what it specifies terom@0: return arg.fmt_arg() terom@0: terom@0: elif isinstance(arg, unicode) : terom@0: # recurse with the str version terom@0: # XXX: what encoding to use? terom@0: return self._fmt_arg(arg.encode('utf8')) terom@0: terom@0: elif isinstance(arg, str) : terom@0: # XXX: quoting terom@0: return arg terom@0: terom@0: else : terom@0: # try and use it as a string terom@0: return self._fmt_arg(str(arg)) terom@0: terom@0: def _fmt_data (self) : terom@0: """ terom@0: Formats the statement name/params as a single line terom@0: """ terom@0: terom@0: return "%s%s" % (self.name, (' ' + ' '.join(self._fmt_arg(a) for a in self.args)) if self.args else '') terom@0: terom@0: class Literal (Statement) : terom@0: """ terom@0: A literal is something that goes into the config file as-is, with no formatting or escaping applied. terom@0: """ terom@0: terom@0: def __init__ (self, literal) : terom@0: self.literal = literal terom@0: terom@0: def fmt_arg (self) : terom@0: return self.literal terom@0: terom@0: def fmt_lines (self) : terom@0: yield self.literal terom@0: terom@0: class Parameter (Statement) : terom@0: """ terom@0: A parameter is a single statement that configures the behaviour of something. terom@0: terom@0: Parameters have a name, and optionally, a number of arguments, and are formatted as statements terminated with terom@0: a semicolon. terom@0: """ terom@0: terom@0: def fmt_lines (self) : terom@0: """ terom@0: Yields a single ;-terminated line terom@0: """ terom@0: terom@0: yield "%s;" % self._fmt_data() terom@0: terom@0: class Declaration (Statement) : terom@0: """ terom@0: A declaration begins like a statement (with name and args), but then contains a block of any number of terom@0: parameters followed by any number of nested declarations. terom@0: terom@0: [ [ ... ] ] { terom@0: [ ] terom@0: [ ] terom@0: } terom@0: terom@0: """ terom@0: terom@0: def __init__ (self, name, args=[], params=[], decls=[]) : terom@0: """ terom@0: The name/args will be formatted as in Statement, but params should be an iterable of Parameters, and decls terom@0: an iterable of Declarations. terom@0: """ terom@0: terom@0: # init the statement bit terom@0: Statement.__init__(self, name, *args) terom@0: terom@0: # store the iterables terom@0: self.params = params terom@0: self.decls = decls terom@0: terom@0: def fmt_lines (self) : terom@0: """ terom@0: Yields a header line, a series of indented body lines, and the footer line terom@0: """ terom@0: terom@0: # the header to open the block terom@0: yield "%s {" % self._fmt_data() terom@0: terom@0: # then output each content line terom@0: for stmt in itertools.chain(self.params, self.decls) : terom@0: # ..indented terom@0: for line in stmt.fmt_lines() : terom@0: yield "\t%s" % line terom@0: terom@0: # and then close the block terom@0: yield "}" terom@0: terom@0: class SharedNetwork (Declaration) : terom@0: """ terom@0: A shared-network declaration is used to define a set of subnets that share the same physical network, terom@0: optionally with some shared params. terom@0: terom@0: shared-network { terom@0: [ parameters ] terom@0: [ declarations ] terom@0: } terom@0: """ terom@0: terom@0: def __init__ (self, name, params=[], decls=[]) : terom@0: """ terom@0: @param name the name of the shared-subnet terom@0: @param params optional parameters terom@0: @param decls the iterable of subnets or other declarations in the shared network terom@0: """ terom@0: terom@0: super(SharedNetwork, self).__init__("shared-network", [name], params, decls) terom@0: terom@0: class Subnet (Declaration) : terom@0: """ terom@0: A subnet is used to provide the information about a subnet required to identify whether or not an IP address is terom@0: on that subnet, and may also be used to specify parameters/declarations for that subnet. terom@0: terom@0: subnet netmask { terom@0: [ parameters ] terom@0: [ declarations ] terom@0: } terom@0: """ terom@0: terom@0: def __init__ (self, network, params=[], decls=[]) : terom@0: """ terom@0: @param network the addr.Network for the subnet terom@0: @param params optional parameters terom@0: @param decls optional decls, e.g. subnets terom@0: """ terom@0: terom@0: super(Subnet, self).__init__("subnet", [network.net(), "netmask", network.netmask()], params, decls) terom@0: terom@0: class Group (Declaration) : terom@0: """ terom@0: A group is simply used to apply a set of parameters to a set of declarations. terom@0: terom@0: group { terom@0: [ parameters ] terom@0: [ declarations ] terom@0: } terom@0: """ terom@0: terom@0: def __init__ (self, params=[], decls=[]) : terom@0: super(Group, self).__init__("group", [], params, decls) terom@0: terom@0: terom@0: class Host (Declaration) : terom@0: """ terom@0: A host is used to match a request against specific host, and then apply settings for that host. terom@0: terom@0: The "hostname" is the DHCP name to identify the host. terom@0: terom@0: If no dhcp-client-identifier option is specified in the parameters, then the host is matched using the terom@0: "hardware" parameter. terom@0: terom@0: host { terom@0: [ parameters ] terom@0: [ declarations ] terom@0: } terom@0: """ terom@0: terom@0: def __init__ (self, hostname, params=[], decls=[]) : terom@0: super(Host, self).__init__("host", [hostname], params, decls) terom@0: terom@0: class Option (Parameter) : terom@0: """ terom@0: A generic 'option' parameter for a dhcpd.conf file terom@0: """ terom@0: terom@0: def __init__ (self, name, *args) : terom@0: """ terom@0: Formatted as a Satement with a name of "option ". terom@0: """ terom@0: terom@0: super(Option, self).__init__("option %s" % name, *args) terom@0: