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@1: class Section (conf.ConfObject) : terom@1: """ terom@1: A section holds a list of params and a list of decls terom@1: """ terom@1: terom@1: def __init__ (self, params=None, decls=None) : terom@0: """ terom@1: If params/decls are given, those are the used as the initial contents of this section terom@1: """ terom@0: terom@1: self.params = params or [] terom@1: self.decls = decls or [] terom@1: terom@1: def add_param (self, param) : terom@1: """ terom@1: Add the given Parameter to the end of this section's params terom@1: """ terom@1: terom@1: self.params.append(param) terom@1: terom@1: def add_params (self, params) : terom@1: for param in params : terom@1: self.add_param(param) terom@1: terom@1: def add_decl (self, decl) : terom@1: """ terom@1: Add the given Declaration to the end of this section's decls terom@1: """ terom@1: terom@1: self.decls.append(decl) terom@1: terom@1: def add_decls (self, decls) : terom@1: for decl in decls : terom@1: self.add_decl(decl) terom@1: terom@1: def fmt_lines (self) : terom@1: """ terom@1: Format all of our params and decls, in that order terom@1: """ terom@1: terom@1: # then output each content line terom@1: for stmt in itertools.chain(self.params, self.decls) : terom@1: # skip Nones terom@1: if stmt is None : terom@1: continue terom@1: terom@1: for line in stmt.fmt_lines() : terom@1: yield line terom@1: terom@1: class ConfFile (Section, conf.File) : terom@1: DEFAULT_NAME = "dhcpd.conf" terom@1: DEFAULT_PATH = "/etc/dhcp3/dhcpd.conf" terom@1: terom@1: def __init__ (self, name=DEFAULT_NAME, path=DEFAULT_PATH, params=None, decls=None) : terom@1: """ terom@1: Initialize the dhcpd config file, but don't open it yet. terom@0: """ terom@0: terom@1: conf.File.__init__(self, name, path) terom@1: Section.__init__(self, params, decls) terom@0: terom@1: class Statement (conf.ConfObject) : terom@0: """ terom@1: 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@1: terom@1: Arguments given as None will be ignored. 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@1: Formats the statement name/params as a single line, ignoring None terom@0: """ terom@0: terom@1: return "%s%s" % (self.name, (' ' + ' '.join(self._fmt_arg(a) for a in self.args if a is not None)) 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@1: a semicolon. For convenience, params/decls that are None are ignored. 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@1: class Declaration (Section, Statement) : terom@0: """ terom@1: A declaration begins like a statement (with name and args), but then contains a curly-braces-delimited block terom@1: that acts like a Section. terom@0: terom@0: [ [ ... ] ] { terom@1: [
] terom@0: } terom@0: terom@0: """ terom@0: terom@1: def __init__ (self, name, args=[], params=None, decls=None) : 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@1: Section.__init__(self, params, decls) terom@0: Statement.__init__(self, name, *args) 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@1: # then output the section stuff, indented terom@1: for line in Section.fmt_lines(self) : terom@1: yield "\t%s" % line terom@1: 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@1: def __init__ (self, network, params=None, decls=None) : 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@1: def __init__ (self, params=None, decls=None) : 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@1: def __init__ (self, hostname, params=None, decls=None) : 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: