Re: pmrep: add support for swapping columns and rows in stdout output

"Mark Goodwin" <[email protected]>
Newsgroups gmane.comp.sysutils.pcp
Message-ID <[email protected]>
Hi Marko, the new patch looks fine, and I like the choice of '~' as a
column filler in -X mode.
I've tested the -X "label" option seems to work fine and the instance
column width is set appropriately.
One nit with the new QA qa/1069 seems to be failing with a formatting issue:

> Traceback (most recent call last):
>   File "/usr/bin/pmrep", line 1622, in <module>
>     P.execute()
>   File "/usr/bin/pmrep", line 1008, in execute
>     self.report(self.pmfg_ts())
>   File "/usr/bin/pmrep", line 1027, in report
>     self.write_stdout(tstamp)
>   File "/usr/bin/pmrep", line 1351, in write_stdout
>     self.write_stdout_colxrow(timestamp)
>   File "/usr/bin/pmrep", line 1554, in write_stdout_colxrow
>     output += nfmt.format(*tuple(line)) + "\n"
> TypeError: non-empty format string passed to object.__format__


Anyway, I've committed your patch and am about to merge to master. So if
you can repro and fix that QA failure, just send an incremental patch.

Thanks
-- Mark

On Wed, Nov 9, 2016 at 9:17 AM, Marko Myllynen <[email protected]> wrote:

> Hi,
>
> On 2016-11-08 01:09, Mark Goodwin wrote:
> >
> > yes that would work, or perhaps a quoted string argument could determine
> > the column width,
> > and also provide a heading for the instance column. e.g.
> >     -X "             Device Name"
> >
> > (one problem with dm devices is they tend to be very long (especially
> > docker / thin-pool LVs).
>
> Hmm, ok, perhaps this is still feasible, I've now implemented this
> approach.
>
> > yep sounds good. Maybe not '-' since it's a valid numerical prefix
> > (unary negation).
> > And "." would have the same problem. I guess ':' or '_' might be OK?
>
> How about ~ ?
>
> -----
>
> Allows easier grepping (if -i is not feasible) and mimicing some
> other utilities more closely, e.g., nicstat(1):
>
> https://github.com/superjamie/pcp/commit/c6969807a7dfdf2bce6f3b1dff0ec1
> 084bb947f1
> ---
>  qa/1069                      |   2 +
>  qa/1069.out                  | 124 +++++++++++++++++++++++++++++
>  src/bashrc/pcp_completion.sh |   2 +-
>  src/pmrep/TODO               |   3 +-
>  src/pmrep/pmrep.1            |  17 +++-
>  src/pmrep/pmrep.conf         |   1 +
>  src/pmrep/pmrep.conf.5       |   9 ++-
>  src/pmrep/pmrep.py           | 186 ++++++++++++++++++++++++++++++
> ++++++++++++-
>  src/zshrc/_pcp               |   1 +
>  9 files changed, 336 insertions(+), 9 deletions(-)
>
> diff --git a/qa/1069 b/qa/1069
> index 7d216a9..e22bed9 100755
> --- a/qa/1069
> +++ b/qa/1069
> @@ -86,6 +86,8 @@ pmrep -T 7s -t 3 -p $log -c $tmp.config -x sample |
> _path_filter \
>  | _archive_filter
>  echo "---"
>  pmrep -s 5 -t 2 $log -c $tmp.config -G -H -U sample
> +pmrep -s 5 -t 2 $log2 -c $tmp.config -p -l '|' -X "" disk.dev.read
> disk.dev.write disk.dev.total network.interface.mtu mem.util.free
> kernel.all.load mem.util.used
> +pmrep -s 5 -t 2 $log2 -c $tmp.config -p -l '|' -X "Instance name"
> disk.dev.read disk.dev.write disk.dev.total network.interface.mtu
> mem.util.free kernel.all.load mem.util.used
>
>  echo "== exercise non-integer options"
>  pmrep -T 2.5s -t 0.5 -p $log -c $tmp.config -x sample | _path_filter \
> diff --git a/qa/1069.out b/qa/1069.out
> index 6c3723b..ab483d5 100644
> --- a/qa/1069.out
> +++ b/qa/1069.out
> @@ -78,6 +78,130 @@ HH:MM:SS        1
>        1.000        1000.033
>        1.000        1000.001
>        1.000        1000.016
> +        |        |d.d.read|d.d.write|d.d.total|
> n.i.mtu|m.u.free|k.a.load|m.u.used
> +        |        | count/s|  count/s|  count/s|   byte|   Kbyte|
> |   Kbyte
> +00:47:01|sda     |     N/A|      N/A|      N/A|      ~|       ~|
>  ~|       ~
> +00:47:01|sdb     |     N/A|      N/A|      N/A|      ~|       ~|
>  ~|       ~
> +00:47:01|sdc     |     N/A|      N/A|      N/A|      ~|       ~|
>  ~|       ~
> +00:47:01|hdc     |     N/A|      N/A|      N/A|      ~|       ~|
>  ~|       ~
> +00:47:01|sdd     |     N/A|      N/A|      N/A|      ~|       ~|
>  ~|       ~
> +00:47:01|sde     |     N/A|      N/A|      N/A|      ~|       ~|
>  ~|       ~
> +00:47:01|lo      |       ~|        ~|        ~|  16436|       ~|
>  ~|       ~
> +00:47:01|eth0    |       ~|        ~|        ~|   1500|       ~|
>  ~|       ~
> +00:47:01|None    |       ~|        ~|        ~|      ~|   38220|       ~|
> 6068648
> +00:47:01|1 minute|       ~|        ~|        ~|      ~|       ~|
>  0.820|       ~
> +00:47:01|5 minute|       ~|        ~|        ~|      ~|       ~|
>  0.700|       ~
> +00:47:01|15 minut|       ~|        ~|        ~|      ~|       ~|
>  0.590|       ~
> +00:47:03|sda     |   0.000|    0.500|    0.500|      ~|       ~|
>  ~|       ~
> +00:47:03|sdb     |  30.000|  296.500|  326.500|      ~|       ~|
>  ~|       ~
> +00:47:03|sdc     |   0.500|    0.500|    1.000|      ~|       ~|
>  ~|       ~
> +00:47:03|hdc     |   0.000|    0.000|    0.000|      ~|       ~|
>  ~|       ~
> +00:47:03|sdd     |   0.000|    0.000|    0.000|      ~|       ~|
>  ~|       ~
> +00:47:03|sde     |   1.000|   12.000|   12.000|      ~|       ~|
>  ~|       ~
> +00:47:03|lo      |       ~|        ~|        ~|  16436|       ~|
>  ~|       ~
> +00:47:03|eth0    |       ~|        ~|        ~|   1500|       ~|
>  ~|       ~
> +00:47:03|None    |       ~|        ~|        ~|      ~|   38220|       ~|
> 6068648
> +00:47:03|1 minute|       ~|        ~|        ~|      ~|       ~|
>  0.820|       ~
> +00:47:03|5 minute|       ~|        ~|        ~|      ~|       ~|
>  0.700|       ~
> +00:47:03|15 minut|       ~|        ~|        ~|      ~|       ~|
>  0.590|       ~
> +00:47:05|sda     |   0.000|    0.500|    0.500|      ~|       ~|
>  ~|       ~
> +00:47:05|sdb     |  30.000|  296.000|  326.000|      ~|       ~|
>  ~|       ~
> +00:47:05|sdc     |   0.000|    0.500|    0.500|      ~|       ~|
>  ~|       ~
> +00:47:05|hdc     |   0.000|    0.000|    0.000|      ~|       ~|
>  ~|       ~
> +00:47:05|sdd     |   0.000|    0.000|    0.000|      ~|       ~|
>  ~|       ~
> +00:47:05|sde     |   1.000|   11.500|   12.500|      ~|       ~|
>  ~|       ~
> +00:47:05|lo      |       ~|        ~|        ~|  16436|       ~|
>  ~|       ~
> +00:47:05|eth0    |       ~|        ~|        ~|   1500|       ~|
>  ~|       ~
> +00:47:05|None    |       ~|        ~|        ~|      ~|   38220|       ~|
> 6068648
> +00:47:05|1 minute|       ~|        ~|        ~|      ~|       ~|
>  0.820|       ~
> +00:47:05|5 minute|       ~|        ~|        ~|      ~|       ~|
>  0.700|       ~
> +00:47:05|15 minut|       ~|        ~|        ~|      ~|       ~|
>  0.590|       ~
> +00:47:07|sda     |   0.000|    0.500|    0.500|      ~|       ~|
>  ~|       ~
> +00:47:07|sdb     |  30.000|  296.500|  326.500|      ~|       ~|
>  ~|       ~
> +00:47:07|sdc     |   0.500|    0.500|    1.000|      ~|       ~|
>  ~|       ~
> +00:47:07|hdc     |   0.000|    0.000|    0.000|      ~|       ~|
>  ~|       ~
> +00:47:07|sdd     |   0.000|    0.000|    0.000|      ~|       ~|
>  ~|       ~
> +00:47:07|sde     |   1.000|   11.500|   12.500|      ~|       ~|
>  ~|       ~
> +00:47:07|lo      |       ~|        ~|        ~|  16436|       ~|
>  ~|       ~
> +00:47:07|eth0    |       ~|        ~|        ~|   1500|       ~|
>  ~|       ~
> +00:47:07|None    |       ~|        ~|        ~|      ~|   38220|       ~|
> 6068648
> +00:47:07|1 minute|       ~|        ~|        ~|      ~|       ~|
>  0.820|       ~
> +00:47:07|5 minute|       ~|        ~|        ~|      ~|       ~|
>  0.700|       ~
> +00:47:07|15 minut|       ~|        ~|        ~|      ~|       ~|
>  0.590|       ~
> +00:47:09|sda     |   0.000|    0.500|    0.500|      ~|       ~|
>  ~|       ~
> +00:47:09|sdb     |  30.000|  296.000|  326.000|      ~|       ~|
>  ~|       ~
> +00:47:09|sdc     |   0.500|    0.500|    0.500|      ~|       ~|
>  ~|       ~
> +00:47:09|hdc     |   0.000|    0.000|    0.000|      ~|       ~|
>  ~|       ~
> +00:47:09|sdd     |   0.000|    0.000|    0.000|      ~|       ~|
>  ~|       ~
> +00:47:09|sde     |   0.500|   11.500|   12.500|      ~|       ~|
>  ~|       ~
> +00:47:09|lo      |       ~|        ~|        ~|  16436|       ~|
>  ~|       ~
> +00:47:09|eth0    |       ~|        ~|        ~|   1500|       ~|
>  ~|       ~
> +00:47:09|None    |       ~|        ~|        ~|      ~|   38220|       ~|
> 6068648
> +00:47:09|1 minute|       ~|        ~|        ~|      ~|       ~|
>  0.820|       ~
> +00:47:09|5 minute|       ~|        ~|        ~|      ~|       ~|
>  0.700|       ~
> +00:47:09|15 minut|       ~|        ~|        ~|      ~|       ~|
>  0.590|       ~
> +        |Instance name|d.d.read|d.d.write|d.d.total|n.i.mtu|m.u.free|k.a.
> load|m.u.used
> +        |             | count/s|  count/s|  count/s|   byte|   Kbyte|
>     |   Kbyte
> +00:47:01|sda          |     N/A|      N/A|      N/A|      ~|       ~|
>    ~|       ~
> +00:47:01|sdb          |     N/A|      N/A|      N/A|      ~|       ~|
>    ~|       ~
> +00:47:01|sdc          |     N/A|      N/A|      N/A|      ~|       ~|
>    ~|       ~
> +00:47:01|hdc          |     N/A|      N/A|      N/A|      ~|       ~|
>    ~|       ~
> +00:47:01|sdd          |     N/A|      N/A|      N/A|      ~|       ~|
>    ~|       ~
> +00:47:01|sde          |     N/A|      N/A|      N/A|      ~|       ~|
>    ~|       ~
> +00:47:01|lo           |       ~|        ~|        ~|  16436|       ~|
>    ~|       ~
> +00:47:01|eth0         |       ~|        ~|        ~|   1500|       ~|
>    ~|       ~
> +00:47:01|None         |       ~|        ~|        ~|      ~|   38220|
>    ~| 6068648
> +00:47:01|1 minute     |       ~|        ~|        ~|      ~|       ~|
>  0.820|       ~
> +00:47:01|5 minute     |       ~|        ~|        ~|      ~|       ~|
>  0.700|       ~
> +00:47:01|15 minute    |       ~|        ~|        ~|      ~|       ~|
>  0.590|       ~
> +00:47:03|sda          |   0.000|    0.500|    0.500|      ~|       ~|
>    ~|       ~
> +00:47:03|sdb          |  30.000|  296.500|  326.500|      ~|       ~|
>    ~|       ~
> +00:47:03|sdc          |   0.500|    0.500|    1.000|      ~|       ~|
>    ~|       ~
> +00:47:03|hdc          |   0.000|    0.000|    0.000|      ~|       ~|
>    ~|       ~
> +00:47:03|sdd          |   0.000|    0.000|    0.000|      ~|       ~|
>    ~|       ~
> +00:47:03|sde          |   1.000|   12.000|   12.000|      ~|       ~|
>    ~|       ~
> +00:47:03|lo           |       ~|        ~|        ~|  16436|       ~|
>    ~|       ~
> +00:47:03|eth0         |       ~|        ~|        ~|   1500|       ~|
>    ~|       ~
> +00:47:03|None         |       ~|        ~|        ~|      ~|   38220|
>    ~| 6068648
> +00:47:03|1 minute     |       ~|        ~|        ~|      ~|       ~|
>  0.820|       ~
> +00:47:03|5 minute     |       ~|        ~|        ~|      ~|       ~|
>  0.700|       ~
> +00:47:03|15 minute    |       ~|        ~|        ~|      ~|       ~|
>  0.590|       ~
> +00:47:05|sda          |   0.000|    0.500|    0.500|      ~|       ~|
>    ~|       ~
> +00:47:05|sdb          |  30.000|  296.000|  326.000|      ~|       ~|
>    ~|       ~
> +00:47:05|sdc          |   0.000|    0.500|    0.500|      ~|       ~|
>    ~|       ~
> +00:47:05|hdc          |   0.000|    0.000|    0.000|      ~|       ~|
>    ~|       ~
> +00:47:05|sdd          |   0.000|    0.000|    0.000|      ~|       ~|
>    ~|       ~
> +00:47:05|sde          |   1.000|   11.500|   12.500|      ~|       ~|
>    ~|       ~
> +00:47:05|lo           |       ~|        ~|        ~|  16436|       ~|
>    ~|       ~
> +00:47:05|eth0         |       ~|        ~|        ~|   1500|       ~|
>    ~|       ~
> +00:47:05|None         |       ~|        ~|        ~|      ~|   38220|
>    ~| 6068648
> +00:47:05|1 minute     |       ~|        ~|        ~|      ~|       ~|
>  0.820|       ~
> +00:47:05|5 minute     |       ~|        ~|        ~|      ~|       ~|
>  0.700|       ~
> +00:47:05|15 minute    |       ~|        ~|        ~|      ~|       ~|
>  0.590|       ~
> +00:47:07|sda          |   0.000|    0.500|    0.500|      ~|       ~|
>    ~|       ~
> +00:47:07|sdb          |  30.000|  296.500|  326.500|      ~|       ~|
>    ~|       ~
> +00:47:07|sdc          |   0.500|    0.500|    1.000|      ~|       ~|
>    ~|       ~
> +00:47:07|hdc          |   0.000|    0.000|    0.000|      ~|       ~|
>    ~|       ~
> +00:47:07|sdd          |   0.000|    0.000|    0.000|      ~|       ~|
>    ~|       ~
> +00:47:07|sde          |   1.000|   11.500|   12.500|      ~|       ~|
>    ~|       ~
> +00:47:07|lo           |       ~|        ~|        ~|  16436|       ~|
>    ~|       ~
> +00:47:07|eth0         |       ~|        ~|        ~|   1500|       ~|
>    ~|       ~
> +00:47:07|None         |       ~|        ~|        ~|      ~|   38220|
>    ~| 6068648
> +00:47:07|1 minute     |       ~|        ~|        ~|      ~|       ~|
>  0.820|       ~
> +00:47:07|5 minute     |       ~|        ~|        ~|      ~|       ~|
>  0.700|       ~
> +00:47:07|15 minute    |       ~|        ~|        ~|      ~|       ~|
>  0.590|       ~
> +00:47:09|sda          |   0.000|    0.500|    0.500|      ~|       ~|
>    ~|       ~
> +00:47:09|sdb          |  30.000|  296.000|  326.000|      ~|       ~|
>    ~|       ~
> +00:47:09|sdc          |   0.500|    0.500|    0.500|      ~|       ~|
>    ~|       ~
> +00:47:09|hdc          |   0.000|    0.000|    0.000|      ~|       ~|
>    ~|       ~
> +00:47:09|sdd          |   0.000|    0.000|    0.000|      ~|       ~|
>    ~|       ~
> +00:47:09|sde          |   0.500|   11.500|   12.500|      ~|       ~|
>    ~|       ~
> +00:47:09|lo           |       ~|        ~|        ~|  16436|       ~|
>    ~|       ~
> +00:47:09|eth0         |       ~|        ~|        ~|   1500|       ~|
>    ~|       ~
> +00:47:09|None         |       ~|        ~|        ~|      ~|   38220|
>    ~| 6068648
> +00:47:09|1 minute     |       ~|        ~|        ~|      ~|       ~|
>  0.820|       ~
> +00:47:09|5 minute     |       ~|        ~|        ~|      ~|       ~|
>  0.700|       ~
> +00:47:09|15 minute    |       ~|        ~|        ~|      ~|       ~|
>  0.590|       ~
>  == exercise non-integer options
>
>    archive: QAPATH/archives/sample-secs
> diff --git a/src/bashrc/pcp_completion.sh b/src/bashrc/pcp_completion.sh
> index 9bffae0..ff11b9d 100644
> --- a/src/bashrc/pcp_completion.sh
> +++ b/src/bashrc/pcp_completion.sh
> @@ -42,7 +42,7 @@ _pcp_complete()
>          ;;
>
>          pmrep)
> -        opt_regex="-[AaBbcCdDeEfFGhHiKLloOpPqQrsStTuUVwxyYzZ]"
> +        opt_regex="-[AaBbCcDdEeFfGHhiKLlOoPpQqrSsTtUuVwXxYyZz]"
>          curpos_expand=1
>          ;;
>
> diff --git a/src/pmrep/TODO b/src/pmrep/TODO
> index 317b4a7..ecb4c0b 100644
> --- a/src/pmrep/TODO
> +++ b/src/pmrep/TODO
> @@ -1,5 +1,4 @@
>  -  opt to write cols per row with stdout output [-k]
> --  opt to swap cols and rows (e.g., compare ps vs sar)
>  -  -Q/-B/-Y to override per-metric settings unit/scale
>  -  shorten unit display count/s -> c/s if needed
>  -  opt to output in short format (see pmdumptext -F / pmNumberStr) [-m/-M]
> @@ -27,7 +26,7 @@
>  -----
>
>  In use / reserved cmd line options:
> -AaBbcCdDeEfFGhHiKLloOpPqQrsStTuUVwxyYzZ
> +AaBbCcDdEeFfGHhiKLlOoPpQqrSsTtUuVwXxYyZz
>
>  Tentatively planned:
>  IkMmR
> diff --git a/src/pmrep/pmrep.1 b/src/pmrep/pmrep.1
> index 7ccfb08..ec1caaf 100644
> --- a/src/pmrep/pmrep.1
> +++ b/src/pmrep/pmrep.1
> @@ -44,6 +44,7 @@
>  [\f3\-t\f1 \f2interval\f1]
>  [\f3\-T\f1 \f2endtime\f1]
>  [\f3\-w\f1 \f2width\f1]
> +[\f3\-X\f1 \f2label\f1]
>  [\f3\-y\f1 \f2time-scale\f1]
>  [\f3\-Z\f1 \f2timezone\f1]
>  \f2metricspec [...]\f1
> @@ -340,9 +341,9 @@ method which is mostly the same as that described in
>  An empty
>  .I format
>  string (i.e., '') will remove the timestamps from the output.
> -The default with stdout is
> +The default with \fIstdout\fR is
>  .BR %H:%M:%S .
> -The default with csv is
> +The default with \fIcsv\fR is
>  .BR "%Y-%m-%d %H:%M:%S" .
>  .TP
>  .B \-F
> @@ -632,7 +633,7 @@ Omit unit information from of headers.
>  Display version number and exit.
>  .TP
>  .B \-w
> -Set the stdout output column
> +Set the \fIstdout\fR output column
>  .IR width .
>  Strings will be truncated to this width.
>  The default
> @@ -645,6 +646,16 @@ override possible per-metric specifications.
>  .B \-x
>  Print extended header.
>  .TP
> +.B \-X
> +Swap columns and rows in \fIstdout\fR output, reporting one instance per
> +line, using \fIlabel\fR as the label for instance column (set to an empty
> +string \fB""\fR to enable swapping without a specific label).
> +This is convenient to allow easily using
> +.BR grep (1)
> +to filter results or to more closely mimic other utilities.
> +See also
> +.BR -i .
> +.TP
>  .B \-y
>  .I Unit/scale
>  for time metrics, possible values include
> diff --git a/src/pmrep/pmrep.conf b/src/pmrep/pmrep.conf
> index fcdd8e5..87dd258 100644
> --- a/src/pmrep/pmrep.conf
> +++ b/src/pmrep/pmrep.conf
> @@ -18,6 +18,7 @@
>  #delay = no
>  #type = default
>  #instances =
> +#colxrow =
>  #width =
>  #precision = 3
>  #delimiter =
> diff --git a/src/pmrep/pmrep.conf.5 b/src/pmrep/pmrep.conf.5
> index 48550bc..afeba0e 100644
> --- a/src/pmrep/pmrep.conf.5
> +++ b/src/pmrep/pmrep.conf.5
> @@ -197,11 +197,18 @@ Corresponding command line option is \fB-r\fR.
> Allowed values are
>  .P
>  instances (string)
>  .RS 4
> -Defines the instances to be reported. Corresponding command line
> +Indicates the instances to be reported. Corresponding command line
>  option is \fB-i\fR. Undefined (all current instances are reported)
>  by default.
>  .RE
>  .P
> +colxrow (string)
> +.RS 4
> +Indicates to swap columns and rows in \fIstdout\fR output using
> +the given label. Corresponding command line option is \fB-X\fR.
> +Undefined (no swapping) by default.
> +.RE
> +.P
>  width (integer)
>  .RS 4
>  Indicates the width of stdout output columns. Corresponding command line
> diff --git a/src/pmrep/pmrep.py b/src/pmrep/pmrep.py
> index 05110c0..8132a0c 100755
> --- a/src/pmrep/pmrep.py
> +++ b/src/pmrep/pmrep.py
> @@ -82,6 +82,7 @@ OUTTIME = "%H:%M:%S"
>  ZBXPORT = 10051
>  ZBXPRFX = "pcp."
>  NO_VAL  = "N/A"
> +NO_INST = "~"
>  TRUNC   = "xxx"
>  VERSION = 1
>
> @@ -174,7 +175,7 @@ class PMReporter(object):
>                       'extheader', 'repeat_header', 'timefmt', 'interpol',
>                       'count_scale', 'space_scale', 'time_scale',
> 'version',
>                       'zabbix_server', 'zabbix_port', 'zabbix_host',
> 'zabbix_interval',
> -                     'speclocal', 'instances')
> +                     'speclocal', 'instances', 'colxrow')
>
>          # Special command line switches
>          self.arghelp = ('-?', '--help', '-V', '--version')
> @@ -206,6 +207,7 @@ class PMReporter(object):
>          self.delay = 0
>          self.type = 0
>          self.instances = []
> +        self.colxrow = None
>          self.width = 0
>          self.precision = 3 # .3f
>          self.delimiter = None
> @@ -246,7 +248,7 @@ class PMReporter(object):
>          opts = pmapi.pmOptions()
>          opts.pmSetOptionCallback(self.option)
>          opts.pmSetOverrideCallback(self.option_override)
> -        opts.pmSetShortOptions("a:h:LK:c:Co:F:e:D:V?HUGpA:S:T:O:s:
> t:Z:zdri:w:P:l:xE:f:uq:b:y:")
> +        opts.pmSetShortOptions("a:h:LK:c:Co:F:e:D:V?HUGpA:S:T:O:s:
> t:Z:zdri:X:w:P:l:xE:f:uq:b:y:")
>          opts.pmSetShortUsage("[option...] metricspec [...]")
>
>          opts.pmSetLongOptionHeader("General options")
> @@ -281,6 +283,7 @@ class PMReporter(object):
>          opts.pmSetLongOption("delay", 0, "d", "", "delay, pause between
> updates for archive replay")
>          opts.pmSetLongOption("raw", 0, "r", "", "output raw counter
> values (no rate conversion)")
>          opts.pmSetLongOption("instances", 1, "i", "STR", "instances to
> report (default: all current)")
> +        opts.pmSetLongOption("colxrow", 1, "X", "STR", "swap stdout
> columns and rows using header label")
>          opts.pmSetLongOption("width", 1, "w", "N", "default column width")
>          opts.pmSetLongOption("precision", 1, "P", "N", "N digits after
> the decimal separator (if width enough)")
>          opts.pmSetLongOption("delimiter", 1, "l", "STR", "delimiter to
> separate csv/stdout columns")
> @@ -347,6 +350,8 @@ class PMReporter(object):
>              self.type = 1
>          elif opt == 'i':
>              self.instances = self.instances + self.parse_instances(optarg)
> +        elif opt == 'X':
> +            self.colxrow = optarg
>          elif opt == 'w':
>              self.width = int(optarg)
>          elif opt == 'P':
> @@ -1034,6 +1039,13 @@ class PMReporter(object):
>
>      def prepare_stdout(self):
>          """ Prepare stdout output """
> +        if self.colxrow is None:
> +            self.prepare_stdout_std()
> +        else:
> +            self.prepare_stdout_colxrow()
> +
> +    def prepare_stdout_std(self):
> +        """ Prepare standard formatted stdout output """
>          index = 0
>          if self.timestamp == 0:
>              #self.format = "{:}{}"
> @@ -1058,6 +1070,40 @@ class PMReporter(object):
>          l = len(str(index-1)) + 2
>          self.format = self.format[:-l]
>
> +    def prepare_stdout_colxrow(self):
> +        """ Prepare columns and rows swapped stdout output """
> +        index = 0
> +
> +        # Timestamp
> +        if self.timestamp == 0:
> +            self.format = "{0:}{1}"
> +            index += 2
> +        else:
> +            tstamp = datetime.fromtimestamp(time.
> time()).strftime(self.timefmt)
> +            self.format = "{0:<" + str(len(tstamp)) + "." +
> str(len(tstamp)) + "}{1}"
> +            index += 2
> +
> +        # Instance name
> +        if self.colxrow:
> +            self.format += "{2:<" + str(len(self.colxrow)) + "." +
> str(len(self.colxrow)) + "}{3}"
> +        else:
> +            self.format += "{2:<" + str(8) + "." + str(8) + "}{3}"
> +        index += 2
> +
> +        # Metrics
> +        for i, metric in enumerate(self.metrics):
> +            l = str(self.metrics[metric][4])
> +            # Value truncated and aligned
> +            self.format += "{" + str(index) + ":>" + l + "." + l + "}"
> +            index += 1
> +            # Dummy
> +            self.format += "{" + str(index) + "}"
> +            index += 1
> +
> +        # Drop the last dummy
> +        l = len(str(index-1)) + 2
> +        self.format = self.format[:-l]
> +
>      def write_ext_header(self):
>          """ Write extended header """
>          comm = "#" if self.output == OUTPUT_CSV else ""
> @@ -1143,8 +1189,17 @@ class PMReporter(object):
>              names = ["", self.delimiter] # no timestamp on header line
>              insts = ["", self.delimiter] # no timestamp on instances line
>              units = ["", self.delimiter] # no timestamp on units line
> +            if self.colxrow is not None:
> +                names += [self.colxrow, self.delimiter]
> +                units += ["", self.delimiter]
>              prnti = 0
>              for i, metric in enumerate(self.metrics):
> +                if self.colxrow is not None:
> +                    names.append(self.metrics[metric][0])
> +                    names.append(self.delimiter)
> +                    units.append(self.metrics[metric][2][0])
> +                    units.append(self.delimiter)
> +                    continue
>                  prnti = 1 if self.insts[i][0][0] != PM_IN_NULL else prnti
>                  for j in range(len(self.insts[i][0])):
>                      names.append(self.metrics[metric][0])
> @@ -1299,6 +1354,13 @@ class PMReporter(object):
>
>      def write_stdout(self, timestamp):
>          """ Write a line to stdout """
> +        if self.colxrow is None:
> +            self.write_stdout_std(timestamp)
> +        else:
> +            self.write_stdout_colxrow(timestamp)
> +
> +    def write_stdout_std(self, timestamp):
> +        """ Write a line to standard formatted stdout """
>          if timestamp == None:
>              # Silent goodbye
>              return
> @@ -1373,6 +1435,126 @@ class PMReporter(object):
>          nfmt = nfmt[:-l]
>          self.writer.write(nfmt.format(*tuple(line)) + "\n")
>
> +    def write_stdout_colxrow(self, timestamp):
> +        """ Write a line to columns and rows swapped stdout """
> +        if timestamp == None:
> +            # Silent goodbye
> +            return
> +
> +        # Collect the instances in play
> +        insts = []
> +        for i in range(len(self.metrics)):
> +            for instance in self.insts[i][1]:
> +                if instance not in insts:
> +                    insts.append(instance)
> +
> +        # Avoid crossing the C/Python boundary more than once per metric
> +        res = OrderedDict()
> +        for i, metric in enumerate(self.metrics):
> +            res[metric] = []
> +            try:
> +                for inst, name, val in self.metrics[metric][5]():
> +                    try:
> +                        res[metric].append([inst, name, val()])
> +                    except:
> +                        res[metric].append([inst, name, NO_VAL])
> +                if not res[metric]:
> +                    res[metric].append(['', '', NO_VAL])
> +            except:
> +                res[metric].append(['', '', NO_VAL])
> +
> +        # Avoid per-line I/O
> +        output = ""
> +
> +        # Painfully iterate over what we have, the logic below
> +        # being that we need to construct each line independently
> +        for instance in insts:
> +            # Split on dummies
> +            fmt = re.split("{\\d+}", self.format)
> +
> +            # Start a new line
> +            k = 0
> +            line = []
> +
> +            # Add timestamp as wanted
> +            if self.timestamp == 0:
> +                line.append("")
> +            else:
> +                line.append(timestamp)
> +            line.append(self.delimiter)
> +            k += 1
> +
> +            # Add instance
> +            line.append(instance)
> +            line.append(self.delimiter)
> +            k += 1
> +
> +            # Look for this instance from each metric
> +            for metric in self.metrics:
> +                l = self.metrics[metric][4]
> +
> +                found = 0
> +                value = NO_VAL
> +                for inst in res[metric]:
> +                    if inst[1] == instance:
> +                        # This metric has the instance we're
> +                        # processing, grab it and format below
> +                        value = inst[2]
> +                        found = 1
> +                        break
> +
> +                if not found:
> +                        # Not an instance this metric has,
> +                        # add a placeholder and move on
> +                        line.append(NO_INST)
> +                        line.append(self.delimiter)
> +                        k += 1
> +                        continue
> +
> +                # Make sure the value fits
> +                if type(value) is int or type(value) is long:
> +                    if len(str(value)) > l:
> +                        value = TRUNC
> +                    else:
> +                        fmt[k] = "{X:" + str(l) + "d}"
> +
> +                if type(value) is float and not math.isinf(value):
> +                    c = self.precision
> +                    s = len(str(int(value)))
> +                    if s > l:
> +                        c = -1
> +                        value = TRUNC
> +                    for f in reversed(range(c+1)):
> +                        r = "{X:" + str(l) + "." + str(c) + "f}"
> +                        t = "{0:" + str(l) + "." + str(c) + "f}"
> +                        if len(t.format(value)) > l:
> +                            c -= 1
> +                        else:
> +                            fmt[k] = r
> +                            break
> +
> +                # Finally add the value
> +                line.append(value)
> +                line.append(self.delimiter)
> +                k += 1
> +
> +            # Print the line in a Python 2.6 compatible manner
> +            del line[-1]
> +            index = 0
> +            nfmt = ""
> +            for f in fmt:
> +                if type(line[index]) is float and math.isinf(line[index]):
> +                    line[index] = "inf"
> +                nfmt += f.replace("{X:", "{" + str(index) + ":")
> +                index += 1
> +                nfmt += "{" + str(index) + "}"
> +                index += 1
> +            l = len(str(index-1)) + 2
> +            nfmt = nfmt[:-l]
> +            output += nfmt.format(*tuple(line)) + "\n"
> +
> +        self.writer.write(output)
> +
>      def write_zabbix(self, timestamp):
>          """ Write (send) metrics to a Zabbix server """
>          if timestamp == None:
> diff --git a/src/zshrc/_pcp b/src/zshrc/_pcp
> index b625164..6e8e806 100644
> --- a/src/zshrc/_pcp
> +++ b/src/zshrc/_pcp
> @@ -440,6 +440,7 @@ _pcp () {
>        "(-d --delay --container -h --host -L --local-PMDA -K --spec-local
> -u --no-interpol $exargs)"{-d,--delay}'[delay between updates in archive
> mode]' \
>        "(-r --raw $exargs)"{-r,--raw}'[report raw values]' \
>        "($exargs)"\*{-i+,--instances}'[specify instances to
> report]:instances:->instances' \
> +      "(-X --colxrow $exargs)"{-X+,--colxrow}'[swap stdout columns and
> rows using header label]:label:' \
>        "(-w --width $exargs)"{-w+,--width}'[set default column
> width]:width:' \
>        "(-P --precision $exargs)"{-P+,--precision}'[set floating point
> precision]:precision:' \
>        "(-l --delimiter $exargs)"{-l+,--delimiter}'[set column
> delimiter]:delimiter:' \
>
> Thanks,
>
> --
> Marko Myllynen
>
> 
>
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.