--- a/pvl/dhcp/hosts.py Sun Feb 10 18:31:38 2013 +0200
+++ b/pvl/dhcp/hosts.py Sun Feb 10 19:08:53 2013 +0200
@@ -132,122 +132,3 @@
log.info("Insert: %s", attrs)
self.insert(attrs)
-import configobj
-from pvl.verkko.utils import IPv4Address, IPv4Network
-
-class DHCPHostRule (object) :
- """
- A rule matching DHCP hosts.
- """
-
- @classmethod
- def load (cls, file, name=None) :
- """
- Load from config file.
- """
-
- config = configobj.ConfigObj(file)
-
- return cls.load_section(name, config)
-
- @classmethod
- def load_section (cls, name, section) :
- """
- Rule from sub-sections and section.
- """
-
- # recurse
- rules = tuple(cls.load_section(subsection, section[subsection]) for subsection in section.sections)
-
- # rule
- attrs = dict((name, section[name]) for name in section.scalars)
-
- try :
- return cls.config(name, rules, **attrs)
-
- except ValueError as ex :
- raise ValueError("[%s] %s" % (name, ex))
-
- @classmethod
- def config (cls, name, rules, gateway=None, network=None, interval=None) :
- """
- Rule from section.
- """
-
- if interval :
- log.warn("%s: interval: not implemented", name)
-
- if network :
- network = IPv4Network(network)
- else :
- network = None
-
- return cls(name, rules,
- gateway = gateway,
- network = network,
- )
-
- @classmethod
- def rule (cls, name, gateway=None, network=None) :
- if network :
- network = IPv4Network(network)
- else :
- network = None
-
- return cls(name, (),
- gateway = gateway,
- network = network,
- )
-
- def __init__ (self, name, rules, gateway=None, network=None) :
- """
- Match as name by gateway/network.
- """
-
- self.name = name
- self.rules = rules
-
- self.gateway = gateway
- self.network = network
-
- log.info("%s: gateway=%s, network=%s: %s", name, gateway, network, ' '.join(str(rule) for rule in rules))
-
- def match (self, host) :
- """
- Match against given host.
- """
-
- if self.gateway and host.gw != self.gateway :
- return False
-
- if self.network and IPv4Address(host.ip) not in self.network :
- return False
-
- return True
-
- def apply (self, host) :
- """
- Match host against our rule and any sub rules, returning matching DHCPHostRule, or None.
- """
-
- match = self.match(host)
-
- log.debug("%s: match: %s: %s", self, host, match)
-
- if not match :
- return
-
- for rule in self.rules :
- apply = rule.apply(host)
-
- if apply :
- return apply
-
- log.debug("%s: apply: %s", self, host)
-
- return self
-
- def __str__ (self) :
- return self.name or ''
-
-
--- a/pvl/dhcp/leases.py Sun Feb 10 18:31:38 2013 +0200
+++ b/pvl/dhcp/leases.py Sun Feb 10 19:08:53 2013 +0200
@@ -8,6 +8,8 @@
import logging; log = logging.getLogger('pvl.dhcp.leases')
+DHCPD_LEASES = '/var/lib/dhcp/dhcpd.leases'
+
class DHCPLeasesParser (object) :
"""
Simplistic parser for a dhcpd.leases file.
@@ -218,7 +220,7 @@
lease_date_fmt = LEASE_DATE_FMT_DEFAULT
- def __init__ (self, path) :
+ def __init__ (self, path=DHCPD_LEASES) :
"""
path - path to dhcpd.leases file
"""
@@ -416,7 +418,7 @@
Iterate over all leases.
"""
- return self.leases.itervalues()
+ return self._leases.itervalues()
# utils
def lease_state (self, lease) :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/dhcp/rule.py Sun Feb 10 19:08:53 2013 +0200
@@ -0,0 +1,202 @@
+"""
+ Match DHCP clients by network.
+"""
+
+import optparse
+import configobj
+
+from pvl.verkko.utils import IPv4Address, IPv4Network
+
+import logging; log = logging.getLogger('pvl.dhcp.rule')
+
+def parser (parser) :
+ """
+ Optparse options for DHCPRule
+ """
+
+ parser.set_defaults(
+ dhcp_network = [],
+ dhcp_gateway = [],
+ )
+
+ parser = optparse.OptionGroup(parser, "DHCP host/lease matching")
+
+ parser.add_option('--dhcp-rules', metavar='CONF',
+ help="dhcp plugin instances by network/gateway")
+
+ parser.add_option('--dhcp-network', metavar='NET', action='append',
+ help="dhcp plugin instance by network")
+
+ parser.add_option('--dhcp-gateway', metavar='NET', action='append',
+ help="dhcp plugin instance by gateway")
+
+ return parser
+
+def apply_rules (options) :
+ """
+ Yield DHCPRules from options.
+ """
+
+ for gateway in options.dhcp_gateway :
+ yield DHCPRule.rule(gateway, gateway=gateway)
+
+ for network in options.dhcp_network :
+ yield DHCPRule.rule(network, network=network)
+
+ if options.dhcp_rules :
+ yield DHCPRule.load(open(options.dhcp_rules))
+
+def apply (options) :
+ """
+ Return DHCPRule from options.
+ """
+
+ return DHCPRule(None, tuple(apply_rules(options)))
+
+class DHCPRule (object) :
+ """
+ A rule matching DHCP hosts/leases by gateway/network.
+
+ Note that leases don't have a gateway, but they're assumed to be valid, so we just ignore any gateway criteria for for unknown gateways.
+ """
+
+
+ @classmethod
+ def load (cls, file, name=None) :
+ """
+ Load from config file.
+ """
+
+ config = configobj.ConfigObj(file)
+
+ return cls.load_section(name, config)
+
+ @classmethod
+ def load_section (cls, name, section) :
+ """
+ Rule from sub-sections and section.
+ """
+
+ # recurse
+ rules = tuple(cls.load_section(subsection, section[subsection]) for subsection in section.sections)
+
+ # rule
+ attrs = dict((name, section[name]) for name in section.scalars)
+
+ try :
+ return cls.config(name, rules, **attrs)
+
+ except ValueError as ex :
+ raise ValueError("[%s] %s" % (name, ex))
+
+ @classmethod
+ def config (cls, name, rules, gateway=None, network=None, interval=None) :
+ """
+ Rule from section.
+ """
+
+ if interval :
+ log.warn("%s: interval: not implemented", name)
+
+ if network :
+ network = IPv4Network(network)
+ else :
+ network = None
+
+ return cls(name, rules,
+ gateway = gateway,
+ network = network,
+ )
+
+ @classmethod
+ def rule (cls, name, gateway=None, network=None) :
+ if network :
+ network = IPv4Network(network)
+ else :
+ network = None
+
+ return cls(name, (),
+ gateway = gateway,
+ network = network,
+ )
+
+ def __init__ (self, name, rules, gateway=None, network=None) :
+ """
+ Match as name by gateway/network.
+ """
+
+ self.name = name
+ self.rules = rules
+
+ self.gateway = gateway
+ self.network = network
+
+ log.info("%s: gateway=%s, network=%s: %s", name, gateway, network, ' '.join(str(rule) for rule in rules))
+
+ def match (self, host) :
+ """
+ Match against given host.
+ """
+
+ if self.gateway :
+ gateway = host.get('gw')
+
+ if gateway and gateway != self.gateway :
+ gateway = False
+
+ else :
+ gateway = None
+
+ if self.network :
+ network = host.get('ip')
+
+ if network and network != self.network :
+ network = False
+ else :
+ network = None
+
+ # decide
+ if network is False or gateway is False :
+ # negative
+ return False
+
+ elif network or gateway :
+ # (partial) positive
+ return True
+
+ else :
+ # unknown
+ return None
+
+ def apply (self, host) :
+ """
+ Match { gw: str, ip: IPv4Address } against our rule and any sub rules, returning matching DHCPHostRule, or None.
+ """
+
+ match = self.match(host)
+
+ log.debug("%s: match: %s: %s", self, host, match)
+
+ if match is False :
+ # negative
+ return
+
+ for rule in self.rules :
+ apply = rule.apply(host)
+
+ if apply :
+ return apply
+
+ log.debug("%s: apply: %s", self, host)
+
+ if match :
+ # positive
+ return self
+ else :
+ # unknown
+ return None
+
+ def __str__ (self) :
+ return self.name or ''
+
+