root / plugins / network / shorewall-accounting_ @ 7046ba80
Historique | Voir | Annoter | Télécharger (6,02 ko)
| 1 |
#!/usr/bin/env python |
|---|---|
| 2 |
# shorewall_accounting v1.3 |
| 3 |
# |
| 4 |
# A munin plugin for tracking traffic as recorded by shorewall accounting rules. |
| 5 |
# See "man shorewall-accounting" for all possible accounting rules. |
| 6 |
# Basically this plugin examines the output of "shorewall -x show accounting". |
| 7 |
# See http://atlee.ca/blog/2006/01/20/munin-shorewall/ for a description of |
| 8 |
# the original script by Chris AtLee. |
| 9 |
# |
| 10 |
# Copyright 2010-2012 Lars Kruse <devel@sumpralle.de> |
| 11 |
# Copyright 2006 Chris AtLee <chris@atlee.ca> |
| 12 |
# |
| 13 |
# Original publication: http://atlee.ca/blog/2006/01/20/munin-shorewall/ |
| 14 |
# Released under the GPL v3 or later |
| 15 |
# |
| 16 |
# You can use symlinks of this script according to the following pattern: |
| 17 |
# shorewall-accounting_prot: use only rules with a specific protocol |
| 18 |
# shorewall-accounting_in: use only rules with a specific input interface |
| 19 |
# shorewall-accounting_out: use only rules with a specific output interface |
| 20 |
# shorewall-accounting_source: use only rules with a specific source IP |
| 21 |
# shorewall-accounting_destination: use only rules with a specific destination IP |
| 22 |
# shorewall-accounting_details: use only rules with specific details (e.g. a port) |
| 23 |
# |
| 24 |
# Here "specific" means: non-default (e.g. a protocol was specified). |
| 25 |
# Combinations are allowed: |
| 26 |
# shorewall-accounting_prot_in_source: use only rules with a specific protocol, input interface and source IP |
| 27 |
# |
| 28 |
# Environment variables: |
| 29 |
# SHOREWALL_BIN - defaults to /sbin/shorewall |
| 30 |
# |
| 31 |
# Changelog: |
| 32 |
# v1.3 - 2012/04/02 |
| 33 |
# * renamed plugin file from "shorewall_accounting" to "shorewall-accounting_" |
| 34 |
# * CAUTION: rename your symlinks and plugin config section! |
| 35 |
# * added optional SHOREWALL_BIN environment variable |
| 36 |
# * improved labels for rules (don't mask dots) |
| 37 |
# |
| 38 |
#%# family=auto |
| 39 |
#%# capabilities=autoconf |
| 40 |
|
| 41 |
import sys |
| 42 |
import os |
| 43 |
import commands |
| 44 |
import re |
| 45 |
|
| 46 |
PLUGIN_BASE_NAME = "shorewall-accounting" |
| 47 |
SHOREWALL_BIN = os.environ.get("SHOREWALL_BIN", "/sbin/shorewall")
|
| 48 |
ACCOUNTING_LINE_EXP = re.compile(r"^\s*\d+\s+(\d+)\s+(?P<prot>\w+)\s+(?P<opt>[\w-]+)\s+(?P<in>[\w*]+)\s+(?P<out>[\w*]+)\s+(?P<source>[\w./+-]+)\s+(?P<destination>[\w./+-]+)\s*(?P<details>.*)\s*$") |
| 49 |
KEY_ORDER = ["prot", "in", "out", "source", "destination", "details"] |
| 50 |
FILTER_PATTERNS = {
|
| 51 |
"prot": r"^all$", |
| 52 |
"in": r"^\*$", |
| 53 |
"out": r"^\*$", |
| 54 |
"source": r"^0\.0\.0\.0/0$", |
| 55 |
"destination": r"^0\.0\.0\.0/0$", |
| 56 |
"details": r"^$", |
| 57 |
} |
| 58 |
REPLACE_PATTERNS = {
|
| 59 |
"prot": ("^all$", "allProt"),
|
| 60 |
"in": (r"^\*$", "allIn"), |
| 61 |
"out": (r"^\*$", "allOut"), |
| 62 |
"source": (r"^0\.0\.0\.0/0", "allSrc"), |
| 63 |
"destination": (r"^0\.0\.0\.0/0", "allDst"), |
| 64 |
"details": (r"^multiport\s+", ""), |
| 65 |
} |
| 66 |
|
| 67 |
|
| 68 |
def get_accounting_rule_fieldname_and_label(regdict): |
| 69 |
items = [] |
| 70 |
# filter and clean all requested keys |
| 71 |
for key in KEY_ORDER: |
| 72 |
raw = regdict[key] |
| 73 |
pattern, replacement = REPLACE_PATTERNS[key] |
| 74 |
value = re.sub(pattern, replacement, raw).strip() |
| 75 |
if value: |
| 76 |
items.append(value) |
| 77 |
result = "_".join(items) |
| 78 |
# clean the fieldname: http://munin-monitoring.org/wiki/notes_on_datasource_names |
| 79 |
result = re.sub(r"^[^A-Za-z_]", "_", result) |
| 80 |
fieldname = re.sub(r"[^A-Za-z0-9_]", "_", result) |
| 81 |
# keep dots (for IP addresses) |
| 82 |
label = re.sub(r"[^A-Za-z0-9_\.]", "_", result) |
| 83 |
return fieldname, label |
| 84 |
|
| 85 |
def is_wanted(regdict, filter_list): |
| 86 |
for item in filter_list: |
| 87 |
# is the item empty? |
| 88 |
if not regdict[item]: |
| 89 |
return False |
| 90 |
# is the default value (unfiltered) set? |
| 91 |
if re.search(FILTER_PATTERNS[item], regdict[item]): |
| 92 |
return False |
| 93 |
return True |
| 94 |
|
| 95 |
def get_bytes_by_chain(filter_list): |
| 96 |
status, output = commands.getstatusoutput("'%s' -x show accounting" \
|
| 97 |
% SHOREWALL_BIN) |
| 98 |
if status != 0: |
| 99 |
raise OSError("Error running command (%s)[%i]: %s" % (SHOREWALL_BIN,
|
| 100 |
status, output)) |
| 101 |
chains = {}
|
| 102 |
for line in output.splitlines(): |
| 103 |
m = ACCOUNTING_LINE_EXP.match(line) |
| 104 |
if m is not None: |
| 105 |
# check if this line was filtered |
| 106 |
if not is_wanted(m.groupdict(), filter_list): |
| 107 |
continue |
| 108 |
fieldname, label = get_accounting_rule_fieldname_and_label(m.groupdict()) |
| 109 |
bytes = int(m.group(1)) |
| 110 |
if fieldname in chains: |
| 111 |
chains[fieldname][1] += bytes |
| 112 |
else: |
| 113 |
chains[fieldname] = [label, bytes] |
| 114 |
retval = [] |
| 115 |
names = chains.keys() |
| 116 |
names.sort() |
| 117 |
for name in names: |
| 118 |
retval.append((name, chains[name][0], chains[name][1])) |
| 119 |
return retval |
| 120 |
|
| 121 |
|
| 122 |
# extract the filters from the symlink's name |
| 123 |
# (e.g. "shorewall-accounting_in_out_details" -> in, out, details) |
| 124 |
call_name = os.path.basename(sys.argv[0]) |
| 125 |
if call_name.startswith(PLUGIN_BASE_NAME): |
| 126 |
suffix = call_name[len(PLUGIN_BASE_NAME):] |
| 127 |
suffixes = suffix.split("_")
|
| 128 |
# use only suffixes that are listed in FILTER_PATTERNS |
| 129 |
filter_list = [item for item in suffixes if item in FILTER_PATTERNS] |
| 130 |
else: |
| 131 |
filter_list = [] |
| 132 |
|
| 133 |
if len(sys.argv) > 1: |
| 134 |
if sys.argv[1] == "autoconf": |
| 135 |
status, output = commands.getstatusoutput("'%s' -x show accounting" \
|
| 136 |
% SHOREWALL_BIN) |
| 137 |
if (status != 0) or not output: |
| 138 |
print "no" |
| 139 |
else: |
| 140 |
print "yes" |
| 141 |
elif sys.argv[1] == "config": |
| 142 |
if not filter_list: |
| 143 |
title_addon = "all" |
| 144 |
else: |
| 145 |
title_addon = " / ".join(filter_list) |
| 146 |
print "graph_title Shorewall accounting: %s" % title_addon |
| 147 |
print "graph_category network" |
| 148 |
print "graph_vlabel bits per ${graph_period}"
|
| 149 |
for chain, label, bytes in get_bytes_by_chain(filter_list): |
| 150 |
label = " ".join([item for item in label.split("_")
|
| 151 |
if not item.lower().startswith("all")])
|
| 152 |
if not label: |
| 153 |
label = "all" |
| 154 |
print "%s.min 0" % chain |
| 155 |
print "%s.type DERIVE" % chain |
| 156 |
print "%s.label %s" % (chain, label) |
| 157 |
print "%s.cdef %s,8,*" % (chain, chain) |
| 158 |
sys.exit(0) |
| 159 |
|
| 160 |
for chain, label, bytes in get_bytes_by_chain(filter_list): |
| 161 |
print "%s.value %i" % (chain, bytes) |
| 162 |
|
