#!/usr/bin/env python
import os.path, fnmatch
import optparse, logging
from rrdweb import graph, html
def parse_args (args) :
global options
parser = optparse.OptionParser(
usage = "%prog [options] <target> [...]"
)
# generic
parser.add_option('-q', "--quiet", help="No output in normal operation",
action='store_const', dest="loglvl", const=logging.WARNING,
)
parser.add_option('-v', "--verbose", help="More output",
action='store_const', dest="loglvl", const=logging.INFO,
)
parser.add_option('-D', "--debug", help="Even more output",
action='store_const', dest="loglvl", const=logging.DEBUG,
)
# options
parser.add_option( "--rrd-type", default="mrtg", help="RRD style to use: mrtg, collectd_ifoctets")
# paths
parser.add_option('-R', "--rrd-dir", default="rrd", help="Path to directory containing .rrd files")
parser.add_option('-I', "--img-dir", default="img", help="Path to directory containing .png files")
parser.add_option('-W', "--web-dir", default="web", help="Path to directory containing .html files")
parser.add_option('-T', "--tpl-dir", default="templates", help="Path to directory containing .html templates files")
# urls
parser.add_option('-P', "--url-prefix", default="", help="Prefix for URLs")
parser.add_option( "--img-url", default="img", help="URL to directory containing .png files")
parser.add_option( "--web-url", default="web", help="URL to directory containing .html files")
# defaults
parser.set_defaults(
loglvl = logging.INFO,
)
## parse
options, targets = parser.parse_args(args)
## apply
logging.basicConfig(
format = "[%(levelname)8s] %(funcName)20s : %(message)s",
level = options.loglvl,
)
return targets
def scan_targets () :
"""
Scan for targets from rrd_dir
"""
# root dir for searching
rrd_dir = options.rrd_dir
# recurse
for dirpath, dirs, files in os.walk(rrd_dir) :
for name in files :
# full path
path = os.path.join(dirpath, name)
# relative path from rrd_dir
relpath = path[len(os.path.commonprefix([rrd_dir, path])):]
# skip dotfiles
if name.startswith('.') :
continue
# foo/bar, .rrd
target, ext = os.path.splitext(relpath)
if ext.lower() == '.rrd' :
# as target name
yield target
def check_output_file (path) :
"""
Create the directory for the given output path if missing
"""
dir, file = os.path.split(path)
if not os.path.exists(dir) :
logging.warn("Create directory for output file %s: %s", file, dir)
os.makedirs(dir)
def target_graph (target, style, interval) :
"""
Generate .rrd -> .png for given target
"""
# XXX: title..
title = target
# compose paths
rrd_path = os.path.join(options.rrd_dir, target) + '.rrd'
out_path = os.path.join(options.img_dir, style, interval, target) + '.png'
# create out path if not exists
check_output_file(out_path)
logging.debug("%s: %s -> %s", target, rrd_path, out_path)
# graph function to use
graph_func = getattr(graph, options.rrd_type)
# graph
graph_func(style, interval, title, rrd_path, out_path)
def target_html (target, formatter) :
"""
Generate .html for given target
"""
# XXX: title..
title = target
# build paths
html_path = os.path.join(options.web_dir, target + '.html')
# create out path if not exists
check_output_file(html_path)
logging.debug("%s: %s", target, html_path)
# render
open(html_path, 'w').write(formatter.target(target, title).encode('utf-8'))
def overview_html (targets, formatter) :
"""
Generate .html index
"""
# paths
overview_path = os.path.join(options.web_dir, "index.html")
# create out path if not exists
check_output_file(overview_path)
logging.debug("%s", overview_path)
# as (target, title) pairs
open(overview_path, 'w').write(formatter.overview('/', ((target, target) for target in targets)).encode('utf-8'))
def generate_target (target, formatter) :
# overview
target_graph(target, 'overview', 'daily')
# details
for interval in ('daily', 'weekly', 'yearly') :
target_graph(target, 'detail', interval)
# html for target
target_html(target, formatter)
def generate (targets, filters=None):
if not targets :
# autodetect
targets = sorted(list(scan_targets()))
logging.info("Autodetected %d targets", len(targets))
if filters :
# filter targets
targets = [target for target in targets if any(fnmatch.fnmatch(target.name, filter) for filter in filters)]
logging.info("Updating %d targets", len(targets))
# overview
formatter = html.Formatter(
url_prefix = options.url_prefix,
img_url = html.urljoin(options.url_prefix, options.img_url, "%(style)s", "%(interval)s", "%(target)s.png"),
target_url = html.urljoin(options.url_prefix, options.web_url, "%(target)s.html"),
)
# html for overview
overview_html(targets, formatter)
for target in targets :
logging.info("Target: %s", target)
# HTML and PNGs
generate_target(target, formatter)
def main (args) :
# parse args
targets = parse_args(args)
# run
generate(targets)
if __name__ == '__main__' :
from sys import argv
main(argv[1:])