rename pvl.syslog-dhcp as pvl.dhcp-syslog, and pvl.verkko-rrd-interfaces as pvl.rrd-interfaces
#!/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/<host>.%s/%s/%s-<port>.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] [<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=None,
help="Collectd plugin to use: <input>-<plugin>.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: <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(
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 :
# <path>/<domain>-<plugin>.txt -> <path>/<domain>-<plugin>
basepath, _ = os.path.splitext(txt)
# <path>/<domain>-<plugin> -> <path>/<domain>, <plugin>
if '-' in basepath :
basepath, collectd_plugin = basepath.rsplit('-', 1)
else :
collectd_plugin = None
# <path>/<domain> -> <domain>
_, 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))