pvl.syslog: implement pvl.syslog.args.apply -> SyslogSource as in pvl.verkko-dhcp
authorTero Marttila <terom@fixme.fi>
Thu, 03 Jan 2013 00:38:26 +0200
changeset 43 9d13b101beab
parent 42 a9d7563f7396
child 44 977442ccb72d
pvl.syslog: implement pvl.syslog.args.apply -> SyslogSource as in pvl.verkko-dhcp
bin/pvl.verkko-syslog
pvl/syslog/args.py
pvl/syslog/fifo.py
pvl/syslog/parser.py
pvl/syslog/syslog.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/pvl.verkko-syslog	Thu Jan 03 00:38:26 2013 +0200
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+
+"""
+    Monitor DHCP use.
+"""
+
+__version__ = '0.0'
+
+import pvl.args
+import pvl.syslog.args
+
+import logging, optparse
+
+log = logging.getLogger('main')
+
+def parse_options (argv) :
+    """
+        Parse command-line arguments.
+    """
+
+    prog = argv[0]
+
+    parser = optparse.OptionParser(
+            prog        = prog,
+            usage       = '%prog: [options]',
+            version     = __version__,
+
+            # module docstring
+            description = __doc__,
+    )
+    
+    # options
+    parser.add_option_group(pvl.args.parser(parser))
+
+    ## syslog
+    parser.add_option_group(pvl.syslog.args.parser(parser))
+
+    # defaults
+    parser.set_defaults(
+
+    )
+    
+    # parse
+    options, args = parser.parse_args(argv[1:])
+    
+    # apply
+    pvl.args.apply(options, prog)
+
+    return options, args
+
+def main (argv) :
+    options, args = parse_options(argv)
+    
+    syslog = pvl.syslog.args.apply(options)
+  
+    log.info("Start processing syslog messages...")
+
+    for item in syslog.loop() :
+        print item
+    
+    # done
+    log.info("Exiting...")
+    return 0
+
+if __name__ == '__main__':
+    import sys
+
+    sys.exit(main(sys.argv))
--- a/pvl/syslog/args.py	Wed Jan 02 20:46:54 2013 +0200
+++ b/pvl/syslog/args.py	Thu Jan 03 00:38:26 2013 +0200
@@ -43,10 +43,17 @@
     if options.syslog_fifo :
         # read fifo
         source = fifo.Fifo(options.syslog_fifo)
+        poll = True
+
+    elif options.syslog_tail :
+        # tail file
+        source = tail.TailFile(options.syslog_file)
+        poll = options.syslog_tail # float
 
     elif options.syslog_file :
-        # tail file
-        source = tail.TailFile(options.syslog_file)
+        # read file
+        source = open(options.syslog_file)
+        poll = False
 
     elif optional :
         log.debug("No --syslog source given")
@@ -62,5 +69,7 @@
     )
 
     # build
-    return SyslogSource(source, parser)
+    return SyslogSource(source, parser,
+            poll    = poll
+    )
 
--- a/pvl/syslog/fifo.py	Wed Jan 02 20:46:54 2013 +0200
+++ b/pvl/syslog/fifo.py	Thu Jan 03 00:38:26 2013 +0200
@@ -38,7 +38,7 @@
 
     def _open (self) :
         """
-            Open the internal fd.
+            Open the internal fd (nonblocking).
         """
 
         assert self._fd is None
--- a/pvl/syslog/parser.py	Wed Jan 02 20:46:54 2013 +0200
+++ b/pvl/syslog/parser.py	Thu Jan 03 00:38:26 2013 +0200
@@ -148,5 +148,5 @@
             if self.filter(line, item) :
                 yield item
 
+    __call__ = process
 
-
--- a/pvl/syslog/syslog.py	Wed Jan 02 20:46:54 2013 +0200
+++ b/pvl/syslog/syslog.py	Thu Jan 03 00:38:26 2013 +0200
@@ -2,6 +2,8 @@
     Syslog handling.
 """
 
+import select
+
 import logging; log = logging.getLogger('pvl.syslog.source')
 
 class SyslogSource (object) :
@@ -9,16 +11,22 @@
         A source of syslog items.
     """
     
-    def __init__ (self, source, parser) :
+    def __init__ (self, source, parser, poll=None) :
         """
             Using given underlying line source.
+
+                source          - file-like object with poll() and iter()
+                parser          - process() bytes -> messages
+                poll            - using given polling style:
+                    True            - select() source
+                    float           - given interval
+                    None/False      - single-shot
         """
 
         self.source = source
         self.parser = parser
 
-        # proxy
-        self.poll = source.poll
+        self._poll = poll
 
     def __iter__ (self) :
         """
@@ -26,6 +34,77 @@
         """
         
         # directly iter across source
-        for item in self.parser.process(self.source) :
+        for item in self.parser(self.source) :
             yield item
+        
+    def poll (self, timeout=None) :
+        """
+            Poll source for input, with given timeout in seconds (float).
+
+            A timeout of None indicates to block forever, False indicates to never block.
+
+            Returns True if we have input waiting, False on timeout with no input. None on indeterminate.
+        """
+
+        read = write = ()
+        
+        if timeout is True :
+            timeout = None
+            read += (self.source, )
+
+        elif timeout is False :
+            timeout = 0.0
+
+        log.debug("%f", timeout)
+    
+        # select
+        read, write, ex = select.select(read, write, [], timeout)
+
+        if read :
+            return True
+
+        else :
+            # timeout
+            return False
+
+    def loop (self) :
+        """
+            Continuously read items from syslog source, blocking as suitable.
+
+            Returns once no more lines are available.
+
+            XXX: reconnect?
+        """
+        
+        # mainloop
+        while True :
+            # pull in messages
+            for item in self :
+                yield item
             
+            # poll
+            if self._poll :
+                # wait
+                self.poll(self._poll)
+            else :
+                # done
+                break
+
+            log.debug("tick")
+
+        log.debug("exit")
+    
+    def stop (self) :
+        """
+            Stop loop after current iteration.
+        """
+
+        self._poll = False
+
+    def main (self) :
+        """
+            Mainloop.
+        """
+
+        # TODO: SIGINT -> stop()
+        return loop()