Skip to content

Commit

Permalink
New metric namespace implemented and working fine.
Browse files Browse the repository at this point in the history
  • Loading branch information
root committed Sep 11, 2020
1 parent 9aeebd8 commit e9d11dd
Show file tree
Hide file tree
Showing 12 changed files with 122 additions and 240 deletions.
20 changes: 0 additions & 20 deletions abdstats.py

This file was deleted.

20 changes: 0 additions & 20 deletions arcstats.py

This file was deleted.

20 changes: 0 additions & 20 deletions dbufstats.py

This file was deleted.

20 changes: 0 additions & 20 deletions dmu_tx.py

This file was deleted.

20 changes: 0 additions & 20 deletions dnodestats.py

This file was deleted.

35 changes: 14 additions & 21 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,26 @@

app = Flask('zfs_exporter')

import arcstats
import abdstats
import zfetchstats
import dnodestats
import dbufstats
import dmu_tx
import xuiostats
import zilstats
import vdevstats
import pools
from zfx_collectors import StatsCollector, PoolstatsCollector

REGISTRY.unregister(PROCESS_COLLECTOR)
REGISTRY.unregister(PLATFORM_COLLECTOR)
REGISTRY.register(arcstats.ArcstatsCollector())
REGISTRY.register(abdstats.AbdstatsCollector())
REGISTRY.register(zfetchstats.ZfetchstatsCollector())
REGISTRY.register(dnodestats.DnodestatsCollector())
REGISTRY.register(dbufstats.DbufstatsCollector())
REGISTRY.register(dmu_tx.DmutxCollector())
REGISTRY.register(xuiostats.XuiostatsCollector())
REGISTRY.register(zilstats.ZilstatsCollector())
REGISTRY.register(vdevstats.VdevstatsCollector())
REGISTRY.register(pools.PoolstatsCollector())
REGISTRY.register(StatsCollector('/proc/spl/kstat/zfs/arcstats', 'zfs_arc_', 'ARC statistics'))
REGISTRY.register(StatsCollector('/proc/spl/kstat/zfs/abdstats', 'zfs_abd_', 'ABD statistics'))
REGISTRY.register(StatsCollector('/proc/spl/kstat/zfs/dbufstats', 'zfs_dbuf_', 'DBUF statistics'))
REGISTRY.register(StatsCollector('/proc/spl/kstat/zfs/dnodestats', 'zfs_', 'DNode statistics'))
REGISTRY.register(StatsCollector('/proc/spl/kstat/zfs/dmu_tx', 'zfs_', 'DMU TX statistics'))
REGISTRY.register(StatsCollector('/proc/spl/kstat/zfs/vdev_cache_stats', 'zfs_vdev_cache_', 'VDev cache statistics'))
REGISTRY.register(StatsCollector('/proc/spl/kstat/zfs/vdev_mirror_stats', 'zfs_vdev_mirror_', 'VDev mirror statistics'))
REGISTRY.register(StatsCollector('/proc/spl/kstat/zfs/xuio_stats', 'zfs_xuio_', 'XUIO statistics'))
REGISTRY.register(StatsCollector('/proc/spl/kstat/zfs/zfetchstats', 'zfs_zfetch_', 'ZFetch statistics'))
REGISTRY.register(StatsCollector('/proc/spl/kstat/zfs/zil', 'zfs_', 'ZIL statistics'))
REGISTRY.register(PoolstatsCollector())


@app.route('/')
def status():
return Response('PID: {}\n'.format(os.getpid()))
return Response('ZFS Exporter is running (PID: {})\n'.format(os.getpid()))


@app.route('/metrics')
Expand Down
27 changes: 0 additions & 27 deletions pools.py

This file was deleted.

32 changes: 0 additions & 32 deletions vdevstats.py

This file was deleted.

20 changes: 0 additions & 20 deletions xuiostats.py

This file was deleted.

20 changes: 0 additions & 20 deletions zfetchstats.py

This file was deleted.

108 changes: 108 additions & 0 deletions zfx_collectors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import os
import re
from prometheus_client.core import GaugeMetricFamily

class StatsCollector(object):
"""
Generic ZFS statistic collector.
"""
def __init__(self, path, m_pfx, m_desc):
"""
Initialized the collector object.
Parameters
----------
path : str
The full path to the metric file.
m_pfx : str
Prefix for the names of generated metrics.
m_desc : str
Metric description string that will be set for every metric from the file.
"""
self.path = path
self.m_pfx = m_pfx
self.m_desc = m_desc


def collect(self):
"""
Collects metrics from the stats file. The expected syntax of the lines
with metric data has 3 columns, ie.:
hits 4 168948958
misses 4 4767869
...
Returns
-------
Yields metrics of the Gauge type.
"""
try:
with open(self.path, 'r') as f:
mline = re.compile('^[_a-z]+ +[0-9] +[0-9]+$')
for l in f:
if not mline.match(l):
continue
items = re.split(r' +', l)
if len(items) != 3 and type(items[1]) == int:
continue
yield GaugeMetricFamily(self.m_pfx + items[0], self.m_desc, value=float(items[2]))
except Exception as e:
print('Error: {}'.format(str(e)))


class PoolstatsCollector(object):
"""
Per pool statistic collector.
"""
def __init__(self):
self.states = { # valid pool states
'ONLINE': 1.0,
'DEGRADED': 0.2,
'FAULTED': 0.1,
'OFFLINE': 0.0,
'UNAVAIL': -1.0,
'REMOVED': -2.0
}


def get_state_num(self, state):
"""
Translates the state string into the corresponding float number
or returns -3.0 if not found.
"""
return self.states[state] if state in self.states.keys() else -3.0 # UNKNOWN


def collect(self):
"""
Discovers the pools by searching for folders in /proc/spl/kstat/zfs,
then extracts the state and IO stats from the files.
Returns
-------
Yields metrics of the Gauge type.
"""
# Metric declarations
# Find the ZFS pools
for pooldir in [f.path for f in os.scandir('/proc/spl/kstat/zfs') if f.is_dir()]:
poolname = pooldir.split('/')[-1]
# Load state from file
try:
with open(pooldir + '/state', 'r') as f:
for l in f:
STATE = GaugeMetricFamily('zfs_pool_state', 'Pool state (ONLINE = 1, OFFLINE = 0)', labels=['pool'])
STATE.add_metric([poolname], self.get_state_num(l.strip()))
yield STATE
except Exception as e:
print('Error: {}'.format(str(e)))
# Load IO stats from file
iostats_p = pooldir + '/io'
lines = open(iostats_p, 'r').readlines() if os.path.isfile(iostats_p) else []
if len(lines) > 2:
keys = re.split(r' +', lines[1].strip())
vals = re.split(r' +', lines[2].strip())
for (key, value) in zip(keys, vals):
IOSTATS = GaugeMetricFamily('zfs_pool_io_' + key, 'Pool IO statistics', labels=['pool'])
IOSTATS.add_metric([poolname], value)
yield IOSTATS
20 changes: 0 additions & 20 deletions zilstats.py

This file was deleted.

0 comments on commit e9d11dd

Please sign in to comment.