''' Python implementation of the "simple" Performance Metrics Domain Agent. ''' # # Copyright (c) 2013 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. # import os import time import ctypes from ctypes import c_int, POINTER, cast import cpmapi as c_api from pcp.pmda import PMDA, pmdaMetric, pmdaIndom, pmdaInstid from pcp.pmapi import pmUnits, pmContext as PCP class SimplePMDA(PMDA): ''' A simple Performance Metrics Domain Agent with very simple metrics. Install it and make basic use of it, as follows: # $PCP_PMDAS_DIR/simple/Install [select python option] $ pminfo -fmdtT simple Then experiment with the simple.conf file (see simple.now metrics). ''' # simple.color instance domain red = 0 green = 100 blue = 200 colors = [pmdaInstid(0, 'red'), pmdaInstid(1, 'green'), pmdaInstid(2, 'blue')] # simple.now instance domain configfile = '' timeslices = {} times = () # simple.numfetch properties numfetch = 0 oldfetch = -1 def simple_instance(self, serial): ''' Called once per "instance request" PDU ''' # self.log("instance update for %d" % serial) if (serial == 1): self.simple_timenow_check() def simple_fetch(self): ''' Called once per "fetch" PDU, before callbacks ''' self.numfetch += 1 self.simple_timenow_check() def simple_fetch_color_callback(self, item, inst): ''' Returns a list of value,status (single pair) for color cluster Helper for the fetch callback ''' if (item == 0): if (inst != c_api.PM_IN_NULL): return [c_api.PM_ERR_INST, 0] return [self.numfetch, 1] elif (item == 1): if (inst == 0): self.red = (self.red + 1) % 255 return [self.red, 1] elif (inst == 1): self.green = (self.green + 1) % 255 return [self.green, 1] elif (inst == 2): self.blue = (self.blue + 1) % 255 return [self.blue, 1] else: return [c_api.PM_ERR_INST, 0] else: return [c_api.PM_ERR_PMID, 0] def simple_fetch_times_callback(self, item, inst): ''' Returns a list of value,status (single pair) for times cluster Helper for the fetch callback ''' if (inst != c_api.PM_IN_NULL): return [c_api.PM_ERR_INST, 0] if (self.oldfetch < self.numfetch): # get current values, if needed self.times = os.times() self.oldfetch = self.numfetch if (item == 2): return [self.times[0], 1] elif (item == 3): return [self.times[1], 1] return [c_api.PM_ERR_PMID, 0] def simple_fetch_now_callback(self, item, inst): ''' Returns a list of value,status (single pair) for "now" cluster Helper for the fetch callback ''' if (item == 4): voidp = self.inst_lookup(self.now_indom, inst) if (voidp == None): return [c_api.PM_ERR_INST, 0] valuep = cast(voidp, POINTER(c_int)) return [valuep.contents.value, 1] return [c_api.PM_ERR_PMID, 0] def simple_fetch_callback(self, cluster, item, inst): ''' Main fetch callback, defers to helpers for each cluster. Returns a list of value,status (single pair) for requested pmid/inst ''' # self.log("fetch callback for %d.%d[%d]" % (cluster, item, inst)) if (cluster == 0): return self.simple_fetch_color_callback(item, inst) elif (cluster == 1): return self.simple_fetch_times_callback(item, inst) elif (cluster == 2): return self.simple_fetch_now_callback(item, inst) return [c_api.PM_ERR_PMID, 0] def simple_store_count_callback(self, val): ''' Helper for the store callback, handles simple.numfetch ''' sts = 0 if (val < 0): sts = c_api.PM_ERR_SIGN val = 0 self.numfetch = val return sts def simple_store_color_callback(self, val, inst): ''' Helper for the store callback, handles simple.color ''' sts = 0 if (val < 0): sts = c_api.PM_ERR_SIGN val = 0 elif (val > 255): sts = c_api.PM_ERR_CONV val = 255 if (inst == 0): self.red = val elif (inst == 1): self.green = val elif (inst == 2): self.blue = val else: sts = c_api.PM_ERR_INST return sts def simple_store_callback(self, cluster, item, inst, val): ''' Store callback, executed when a request to write to a metric happens Defers to helpers for each storable metric. Returns a single value. ''' if (cluster == 0): if (item == 0): return self.simple_store_count_callback(val) elif (item == 1): return self.simple_store_color_callback(val, inst) else: return c_api.PM_ERR_PMID elif ((cluster == 1 and (item == 2 or item == 3)) or (cluster == 2 and item == 4)): return c_api.PM_ERR_PERMISSION return c_api.PM_ERR_PMID def simple_timenow_check(self): ''' Read our configuration file and update instance domain ''' self.timeslices.clear() try: cfg = open(self.configfile) fields = time.localtime() values = {'sec': c_int(fields[5]), 'min': c_int(fields[4]), 'hour': c_int(fields[3])} config = cfg.readline().strip() for key in config.split(','): if (key != '' and values[key] != None): self.timeslices[key] = values[key] finally: cfg.close() self.replace_indom(self.now_indom, self.timeslices) def __init__(self, name, domain): PMDA.__init__(self, name, domain) self.configfile = PCP.pmGetConfig('PCP_PMDAS_DIR') self.configfile += '/' + name + '/' + name + '.conf' self.connect_pmcd(); self.color_indom = self.indom(0) self.add_indom(pmdaIndom(self.color_indom, self.colors)) self.now_indom = self.indom(1) self.add_indom(pmdaIndom(self.now_indom, self.timeslices)) self.add_metric(name + '.numfetch', pmdaMetric(self.pmid(0, 0), c_api.PM_TYPE_U32, c_api.PM_INDOM_NULL, c_api.PM_SEM_INSTANT, pmUnits(0, 0, 0, 0, 0, 0))) self.add_metric(name + '.color', pmdaMetric(self.pmid(0, 1), c_api.PM_TYPE_32, self.color_indom, c_api.PM_SEM_INSTANT, pmUnits(0, 0, 0, 0, 0, 0))) self.add_metric(name + '.time.user', pmdaMetric(self.pmid(1, 2), c_api.PM_TYPE_DOUBLE, c_api.PM_INDOM_NULL, c_api.PM_SEM_COUNTER, pmUnits(0, 1, 0, 0, c_api.PM_TIME_SEC, 0))) self.add_metric(name + '.time.sys', pmdaMetric(self.pmid(1, 3), c_api.PM_TYPE_DOUBLE, c_api.PM_INDOM_NULL, c_api.PM_SEM_COUNTER, pmUnits(0, 1, 0, 0, c_api.PM_TIME_SEC, 0))) self.add_metric(name + '.now', pmdaMetric(self.pmid(2, 4), c_api.PM_TYPE_U32, self.now_indom, c_api.PM_SEM_INSTANT, pmUnits(0, 0, 0, 0, 0, 0))) self.set_fetch(self.simple_fetch) self.set_instance(self.simple_instance) self.set_fetch_callback(self.simple_fetch_callback) self.set_store_callback(self.simple_store_callback) self.set_user(PCP.pmGetConfig('PCP_USER')) self.simple_timenow_check() if __name__ == '__main__': SimplePMDA('simple', 253).run()