pvl.dhcp.hosts: implement DHCPHostsDatabase.select(), and DHCPHostRule
authorTero Marttila <terom@paivola.fi>
Sun, 10 Feb 2013 18:31:38 +0200
changeset 210 544a6a58ed28
parent 209 cf883e2e1bff
child 211 cf74bbb95d2b
pvl.dhcp.hosts: implement DHCPHostsDatabase.select(), and DHCPHostRule
pvl/dhcp/hosts.py
--- a/pvl/dhcp/hosts.py	Sun Feb 10 16:52:51 2013 +0200
+++ b/pvl/dhcp/hosts.py	Sun Feb 10 18:31:38 2013 +0200
@@ -23,6 +23,19 @@
         log.info("Creating database tables: dhcp_hosts")
         db.dhcp_hosts.create(self.db.engine, checkfirst=True)
 
+    def select (self, distinct=(db.dhcp_hosts.c.gw, db.dhcp_hosts.c.ip), interval=None) :
+        """
+            SELECT unique gw/ip hosts, for given interval.
+        """
+
+        query = db.select(distinct, distinct=True)
+
+        if interval :
+            # timedelta
+            query = query.where(db.func.now() - db.dhcp_hosts.c.last_seen < interval)
+
+        return self.db.select(query)
+
     def insert (self, attrs) :
         """
             INSERT new host
@@ -119,4 +132,122 @@
             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 ''
+
+