Skip to content

Commit

Permalink
Port shell script
Browse files Browse the repository at this point in the history
  • Loading branch information
lstrojny committed Nov 20, 2023
1 parent f5c56e7 commit fdcc68b
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 1 deletion.
2 changes: 1 addition & 1 deletion zfs-snapshots.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def zfs_list_snapshots():
break
yield stdout_line.decode("utf-8")
return_code = popen.wait()
if return_code:
if return_code > 0:
raise subprocess.CalledProcessError(return_code, cmd)


Expand Down
109 changes: 109 additions & 0 deletions zfs_zpool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env python3
import os
import subprocess
from concurrent.futures import ThreadPoolExecutor
from typing import Tuple

from prometheus_client import CollectorRegistry, Gauge, generate_latest

ZPOOL_METADATA_LABELS = ("health", "version", "readonly", "ashift", "autoreplace", "failmode")


def zpool_metadata(registry):
metric = Gauge("zpool", "Constant metric with metadata about the zpool",
labelnames=['zpool_name', *ZPOOL_METADATA_LABELS], namespace='zfs', registry=registry, )
cmd = ('zpool', 'list', '-H', '-o', 'name,' + ",".join(ZPOOL_METADATA_LABELS))
for constant_labels in run(cmd):
metric.labels(*constant_labels).set(1)


def run(cmd):
popen = subprocess.Popen(
cmd, stdout=subprocess.PIPE, env=dict(os.environ, LC_ALL="C")
)
for stdout_line in iter(popen.stdout.readline, ""):
if stdout_line == b"":
break
yield stdout_line.strip().decode("utf-8").split("\t")

return_code = popen.wait()
if return_code > 0:
raise subprocess.CalledProcessError(return_code, cmd)


ZPOOL_INFO_METRICS = (
("size", "Total size of the storage pool", "bytes"),
("free", "The amount of free space available in the pool", "bytes"),
("freeing", "The amount of space waiting to be reclaimed from destroyed filesystems or snapshots", "bytes"),
('dedupratio', "The deduplication ratio", ""),
("fragmentation", "The amount of fragmentation in the pool", "")
)


def zpool_info(registry):
cmd = ('zpool', 'list', '-Hp', '-o', "name," + ','.join([col for (col, *_) in ZPOOL_INFO_METRICS]))
metrics = {}
for line in run(cmd):
for (idx, (col, doc, unit)) in enumerate(ZPOOL_INFO_METRICS, 1):
if col not in metrics:
metrics[col] = Gauge(col, documentation=doc, unit=unit, namespace='zfs_zpool', registry=registry,
labelnames=["zpool_name"])
metrics[col].labels((line[0])).set(float(line[idx]))


DATASET_METADATA_LABELS = ("type", "creation", "mounted", "mountpoint", "checksum", "compression", "readonly",
"version", "dedup", "volblocksize")

DATASET_TYPES = ("filesystem", "volume")


def dataset_metadata(registry):
cmd = ("zfs", "list", "-Hp", "-t", ",".join(DATASET_TYPES), "-o", "name," + ",".join(DATASET_METADATA_LABELS))
metric = Gauge("dataset", documentation="Constant metric with metadata about the zfs dataset", namespace="zfs",
registry=registry, labelnames=["dataset_name", *DATASET_METADATA_LABELS])
for line in run(cmd):
metric.labels(*line).set(1)


DATASET_INFO_METRICS = (
("used", "The amount of space consumed by this dataset and all its descendents", "bytes"),
("available", "The amount of space available to the dataset and all its children", "bytes"),
("referenced",
"The amount of data that is accessible by this dataset, which may or may not be shared with other datasets in the pool",
"bytes"),
("compressratio",
"For non-snapshots, the compression ratio achieved for the used space of this dataset, expressed as a multiplier",
""),
("reservation", "The minimum amount of space guaranteed to a dataset and its descendants", "bytes"),
("refreservation", "The minimum amount of space guaranteed to a dataset, not including its descendents", "bytes"),
("volsize", "For volumes, specifies the logical size of the volume", "bytes")
)


def dataset_metrics(registry):
cmd = ("zfs", "list", "-Hp", "-t", ",".join(DATASET_TYPES), "-o", "name," + ",".join([col for (col, *_) in DATASET_INFO_METRICS]))
metrics = {}
for line in run(cmd):
for (idx, (col, doc, unit)) in enumerate(DATASET_INFO_METRICS, 1):
if col not in metrics:
metrics[col] = Gauge(col, documentation=doc, unit=unit, registry=registry, labelnames=["dataset_name"],
namespace="zfs_dataset")

if line[idx] == "-":
continue

metrics[col].labels((line[0])).set(float(line[idx].rstrip("x")))


def main():
registry = CollectorRegistry()

funcs = (zpool_metadata, zpool_info, dataset_metadata, dataset_metrics)
with ThreadPoolExecutor(max_workers=len(funcs)) as executor:
for func in funcs:
executor.submit(func, registry)

print(generate_latest(registry).decode(), end="")


main()

0 comments on commit fdcc68b

Please sign in to comment.