pcp
[Top] [All Lists]

Re: pmrep: convert to use pmfg

To: "Frank Ch. Eigler" <fche@xxxxxxxxxx>
Subject: Re: pmrep: convert to use pmfg
From: Marko Myllynen <myllynen@xxxxxxxxxx>
Date: Sun, 17 Jul 2016 23:34:33 +0300
Cc: Nathan Scott <nathans@xxxxxxxxxx>, pcp@xxxxxxxxxxx
Delivered-to: pcp@xxxxxxxxxxx
In-reply-to: <577FE2EC.9020103@xxxxxxxxxx>
Organization: Red Hat
References: <576926AB.7070608@xxxxxxxxxx> <717761407.765523.1466558073136.JavaMail.zimbra@xxxxxxxxxx> <576FDCE3.9010706@xxxxxxxxxx> <y0mshvwjbpl.fsf@xxxxxxxx> <577F8261.5060008@xxxxxxxxxx> <20160708152452.GA29879@xxxxxxxxxx> <577FE2EC.9020103@xxxxxxxxxx>
Reply-to: Marko Myllynen <myllynen@xxxxxxxxxx>
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.2
Hi,

On 2016-07-08 20:29, Marko Myllynen wrote:
> On 2016-07-08 18:24, Frank Ch. Eigler wrote:
>>> [...]
>>> I think I've sorted out the above but I've got one related question, is
>>> this still expected based on your explanation above or something else?
>>
>> Yes, I think it's the same thing.
>>
>>> So pmval(1) describes units as "millisec (converting to time
>>> utilization)" [...] Is the only confusion on this end or is pmval(1)
>>> perhaps a bit misleading here?
>>
>> The key is that pmval's "time utilization" parenthetical overrides the
>> previous "milliseconds" scale and replaces it with an implicit
>> "sec/sec" one.

I noticed that pmdumptext(1) does the same thing after all - when in
its "interactive mode."

But given that pmrep(1) already supports specifying unit/scale even on
per-metric basis, we don't have to hard-code this but we can give the
choice to the user. For instance, with kernel.all.cpu.user by default
show the unscaled ms/s value but by defining time-scale with -y the
user gets the time utilization (s/s) if s/he so wishes. I adjusted
the man page for this, please do reword if you can think how to state
this more clearly there.

Below is now finally a patch which I don't plan to update at this
stage any more unless there's some feedback (IOW, I think it's ready
for merging). The 1071 output still differs mysteriously, I can't
explain why pmfg finds "extra" values, and only for some metrics (I'd
have expected it to find them for none or all given the pmdumplog -a
output for the archive in question). But since we're getting more
data than before, perhaps that's just a good thing.

There might still be room for some minor internal cleanups in few
places but I think it's best to do this kind of migration somewhat
conservatively.

---
 qa/1069.out          | 100 ++++++++++----------
 qa/1071.out          |  12 ++-
 src/pmrep/pmrep.1    |  16 +++-
 src/pmrep/pmrep.conf |   7 +-
 src/pmrep/pmrep.py   | 251 +++++++++++++++------------------------------------
 5 files changed, 150 insertions(+), 236 deletions(-)

diff --git a/qa/1069.out b/qa/1069.out
index 2c330b3..2243722 100644
--- a/qa/1069.out
+++ b/qa/1069.out
@@ -19,28 +19,28 @@ HH:MM:SS        1
                                                
 == basic archive mode reporting
           s.seconds  s.milliseconds
-               util            util
+                s/s            ms/s
 20:36:45        N/A             N/A
 20:36:47        N/A             N/A
-20:36:49       1.00            1.00
-20:36:51       1.00            1.00
-20:36:53       1.00            1.00
+20:36:49       1.00         1000.03
+20:36:51       1.00         1000.00
+20:36:53       1.00         1000.02
 == basic configuration file handling
   s.seconds  s.milliseconds
-       util            util
+        s/s            ms/s
         N/A             N/A
         N/A             N/A
-      1.000           1.000
-      1.000           1.000
-      1.000           1.000
+      1.000        1000.033
+      1.000        1000.001
+      1.000        1000.016
 == exercise various output options
                      s.seconds        s.milliseconds
-                          util                  util
+                           s/s                  ms/s
 20:36:45                   N/A                   N/A
 20:36:47                   N/A                   N/A
-20:36:49                 1.000                 1.000
-20:36:51                 1.000                 1.000
-20:36:53                 1.000                 1.000
+20:36:49                 1.000              1000.033
+20:36:51                 1.000              1000.001
+20:36:53                 1.000              1000.016
 ---
 
   archive: QAPATH/archives/sample-secs
@@ -54,7 +54,7 @@ HH:MM:SS        1
  duration: 8 sec
 
   s.seconds  s.milliseconds
-       util            util
+        s/s            ms/s
 ---
 
   archive: QAPATH/archives/sample-secs
@@ -68,16 +68,16 @@ HH:MM:SS        1
  duration: 7 sec
 
           s.seconds  s.milliseconds
-               util            util
+                s/s            ms/s
 20:36:45        N/A             N/A
 20:36:48        N/A             N/A
-20:36:51      1.000           1.000
+20:36:51      1.000         999.992
 ---
         N/A             N/A
         N/A             N/A
-      1.000           1.000
-      1.000           1.000
-      1.000           1.000
+      1.000        1000.033
+      1.000        1000.001
+      1.000        1000.016
 == exercise non-integer options
 
   archive: QAPATH/archives/sample-secs
@@ -91,13 +91,13 @@ HH:MM:SS        1
  duration: 2.500 sec
 
           s.seconds  s.milliseconds
-               util            util
+                s/s            ms/s
 20:36:45        N/A             N/A
 20:36:45        N/A             N/A
-20:36:46      2.000           1.000
-20:36:46      0.000           1.000
-20:36:47      2.000           1.000
-20:36:47      0.000           1.000
+20:36:46      2.000        1000.216
+20:36:46      0.000        1000.216
+20:36:47      2.000        1000.216
+20:36:47      0.000        1000.205
 == exercise raw counter mode
   s.seconds  s.milliseconds
         sec        millisec
@@ -108,12 +108,12 @@ HH:MM:SS        1
      380440   380439679.413
 == exercise timezone setting
           s.seconds  s.milliseconds
-               util            util
+                s/s            ms/s
 10:36:45        N/A             N/A
 10:36:47        N/A             N/A
-10:36:49      1.000           1.000
-10:36:51      1.000           1.000
-10:36:53      1.000           1.000
+10:36:49      1.000        1000.033
+10:36:51      1.000        1000.001
+10:36:53      1.000        1000.016
 == exercise CSV and alternate delimiters
 Time,sample.seconds,sample.milliseconds
 2000-05-01 20:36:45,"",""
@@ -131,15 +131,15 @@ Time|sample.seconds|sample.milliseconds
 ok
 == exercise repeated headers option
   s.seconds
-       util
+        s/s
         N/A
         N/A
   s.seconds
-       util
+        s/s
       1.000
       1.000
   s.seconds
-       util
+        s/s
       1.000
 == exercise units conversion options
                  m.u.free         m.u.used
@@ -174,23 +174,23 @@ ok
 00:47:09            0.000         3000.000           50.000            0.000   
         0.000           50.000
              d.d.avactive     d.d.avactive     d.d.avactive     d.d.avactive   
  d.d.avactive     d.d.avactive
                       sda              sdb              sdc              hdc   
           sdd              sde
-                     util             util             util             util   
          util             util
+                      s/s              s/s              s/s              s/s   
           s/s              s/s
 08:58:23              N/A              N/A              N/A              N/A   
           N/A              N/A
-08:59:23         0.000000         0.000056         0.000006         0.000000   
      0.000000         0.000005
-09:00:23         0.000004         0.000089         0.000008         0.000000   
      0.000000         0.000021
-09:01:23         0.000000         0.000175         0.000013         0.000000   
      0.000000         0.000036
-09:02:23         0.000000         0.000054         0.000003         0.000000   
      0.000000         0.000005
+08:59:23         0.000200         0.055902         0.005567         0.000000   
      0.000000         0.004883
+09:00:23         0.003633         0.088769         0.008017         0.000000   
      0.000000         0.021034
+09:01:23         0.000200         0.175189         0.012884         0.000000   
      0.000000         0.036451
+09:02:23         0.000217         0.054218         0.002750         0.000000   
      0.000000         0.005050
              d.d.avactive     d.d.avactive     d.d.avactive     d.d.avactive   
  d.d.avactive     d.d.avactive
                       sda              sdb              sdc              hdc   
           sdd              sde
-                     util             util             util             util   
          util             util
+                     Âs/s             Âs/s             Âs/s             Âs/s   
          Âs/s             Âs/s
 08:58:23              N/A              N/A              N/A              N/A   
           N/A              N/A
-08:59:23         0.200005        55.901528         5.566819         0.000000   
      0.000000         4.883467
-09:00:23         3.633431        88.769056         8.016882         0.000000   
      0.000000        21.033900
-09:01:23         0.200007       175.189301        12.883772         0.000000   
      0.000000        36.451242
-09:02:23         0.216672        54.218071         2.750071         0.000000   
      0.000000         5.050131
+08:59:23       200.005467     55901.527975      5566.818826         0.000000   
      0.000000      4883.466815
+09:00:23      3633.431133     88769.056034      8016.882454         0.000000   
      0.000000     21033.899496
+09:01:23       200.006814    175189.301449     12883.772241         0.000000   
      0.000000     36451.241772
+09:02:23       216.672278     54218.070915      2750.071227         0.000000   
      0.000000      5050.130798
 == derived metrics configuration file
   s.combo
-     util
+      s/s
       N/A
       N/A
     1.001
@@ -198,7 +198,7 @@ ok
     1.001
 == derived metrics directly on command line
   s.combo
-     util
+      s/s
       N/A
       N/A
     1.001
@@ -206,20 +206,20 @@ ok
     1.001
 == extended existing sample configuration
   s.combo  s.seconds  s.milliseconds
-     util       util            util
+      s/s        s/s            ms/s
       N/A        N/A             N/A
       N/A        N/A             N/A
-    1.001      1.000           1.000
-    1.001      1.000           1.000
-    1.001      1.000           1.000
+    1.001      1.000        1000.033
+    1.001      1.000        1000.001
+    1.001      1.000        1000.016
 == exercise good config version
   s.seconds  s.milliseconds
-       util            util
+        s/s            ms/s
         N/A             N/A
         N/A             N/A
-      1.000           1.000
-      1.000           1.000
-      1.000           1.000
+      1.000        1000.033
+      1.000        1000.001
+      1.000        1000.016
 == exercise bad config version
 Incompatible configuration file version (read v99, need v1).
 == un/interpolated archive mode reporting
diff --git a/qa/1071.out b/qa/1071.out
index 738e589..5beab6f 100644
--- a/qa/1071.out
+++ b/qa/1071.out
@@ -17,6 +17,7 @@ QA output created by 1071
 14:39:14         Linux         linux         15707      13573328             0 
       507689             8      16053852             3
 14:39:15         Linux         linux         15707      13573256             0 
       507691             8      16053852             3
 14:39:16         Linux         linux         15708      13573256             0 
       507692             8      16053852             3
+14:39:17           N/A         linux           N/A           N/A           N/A 
          N/A             8      16053852             3
 
   archive: QAPATH/archives/rep
      host: slack
@@ -30,11 +31,12 @@ QA output created by 1071
 
           p.p.start_ti  n.i.baudrate        k.a.hz  d.a.read_byt   k.a.sysfork 
   k.p.c.user      k.a.load  h.c.bogomips     h.c.clock     n.i.speed
           000001 /usr/       enp0s25                                           
         cpu0      1 minute          cpu0          cpu0       enp0s25
-              millisec        byte/s       count/s       Kbyte/s       count/s 
         util                                     count       Mbyte/s
+              millisec        byte/s       count/s       Kbyte/s       count/s 
         ms/s                                     count       Mbyte/s
 14:39:13            20    3168713760           100           N/A           N/A 
          N/A        0.0900     5587.4199     3029.0310   781261.9375
 14:39:14            20    3168713760           100        0.0000        0.0000 
       0.0000        0.0900     5587.4199     3029.0310   781261.9375
 14:39:15            20    3168713760           100        0.0000        0.0000 
       0.0000        0.0800     5587.4199     2800.0000   781261.9375
-14:39:16            20    3168713760           100        0.0000        1.0000 
       0.0100        0.0800     5587.4199     2800.0000   781261.9375
+14:39:16            20    3168713760           100        0.0000        1.0000 
      10.0000        0.0800     5587.4199     2800.0000   781261.9375
+14:39:17            20    3168713760           100           N/A           N/A 
          N/A           N/A     5587.4199     2800.1089   781261.9375
 == testing raw metrics reporting
 
   archive: QAPATH/archives/rep
@@ -53,6 +55,7 @@ QA output created by 1071
 14:39:14         Linux         linux         15707      13573328             0 
       507689             8      16053852             3
 14:39:15         Linux         linux         15707      13573256             0 
       507691             8      16053852             3
 14:39:16         Linux         linux         15708      13573256             0 
       507692             8      16053852             3
+14:39:17           N/A         linux           N/A           N/A           N/A 
          N/A             8      16053852             3
 
   archive: QAPATH/archives/rep
      host: slack
@@ -71,6 +74,7 @@ QA output created by 1071
 14:39:14            20    3168713760           100       8736634       3995200 
      4349650        0.0900     5587.4199     3029.0310   781261.9375
 14:39:15            20    3168713760           100       8736634       3995200 
      4349650        0.0800     5587.4199     2800.0000   781261.9375
 14:39:16            20    3168713760           100       8736634       3995201 
      4349660        0.0800     5587.4199     2800.0000   781261.9375
+14:39:17            20    3168713760           100           N/A           N/A 
          N/A           N/A     5587.4199     2800.1089   781261.9375
 == testing uninterpolated metrics reporting
 
   archive: QAPATH/archives/rep
@@ -103,11 +107,11 @@ QA output created by 1071
 
           p.p.start_ti  n.i.baudrate        k.a.hz  d.a.read_byt   k.a.sysfork 
   k.p.c.user      k.a.load  h.c.bogomips     h.c.clock     n.i.speed
           000001 /usr/       enp0s25                                           
         cpu0      1 minute          cpu0          cpu0       enp0s25
-              millisec        byte/s       count/s       Kbyte/s       count/s 
         util                                     count       Mbyte/s
+              millisec        byte/s       count/s       Kbyte/s       count/s 
         ms/s                                     count       Mbyte/s
 14:39:13            20    3168713760           100           N/A           N/A 
          N/A        0.0900     5587.4199     3029.0310   781261.9375
 14:39:14            20    3168713760           100        0.0000        0.0000 
       0.0000        0.0900     5587.4199     2723.4370   781261.9375
 14:39:15            20    3168713760           100        0.0000        0.0000 
       0.0000        0.0800     5587.4199     2800.0000   781261.9375
-14:39:16            20    3168713760           100        0.0000        0.9999 
       0.0100        0.0800     5587.4199     2800.0000   781261.9375
+14:39:16            20    3168713760           100        0.0000        0.9999 
       9.9987        0.0800     5587.4199     2800.0000   781261.9375
 14:39:17            20    3168713760           100        0.0000        2.0002 
       0.0000        0.0800     5587.4199     2800.1089   781261.9375
 == testing uninterpolated raw metrics reporting
 
diff --git a/src/pmrep/pmrep.1 b/src/pmrep/pmrep.1
index 2e91f92..ccccf6e 100644
--- a/src/pmrep/pmrep.1
+++ b/src/pmrep/pmrep.1
@@ -173,6 +173,16 @@ Too-wide numeric values for output will not be printed 
(apart from
 decimal places, numeric values will never be silently truncated).
 Too-wide strings will be truncated.
 .P
+As a special case with metrics that are counters with time units
+(nanoseconds to hours), the
+.I unit/scale
+can be used to change the default reporting (for example,
+milliseconds / second) to normalize to the range zero to one
+by setting this to
+.B sec
+(see also
+.BR -y ).
+.P
 The following
 .I metricspec
 requests the metric
@@ -240,7 +250,8 @@ for space (byte) metrics, possible values include
 .BR Mbytes ,
 .BR MB ,
 and so forth up to
-.BR Ebytes .
+.BR Ebytes ,
+.BR EB .
 This option will
 .I not
 override possible per-metric specifications.
@@ -583,8 +594,9 @@ for time metrics, possible values include
 .BR microsec ,
 .BR us ,
 .BR millisec ,
+.BR ms ,
 and so forth up to
-.BR hours ,
+.BR hour ,
 .BR hr .
 This option will
 .I not
diff --git a/src/pmrep/pmrep.conf b/src/pmrep/pmrep.conf
index 4c9949b..d31e483 100644
--- a/src/pmrep/pmrep.conf
+++ b/src/pmrep/pmrep.conf
@@ -96,20 +96,25 @@ mem.vmstat.pgpgout = bo,,,,6
 kernel.all.intr = in,,,,6
 kernel.all.pswitch = cs,,,,6
 alluser = kernel.all.cpu.alluserp
-alluser.formula = 100 * (kernel.all.cpu.user + kernel.all.cpu.nice) / hinv.ncpu
 alluser.label = us
+alluser.formula = 100 * (kernel.all.cpu.user + kernel.all.cpu.nice) / hinv.ncpu
+alluser.unit = s
 sys = kernel.all.cpu.sysp
 sys.label = sy
 sys.formula = 100 * kernel.all.cpu.sys / hinv.ncpu
+sys.unit = s
 idle = kernel.all.cpu.idlep
 idle.label = id
 idle.formula = 100 * kernel.all.cpu.idle / hinv.ncpu
+idle.unit = s
 wtotal = kernel.all.cpu.wait.totalp
 wtotal.label =  wa
 wtotal.formula = 100 * kernel.all.cpu.wait.total / hinv.ncpu
+wtotal.unit = s
 steal = kernel.all.cpu.stealp
 steal.label = st
 steal.formula = 100 * kernel.all.cpu.steal / hinv.ncpu
+steal.unit = s
 
 # An example metric set
 [example-1]
diff --git a/src/pmrep/pmrep.py b/src/pmrep/pmrep.py
index e548729..5f2b67d 100755
--- a/src/pmrep/pmrep.py
+++ b/src/pmrep/pmrep.py
@@ -218,16 +218,14 @@ class PMReporter(object):
 
         # Performance metrics store
         # key - metric name
-        # values - 0:label, 1:instance(s), 2:unit/scale, 3:type, 4:width
+        # values - 0:label, 1:instance(s), 2:unit/scale, 3:type, 4:width, 
5:pmfg item
         self.metrics = OrderedDict()
+        self.pmfg = None
+        self.pmfg_ts = None
 
         # Corresponding config file metric specifiers
         self.metricspec = ('label', 'instance', 'unit', 'type', 'width', 
'formula')
 
-        self.prevvals = None
-        self.currvals = None
-        self.ptstamp = 0
-        self.ctstamp = 0
         self.pmids = []
         self.descs = []
         self.insts = []
@@ -565,7 +563,10 @@ class PMReporter(object):
         self.opts.pmSetOptionFlags(flags | pmapi.c_api.PM_OPTFLAG_DONE)
         pmapi.c_api.pmEndOptions()
 
-        self.context = pmapi.pmContext(context, self.source)
+        if not self.source: self.source = "@" # XXX
+        self.pmfg = pmapi.fetchgroup(context, self.source)
+        self.pmfg_ts = self.pmfg.extend_timestamp()
+        self.context = self.pmfg.get_context()
 
         if pmapi.c_api.pmSetContextOptions(self.context.ctx, self.opts.mode, 
self.opts.delta):
             raise pmapi.pmUsageErr()
@@ -653,9 +654,11 @@ class PMReporter(object):
     def format_metric_label(self, label):
         """ Format a metric label """
         # See src/libpcp/src/units.c
-        label = label.replace(" / nanosec", "/ns").replace(" / microsec", 
"/Âs")
-        label = label.replace(" / millisec", "/ms").replace(" / sec", "/s")
-        label = label.replace(" / min", "/min").replace(" / hour", "/h")
+        if ' / ' in label:
+            label = label.replace("nanosec", "ns").replace("microsec", "Âs")
+            label = label.replace("millisec", "ms").replace("sec", "s")
+            label = label.replace("min", "min").replace("hour", "h")
+            label = label.replace(" / ", "/")
         return label
 
     def validate_metrics(self):
@@ -719,7 +722,7 @@ class PMReporter(object):
         # Finalize the metrics set
         for i, metric in enumerate(self.metrics):
             # Fill in all fields for easier checking later
-            for index in range(0, 5):
+            for index in range(0, 6):
                 if len(self.metrics[metric]) <= index:
                     self.metrics[metric].append(None)
 
@@ -732,7 +735,10 @@ class PMReporter(object):
                 self.metrics[metric][0] = name[:-2] + m
 
             # Rawness
-            if self.metrics[metric][3] == 'raw' or self.type == 1:
+            if self.metrics[metric][3] == 'raw' or self.type == 1 or \
+               self.output == OUTPUT_ARCHIVE or \
+               self.output == OUTPUT_CSV or \
+               self.output == OUTPUT_ZABBIX:
                 self.metrics[metric][3] = 1
             else:
                 self.metrics[metric][3] = 0
@@ -764,15 +770,16 @@ class PMReporter(object):
                 if not done:
                     self.metrics[metric][2] = unitstr
             # Set unit/scale for non-raw numeric metrics
+            mtype = None
             try:
                 if self.metrics[metric][3] == 0 and \
                    self.descs[i].contents.type != PM_TYPE_STRING:
                     (unitstr, mult) = 
self.context.pmParseUnitsStr(self.metrics[metric][2])
                     label = self.metrics[metric][2]
                     if self.descs[i].sem == PM_SEM_COUNTER:
-                        label += "/s"
-                        if self.descs[i].contents.units.dimTime == 1:
-                            label = "util"
+                        mtype = PM_TYPE_DOUBLE
+                        if '/' not in label:
+                            label += " / s"
                     label = self.format_metric_label(label)
                     self.metrics[metric][2] = (label, unitstr, mult)
                 else:
@@ -792,16 +799,12 @@ class PMReporter(object):
             if self.metrics[metric][4] < len(TRUNC):
                 self.metrics[metric][4] = len(TRUNC) # Forced minimum
 
-    # RHBZ#1264147
-    def pmids_to_ctypes(self, pmids):
-        """ Convert a Python list of pmids (numbers) to
-            a ctypes LP_c_uint (a C array of uints).
-        """
-        from ctypes import c_uint
-        pmidA = (c_uint * len(pmids))()
-        for i, p in enumerate(pmids):
-            pmidA[i] = c_uint(p)
-        return pmidA
+            # Add fetchgroup item
+            scale = self.metrics[metric][2][0].replace("Âs", "microsec")
+            ins = 1 if self.insts[i][0][0] == PM_IN_NULL else 
len(self.insts[i][0])
+            self.metrics[metric][5] = []
+            for j in range(ins):
+                self.metrics[metric][5].append(self.pmfg.extend_item(metric, 
mtype, scale, self.insts[i][1][j]))
 
     def get_local_tz(self, set_dst=-1):
         """ Figure out the local timezone using the PCP convention """
@@ -906,143 +909,44 @@ class PMReporter(object):
 
         lines = 0
         while self.samples != 0:
+            # Repeat the header if needed
             if self.output == OUTPUT_STDOUT:
                 if lines > 1 and self.repeat_header == lines:
                     self.write_header()
                     lines = 0
                 lines += 1
 
+            # Fetch values
             try:
-                result = self.context.pmFetch(self.pmids_to_ctypes(self.pmids))
+                self.pmfg.fetch()
             except pmapi.pmErr as error:
                 if error.args[0] == PM_ERR_EOL:
                     break
                 raise error
-            self.extract(result)
-            if self.ctstamp == 0:
-                self.ctstamp = copy.copy(result.contents.timestamp)
-            self.ptstamp = self.ctstamp
-            self.ctstamp = copy.copy(result.contents.timestamp)
-
-            if self.context.type == PM_CONTEXT_ARCHIVE:
-                if float(self.ctstamp) < float(self.opts.pmGetOptionOrigin()):
-                    self.context.pmFreeResult(result)
-                    continue
-                if float(self.ctstamp) > float(self.opts.pmGetOptionFinish()):
-                    self.context.pmFreeResult(result)
-                    break
 
-            self.report(self.ctstamp, self.currvals)
-            self.context.pmFreeResult(result)
+            # Report and prepare for the next round
+            self.report(self.pmfg_ts())
             if self.samples and self.samples > 0:
                 self.samples -= 1
             if self.delay and self.interpol and self.samples != 0:
                 self.context.pmtimevalSleep(self.interval)
 
         # Allow modules to flush buffered values / say goodbye
-        self.report(None, None)
-
-    def extract(self, result):
-        """ Extract the metric values from pmResult structure """
-        # Metrics incl. all instance values, must match self.format on return
-        values = []
-
-        for i, metric in enumerate(self.metrics):
-            # Per-metric values incl. all instance values
-            # We use dict to make it easier to deal with gone/unknown instances
-            values.append({})
-
-            # Populate instance fields to have values for unavailable instances
-            # Values are (instance id, instance name, instance value)
-            for inst in self.insts[i][0]:
-                values[i][inst] = (-1, None, NO_VAL)
-
-            # No values available for this metric
-            if result.contents.get_numval(i) == 0:
-                continue
-
-            # Process all fetched instances
-            for j in range(result.contents.get_numval(i)):
-                inst = result.contents.get_inst(i, j)
-
-                # Locate the correct instance and its position
-                if inst >= 0:
-                    if inst not in self.insts[i][0]:
-                        # Ignore newly emerged instances
-                        continue
-                    k = 0
-                    while inst != self.insts[i][0][k]:
-                        k += 1
+        self.report(None)
 
-                # Extract and scale the value
-                try:
-                    # Use native type if no rescaling needed
-                    if self.descs[i].contents.type == PM_TYPE_STRING or \
-                       self.metrics[metric][3] == 1 or \
-                       (self.metrics[metric][2][2] == 1 and \
-                        str(self.descs[i].contents.units) == \
-                        str(self.metrics[metric][2][1])):
-                        rescale = 0
-                        vtype = self.descs[i].contents.type
-                    else:
-                        rescale = 1
-                        vtype = PM_TYPE_DOUBLE
-
-                    atom = self.context.pmExtractValue(
-                        result.contents.get_valfmt(i),
-                        result.contents.get_vlist(i, j),
-                        self.descs[i].contents.type,
-                        vtype)
-
-                    if rescale:
-                        atom = self.context.pmConvScale(
-                            vtype,
-                            atom, self.descs, i,
-                            self.metrics[metric][2][1])
-
-                    val = atom.dref(vtype)
-
-                    if rescale:
-                        val *= self.metrics[metric][2][2]
-                        val = int(val) if val == int(val) else val
-
-                    if inst >= 0:
-                        values[i][inst] = (inst, self.insts[i][1][k], val)
-                    else:
-                        values[i][PM_IN_NULL] = (-1, None, val)
-
-                except pmapi.pmErr as error:
-                    sys.stderr.write("%s: %s, aborting.\n" % (metric, 
str(error)))
-                    sys.exit(1)
-
-        # Convert dicts to lists
-        vals = []
-        for v in values:
-            vals.append(v.values())
-        values = vals
-
-        # Store current and previous values
-        # Output modules need to handle non-existing self.prevvals
-        self.prevvals = self.currvals
-        self.currvals = values
-
-    def report(self, tstamp, values):
+    def report(self, tstamp):
         """ Report the metric values """
         if tstamp != None:
-            ts = self.context.pmLocaltime(tstamp.tv_sec)
-            us = int(tstamp.tv_usec)
-            dt = datetime(ts.tm_year+1900, ts.tm_mon+1, ts.tm_mday,
-                          ts.tm_hour, ts.tm_min, ts.tm_sec, us, None)
-            tstamp = dt.strftime(self.timefmt)
+            tstamp = tstamp.strftime(self.timefmt)
 
         if self.output == OUTPUT_ARCHIVE:
-            self.write_archive(tstamp, values)
+            self.write_archive(tstamp)
         if self.output == OUTPUT_CSV:
-            self.write_csv(tstamp, values)
+            self.write_csv(tstamp)
         if self.output == OUTPUT_STDOUT:
-            self.write_stdout(tstamp, values)
+            self.write_stdout(tstamp)
         if self.output == OUTPUT_ZABBIX:
-            self.write_zabbix(tstamp, values)
+            self.write_zabbix(tstamp)
 
     def prepare_writer(self):
         """ Prepare generic stdout writer """
@@ -1112,10 +1016,8 @@ class PMReporter(object):
         duration = int(duration) if duration == int(duration) else 
"{0:.3f}".format(duration)
 
         if self.context.type == PM_CONTEXT_ARCHIVE:
-            if not self.interpol:
-                endtime = float(self.context.pmGetArchiveEnd())
             if endtime > float(self.context.pmGetArchiveEnd()):
-                endtime = float(self.context.pmGetArchiveEnd())
+                endtime = self.context.pmGetArchiveEnd()
             if not self.interpol and self.opts.pmGetOptionSamples():
                 samples = str(samples) + " (requested)"
             elif not self.interpol:
@@ -1191,7 +1093,7 @@ class PMReporter(object):
             if self.context.type == PM_CONTEXT_ARCHIVE:
                 self.delay = 0
                 self.interpol = 0
-                self.zabbix_interval = 250 # See zabbix_sender(8), 
pmrep.conf(5)
+                self.zabbix_interval = 250 # See zabbix_sender(8)
                 self.writer.write("Sending %d archived metrics to Zabbix 
server %s...\n(Ctrl-C to stop)\n" % (len(self.pmids), self.zabbix_server))
                 return
 
@@ -1204,9 +1106,9 @@ class PMReporter(object):
             else:
                 self.writer.write("...\n(Ctrl-C to stop)\n")
 
-    def write_archive(self, timestamp, values):
+    def write_archive(self, timestamp):
         """ Write an archive record """
-        if timestamp == None and values == None:
+        if timestamp == None:
             # Complete and close
             self.pmi.pmiEnd()
             self.pmi = None
@@ -1238,25 +1140,27 @@ class PMReporter(object):
         for i, metric in enumerate(self.metrics):
             ins = 1 if self.insts[i][0][0] == PM_IN_NULL else 
len(self.insts[i][0])
             for j in range(ins):
-                if str(list(values[i])[j][2]) != NO_VAL:
-                    data = 1
+                try:
+                    value = self.metrics[metric][5][j]()
                     inst = self.insts[i][1][j]
+                    data = 1
                     if self.descs[i].contents.type == PM_TYPE_STRING:
-                        self.pmi.pmiPutValue(metric, inst, 
str(list(values[i])[j][2]))
+                        self.pmi.pmiPutValue(metric, inst, value)
                     elif self.descs[i].contents.type == PM_TYPE_FLOAT or \
                          self.descs[i].contents.type == PM_TYPE_DOUBLE:
-                        self.pmi.pmiPutValue(metric, inst, "%f" % 
list(values[i])[j][2])
+                        self.pmi.pmiPutValue(metric, inst, "%f" % value)
                     else:
-                        self.pmi.pmiPutValue(metric, inst, "%d" % 
list(values[i])[j][2])
+                        self.pmi.pmiPutValue(metric, inst, "%d" % value)
+                except:
+                    pass
 
         # Flush
         if data:
-            # pylint: disable=maybe-no-member
-            self.pmi.pmiWrite(self.ctstamp.tv_sec, self.ctstamp.tv_usec)
+            self.pmi.pmiWrite(int(self.pmfg_ts().strftime('%s')), 
self.pmfg_ts().microsecond)
 
-    def write_csv(self, timestamp, values):
+    def write_csv(self, timestamp):
         """ Write results in CSV format """
-        if timestamp == None and values == None:
+        if timestamp == None:
             # Silent goodbye
             return
 
@@ -1266,7 +1170,10 @@ class PMReporter(object):
             ins = 1 if self.insts[i][0][0] == PM_IN_NULL else 
len(self.insts[i][0])
             for j in range(ins):
                 line += self.delimiter
-                value = list(values[i])[j][2]
+                try:
+                    value = self.metrics[metric][5][j]()
+                except:
+                    value = NO_VAL
                 if type(value) is float:
                     fmt = "." + str(self.precision) + "f"
                     line += format(value, fmt)
@@ -1281,9 +1188,9 @@ class PMReporter(object):
                         line += str("\"" + value + "\"")
         self.writer.write(line + "\n")
 
-    def write_stdout(self, timestamp, values):
+    def write_stdout(self, timestamp):
         """ Write a line to stdout """
-        if timestamp == None and values == None:
+        if timestamp == None:
             # Silent goodbye
             return
 
@@ -1301,29 +1208,13 @@ class PMReporter(object):
         for i, metric in enumerate(self.metrics):
             l = self.metrics[metric][4]
 
-            for j in range(len(values[i])):
+            for j in range(len(self.metrics[metric][5])):
                 k += 1
 
-                # Raw or rate
-                if self.metrics[metric][3] or \
-                  self.descs[i].sem != PM_SEM_COUNTER or \
-                  list(values[i])[j][2] == NO_VAL:
-                    # Raw
-                    value = list(values[i])[j][2]
-                elif not self.metrics[metric][3] and \
-                  (self.prevvals == None or list(self.prevvals[i])[j][2] == 
NO_VAL):
-                    # Rate not yet possible
+                try:
+                    value = self.metrics[metric][5][j]()
+                except:
                     value = NO_VAL
-                else:
-                    # Rate
-                    scale = 1
-                    if self.descs[i].contents.units.dimTime != 0:
-                        if self.descs[i].contents.units.scaleTime > 
PM_TIME_SEC:
-                            scale = pow(60, (PM_TIME_SEC - 
self.descs[i].contents.units.scaleTime))
-                        else:
-                            scale = pow(1000, (PM_TIME_SEC - 
self.descs[i].contents.units.scaleTime))
-                    delta = scale * (float(self.ctstamp) - float(self.ptstamp))
-                    value = (list(values[i])[j][2] - 
list(self.prevvals[i])[j][2]) / delta if delta else 0
 
                 # Make sure the value fits
                 if type(value) is int or type(value) is long:
@@ -1369,9 +1260,9 @@ class PMReporter(object):
         nfmt = nfmt[:-l]
         self.writer.write(nfmt.format(*tuple(line)) + "\n")
 
-    def write_zabbix(self, timestamp, values):
+    def write_zabbix(self, timestamp):
         """ Write (send) metrics to a Zabbix server """
-        if timestamp == None and values == None:
+        if timestamp == None:
             # Send any remaining buffered values
             if self.zabbix_metrics:
                 send_to_zabbix(self.zabbix_metrics, self.zabbix_server, 
self.zabbix_port)
@@ -1379,7 +1270,7 @@ class PMReporter(object):
             return
 
         # Collect the results
-        ts = float(self.ctstamp)
+        ts = self.pmfg_ts().timestamp()
         if self.zabbix_prevsend == None:
             self.zabbix_prevsend = ts
         for i, metric in enumerate(self.metrics):
@@ -1388,9 +1279,11 @@ class PMReporter(object):
                 key = ZBXPRFX + metric
                 if self.insts[i][1][j]:
                     key += "[" + str(self.insts[i][1][j]) + "]"
-                val = str(list(values[i])[j][2])
-                if val != NO_VAL:
-                    self.zabbix_metrics.append(ZabbixMetric(self.zabbix_host, 
key, val, ts))
+                try:
+                    value = str(self.metrics[metric][5][j]())
+                    self.zabbix_metrics.append(ZabbixMetric(self.zabbix_host, 
key, value, ts))
+                except:
+                    pass
 
         # Send when needed
         if self.context.type == PM_CONTEXT_ARCHIVE:

Thanks,

-- 
Marko Myllynen

<Prev in Thread] Current Thread [Next in Thread>