Skip to content

Commit 7a51f6a

Browse files
committed
Collect dhcp-stats lacking "vlan" in the location
1 parent bdd562b commit 7a51f6a

File tree

2 files changed

+64
-18
lines changed

2 files changed

+64
-18
lines changed

contrib/scripts/isc_dhpcd_graphite/README.rst

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,19 @@ is "vlan1" the resulting graphite path is ``nav.bloviate.vlan1``.
2727
Assumptions about Location
2828
--------------------------
2929

30-
The script assumes that the value of the ``location``-key contains a vlan-name
31-
of the form "vlanNUMBER", regex ``vlan\d+``. If this is not the case, a warning
32-
is issued on stderr and that row of results is not passed on to graphite.
30+
The script will escape the location-string to make a valid graphite key, that
31+
is: replace any signs not in [-A-Za-z0-9_] with an underscore. A "location" of
32+
the form "10.0.0.1/24" will be transformed to "10_0_0_1_24". If there are no
33+
valid letters left after the escape, a warning is issued on stderr and that
34+
row of results is not passed on to graphite.
35+
36+
Extracting a vlan from locations
37+
--------------------------------
38+
39+
If the flag '--extract-vlan' is used, it will be assumed that the value of the
40+
``location``-key contains a vlan-name of the form "vlanNUMBER", regex
41+
``vlan\d+``. If this is not the case, a warning is issued on stderr and that
42+
row of results is not passed on to graphite.
3343

3444
The location-value is normalized so that a value of "Student vlan2 new" is sent
3545
to graphite as "vlan2".

contrib/scripts/isc_dhpcd_graphite/isc_dhpcd_graphite.py

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818
import sys
1919
from time import time
2020

21+
from nav.metrics.names import escape_metric_name
22+
23+
2124
DEFAULT_PREFIX = "nav.dhcp"
2225
DEFAULT_CONFIG_FILE = "/etc/dhcpd/dhcpd.conf"
2326
DEFAULT_CMD_PATH = pathlib.Path("/usr/bin/dhcpd-pools")
2427
DEFAULT_PORT = "2004"
25-
DEFAULT_PROTOCOL = 'text' # MB doesn't trust pickle so we go with text
28+
DEFAULT_PROTOCOL = "text" # MB doesn't trust pickle so we go with text
2629

2730
# graphite likes pickle protocol 2. Python 3: 3, Python 3.8+: 4
2831
PICKLE_PROTOCOL = range(0, pickle.HIGHEST_PROTOCOL + 1)
@@ -33,6 +36,7 @@
3336
"touched": "touch",
3437
"free": "free",
3538
}
39+
VERSION="0.2"
3640

3741

3842
Metric = namedtuple("Metric", ["path", "value", "timestamp"])
@@ -77,11 +81,27 @@ def parse_args():
7781
"--location",
7882
help=(
7983
"Location, if any, to append to the metric prefix to build the path."
80-
' If the vlan is named "vlan1" and the location is "building1.cellar"'
84+
' If the location is named "vlan1" and the location is "building1.cellar"'
8185
" the resulting metric path would be PREFIX.building1.cellar.vlan1"
8286
),
8387
type=str,
8488
)
89+
parser.add_argument(
90+
"--extract-vlan",
91+
help=(
92+
"Try to extract the name of a vlan from the location."
93+
' If the vlan is named "vlan1_baluba" '
94+
" the resulting metric path would be PREFIX.vlan1"
95+
),
96+
action="store_true",
97+
default=False,
98+
)
99+
parser.add_argument(
100+
"--version",
101+
help="Show version of script and exit",
102+
action="store_true",
103+
default=False,
104+
)
85105
protocol_choices = ("text",) + tuple(str(p) for p in PICKLE_PROTOCOL)
86106
parser.add_argument(
87107
"-P",
@@ -115,6 +135,16 @@ def parse_args():
115135
return args
116136

117137

138+
def get_config_from_args(args):
139+
config = None
140+
if getattr(args, "extract_vlan", False):
141+
class Config:
142+
pass
143+
config = Config()
144+
config.extract_vlan = True
145+
return config
146+
147+
118148
# run command and store json output
119149
def exec_dhcpd_pools(config_file, cmd_path=DEFAULT_CMD_PATH):
120150
flags = f"-c {config_file} {FLAGS}".split()
@@ -126,24 +156,24 @@ def exec_dhcpd_pools(config_file, cmd_path=DEFAULT_CMD_PATH):
126156

127157

128158
# reformat the data
129-
def render(jsonblob, prefix, protocol=DEFAULT_PROTOCOL):
159+
def render(jsonblob, prefix, protocol=DEFAULT_PROTOCOL, config=None):
130160
if isinstance(protocol, int):
131-
return _render_pickle(jsonblob, prefix, protocol)
132-
return _render_text(jsonblob, prefix)
161+
return _render_pickle(jsonblob, prefix, protocol, config)
162+
return _render_text(jsonblob, prefix, config)
133163

134164

135-
def _render_text(jsonblob, prefix):
165+
def _render_text(jsonblob, prefix, config=None):
136166
template = "{metric.path} {metric.value} {metric.timestamp}\n"
137-
input = _tuplify(jsonblob, prefix)
167+
input = _tuplify(jsonblob, prefix, config)
138168
output = []
139169
for metric in input:
140170
line = template.format(metric=metric)
141171
output.append(line)
142172
return "".join(output).encode("ascii")
143173

144174

145-
def _render_pickle(jsonblob, prefix, protocol):
146-
input = _tuplify(jsonblob, prefix)
175+
def _render_pickle(jsonblob, prefix, protocol,config=None):
176+
input = _tuplify(jsonblob, prefix, config)
147177
output = []
148178
for metric in input:
149179
output.append((metric.path, (metric.timestamp, metric.value)))
@@ -153,12 +183,14 @@ def _render_pickle(jsonblob, prefix, protocol):
153183
return message
154184

155185

156-
def _tuplify(jsonblob, prefix):
186+
def _tuplify(jsonblob, prefix, config=None):
157187
timestamp = trunc(time())
158188
data = jsonblob["shared-networks"]
159189
output = list()
160190
for vlan_stat in data:
161-
vlan = _clean_vlan(vlan_stat["location"])
191+
vlan = escape_metric_name(vlan_stat["location"])
192+
if config:
193+
vlan = _extract_vlan(vlan)
162194
if not vlan:
163195
continue
164196
for key, metric in METRIC_MAPPER.items():
@@ -168,7 +200,7 @@ def _tuplify(jsonblob, prefix):
168200
return output
169201

170202

171-
def _clean_vlan(location):
203+
def _extract_vlan(location):
172204
regex = re.search("vlan\d+", location)
173205
if regex:
174206
return regex.group()
@@ -193,13 +225,17 @@ def send_to_graphite(metrics_blob, server, port):
193225

194226
def main():
195227
args = parse_args()
228+
if args.version:
229+
print(f"version: {VERSION}")
230+
sys.exit(0)
231+
config = get_config_from_args(args)
196232
jsonblob = exec_dhcpd_pools(args.config_file, args.command)
197-
output = render(jsonblob, args.actual_prefix, args.protocol)
233+
output = render(jsonblob, args.actual_prefix, args.protocol, config)
198234
if args.noop:
199235
if args.protocol == "text":
200-
print(output.decode('ascii'))
236+
print(output.decode("ascii"))
201237
else:
202-
print(hexlify(output).decode('ascii'))
238+
print(hexlify(output).decode("ascii"))
203239
else:
204240
send_to_graphite(output, args.server, args.port)
205241

0 commit comments

Comments
 (0)