diff -r 5fc4c5e83b72 -r 6f339a8a87dc pvl/dhcp/leases.py --- a/pvl/dhcp/leases.py Sat Jan 26 11:48:02 2013 +0200 +++ b/pvl/dhcp/leases.py Sat Jan 26 11:49:16 2013 +0200 @@ -205,9 +205,9 @@ for item in self.parse(line) : yield item -class DHCPLeasesDatabase (object) : +class DHCPLeases (object) : """ - Process log-structured leases file. + Process log-structured leases file, updated by dhcpd. """ LEASE_DATES = ('starts', 'ends', 'tstp', 'tsfp', 'atsfp', 'cltt') @@ -224,20 +224,20 @@ """ # tail; handles file re-writes - self.source = pvl.syslog.tail.TailFile(path) + self.source = pvl.syslog.tail.Tail(path) # parser state self.parser = DHCPLeasesParser() # initial leases state - self.leases = None + self._leases = None def reset (self) : """ Reset state, if we started to read a new file. """ - self.leases = {} + self._leases = {} def process_lease_item_date (self, args) : """ @@ -296,7 +296,7 @@ """ # replace any existing - lease = self.leases[lease_name] = {} + lease = self._leases[lease_name] = {} # meta lease['lease'] = lease_name @@ -354,8 +354,8 @@ log.debug("lease: %s: %s", lease, items) - if lease in self.leases : - old = self.leases[lease] + if lease in self._leases : + old = self._leases[lease] else : old = None @@ -369,22 +369,18 @@ else : log.warn("unknown block: %s: %s", type, args) - def process (self) : + def readleases (self) : """ Read new lines from the leases database and update our state. - - XXX: Returns - (sync, leases) - whereby sync is normally False, and leases the set of (possibly) changed leases, unless during initial - startup and on database replacement, when the sync is True, and the entire set of valid leases is returned. + Yields changed leases. On startup and on periodic database reset, all leases are yielded. """ # handle file replace by reading until EOF sync = False # leases = [] - if self.leases is None : + if self._leases is None : # initial sync self.reset() sync = True @@ -413,7 +409,9 @@ # else : # return False, leases - def __iter__ (self) : + __iter__ = readleases + + def leases (self) : """ Iterate over all leases. """ @@ -440,6 +438,105 @@ return state +# XXX: from db.dhcp_leases instead? +import pvl.verkko.db as db + +class DHCPLeasesDatabase (object) : + """ + pvl.verkko.Database dhcp_leases model for updates. + """ + + def __init__ (self, db) : + """ + db - pvl.verkko.Database + """ + + self.db = db + + def create (self) : + """ + CREATE TABLEs + """ + + log.info("Creating database tables: dhcp_leases") + db.dhcp_leases.create(self.db.engine, checkfirst=True) + + def update (self, lease) : + """ + Try an extend an existing lease? + """ + + c = db.dhcp_leases.c + + ip = lease['lease'] + mac = lease.get('hwaddr') + starts = lease['starts'] + ends = lease.get('ends') + + update = db.dhcp_leases.update() + + # XXX: if ends is None? + if mac : + # renew lease..? + update = update.where((c.ip == ip) & (c.mac == mac) & ((starts < c.ends) | (c.ends == None))) + else : + # new state for lease..? + update = update.where((c.ip == ip) & ((starts < c.ends) | (c.ends == ends))) + + update = update.values( + state = lease['binding-state'], + next = lease.get('next-binding-state'), + ends = ends, + ) + + if lease.get('client-hostname') : + update = update.values(hostname = lease['client-hostname']) + + return self.db.update(update) > 0 + + def insert (self, lease) : + """ + Record a new lease. + """ + + c = db.dhcp_leases.c + + query = db.dhcp_leases.insert().values( + ip = lease['lease'], + mac = lease['hwaddr'], + hostname = lease.get('client-hostname'), + + starts = lease['starts'], + ends = lease.get('ends'), + + state = lease['binding-state'], + next = lease.get('next-binding-state'), + ) + + return self.db.insert(query) + + def __call__ (self, lease) : + """ + Process given DHCP lease to update currently active lease, or insert a new one. + + XXX: transaction? *leases? + """ + + # update existing? + if self.update(lease) : + log.info("Update: %s", lease) + + elif lease.get('hwaddr') : + # new + id = self.insert(lease) + + log.info("Insert: %s -> %d", lease, id) + + else : + # may be a free lease + log.warn("Ignored lease: %s", lease) + + if __name__ == '__main__' : import logging