diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
commit | 47e6e7c84f008a53061e661f31ae96629bc694ef (patch) | |
tree | 648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /src/pmdas/dmcache | |
download | pcp-debian.tar.gz |
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'src/pmdas/dmcache')
-rw-r--r-- | src/pmdas/dmcache/GNUmakefile | 37 | ||||
-rw-r--r-- | src/pmdas/dmcache/Install | 35 | ||||
-rw-r--r-- | src/pmdas/dmcache/Remove | 25 | ||||
-rw-r--r-- | src/pmdas/dmcache/pmdadmcache.python | 264 |
4 files changed, 361 insertions, 0 deletions
diff --git a/src/pmdas/dmcache/GNUmakefile b/src/pmdas/dmcache/GNUmakefile new file mode 100644 index 0000000..243f08f --- /dev/null +++ b/src/pmdas/dmcache/GNUmakefile @@ -0,0 +1,37 @@ +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +IAM = dmcache +PYSCRIPT = pmda$(IAM).python +LSRCFILES = Install Remove $(PYSCRIPT) + +DOMAIN = DMCACHE +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = domain.h $(IAM).log + +default_pcp default: check_domain + +include $(BUILDRULES) + +install_pcp install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 $(PYSCRIPT) $(PMDADIR)/$(PYSCRIPT) + +check_domain: ../../pmns/stdpmid + $(DOMAIN_PYTHONRULE) diff --git a/src/pmdas/dmcache/Install b/src/pmdas/dmcache/Install new file mode 100644 index 0000000..64ef3fa --- /dev/null +++ b/src/pmdas/dmcache/Install @@ -0,0 +1,35 @@ +#! /bin/sh +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the Device Mapper Cache PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=dmcache +python_opt=true +daemon_opt=false +forced_restart=true + +which dmsetup >/dev/null 2>&1 +if [ $? -ne 0 ] +then + echo "Device Mapper 'dmsetup' binary not found, cannot proceed" + exit 1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/dmcache/Remove b/src/pmdas/dmcache/Remove new file mode 100644 index 0000000..6586d55 --- /dev/null +++ b/src/pmdas/dmcache/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the Device Mapper Cache PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=dmcache + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/dmcache/pmdadmcache.python b/src/pmdas/dmcache/pmdadmcache.python new file mode 100644 index 0000000..35db1a0 --- /dev/null +++ b/src/pmdas/dmcache/pmdadmcache.python @@ -0,0 +1,264 @@ +''' +Performance Metrics Domain Agent exporting Device Mapper Cache metrics. +''' +# +# Copyright (c) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +from pcp.pmda import PMDA, pmdaMetric, pmdaIndom, pmdaUnits +from ctypes import POINTER, Structure, cast, c_ulonglong, c_uint +from cpmapi import (PM_SEM_DISCRETE, PM_SEM_INSTANT, PM_SEM_COUNTER, + PM_ERR_INST, PM_ERR_PMID, PM_SPACE_BYTE, PM_SPACE_KBYTE, + PM_TYPE_U32, PM_TYPE_U64, PM_TYPE_STRING, PM_COUNT_ONE) +from subprocess import PIPE, Popen +from os import getenv + +IOMODES = { 'writeback': 1, 'writethrough': 2, 'passthrough': 3 } + +class DmCacheStats(Structure): + ''' Metric values associated with each dm-cache target ''' + _fields_ = [("size", c_ulonglong), + ("metadata_block_size", c_uint), + ("metadata_used", c_ulonglong), + ("metadata_total", c_ulonglong), + ("cache_block_size", c_uint), + ("cache_used", c_ulonglong), + ("cache_total", c_ulonglong), + ("read_hits", c_uint), + ("read_misses", c_uint), + ("write_hits", c_uint), + ("write_misses", c_uint), + ("demotions", c_uint), + ("promotions", c_uint), + ("dirty", c_ulonglong), + ("iomode", c_uint)] + + def __init__(self, text): + Structure.__init__(self) + self.parse(text) + + def parse(self, line): + ''' + Parse an individual line of dmcache statistics text. The format is: + <name>: <start> <end> <target> [Values...] + Values are as in Documentation/device-mapper/cache.txt Status section: + <metadata block size> <#used metadata blocks>/<#total metadata blocks> + <cache block size> <#used cache blocks>/<#total cache blocks> + <#read hits> <#read misses> <#write hits> <#write misses> + <#demotions> <#promotions> <#dirty> <#features> <features>* + ''' + data = line.replace('/', ' ').split(' ') + if len(data) < 19: + raise ValueError(line) + mbsize = long(data[4]) * 512 # metadata block size (from sectors) + cbsize = long(data[7]) * 512 # cachedev block size (from sectors) + + self.size = long(data[2]) - long(data[1]) + self.metadata_block_size = mbsize + self.metadata_used = long(data[5]) * mbsize / 1024 + self.metadata_total = long(data[6]) * mbsize / 1024 + self.cache_block_size = cbsize + self.cache_used = long(data[8]) * cbsize / 1024 + self.cache_total = long(data[9]) * cbsize / 1024 + self.read_hits = long(data[10]) + self.read_misses = long(data[11]) + self.write_hits = long(data[12]) + self.write_misses = long(data[13]) + self.demotions = long(data[14]) + self.promotions = long(data[15]) + self.dirty = long(data[16]) * cbsize / 1024 + self.iomode = IOMODES[data[18]] + + def io_mode(self): + ''' Human-readable representation of numeric io_mode encoding ''' + for mode in IOMODES: + if self.iomode == IOMODES[mode]: + return mode + return 'unknown' + +class DmCachePMDA(PMDA): + ''' + Performance Metrics Domain Agent exporting dm-cache module metrics. + Install and make basic use of it, if you use dm-cache, as follows: + # $PCP_PMDAS_DIR/dmcache/Install + $ pminfo -fmdtT dmcache + ''' + + def __init__(self, name, domain): + ''' + Setup metric table (see drivers/md/dm-cache-target.c::cache_status), + the cache devices instance domain, and our callback routines. + ''' + PMDA.__init__(self, name, domain) + + # dm-cache devices instance domain + self.caches = {} + self.dmstatus = getenv('DM_STATUS', 'dmsetup status --target=cache') + + self.cache_indom = self.indom(0) + self.add_indom(pmdaIndom(self.cache_indom, self.caches)) + + self.set_fetch(self.dmcache_refresh) + self.set_fetch_callback(self.dmcache_fetch_callback) + + self.add_metric('dmcache.size', + pmdaMetric(self.pmid(0, 0), + PM_TYPE_U64, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(1, 0, 0, PM_SPACE_KBYTE, 0, 0)), + 'Total size of each cache device (Kbytes)') + + self.add_metric('dmcache.metadata.block_size', + pmdaMetric(self.pmid(0, 1), + PM_TYPE_U32, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(1, 0, 0, PM_SPACE_BYTE, 0, 0)), + 'Fixed block size for each metadata block in bytes') + self.add_metric('dmcache.metadata.used', + pmdaMetric(self.pmid(0, 2), + PM_TYPE_U64, self.cache_indom, PM_SEM_INSTANT, + pmdaUnits(1, 0, 0, PM_SPACE_KBYTE, 0, 0)), + 'Number of metadata blocks used') + self.add_metric('dmcache.metadata.total', + pmdaMetric(self.pmid(0, 3), + PM_TYPE_U64, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(1, 0, 0, PM_SPACE_KBYTE, 0, 0)), + 'Total number of metadata blocks available') + + self.add_metric('dmcache.cache.block_size', + pmdaMetric(self.pmid(0, 4), + PM_TYPE_U32, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(1, 0, 0, PM_SPACE_BYTE, 0, 0)), + 'Fixed block size for each metadata block in bytes') + self.add_metric('dmcache.cache.used', + pmdaMetric(self.pmid(0, 5), + PM_TYPE_U64, self.cache_indom, PM_SEM_INSTANT, + pmdaUnits(1, 0, 0, PM_SPACE_KBYTE, 0, 0)), + 'Number of cache blocks used') + self.add_metric('dmcache.cache.total', + pmdaMetric(self.pmid(0, 6), + PM_TYPE_U64, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(1, 0, 0, PM_SPACE_KBYTE, 0, 0)), + 'Total number of cache blocks available') + + self.add_metric('dmcache.read_hits', + pmdaMetric(self.pmid(0, 7), + PM_TYPE_U32, self.cache_indom, PM_SEM_COUNTER, + pmdaUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)), + 'Number of times a READ bio has been mapped to the cache') + self.add_metric('dmcache.read_misses', + pmdaMetric(self.pmid(0, 8), + PM_TYPE_U32, self.cache_indom, PM_SEM_COUNTER, + pmdaUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)), + 'Number of times a READ bio has been mapped to the origin') + self.add_metric('dmcache.write_hits', + pmdaMetric(self.pmid(0, 9), + PM_TYPE_U32, self.cache_indom, PM_SEM_COUNTER, + pmdaUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)), + 'Number of times a WRITE bio has been mapped to the cache') + self.add_metric('dmcache.write_misses', + pmdaMetric(self.pmid(0, 10), + PM_TYPE_U32, self.cache_indom, PM_SEM_COUNTER, + pmdaUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)), + 'Number of times a WRITE bio has been mapped to the origin') + + self.add_metric('dmcache.demotions', + pmdaMetric(self.pmid(0, 11), + PM_TYPE_U32, self.cache_indom, PM_SEM_COUNTER, + pmdaUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)), + 'Number of times a block has been removed from the cache') + self.add_metric('dmcache.promotions', + pmdaMetric(self.pmid(0, 12), + PM_TYPE_U32, self.cache_indom, PM_SEM_COUNTER, + pmdaUnits(0, 0, 1, 0, 0, PM_COUNT_ONE)), + 'Number of times a block has been moved to the cache') + + self.add_metric('dmcache.dirty', + pmdaMetric(self.pmid(0, 13), + PM_TYPE_U64, self.cache_indom, PM_SEM_INSTANT, + pmdaUnits(1, 0, 0, PM_SPACE_KBYTE, 0, 0)), + 'Number of blocks in the cache that differ from the origin') + self.add_metric('dmcache.io_mode_code', + pmdaMetric(self.pmid(0, 14), + PM_TYPE_U32, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(0, 0, 0, 0, 0, 0)), + 'Cache mode code - writeback, writethrough or passthrough') + self.add_metric('dmcache.io_mode', + pmdaMetric(self.pmid(1, 0), + PM_TYPE_STRING, self.cache_indom, PM_SEM_DISCRETE, + pmdaUnits(0, 0, 0, 0, 0, 0)), + 'Cache mode string - writeback, writethrough or passthrough') + + def dmcache_refresh(self): + ''' Refresh the values and instances for all device mapper caches ''' + pipe = Popen(self.dmstatus, shell = True, stdout = PIPE, stderr = PIPE) + output, errors = pipe.communicate() + if errors: + self.err("dmcache_refresh: %s error: %s" % (self.dmstatus, errors)) + + self.caches.clear() + for line in output.splitlines(): + if 'No devices found' in line: + continue + name = line[:line.find(':')] # extract cache name + self.caches[name] = DmCacheStats(line) # extract stats values + self.replace_indom(self.cache_indom, self.caches) + + def dmcache_fetch_callback(self, cluster, item, inst): + ''' + Look up value associated with requested instance (inst) of a + given metric (cluster:item) PMID, returning a [value,status] + ''' + value = self.inst_lookup(self.cache_indom, inst) + if (value == None): + return [PM_ERR_INST, 0] + value = cast(value, POINTER(DmCacheStats)) + cache = value.contents + + if cluster == 1 and item == 0: + return [cache.io_mode(), 1] # human-readable string encoding + elif cluster != 0: + return [PM_ERR_PMID, 0] + + if item == 0: + return [cache.size, 1] + elif item == 1: + return [cache.metadata_block_size, 1] + elif item == 2: + return [cache.metadata_used, 1] + elif item == 3: + return [cache.metadata_total, 1] + elif item == 4: + return [cache.cache_block_size, 1] + elif item == 5: + return [cache.cache_used, 1] + elif item == 6: + return [cache.cache_total, 1] + elif item == 7: + return [cache.read_hits, 1] + elif item == 8: + return [cache.read_misses, 1] + elif item == 9: + return [cache.write_hits, 1] + elif item == 10: + return [cache.write_misses, 1] + elif item == 11: + return [cache.demotions, 1] + elif item == 12: + return [cache.promotions, 1] + elif item == 13: + return [cache.dirty, 1] + elif item == 14: + return [cache.iomode, 1] + return [PM_ERR_PMID, 0] + +if __name__ == '__main__': + DmCachePMDA('dmcache', 129).run() |