bin/pvl.wlan-sta
changeset 238 9702bfb124f6
parent 236 7b37f84b17cc
equal deleted inserted replaced
237:3534d31282b3 238:9702bfb124f6
    35     )
    35     )
    36 
    36 
    37     # common
    37     # common
    38     parser.add_option_group(pvl.args.parser(parser))
    38     parser.add_option_group(pvl.args.parser(parser))
    39     parser.add_option_group(pvl.syslog.args.parser(parser, prog=WLAN_STA_PROG))
    39     parser.add_option_group(pvl.syslog.args.parser(parser, prog=WLAN_STA_PROG))
       
    40     parser.add_option_group(pvl.verkko.db.parser(parser, table=db.wlan_sta))
    40 
    41 
    41     parser.add_option('--interfaces', metavar='PATH',
    42     parser.add_option('--interfaces', metavar='PATH',
    42             help="Load interface/node names from mapping file")
    43             help="Load interface/node names from mapping file")
    43 
       
    44     parser.set_defaults(
       
    45     )
       
    46 
    44 
    47     # parse
    45     # parse
    48     options, args = parser.parse_args(args)
    46     options, args = parser.parse_args(args)
    49 
    47 
    50     # apply
    48     # apply
    51     pvl.args.apply(options)
    49     pvl.args.apply(options)
    52 
    50 
    53     return options, args
    51     return options, args
    54 
    52 
    55 import re
    53 import re
       
    54 from pvl.verkko import db
    56 
    55 
    57 class HostapdHandler (object) :
    56 class WlanStaDatabase (object) :
    58     HOSTAPD_STA_RE = re.compile(r'(?P<wlan>.+?): STA (?P<sta>.+?) (?P<msg>.+)')
    57     HOSTAPD_STA_RE = re.compile(r'(?P<wlan>.+?): STA (?P<sta>.+?) (?P<msg>.+)')
    59 
    58 
    60     def __init__ (self, interface_map=None) :
    59     DB_TABLE = db.wlan_sta
       
    60     DB_LAST_SEEN = db.wlan_sta.c.last_seen
       
    61     DB_SELECT = (db.dhcp_hosts.c.gw, db.dhcp_hosts.c.ip)
       
    62 
       
    63     def __init__ (self, db, interface_map=None) :
    61         """
    64         """
    62             interface_map       - {(hostname, interface): (nodename, wlan)}
    65             interface_map       - {(hostname, interface): (nodename, wlan)}
    63         """
    66         """
       
    67         self.db = db
    64         self.interface_map = interface_map
    68         self.interface_map = interface_map
       
    69 
       
    70 
       
    71     def select (self, distinct=DB_SELECT, interval=None) :
       
    72         """
       
    73             SELECT unique gw/ip hosts, for given interval.
       
    74         """
       
    75 
       
    76         query = db.select(distinct, distinct=True)
       
    77 
       
    78         if interval :
       
    79             # timedelta
       
    80             query = query.where(db.func.now() - self.DB_LAST_SEEN < interval)
       
    81 
       
    82         return self.db.select(query)
       
    83 
       
    84     def insert (self, key, update, timestamp, count=True) :
       
    85         """
       
    86             INSERT new host
       
    87         """
       
    88 
       
    89         query = self.DB_TABLE.insert().values(**key).values(**update).values(
       
    90                 first_seen  = timestamp,
       
    91                 last_seen   = timestamp,
       
    92         )
       
    93 
       
    94         if count :
       
    95             query = query.values(count=1)
       
    96         
       
    97         # -> id
       
    98         return self.db.insert(query)
       
    99 
       
   100     def update (self, key, update, timestamp, count=True) :
       
   101         """
       
   102             UPDATE existing host, or return False if not found.
       
   103         """
       
   104 
       
   105         table = self.DB_TABLE
       
   106         query = table.update()
       
   107 
       
   108         for col, value in key.iteritems() :
       
   109             query = query.where(table.c[col] == value)
       
   110 
       
   111         query = query.values(last_seen=timestamp)
       
   112 
       
   113         if count :
       
   114             query = query.values(count=db.func.coalesce(table.c.count, 0) + 1)
       
   115 
       
   116         query = query.values(**update)
       
   117         
       
   118         # -> any matched rows?
       
   119         return self.db.update(query)
       
   120 
       
   121     def touch (self, key, update, timestamp, **opts) :
       
   122         # update existing?
       
   123         if self.update(key, update, timestamp, **opts) :
       
   124             log.info("Update: %s: %s: %s", key, update, timestamp)
       
   125         else :
       
   126             log.info("Insert: %s: %s: %s", key, update, timestamp)
       
   127             self.insert(key, update, timestamp, **opts)
    65 
   128 
    66     def parse (self, item) :
   129     def parse (self, item) :
    67         """
   130         """
    68             Parse fields from a hostapd syslog message.
   131             Parse fields from a hostapd syslog message.
    69         """
   132         """
    71         match = self.HOSTAPD_STA_RE.match(item['msg'])
   134         match = self.HOSTAPD_STA_RE.match(item['msg'])
    72 
   135 
    73         if not match :
   136         if not match :
    74             return None
   137             return None
    75 
   138 
    76         return dict(item, **match.groupdict())
   139         return match.groupdict()
    77 
   140 
    78     def build_sta (self, match) :
   141     def __call__ (self, item) :
    79         ap, wlan = match['host'], match['wlan']
   142         match = self.parse(item)
       
   143 
       
   144         if not match :
       
   145             return
       
   146 
       
   147         # lookup?
       
   148         ap, wlan = item['host'], match['wlan']
    80         
   149         
    81         # override?
       
    82         if self.interface_map :
   150         if self.interface_map :
    83             mapping = self.interface_map.get((ap, wlan))
   151             mapping = self.interface_map.get((ap, wlan))
    84 
   152 
    85             if mapping :
   153             if mapping :
    86                 ap, wlan = mapping
   154                 ap, wlan = mapping
    87         
   155         
    88         build = dict(
   156         # update/insert
    89                 timestamp       = match['timestamp'],
   157         self.touch(
       
   158             dict(
    90                 ap              = ap,
   159                 ap              = ap,
    91                 wlan            = wlan,
   160                 wlan            = wlan,
    92                 sta             = match['sta'],
   161                 sta             = match['sta'],
       
   162             ), dict(
    93                 msg             = match['msg'],
   163                 msg             = match['msg'],
       
   164             ), item['timestamp']
    94         )
   165         )
    95 
       
    96 
       
    97         return build
       
    98 
   166 
    99 def main (argv) :
   167 def main (argv) :
   100     options, args = parse_argv(argv)
   168     options, args = parse_argv(argv)
       
   169     
       
   170     # database
       
   171     db = pvl.verkko.db.apply(options)
   101 
   172 
   102     if options.interfaces :
   173     if options.interfaces :
   103         interfaces = dict(pvl.rrd.hosts.map_interfaces(options, open(options.interfaces)))
   174         interfaces = dict(pvl.rrd.hosts.map_interfaces(options, open(options.interfaces)))
   104     else :
   175     else :
   105         interfaces = None
   176         interfaces = None
   106 
   177 
   107     # syslog
   178     # syslog
   108     log.info("Open up syslog...")
   179     log.info("Open up syslog...")
   109     syslog = pvl.syslog.args.apply(options)
   180     syslog = pvl.syslog.args.apply(options)
   110     handler = HostapdHandler(interface_map=interfaces)
   181 
       
   182     # handler
       
   183     handler = WlanStaDatabase(db, interface_map=interfaces)
   111 
   184 
   112     log.info("Enter mainloop...")
   185     log.info("Enter mainloop...")
   113     for source in syslog.main() :
   186     for source in syslog.main() :
   114         for item in source:
   187         for item in source:
   115             match = handler.parse(item)
   188             handler(item)
   116 
       
   117             if not match :
       
   118                 continue
       
   119             elif 'sta' in match :
       
   120                 sta = handler.build_sta(match)
       
   121             else :
       
   122                 continue
       
   123         
       
   124             print "{ap:>30}/{wlan:10} {sta:20} : {msg}".format(**sta)
       
   125 
   189 
   126     return 0
   190     return 0
   127     
   191     
   128 if __name__ == '__main__':
   192 if __name__ == '__main__':
   129     import sys
   193     import sys