bin/pvl.verkko-dhcp
changeset 16 51509b5ce1c0
parent 15 66f81f4b6aa7
child 17 29f0cf9220e0
equal deleted inserted replaced
15:66f81f4b6aa7 16:51509b5ce1c0
    90                 ip          = attrs['ip'],
    90                 ip          = attrs['ip'],
    91                 mac         = attrs['mac'],
    91                 mac         = attrs['mac'],
    92                 gw          = attrs['gw'],
    92                 gw          = attrs['gw'],
    93 
    93 
    94                 first_seen  = attrs['timestamp'],
    94                 first_seen  = attrs['timestamp'],
    95 
    95                 count       = 1,
    96                 name        = attrs['name'],
    96 
    97                 last_seen   = attrs['timestamp'],
    97                 last_seen   = attrs['timestamp'],
    98                 last_msg    = attrs['state'],
    98                 state       = attrs['state'],
       
    99                 
       
   100                 name        = attrs.get('name'),
       
   101                 error       = attrs.get('error'),
    99         )
   102         )
   100         result = self.engine.execute(query, **attrs)
   103         result = self.engine.execute(query)
   101         id, = result.inserted_primary_key
   104         id, = result.inserted_primary_key
   102         
   105         
   103         return id
   106         return id
   104 
   107 
   105     def update (self, attrs) :
   108     def update (self, attrs) :
   107             UPDATE existing host, or return False if not found.
   110             UPDATE existing host, or return False if not found.
   108         """
   111         """
   109 
   112 
   110         table = db.dhcp_hosts
   113         table = db.dhcp_hosts
   111 
   114 
   112         query = table.update(
   115         query = table.update()
   113                 # where
   116         query = query.where((table.c.ip == attrs['ip']) & (table.c.mac == attrs['mac']) & (table.c.gw == attrs['gw']))
   114                 (table.c.ip == attrs['ip']) & (table.c.mac == attrs['mac']) & (table.c.gw == attrs['gw']),
   117         query = query.values(
       
   118                 count       = db.func.coalesce(table.c.count, 0) + 1,
   115 
   119 
   116                 # set
   120                 # set
   117                 name        = attrs['name'],
       
   118                 last_seen   = attrs['timestamp'],
   121                 last_seen   = attrs['timestamp'],
   119                 last_msg    = attrs['state'],
   122                 state       = attrs['state'],
   120         )
   123         )
   121         result = self.engine.execute(query, **attrs)
   124         
       
   125         if 'name' in attrs :
       
   126             query = query.values(name = attrs['name'])
       
   127         
       
   128         if 'error' in attrs :
       
   129             query = query.values(error = attrs['error'])
       
   130 
       
   131         result = self.engine.execute(query)
   122         
   132         
   123         # any matched rows?
   133         # any matched rows?
   124         return result.rowcount > 0
   134         return result.rowcount > 0
   125 
   135 
   126     def process (self, item) :
   136     def process (self, item) :
   127         """
   137         """
   128             Process given DHCP syslog message to update the hosts table.
   138             Process given DHCP syslog message to update the hosts table.
   129         """
   139         """
       
   140 
       
   141         attrs = {}
   130         
   142         
   131         # ignore unless we have enough info to fully identify the client
   143         # ignore unless we have enough info to fully identify the client
   132         # this means that we omit DHCPDISCOVER messages, but we get the OFFER/REQUEST/ACK
   144         # this means that we omit DHCPDISCOVER messages, but we get the OFFER/REQUEST/ACK
   133         if any(name not in item for name in ('lease', 'hwaddr', 'gateway')) :
   145         if any(name not in item for name in ('lease', 'hwaddr', 'gateway')) :
   134             # ignore; we require these
   146             # ignore; we require these
   135             return
   147             return
   136 
   148 
       
   149         # do not override error from request on NAK; clear otherwise
       
   150         if item.get('type') == 'DHCPNAK' :
       
   151             pass
       
   152         else :
       
   153             attrs['error'] = item.get('error')
       
   154 
       
   155         # do not override name unless known
       
   156         if item.get('name') :
       
   157             attrs['name'] = item.get('name')
       
   158 
   137         # db: syslog
   159         # db: syslog
   138         ATTR_MAP = (
   160         ATTR_MAP = (
   139             ('ip',          'lease'),
   161             ('ip',          'lease'),
   140             ('mac',         'hwaddr'),
   162             ('mac',         'hwaddr'),
   141             ('gw',          'gateway'),
   163             ('gw',          'gateway'),
   142             ('name',        'hostname'),
       
   143 
   164 
   144             ('timestamp',   'timestamp'),
   165             ('timestamp',   'timestamp'),
   145             ('state',       'type'),
   166             ('state',       'type'),
   146             ('error',       'error'),
       
   147         )
   167         )
   148 
   168 
   149         # attrs
   169         # generic attrs
   150         attrs = dict((key, item.get(name)) for key, name in ATTR_MAP)
   170         for key, name in ATTR_MAP :
       
   171             attrs[key] = item.get(name)
   151 
   172 
   152         # update existing?
   173         # update existing?
   153         if self.update(attrs) :
   174         if self.update(attrs) :
   154             log.info("Update: %s", attrs)
   175             log.info("Update: %s", attrs)
   155 
   176 
   180         if not dhcp_item :
   201         if not dhcp_item :
   181             # ignore
   202             # ignore
   182             return
   203             return
   183 
   204 
   184         dhcp_item['timestamp'] = item['timestamp'] # XXX: fixup DHCPSyslogParser?
   205         dhcp_item['timestamp'] = item['timestamp'] # XXX: fixup DHCPSyslogParser?
       
   206 
       
   207         if item.get('error') :
       
   208             item['error'] = self.filter.parse_error(item['error'])
   185 
   209 
   186         self.db.process(dhcp_item)
   210         self.db.process(dhcp_item)
   187 
   211 
   188     def main (self, source, poll) :
   212     def main (self, source, poll) :
   189         """
   213         """
   210             log.debug("tick")
   234             log.debug("tick")
   211 
   235 
   212 def main (argv) :
   236 def main (argv) :
   213     options, args = parse_options(argv)
   237     options, args = parse_options(argv)
   214 
   238 
       
   239     # db
       
   240     if not options.database :
       
   241         log.error("No database given")
       
   242         return 1
       
   243 
       
   244     log.info("Open up database: %s", options.database)
       
   245     database = DHCPHostsDatabase(options.database)
       
   246 
       
   247     if options.create :
       
   248         database.create()
       
   249     
   215     # syslog
   250     # syslog
   216     log.info("Open up syslog...")
   251     log.info("Open up syslog...")
   217     syslog_parser = pvl.syslog.parser.SyslogParser(prog=DHCP_SYSLOG_PROG) # filter by prog
   252     syslog_parser = pvl.syslog.parser.SyslogParser(prog=DHCP_SYSLOG_PROG) # filter by prog
   218 
   253 
   219     if options.syslog_fifo :
   254     if options.syslog_fifo :
   229         # one-shot file read
   264         # one-shot file read
   230         source = pvl.syslog.tail.TailFile(options.syslog_file)
   265         source = pvl.syslog.tail.TailFile(options.syslog_file)
   231         poll = False # do not poll-loop
   266         poll = False # do not poll-loop
   232 
   267 
   233     else :
   268     else :
   234         log.error("No syslog source given")
   269         log.warning("No syslog source given")
   235         return 1
   270         return 0
   236     
   271 
   237     # db
       
   238     log.info("Open up database: %s", options.database)
       
   239     database = DHCPHostsDatabase(options.database)
       
   240 
       
   241     if options.create :
       
   242         database.create()
       
   243     
       
   244     # handler + main
   272     # handler + main
   245     handler = DHCPSyslogHandler(database)
   273     handler = DHCPSyslogHandler(database)
   246 
   274 
   247     log.info("Enter mainloop...")
   275     log.info("Enter mainloop...")
   248     handler.main(source, poll)
   276     handler.main(source, poll)