diff options
Diffstat (limited to 'src/pmdas/simple')
-rw-r--r-- | src/pmdas/simple/GNUmakefile | 46 | ||||
-rw-r--r-- | src/pmdas/simple/GNUmakefile.install | 53 | ||||
-rw-r--r-- | src/pmdas/simple/Install | 53 | ||||
-rw-r--r-- | src/pmdas/simple/README | 67 | ||||
-rw-r--r-- | src/pmdas/simple/Remove | 25 | ||||
-rw-r--r-- | src/pmdas/simple/help | 82 | ||||
-rw-r--r-- | src/pmdas/simple/pmdasimple.perl | 158 | ||||
-rw-r--r-- | src/pmdas/simple/pmdasimple.python | 244 | ||||
-rw-r--r-- | src/pmdas/simple/pmns | 27 | ||||
-rw-r--r-- | src/pmdas/simple/root | 10 | ||||
-rw-r--r-- | src/pmdas/simple/simple.c | 517 | ||||
-rw-r--r-- | src/pmdas/simple/simple.conf | 1 |
12 files changed, 1283 insertions, 0 deletions
diff --git a/src/pmdas/simple/GNUmakefile b/src/pmdas/simple/GNUmakefile new file mode 100644 index 0000000..a0c50bb --- /dev/null +++ b/src/pmdas/simple/GNUmakefile @@ -0,0 +1,46 @@ +# +# Copyright (c) 2012-2013 Red Hat. +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# 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 + +CFILES = simple.c +CMDTARGET = pmdasimple$(EXECSUFFIX) +LLDLIBS = $(PCP_PMDALIB) +LCFLAGS = -I. +DFILES = README help +SCRIPTS = pmdasimple.perl pmdasimple.python +LSRCFILES = Install Remove pmns root $(DFILES) $(SCRIPTS) \ + simple.conf GNUmakefile.install + +IAM = simple +DOMAIN = SIMPLE +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = domain.h *.o $(IAM).log pmda$(IAM) pmda_$(IAM).so + +default_pcp default: domain.h $(CMDTARGET) + +include $(BUILDRULES) + +install_pcp install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(PMDADIR) + $(INSTALL) -m 644 GNUmakefile.install $(PMDADIR)/Makefile + $(INSTALL) -m 644 root pmns domain.h simple.conf $(PMDADIR) + $(INSTALL) -m 644 $(CFILES) $(DFILES) $(SCRIPTS) $(PMDADIR) + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) diff --git a/src/pmdas/simple/GNUmakefile.install b/src/pmdas/simple/GNUmakefile.install new file mode 100644 index 0000000..2df8bb4 --- /dev/null +++ b/src/pmdas/simple/GNUmakefile.install @@ -0,0 +1,53 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2000,2003,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# 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. +# + +SHELL = sh + +ifdef PCP_CONF +include $(PCP_CONF) +else +PCP_DIR = $(shell echo $$PCP_DIR) +include $(PCP_DIR)/etc/pcp.conf +endif +include $(PCP_INC_DIR)/builddefs + +# remove -Lpath and -Ipath options from builddefs CFLAGS value +# +PCP_LIBS = +TMP := $(CFLAGS:-I%=) +ifdef PCP_DIR +# put -Ipath and -Lpath back but use paths for run-time environment +# +CFLAGS = $(TMP) -I$(PCP_INC_DIR)/.. +LDFLAGS = -L$(PCP_LIB_DIR) +else +CFLAGS = $(TMP) +endif + +IAM = simple +CFILES = $(IAM).c + +LIBTARGET = pmda_$(IAM).$(DSOSUFFIX) +CMDTARGET = pmda$(IAM) +TARGETS = $(LIBTARGET) $(CMDTARGET) + +LLDLIBS = -lpcp_pmda -lpcp $(LIB_FOR_MATH) $(LIB_FOR_PTHREADS) +LDIRT = *.log help.dir help.pag + +default: $(TARGETS) + +install: default + +include $(PCP_INC_DIR)/buildrules diff --git a/src/pmdas/simple/Install b/src/pmdas/simple/Install new file mode 100644 index 0000000..0d3ead3 --- /dev/null +++ b/src/pmdas/simple/Install @@ -0,0 +1,53 @@ +#! /bin/sh +# +# Copyright (c) 2013 Red Hat. +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# 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 simple PMDA and/or PMNS +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=simple +pmda_interface=2 +forced_restart=false + +dso_opt=true +perl_opt=true +python_opt=true +socket_opt=true +socket_inet_def=2078 + +# Set up the simple PMDA (domain 253) InDom cache +# +domain=`sed -n <domain.h -e '/define SIMPLE/{ +s/[ ]*$// +s/.*[ ]// +p +}'` +if [ -z "$domain" ] +then + echo "Arrgh ... cannot extract domain number from domain.h" + exit 1 +fi +if [ -d $PCP_VAR_DIR/config/pmda ] +then + touch $PCP_VAR_DIR/config/pmda/$domain.1 + chown $PCP_USER:$PCP_GROUP $PCP_VAR_DIR/config/pmda/$domain.1 + chmod 644 $PCP_VAR_DIR/config/pmda/$domain.1 +fi + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/simple/README b/src/pmdas/simple/README new file mode 100644 index 0000000..d30dc63 --- /dev/null +++ b/src/pmdas/simple/README @@ -0,0 +1,67 @@ +Simple PMDA +=========== + +This PMDA is an example, that illustrates how a simple PMDA might be +constructed. + +Although the metrics supported as simple, the framework is quite general, +and could be extended to implement a much more complex PMDA. + +Note: + This PMDA may be remade from source and hence requires IDO (or + more specifically a C compiler) to be installed. + + Uses of make(1) may fail (without removing or clobbering files) + if the C compiler cannot be found. This is most likely to + happen when running the PMDA ./Install script. + + The only remedial action is to install the C compiler, or + hand-craft changes to the Makefile. + +Metrics +======= + +The file ./help contains descriptions for all of the metrics exported +by this PMDA. + +Once the PMDA has been installed, the following command will list all +the available metrics and their explanatory "help" text: + + $ pminfo -fT simple + +Installation +============ + + + # cd $PCP_PMDAS_DIR/simple + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDAs currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number. + + + Then simply use + + # ./Install + + and choose both the "collector" and "monitor" installation + configuration options. + + You will be prompted to choose either a daemon implementation + or a DSO implementation of the PMDA, and in the case of the daemon + variant to select an IPC method -- everything else is automated + +De-installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/simple + # ./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/pmcd/simple.log) should be checked for any warnings + or errors. diff --git a/src/pmdas/simple/Remove b/src/pmdas/simple/Remove new file mode 100644 index 0000000..44c4d1e --- /dev/null +++ b/src/pmdas/simple/Remove @@ -0,0 +1,25 @@ +#! /bin/sh +# +# Copyright (c) 1997 Silicon Graphics, Inc. All Rights Reserved. +# +# 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 simple PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=simple + +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/simple/help b/src/pmdas/simple/help new file mode 100644 index 0000000..e31ad0e --- /dev/null +++ b/src/pmdas/simple/help @@ -0,0 +1,82 @@ +# +# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# 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. +# +# simple PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# + +@ SIMPLE.0 Instance domain "colour" for simple PMDA +Universally 3 instances, "red" (0), "green" (1) and "blue" (3). + +@ SIMPLE.1 Dynamic instance domain "time" for simple PMDA +An instance domain which is computed on-the-fly for exporting current time +information. Refer to the help text for simple.now for a more complete +explanation. + +@ simple.numfetch Number of pmFetch operations. +The cumulative number of pmFetch operations directed to the "simple" +PMDA. + +This counter may be modified with pmstore(1). + +@ simple.color Metrics which increment with each fetch +This metric has 3 instances, designated "red", "green" and "blue". + +The value of the metric is monotonic increasing in the range 0 to +255, then back to 0. The different instances have different starting +values, namely 0 (red), 100 (green) and 200 (blue). + +The metric values my be altered using pmstore(1). + +@ simple.time.user Time agent has spent executing user code +The time in seconds that the CPU has spent executing user code for the +agent. + +@ simple.time.sys Time agent has spent executing system code +The time in seconds that the CPU has spent executing system code for +the agent. + +@ simple.now Time of day with a configurable instance domain +The value reflects the current time of day through a dynamically +reconfigurable instance domain. On each metric value fetch request, +the agent checks to see whether the configuration file in +$PCP_PMDAS_DIR/simple/simple.conf has been modified - if it has then +the file is re-parsed and the instance domain for this metric is again +constructed according to its contents. + +This configuration file contains a single line of comma-separated time +tokens from this set: + "sec" (seconds after the minute), + "min" (minutes after the hour), + "hour" (hour since midnight). + +An example configuration file could be: sec,min,hour +and in this case the simple.now metric would export values +for the three instances "sec", "min" and "hour" corresponding +respectively to the components seconds, minutes and hours of the +current time of day. + +The instance domain reflects each token present in the file, and the +values reflect the time at which the PMDA processes the fetch. diff --git a/src/pmdas/simple/pmdasimple.perl b/src/pmdas/simple/pmdasimple.perl new file mode 100644 index 0000000..f6d2da4 --- /dev/null +++ b/src/pmdas/simple/pmdasimple.perl @@ -0,0 +1,158 @@ +# +# Copyright (c) 2012 Red Hat. +# Copyright (c) 2008,2012 Aconex. All Rights Reserved. +# Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. +# +# 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. +# + +use strict; +use warnings; +use PCP::PMDA; + +use vars qw( $pmda $red $green $blue $user $system ); +my ( $numfetch, $oldfetch ) = ( 0, -1 ); +my ( $color_indom, $now_indom ) = ( 0, 1 ); +my ( $red, $green, $blue ) = ( 0, 100, 200 ); + +# simple.now instance domain stuff... +my $simple_config = pmda_config('PCP_PMDAS_DIR') . '/simple/simple.conf'; +my %timeslices; +my $file_error = 0; + +sub simple_instance # called once per ``instance request'' pdu +{ + &simple_timenow_check; +} + +sub simple_fetch # called once per ``fetch'' pdu, before callbacks +{ + $numfetch++; + &simple_timenow_check; +} + +sub simple_fetch_callback # must return array of value,status +{ + my ($cluster, $item, $inst) = @_; + + return (PM_ERR_INST, 0) unless ( $inst == PM_IN_NULL + || ($cluster == 0 && $item == 1) + || ($cluster == 2 && $item == 4) ); + if ($cluster == 0) { + if ($item == 0) { return ($numfetch, 1); } + elsif ($item == 1) { + if ($inst == 0) { return ($red = ($red+1) % 255, 1); } + elsif ($inst == 1) { return ($green = ($green+1) % 255, 1); } + elsif ($inst == 2) { return ($blue = ($blue+1) % 255, 1); } + else { return (PM_ERR_INST, 0); } + } else { return (PM_ERR_PMID, 0); } + } + elsif ($cluster == 1) { + if ($oldfetch < $numfetch) { # get current values, if needed + ($user, $system, undef, undef) = times; + $oldfetch = $numfetch; + } + if ($item == 2) { return ($user, 1); } + elsif ($item == 3) { return ($system, 1); } + else { return (PM_ERR_PMID, 0); } + } + elsif ($cluster == 2 && $item == 4) { + my $value = pmda_inst_lookup($now_indom, $inst); + return (PM_ERR_INST, 0) unless defined($value); + return ($value, 1); + } + return (PM_ERR_PMID, 0); +} + +sub simple_store_callback # must return a single value (scalar context) +{ + my ($cluster, $item, $inst, $val) = @_; + my $sts = 0; + + if ($cluster == 0) { + if ($item == 0) { + if ($val < 0) { $val = 0; $sts = PM_ERR_SIGN; } + $numfetch = $val; + } + elsif ($item == 1) { + if ($val < 0) { $sts = PM_ERR_SIGN; $val = 0; } + elsif ($val > 255) { $sts = PM_ERR_CONV; $val = 255; } + + if ($inst == 0) { $red = $val; } + elsif ($inst == 1) { $green = $val; } + elsif ($inst == 2) { $blue = $val; } + else { $sts = PM_ERR_INST; } + } + else { $sts = PM_ERR_PMID; } + return $sts; + } + elsif ( ($cluster == 1 && ($item == 2 || $item == 3)) + || ($cluster == 2 && $item == 4) ) { + return PM_ERR_PERMISSION; + } + return PM_ERR_PMID; +} + +sub simple_timenow_check +{ + %timeslices = (); + + if (open(CONFIG, $simple_config)) { + my %values; + + ($values{'sec'}, $values{'min'}, $values{'hour'}, + undef,undef,undef,undef,undef) = localtime; + $_ = <CONFIG>; + chomp; # avoid possible \n on last field + foreach my $spec (split(/,/)) { + $timeslices{$spec} = $values{$spec}; + } + close CONFIG; + $file_error = 0; + } + else { + unless ($file_error == $!) { + $pmda->log("read failed on $simple_config: $!"); + $file_error = $!; + } + } + $pmda->replace_indom( $now_indom, \%timeslices); +} + +$pmda = PCP::PMDA->new('simple', 253); +$pmda->connect_pmcd; + +$pmda->add_metric(pmda_pmid(0,0), PM_TYPE_U32, PM_INDOM_NULL, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'simple.numfetch', '', ''); +$pmda->add_metric(pmda_pmid(0,1), PM_TYPE_32, $color_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'simple.color', '', ''); +$pmda->add_metric(pmda_pmid(1,2), PM_TYPE_DOUBLE, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'simple.time.user', '', ''); +$pmda->add_metric(pmda_pmid(1,3), PM_TYPE_DOUBLE, PM_INDOM_NULL, + PM_SEM_COUNTER, pmda_units(0,1,0,0,PM_TIME_SEC,0), + 'simple.time.sys', '', ''); +$pmda->add_metric(pmda_pmid(2,4), PM_TYPE_U32, $now_indom, + PM_SEM_INSTANT, pmda_units(0,0,0,0,0,0), + 'simple.now', '', ''); + +$pmda->add_indom($color_indom, [0 => 'red', 1 => 'green', 2 => 'blue'], '', ''); +$now_indom = $pmda->add_indom($now_indom, {}, '', ''); # initialized on-the-fly +$pmda->set_fetch( \&simple_fetch ); +$pmda->set_instance( \&simple_instance ); +$pmda->set_fetch_callback( \&simple_fetch_callback ); +$pmda->set_store_callback( \&simple_store_callback ); + +$pmda->set_user('pcp'); +&simple_timenow_check; +$pmda->run; diff --git a/src/pmdas/simple/pmdasimple.python b/src/pmdas/simple/pmdasimple.python new file mode 100644 index 0000000..5d0295a --- /dev/null +++ b/src/pmdas/simple/pmdasimple.python @@ -0,0 +1,244 @@ +''' +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() + diff --git a/src/pmdas/simple/pmns b/src/pmdas/simple/pmns new file mode 100644 index 0000000..ab987f4 --- /dev/null +++ b/src/pmdas/simple/pmns @@ -0,0 +1,27 @@ +/* + * Metrics for simple PMDA + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + */ + +simple { + numfetch SIMPLE:0:0 + color SIMPLE:0:1 + time + now SIMPLE:2:4 +} + +simple.time { + user SIMPLE:1:2 + sys SIMPLE:1:3 +} diff --git a/src/pmdas/simple/root b/src/pmdas/simple/root new file mode 100644 index 0000000..d139152 --- /dev/null +++ b/src/pmdas/simple/root @@ -0,0 +1,10 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include <stdpmid> + +root { simple } + +#include "pmns" + diff --git a/src/pmdas/simple/simple.c b/src/pmdas/simple/simple.c new file mode 100644 index 0000000..d0eca16 --- /dev/null +++ b/src/pmdas/simple/simple.c @@ -0,0 +1,517 @@ +/* + * Simple, configurable PMDA + * + * Copyright (c) 2012-2014 Red Hat. + * Copyright (c) 1995,2004 Silicon Graphics, Inc. All Rights Reserved. + * + * 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. + */ + +#include <pcp/pmapi.h> +#include <pcp/impl.h> +#include <pcp/pmda.h> +#include "domain.h" +#include <sys/stat.h> + +/* + * Simple PMDA + * + * This PMDA is a sample that illustrates how a simple PMDA might be + * constructed using libpcp_pmda. + * + * Although the metrics supported are simple, the framework is quite general, + * and could be extended to implement a much more complex PMDA. + * + * Metrics + * simple.numfetch - number of fetches from this PMDA, + * may be re-set using pmStore + * simple.colors - 3 instances ("red", "green" and "blue") + * of a "saw-tooth" sequence + * simple.time.user - time in seconds spent executing user code + * simple.time.sys - time in seconds spent executing system code + * simple.now - current time of day across a dynamically + * re-configurable instance domain. + */ + +/* + * list of instances + */ + +static pmdaInstid color[] = { + { 0, "red" }, { 1, "green" }, { 2, "blue" } +}; + +/* + * instance domains + * COLOR_INDOM uses the classical indomtab[] method + * NOW_INDOM uses the more recent pmdaCache methods, but also appears in + * indomtab[] so that the initialization of the pmInDom and the pmDescs + * in metrictab[] is completed by pmdaInit + */ + +static pmdaIndom indomtab[] = { +#define COLOR_INDOM 0 /* serial number for "color" instance domain */ + { COLOR_INDOM, sizeof(color)/sizeof(color[0]), color }, +#define NOW_INDOM 1 /* serial number for "now" instance domain */ + { NOW_INDOM, 0, NULL }, +}; + +/* this is merely a convenience */ +static pmInDom *now_indom = &indomtab[NOW_INDOM].it_indom; + +/* + * All metrics supported in this PMDA - one table entry for each. + * The 4th field specifies the serial number of the instance domain + * for the metric, and must be either PM_INDOM_NULL (denoting a + * metric that only ever has a single value), or the serial number + * of one of the instance domains declared in the instance domain table + * (i.e. in indomtab, above). + */ + +static pmdaMetric metrictab[] = { +/* numfetch */ + { NULL, + { PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* color */ + { NULL, + { PMDA_PMID(0,1), PM_TYPE_32, COLOR_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +/* time.user */ + { NULL, + { PMDA_PMID(1,2), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) }, }, +/* time.sys */ + { NULL, + { PMDA_PMID(1,3), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_COUNTER, + PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0) }, }, +/* now */ + { NULL, + { PMDA_PMID(2,4), PM_TYPE_U32, NOW_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +}; + +static int numfetch = 0; /* number of pmFetch operations */ +static int red = 0; /* current red value */ +static int green = 100; /* current green value */ +static int blue = 200; /* current blue value */ +static int isDSO = 1; /* =0 I am a daemon */ +static char *username; + +/* data and function prototypes for dynamic instance domain handling */ +static struct timeslice { + int tm_field; + int inst_id; + char *tm_name; +} timeslices[] = { + { 0, 1, "sec" }, { 0, 60, "min" }, { 0, 3600, "hour" } +}; +static int num_timeslices = sizeof(timeslices)/sizeof(timeslices[0]); + +#define SIMPLE_BUFSIZE 256 +static struct stat file_change; /* has time of last configuration change */ +static void simple_timenow_clear(void); +static void simple_timenow_init(void); +static void simple_timenow_refresh(void); +static void simple_timenow_check(void); + +static char mypath[MAXPATHLEN]; + +/* command line option handling - both short and long options */ +static pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMDAOPT_USERNAME, + PMOPT_HELP, + PMDA_OPTIONS_TEXT("\nExactly one of the following options may appear:"), + PMDAOPT_INET, + PMDAOPT_PIPE, + PMDAOPT_UNIX, + PMDAOPT_IPV6, + PMDA_OPTIONS_END +}; +static pmdaOptions opts = { + .short_options = "D:d:i:l:pu:U:6:?", + .long_options = longopts, +}; + +/* + * callback provided to pmdaFetch + */ +static int +simple_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + int sts; + static int oldfetch; + static double usr, sys; + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + + if (inst != PM_IN_NULL && + !(idp->cluster == 0 && idp->item == 1) && + !(idp->cluster == 2 && idp->item == 4)) + return PM_ERR_INST; + + if (idp->cluster == 0) { + if (idp->item == 0) { /* simple.numfetch */ + atom->l = numfetch; + } + else if (idp->item == 1) { /* simple.color */ + switch (inst) { + case 0: /* red */ + red = (red + 1) % 256; + atom->l = red; + break; + case 1: /* green */ + green = (green + 1) % 256; + atom->l = green; + break; + case 2: /* blue */ + blue = (blue + 1) % 256; + atom->l = blue; + break; + default: + return PM_ERR_INST; + } + } + else + return PM_ERR_PMID; + } + else if (idp->cluster == 1) { /* simple.time */ + if (oldfetch < numfetch) { + __pmProcessRunTimes(&usr, &sys); + oldfetch = numfetch; + } + if (idp->item == 2) /* simple.time.user */ + atom->d = usr; + else if (idp->item == 3) /* simple.time.sys */ + atom->d = sys; + else + return PM_ERR_PMID; + } + else if (idp->cluster == 2) { + if (idp->item == 4) { /* simple.now */ + struct timeslice *tsp; + if ((sts = pmdaCacheLookup(*now_indom, inst, NULL, (void *)&tsp)) != PMDA_CACHE_ACTIVE) { + if (sts < 0) + __pmNotifyErr(LOG_ERR, "pmdaCacheLookup failed: inst=%d: %s", inst, pmErrStr(sts)); + return PM_ERR_INST; + } + atom->l = tsp->tm_field; + } + else + return PM_ERR_PMID; + } + else + return PM_ERR_PMID; + + return 0; +} + +/* + * wrapper for pmdaFetch which increments the fetch count and checks for + * a change to the NOW instance domain. + * + * This routine is called once for each pmFetch(3) operation, so is a + * good place to do once-per-fetch functions, such as value caching or + * instance domain evaluation (as we do in simple_timenow_check). + */ +static int +simple_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + numfetch++; + simple_timenow_check(); + simple_timenow_refresh(); + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +/* + * wrapper for pmdaInstance which we need to ensure is called with the + * _current_ contents of the NOW instance domain. + */ +static int +simple_instance(pmInDom indom, int foo, char *bar, __pmInResult **iresp, pmdaExt *pmda) +{ + simple_timenow_check(); + return pmdaInstance(indom, foo, bar, iresp, pmda); +} + +/* + * Re-evaluate the NOW instance domain. + * + * Refer to the help text for simple.now for an explanation of how + * this indom can be modified, or just read the code ... + */ +static void +simple_timenow_check(void) +{ + struct stat statbuf; + static int last_error = 0; + int sep = __pmPathSeparator(); + + /* stat the file & check modification time has changed */ + snprintf(mypath, sizeof(mypath), "%s%c" "simple" "%c" "simple.conf", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + if (stat(mypath, &statbuf) == -1) { + if (oserror() != last_error) { + last_error = oserror(); + __pmNotifyErr(LOG_ERR, "stat failed on %s: %s\n", + mypath, pmErrStr(-last_error)); + } + simple_timenow_clear(); + } + else { + last_error = 0; +#if defined(HAVE_ST_MTIME_WITH_E) + if (statbuf.st_mtime != file_change.st_mtime) { +#elif defined(HAVE_ST_MTIME_WITH_SPEC) + if (statbuf.st_mtimespec.tv_sec != file_change.st_mtimespec.tv_sec || + statbuf.st_mtimespec.tv_nsec != file_change.st_mtimespec.tv_nsec) { +#else + if (statbuf.st_mtim.tv_sec != file_change.st_mtim.tv_sec || + statbuf.st_mtim.tv_nsec != file_change.st_mtim.tv_nsec) { +#endif + simple_timenow_clear(); + simple_timenow_init(); + file_change = statbuf; + } + } +} + +/* + * get values for time.now metric instances + */ +static void +simple_timenow_refresh(void) +{ + time_t t = time(NULL); + struct tm *tptr; + + tptr = localtime(&t); + timeslices[0].tm_field = tptr->tm_sec; + timeslices[1].tm_field = tptr->tm_min; + timeslices[2].tm_field = tptr->tm_hour; +} + +/* + * clear the time.now metric instance domain + */ +static void +simple_timenow_clear(void) +{ + int sts; + + sts = pmdaCacheOp(*now_indom, PMDA_CACHE_INACTIVE); + if (sts < 0) + __pmNotifyErr(LOG_ERR, "pmdaCacheOp(INACTIVE) failed: indom=%s: %s", + pmInDomStr(*now_indom), pmErrStr(sts)); +#ifdef DESPERATE + __pmdaCacheDump(stderr, *now_indom, 1); +#endif +} + +/* + * parse the configuration file for the time.now metric instance domain + */ +static void +simple_timenow_init(void) +{ + int i; + int sts; + int sep = __pmPathSeparator(); + FILE *fp; + char *p, *q; + char buf[SIMPLE_BUFSIZE]; + + snprintf(mypath, sizeof(mypath), "%s%c" "simple" "%c" "simple.conf", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + if ((fp = fopen(mypath, "r")) == NULL) { + __pmNotifyErr(LOG_ERR, "fopen on %s failed: %s\n", + mypath, pmErrStr(-oserror())); + return; + } + if ((p = fgets(&buf[0], SIMPLE_BUFSIZE, fp)) == NULL) { + __pmNotifyErr(LOG_ERR, "fgets on %s found no data\n", mypath); + fclose(fp); + return; + } + if ((q = strchr(p, '\n')) != NULL) + *q = '\0'; /* remove eol character */ + + q = strtok(p, ","); /* and refresh using the updated file */ + while (q != NULL) { + for (i = 0; i < num_timeslices; i++) { + if (strcmp(timeslices[i].tm_name, q) == 0) { + sts = pmdaCacheStore(*now_indom, PMDA_CACHE_ADD, q, ×lices[i]); + if (sts < 0) { + __pmNotifyErr(LOG_ERR, "pmdaCacheStore failed: %s", pmErrStr(sts)); + fclose(fp); + return; + } + break; + } + } + if (i == num_timeslices) + __pmNotifyErr(LOG_WARNING, "ignoring \"%s\" in %s", q, mypath); + q = strtok(NULL, ","); + } +#ifdef DESPERATE + __pmdaCacheDump(stderr, *now_indom, 1); +#endif + if (pmdaCacheOp(*now_indom, PMDA_CACHE_SIZE_ACTIVE) < 1) + __pmNotifyErr(LOG_WARNING, "\"timenow\" instance domain is empty"); + + fclose(fp); +} + +/* + * support the storage of a value into the number of fetches count + */ +static int +simple_store(pmResult *result, pmdaExt *pmda) +{ + int i; + int j; + int val; + int sts = 0; + pmValueSet *vsp = NULL; + __pmID_int *pmidp = NULL; + + /* a store request may affect multiple metrics at once */ + for (i = 0; i < result->numpmid; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + + if (pmidp->cluster == 0) { /* all storable metrics are cluster 0 */ + + switch (pmidp->item) { + case 0: /* simple.numfetch */ + val = vsp->vlist[0].value.lval; + if (val < 0) { + sts = PM_ERR_SIGN; + val = 0; + } + numfetch = val; + break; + + case 1: /* simple.color */ + /* a store request may affect multiple instances at once */ + for (j = 0; j < vsp->numval && sts == 0; j++) { + + val = vsp->vlist[j].value.lval; + if (val < 0) { + sts = PM_ERR_SIGN; + val = 0; + } + if (val > 255) { + sts = PM_ERR_CONV; + val = 255; + } + + switch (vsp->vlist[j].inst) { + case 0: /* red */ + red = val; + break; + case 1: /* green */ + green = val; + break; + case 2: /* blue */ + blue = val; + break; + default: + sts = PM_ERR_INST; + } + } + break; + + default: + sts = PM_ERR_PMID; + break; + } + } + else if ((pmidp->cluster == 1 && + (pmidp->item == 2 || pmidp->item == 3)) || + (pmidp->cluster == 2 && pmidp->item == 4)) { + sts = PM_ERR_PERMISSION; + break; + } + else { + sts = PM_ERR_PMID; + break; + } + } + return sts; +} + + +/* + * Initialise the agent (both daemon and DSO). + */ +void +simple_init(pmdaInterface *dp) +{ + if (isDSO) { + int sep = __pmPathSeparator(); + snprintf(mypath, sizeof(mypath), "%s%c" "simple" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDSO(dp, PMDA_INTERFACE_2, "simple DSO", mypath); + } else { + __pmSetProcessIdentity(username); + } + + if (dp->status != 0) + return; + + dp->version.any.fetch = simple_fetch; + dp->version.any.store = simple_store; + dp->version.any.instance = simple_instance; + + pmdaSetFetchCallBack(dp, simple_fetchCallBack); + + pmdaInit(dp, indomtab, sizeof(indomtab)/sizeof(indomtab[0]), metrictab, + sizeof(metrictab)/sizeof(metrictab[0])); +} + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface dispatch; + + isDSO = 0; + __pmSetProgname(argv[0]); + __pmGetUsername(&username); + + snprintf(mypath, sizeof(mypath), "%s%c" "simple" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmProgname, SIMPLE, + "simple.log", mypath); + + pmdaGetOptions(argc, argv, &opts, &dispatch); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + if (opts.username) + username = opts.username; + + pmdaOpenLog(&dispatch); + pmdaConnect(&dispatch); + simple_init(&dispatch); + simple_timenow_check(); + pmdaMain(&dispatch); + + exit(0); +} diff --git a/src/pmdas/simple/simple.conf b/src/pmdas/simple/simple.conf new file mode 100644 index 0000000..a1927dc --- /dev/null +++ b/src/pmdas/simple/simple.conf @@ -0,0 +1 @@ +sec,min,hour |