c58dd14bf7
Summary: So that it's possible to use it with any other blockdevice provider. Test Plan: N/A Reviewers: sina_rad, h.marvi, mhyousefi, s.afshari Differential Revision: https://phab.hamravesh.ir/D870
122 lines
3.5 KiB
Python
122 lines
3.5 KiB
Python
import json
|
|
import os
|
|
import subprocess
|
|
|
|
from prometheus_client.core import REGISTRY
|
|
from prometheus_client.exposition import start_http_server
|
|
from prometheus_client.metrics_core import GaugeMetricFamily
|
|
|
|
import rawfile_util
|
|
from rawfile_util import attached_loops
|
|
|
|
|
|
def path_stats(path):
|
|
fs_stat = os.statvfs(path)
|
|
return {
|
|
"fs_size": fs_stat.f_frsize * fs_stat.f_blocks,
|
|
"fs_free": fs_stat.f_frsize * fs_stat.f_bfree,
|
|
"fs_files": fs_stat.f_files,
|
|
"fs_files_free": fs_stat.f_ffree,
|
|
}
|
|
|
|
|
|
def volume_stats(volume_id: str) -> dict:
|
|
img_file = rawfile_util.img_file(volume_id)
|
|
dev_stat = img_file.stat()
|
|
stats = {
|
|
"dev_size": dev_stat.st_size,
|
|
"dev_free": dev_stat.st_size - dev_stat.st_blocks * 512,
|
|
}
|
|
mountpoint = volume_to_mountpoint(img_file)
|
|
if mountpoint is not None:
|
|
stats.update(path_stats(mountpoint))
|
|
return stats
|
|
|
|
|
|
class VolumeStatsCollector(object):
|
|
def collect(self):
|
|
VOLUME_ID = "volume_id"
|
|
fs_size = GaugeMetricFamily(
|
|
"rawfile_filesystem_size_bytes",
|
|
"Filesystem size in bytes.",
|
|
labels=[VOLUME_ID],
|
|
)
|
|
fs_free = GaugeMetricFamily(
|
|
"rawfile_filesystem_avail_bytes",
|
|
"Filesystem free space in bytes",
|
|
labels=[VOLUME_ID],
|
|
)
|
|
fs_files = GaugeMetricFamily(
|
|
"rawfile_filesystem_files",
|
|
"Filesystem total file nodes.",
|
|
labels=[VOLUME_ID],
|
|
)
|
|
fs_files_free = GaugeMetricFamily(
|
|
"rawfile_filesystem_files_free",
|
|
"Filesystem total free file nodes",
|
|
labels=[VOLUME_ID],
|
|
)
|
|
dev_size = GaugeMetricFamily(
|
|
"rawfile_device_size_bytes", "Device size in bytes.", labels=[VOLUME_ID]
|
|
)
|
|
dev_free = GaugeMetricFamily(
|
|
"rawfile_device_free_bytes",
|
|
"Device free space in bytes.",
|
|
labels=[VOLUME_ID],
|
|
)
|
|
|
|
for volume_id in rawfile_util.list_all_volumes():
|
|
labels = [volume_id]
|
|
key_to_gauge = {
|
|
"dev_size": dev_size,
|
|
"dev_free": dev_free,
|
|
"fs_size": fs_size,
|
|
"fs_free": fs_free,
|
|
"fs_files": fs_files,
|
|
"fs_files_free": fs_files_free,
|
|
}
|
|
stats = volume_stats(volume_id)
|
|
for key in stats.keys():
|
|
key_to_gauge[key].add_metric(labels, stats[key])
|
|
|
|
return [fs_size, fs_free, fs_files, fs_files_free, dev_size, dev_free]
|
|
|
|
|
|
def volume_to_mountpoint(img_file):
|
|
for dev in attached_loops(img_file):
|
|
mountpoint = dev_to_mountpoint(dev)
|
|
if mountpoint is not None:
|
|
return mountpoint
|
|
return None
|
|
|
|
|
|
def dev_to_mountpoint(dev_name):
|
|
try:
|
|
output = subprocess.run(
|
|
f"findmnt --json --first-only {dev_name}",
|
|
shell=True,
|
|
check=True,
|
|
capture_output=True,
|
|
).stdout.decode()
|
|
data = json.loads(output)
|
|
return data["filesystems"][0]["target"]
|
|
except subprocess.CalledProcessError:
|
|
return None
|
|
|
|
|
|
def mountpoint_to_dev(mountpoint):
|
|
res = subprocess.run(
|
|
f"findmnt --json --first-only --mountpoint {mountpoint}",
|
|
shell=True,
|
|
capture_output=True,
|
|
)
|
|
if res.returncode != 0:
|
|
return None
|
|
data = json.loads(res.stdout.decode().strip())
|
|
return data["filesystems"][0]["source"]
|
|
|
|
|
|
def expose_metrics():
|
|
REGISTRY.register(VolumeStatsCollector())
|
|
start_http_server(9100)
|