diff -r c98944a7db08 -r 3b1437d4b0a1 bin/pvl.rrd-interfaces --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/pvl.rrd-interfaces Tue Feb 19 22:07:40 2013 +0200 @@ -0,0 +1,266 @@ +#!/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 hostreverse (host) : + """ + Reverse hostname. + """ + + return '.'.join(reversed(host.split('.'))) + +def collectd_interfaces (options, file, collectd_domain, collectd_plugin) : + """ + Read collectd (host, type-instance, name) items, and yield (collectd-rrd, out-rrd) tuples. + + file - read host/ports from file + collectd_domain - append given domain to collectd hostname + collectd_plugin - use given collectd plugin's type-instances + """ + + host = None + + log.info("scanning %s/.%s/%s/%s-.rrd", options.collectd_rrd, collectd_domain, collectd_plugin, options.collectd_type) + + 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 = hostreverse(interface_host) + + # 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 : + if options.collectd_instance == 'type' : + type = options.collectd_type + '-' + port + else : + type = options.collectd_type + + if options.collectd_instance == 'plugin' : + plugin = collectd_plugin + '-' + port + else : + plugin = collectd_plugin + + collectd_rrd = os.path.join(options.collectd_rrd, collectd_host, plugin, type) + '.rrd' + + if not os.path.exists(collectd_rrd) : + log.warn("%s: missing collectd rrd: %s", idx, collectd_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. + """ + + log.info("%s", 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, name, 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] [ [...]]', + 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=None, + help="Collectd plugin to use: -.txt") + parser.add_option('--collectd-type', metavar='TYPE', default=COLLECTD_TYPE, + help="Collectd type to use: %default") + + # interface is by plugin, snmp is by type... + parser.add_option('--collectd-instance-plugin', action='store_const', dest='collectd_instance', const='plugin', + help="Collectd by plugin instance") + parser.add_option('--collectd-instance-type', action='store_const', dest='collectd_instance', const='type', + help="Collectd by type instance") + + # 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: .txt -> ") + + # output + parser.add_option('--rrd', metavar='PATH', + help="Output directory for .rrd symlinks: .txt -> /") + parser.add_option('--noop', action='store_true', + help="Scan symlinks, but do not update") + + parser.set_defaults( + collectd_instance = 'type', + 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 : + # /-.txt -> /- + basepath, _ = os.path.splitext(txt) + + # /- -> /, + if '-' in basepath : + basepath, collectd_plugin = basepath.rsplit('-', 1) + else : + collectd_plugin = None + + # / -> + _, basename = os.path.split(basepath) + + # domain? + if options.domain is None : + # reverse-order hostname + domain = hostreverse(basename) + else : + # may be '' + domain = options.domain + + # output dir? + if options.rrd : + rrd_domain = hostreverse(domain) if options.reverse_host else domain + rrd = os.path.join(options.rrd, rrd_domain) + else : + rrd = basepath + + # generate links from spec + links = list(collectd_interfaces(options, open(txt), + collectd_domain = domain, + collectd_plugin = options.collectd_plugin or collectd_plugin or COLLECTD_PLUGIN, + )) + + if not os.path.exists(rrd) : + log.warn("mkdir: %s", rrd) + + if not options.noop : + os.mkdir(rrd) + + # sync missing links + links = list(sync_links(options, links, + dir = rrd, + )) + + # 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))