-
Notifications
You must be signed in to change notification settings - Fork 193
/
zfs-snapshots.py
executable file
·101 lines (81 loc) · 2.58 KB
/
zfs-snapshots.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#!/usr/bin/env python3
import os
import subprocess
from functools import reduce, partial
from itertools import groupby
from operator import itemgetter, add
from prometheus_client import CollectorRegistry, Gauge, generate_latest
def row_to_metric(metric: Gauge, row):
return metric.labels(pool=row[0][0], volume=row[0][1]).set(row[1])
def collect_metrics(metric: Gauge, it) -> None:
list(map(partial(row_to_metric, metric), it))
def zfs_parse_line(line):
cols = line.split("\t")
rest, snapshot = cols[0].rsplit("@", 1)
pool = rest
volume = None
if "/" in rest:
pool, volume = rest.split("/", 1)
volume = "/" + volume
return pool, volume, snapshot, *map(int, cols[1:])
def zfs_list_snapshots():
cmd = [
"zfs",
"list",
"-p",
"-H",
"-t",
"snapshot",
"-o",
"name,used,creation",
]
# zfs list can be relatively slow (couple of seconds)
# Use Popen to incrementally read from stdout to not waste further time
popen = subprocess.Popen(
cmd, stdout=subprocess.PIPE, env=dict(os.environ, LC_ALL="C")
)
for stdout_line in iter(popen.stdout.readline, ""):
stdout_line = stdout_line.strip()
if stdout_line == b"":
break
yield stdout_line.decode("utf-8")
return_code = popen.wait()
if return_code:
raise subprocess.CalledProcessError(return_code, cmd)
def aggregate_rows(rows, index, operator):
return map(
lambda row: (row[0], reduce(operator, map(itemgetter(index), row[1]), 0)), rows
)
NAMESPACE = "zfs_snapshot"
LABEL_NAMES = ["pool", "volume"]
def main():
registry = CollectorRegistry()
latest_time_metric = Gauge(
"latest_time",
"Timestamp of the latest snapshot",
labelnames=LABEL_NAMES,
namespace=NAMESPACE,
registry=registry,
unit="seconds",
)
space_used_metric = Gauge(
"space_used",
"Space used by snapshots in bytes",
labelnames=LABEL_NAMES,
namespace=NAMESPACE,
registry=registry,
unit="bytes",
)
snapshots = map(zfs_parse_line, zfs_list_snapshots())
per_fs = list(
map(
lambda row: (row[0], list(row[1])), groupby(snapshots, lambda row: row[0:2])
)
)
space_used = aggregate_rows(per_fs, -2, add)
latest_time = aggregate_rows(per_fs, -1, max)
collect_metrics(latest_time_metric, latest_time)
collect_metrics(space_used_metric, space_used)
print(generate_latest(registry).decode(), end="")
if __name__ == "__main__":
main()