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 > > > >