pvl.verkko-rrd-interfaces: script to setup symlinks for pvl.verkko.rrd from collectd
authorTero Marttila <terom@paivola.fi>
Sun, 27 Jan 2013 15:07:04 +0200
changeset 187 cbf9371a472d
parent 186 0bfb34281141
child 188 6b3f30fcded7
pvl.verkko-rrd-interfaces: script to setup symlinks for pvl.verkko.rrd from collectd
bin/pvl.verkko-rrd-interfaces
pvl/args.py
setup_verkko.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/pvl.verkko-rrd-interfaces	Sun Jan 27 15:07:04 2013 +0200
@@ -0,0 +1,214 @@
+#!/usr/bin/python
+
+"""
+    Setup symlinks for pvl.verkko-rrd -> collectd based on define host/interface names
+"""
+
+__version__ = '0.1'
+
+import shlex
+import os
+
+import pvl.args
+
+import optparse
+import logging; log = logging.getLogger('main')
+
+def hostjoin (*hosts) :
+    """
+        DNS hostname join.
+    """
+
+    return '.'.join(hosts)
+
+def collectd_interfaces (options, file, collectd_domain) :
+    """
+        Read collectd (host, type-instance, name) items, and yield (collectd-rrd, out-rrd) tuples.
+
+            collectd_domain     - append given domain to collectd hosts
+            dir         - output directory
+    """
+
+    host = None
+
+    for idx, line in enumerate(file, 1) :
+        line = line.rstrip()
+
+        if not line :
+            continue
+        
+        # comment?
+        if line.startswith('#') :
+            continue
+        
+        # line
+        parts = shlex.split(line)
+
+        if not line[0].isspace() :
+            host = parts.pop(0)
+        
+        # host-spec?
+        if '=' in host :
+            collectd_host, interface_host = host.split('=')
+        else :
+            collectd_host = interface_host = host
+        
+        # flip from DNS-ordering -> path-ordering
+        if options.reverse_host :
+            interface_host = '.'.join(reversed(interface_host.split('.')))
+
+        # host has domain in collectd?
+        if collectd_domain :
+            collectd_host = hostjoin(collectd_host, collectd_domain)
+
+        if not parts :
+            # keep host for following lines
+            continue
+
+        port = parts.pop(0)
+        
+        # possibly multiple tags..
+        for tag in parts :
+            collectd_name = options.collectd_type + '-' + port + '.rrd'
+            collectd_rrd = os.path.join(options.collectd_rrd, collectd_host, options.collectd_plugin, collectd_name)
+
+            if not os.path.exists(collectd_rrd) :
+                log.warn("%s: non-existant rrd: %s", idx, rrd)
+                continue
+
+            # out
+            interface_rrd = os.path.join(interface_host, tag + '.rrd')
+
+            log.debug("%s: %s", interface_rrd, collectd_rrd)
+
+            yield collectd_rrd, interface_rrd
+
+def sync_links (options, links, dir) :
+    """
+        Sync given (collectd, name) symlinks in given dir.
+    """
+
+    for path, name in links :
+        link = os.path.join(dir, name)
+
+        # sync
+        if os.path.exists(link) :
+            continue
+            
+        log.info("%s: %s: %s", dir, link, path)
+        
+        yield link, path
+
+def apply_links (options, links) :
+    """
+        Apply given symlinks
+    """
+
+    for link, path in links :
+        linkdir = os.path.dirname(link)
+
+        # do
+        if not os.path.exists(linkdir) :
+            log.warn("mkdir: %s", linkdir)
+            os.mkdir(linkdir)
+
+        print path
+
+        os.symlink(path, link)
+
+COLLECTD_RRD = '/var/lib/collectd/rrd'
+COLLECTD_PLUGIN = 'interfaces'
+COLLECTD_TYPE = 'if_octets'
+
+def parse_argv (argv, doc = __doc__) :
+    """
+        Parse command-line argv, returning (options, args).
+    """
+
+    prog = argv.pop(0)
+    args = argv
+
+    # optparse
+    parser = optparse.OptionParser(
+        prog        = prog,
+        usage       = '%prog: [options] [<input.txt> [...]]',
+        version     = __version__,
+        description = doc,
+    )
+
+    # common
+    parser.add_option_group(pvl.args.parser(parser))
+
+    # options
+    parser.add_option('--collectd-rrd',     metavar='PATH',     default=COLLECTD_RRD,
+            help="Path to collectd rrd: %default")
+    parser.add_option('--collectd-plugin',  metavar='PLUGIN',   default=COLLECTD_PLUGIN,
+            help="Collectd plugin to use: %default")
+    parser.add_option('--collectd-type',    metavar='TYPE',     default=COLLECTD_TYPE,
+            help="Collectd type to use: %default")
+    
+    # hostnames
+    parser.add_option('--reverse-host',     action='store_true',
+            help="Flip host.domain -> domain.host (default)")
+    parser.add_option('--no-reverse-host',  action='store_false', dest='reverse_host',
+            help="Keep host.domain as host.domain")
+    parser.add_option('--domain',           metavar='DOMAIN',  
+            help="Append domain to collectd hostnames: <input>.txt -> <input>")
+
+    # output
+    parser.add_option('--rrd',              metavar='PATH',
+            help="Output directory for .rrd symlinks: <input>.txt -> <input>/")
+    parser.add_option('--noop',             action='store_true',
+            help="Scan symlinks, but do not update")
+
+    parser.set_defaults(
+            reverse_host    = True,
+    )
+
+    # parse
+    options, args = parser.parse_args(args)
+
+    # apply
+    pvl.args.apply(options)
+
+    return options, args
+
+def main (argv) :
+    options, args = parse_argv(argv)
+    
+    for txt in args :
+        # defaults from .txt name
+        base, _ = os.path.splitext(txt)
+        _, basename = os.path.split(base)
+
+        if options.domain is None :
+            domain = '.'.join(reversed(basename.split('.')))
+        else :
+            # may be ''
+            domain = options.domain
+        
+        # generate links from spec
+        links = list(collectd_interfaces(options, open(txt),
+            collectd_domain     = domain,
+        ))
+        
+        # sync missing links
+        links = list(sync_links(options, links,
+            dir     = options.rrd or base,
+        ))
+        
+        # verbose
+        if not options.quiet :
+            for link, path in links :
+                print link, '->', path
+        
+        # apply
+        if not options.noop :
+            apply_links(options, links)
+
+    return 0
+    
+if __name__ == '__main__':
+    import sys
+
+    sys.exit(main(sys.argv))
--- a/pvl/args.py	Sun Jan 27 14:48:40 2013 +0200
+++ b/pvl/args.py	Sun Jan 27 15:07:04 2013 +0200
@@ -94,6 +94,9 @@
         level       = options.loglevel,
         filename    = options.log_file,
     )
+
+    # TODO: use --quiet for stdout output?
+    options.quiet = options.loglevel > logging.WARN
     
     if options.uid or options.gid or not rootok :
         # set uid/gid
--- a/setup_verkko.py	Sun Jan 27 14:48:40 2013 +0200
+++ b/setup_verkko.py	Sun Jan 27 15:07:04 2013 +0200
@@ -28,6 +28,7 @@
         'bin/pvl.syslog-dhcp',
         'bin/pvl.verkko-dhcp',
         'bin/pvl.verkko-rrd',
+        'bin/pvl.verkko-rrd-interfaces',
     ],
 
     data_files  = [