Implement basic metrics
This commit is contained in:
		@@ -17,7 +17,7 @@ Features
 | 
			
		||||
- [ ] Volume modes
 | 
			
		||||
    - [x] `Filesystem` mode
 | 
			
		||||
    - [ ] `Block` mode
 | 
			
		||||
- [ ] Volume metrics
 | 
			
		||||
- [x] Volume metrics
 | 
			
		||||
- [ ] Supports fsTypes
 | 
			
		||||
- [ ] Online expansion: If fs supports it (e.g. ext4, btrfs)
 | 
			
		||||
- [ ] Online shrinking: If fs supports it (e.g. btrfs)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										61
									
								
								metrics.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								metrics.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
import json
 | 
			
		||||
import os
 | 
			
		||||
import threading
 | 
			
		||||
from os.path import basename
 | 
			
		||||
 | 
			
		||||
from prometheus_client import Gauge
 | 
			
		||||
from prometheus_client.exposition import start_http_server
 | 
			
		||||
 | 
			
		||||
import rawfile_util
 | 
			
		||||
from rawfile_util import attached_loops
 | 
			
		||||
from util import run_out
 | 
			
		||||
 | 
			
		||||
VOLUME_ID = "volume_id"
 | 
			
		||||
 | 
			
		||||
fs_size = Gauge(
 | 
			
		||||
    "rawfile_filesystem_size_bytes", "Filesystem size in bytes.", [VOLUME_ID]
 | 
			
		||||
)
 | 
			
		||||
fs_free = Gauge(
 | 
			
		||||
    "rawfile_filesystem_avail_bytes", "Filesystem free space in bytes", [VOLUME_ID]
 | 
			
		||||
)
 | 
			
		||||
dev_size = Gauge("rawfile_device_size_bytes", "Device size in bytes.", [VOLUME_ID])
 | 
			
		||||
dev_free = Gauge(
 | 
			
		||||
    "rawfile_device_free_bytes", "Device free space in bytes.", [VOLUME_ID]
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def collect_stats():
 | 
			
		||||
    blockdevices = json.loads(run_out("lsblk --json").stdout.decode())["blockdevices"]
 | 
			
		||||
 | 
			
		||||
    def dev_to_mountpoint(dev_name):
 | 
			
		||||
        dev_name = basename(dev_name)
 | 
			
		||||
        matches = list(filter(lambda bd: bd["name"] == dev_name, blockdevices))
 | 
			
		||||
        if len(matches) == 0:
 | 
			
		||||
            return None
 | 
			
		||||
        return matches[0]["mountpoint"]
 | 
			
		||||
 | 
			
		||||
    for volume_id in rawfile_util.list_all_volumes():
 | 
			
		||||
        img_file = rawfile_util.img_file(volume_id)
 | 
			
		||||
        labels = {VOLUME_ID: volume_id}
 | 
			
		||||
        dev_stat = img_file.stat()
 | 
			
		||||
        dev_size.labels(**labels).set(dev_stat.st_size)
 | 
			
		||||
        dev_free.labels(**labels).set(
 | 
			
		||||
            dev_stat.st_size - dev_stat.st_blocks * dev_stat.st_blksize
 | 
			
		||||
        )
 | 
			
		||||
        for dev in attached_loops(img_file):
 | 
			
		||||
            mountpoint = dev_to_mountpoint(dev)
 | 
			
		||||
            if mountpoint is None:
 | 
			
		||||
                continue
 | 
			
		||||
            fs_stat = os.statvfs(mountpoint)
 | 
			
		||||
            fs_size.labels(**labels).set(fs_stat.f_frsize * fs_stat.f_blocks)
 | 
			
		||||
            fs_free.labels(**labels).set(fs_stat.f_frsize * fs_stat.f_bfree)
 | 
			
		||||
            break
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def expose_metrics():
 | 
			
		||||
    def collector_loop():
 | 
			
		||||
        collect_stats()
 | 
			
		||||
        threading.Timer(10, collector_loop).start()
 | 
			
		||||
 | 
			
		||||
    collector_loop()
 | 
			
		||||
    start_http_server(9100)
 | 
			
		||||
@@ -7,6 +7,7 @@ import grpc
 | 
			
		||||
 | 
			
		||||
import rawfile_servicer
 | 
			
		||||
from csi import csi_pb2_grpc
 | 
			
		||||
from metrics import expose_metrics
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@click.group()
 | 
			
		||||
@@ -17,7 +18,10 @@ def cli():
 | 
			
		||||
@cli.command()
 | 
			
		||||
@click.option("--endpoint", envvar="CSI_ENDPOINT", default="0.0.0.0:5000")
 | 
			
		||||
@click.option("--nodeid", envvar="NODE_ID")
 | 
			
		||||
def csi_driver(endpoint, nodeid):
 | 
			
		||||
@click.option("--enable-metrics/--disable-metrics", default=True)
 | 
			
		||||
def csi_driver(endpoint, nodeid, enable_metrics):
 | 
			
		||||
    if enable_metrics:
 | 
			
		||||
        expose_metrics()
 | 
			
		||||
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
 | 
			
		||||
    csi_pb2_grpc.add_IdentityServicer_to_server(
 | 
			
		||||
        rawfile_servicer.RawFileIdentityServicer(), server
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
import glob
 | 
			
		||||
import json
 | 
			
		||||
from os.path import basename, dirname
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
from consts import DATA_DIR
 | 
			
		||||
@@ -53,3 +55,8 @@ def attach_loop(file) -> str:
 | 
			
		||||
            return devs[0]
 | 
			
		||||
        next_loop()
 | 
			
		||||
        run(f"losetup --direct-io=on -f {file}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def list_all_volumes():
 | 
			
		||||
    metas = glob.glob(f"{DATA_DIR}/*/disk.meta")
 | 
			
		||||
    return [basename(dirname(meta)) for meta in metas]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,3 +4,4 @@ click
 | 
			
		||||
pyyaml
 | 
			
		||||
pykube-ng
 | 
			
		||||
munch
 | 
			
		||||
prometheus_client
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ grpcio-tools==1.28.1      # via -r requirements.in
 | 
			
		||||
grpcio==1.28.1            # via -r requirements.in, grpcio-tools
 | 
			
		||||
idna==2.9                 # via requests
 | 
			
		||||
munch==2.5.0              # via -r requirements.in
 | 
			
		||||
prometheus-client==0.7.1  # via -r requirements.in
 | 
			
		||||
protobuf==3.11.3          # via grpcio-tools
 | 
			
		||||
pykube-ng==20.4.1         # via -r requirements.in
 | 
			
		||||
pyyaml==5.3.1             # via -r requirements.in, pykube-ng
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user