3 # Copyright (C) 2014 Red Hat.
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the
7 # Free Software Foundation; either version 2 of the License, or (at your
8 # option) any later version.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 """ Rewrite of pmgsys (originally a C++ application) in python
18 Needs to handle the following options at some point:
19 -h (host), -cpudelta (interval), -l (label), -v (verbose),
20 -c (config), -zoom (factor), ... rest through to pmgadgets.
26 from cpmapi
import PM_TYPE_U32
28 PCPARGS
= "" # for pmgadgets-launched child processes to use
30 # magic numbers, from original pmgsys algorithms
40 LOADHEIGHT
= 4 * CPUHEIGHT
+ 3 * VSPACE
42 MEMHEIGHT
= LOADHEIGHT
47 class Machine(object):
48 def __init__(self
, context
):
50 self
.context
= context
65 """ Extract counts of CPUs, disks, interfaces and memory size
67 hinv
= ('hinv.ncpu', 'hinv.ndisk', 'hinv.ninterface', 'hinv.physmem')
68 pmids
= self
.context
.pmLookupName(hinv
)
69 descs
= self
.context
.pmLookupDescs(pmids
)
70 result
= self
.context
.pmFetch(pmids
)
71 hardware
= [0, 0, 0, 0]
73 atom
= self
.context
.pmExtractValue(
74 result
.contents
.get_valfmt(x
),
75 result
.contents
.get_vlist(x
, 0),
76 descs
[x
].contents
.type, PM_TYPE_U32
)
78 context
.pmFreeResult(result
)
79 self
.ncpu
= hardware
[0]
80 self
.ndisk
= hardware
[1]
81 self
.niface
= hardware
[2]
82 self
.memory
= hardware
[3]
85 """ Extract names of CPUs, disks and network interfaces.
87 inst
= ('kernel.percpu.cpu.user', # expand CPU names
88 'disk.dev.total', # expand disk names
89 'disk.partitions.total', # expand disk partition names
90 'network.interface.total.bytes') # expand network interface names
91 pmids
= self
.context
.pmLookupName(inst
)
92 descs
= self
.context
.pmLookupDescs(pmids
)
94 (inst
, self
.cpus
) = self
.context
.pmGetInDom(descs
[0])
95 (inst
, self
.disks
) = self
.context
.pmGetInDom(descs
[1])
96 (inst
, self
.parts
) = self
.context
.pmGetInDom(descs
[2])
97 (inst
, self
.ifaces
) = self
.context
.pmGetInDom(descs
[3])
100 print "CPUs: ", self
.ncpu
101 print "CPU names: ", self
.cpus
102 print "Disks: ", self
.ndisk
103 print "Disk names: ", self
.disks
104 print "Partition names: ", self
.parts
105 print "Interfaces: ", self
.niface
106 print "Interface names: ", self
.ifaces
107 print "Memory: ", self
.memory
109 def get_diskmaps(self
):
110 """ Produce disk -> partition mappings
111 This means grouping "sda1 sda2 sda3 sdb1" into two mappings
112 - "sda" -> (sda1, sda2, sda3) and "sdb" -> (sdb1); done via
113 the disk.dev.total and disk.partitions.total metrics.
114 (original: controller -> disk mappings, but ENODATA)
119 """ Wrap calls to getting counts and subsystem names
126 """ Generate a pmgadgets configuration for this host
128 print "pmgadgets 1", # follow with command line
130 print "\"%s\"" % (arg
),
134 ctiles
= int((self
.ncpu
- 1) / 4 + 1) # always at least one cpu
135 ntiles
= int((self
.niface
- 1) / 4 + 1)
137 cr
= int(math
.sqrt(ctiles
))
141 nr
= int(math
.sqrt(ntiles
))
147 y
= FONTASCENT
+ VSPACE
148 hostname
= self
.context
.pmGetContextHostName()
149 print "_label %d %d \"%s\"" % (HSPACE
, y
, hostname
)
151 baseX
= maxX
= HSPACE
154 print "_actions cpuActions ("
155 print " \"pmchart\"\t\t\"pmchart -c CPU%s\"" % (PCPARGS
)
156 print " \"mpvis *\"\t\t\"mpvis%s\" _default" % (PCPARGS
)
157 # original had IRIX gr_top and gr_osview tools next;
158 # perhaps some fine day we could implement these as
159 # pmgadgets front-end tools (certainly the latter)
162 y
= baseY
+ FONTASCENT
164 print "_label %d %d \"CPU\"" % (x
, y
)
165 print " _actions cpuActions\n"
166 # original: "these should match the colours in mpvis"
167 print "_colourlist cpuColours (blue3 red3 yellow3 cyan3 green3)"
171 ccols
= int((ctiles
+ rows
- 1) / rows
)
174 for rc
in range(0, self
.ncpu
):
175 for ct
in range(0, ccols
):
177 while (cpu
< self
.ncpu
and tc
< 4):
178 print "_multibar %d %d %d %d" % (x
, y
, CPUWIDTH
, CPUHEIGHT
)
179 print(" _update %f" % (CPUDELTA
)).rstrip('0').rstrip('.')
181 print "\tkernel.percpu.cpu.user[\"%s\"]" % (self
.cpus
[cpu
])
182 print "\tkernel.percpu.cpu.sys[\"%s\"]" % (self
.cpus
[cpu
])
183 print "\tkernel.percpu.cpu.intr[\"%s\"]" % (self
.cpus
[cpu
])
184 print "\tkernel.percpu.cpu.wait.total[\"%s\"]" % (self
.cpus
[cpu
])
185 print "\tkernel.percpu.cpu.idle[\"%s\"]" % (self
.cpus
[cpu
])
187 print " _maximum 0.0\n"
188 print " _colourlist cpuColours"
189 print " _actions cpuActions\n"
192 y
+= VSPACE
+ CPUHEIGHT
196 y
-= (VSPACE
+ CPUHEIGHT
) * tc
197 x
+= HSPACE
+ CPUWIDTH
199 y
+= (CPUHEIGHT
+ VSPACE
) * 4 + VSPACE
204 baseX
+= (HSPACE
+ CPUWIDTH
) * ccols
206 # The load gadget and its label
207 print "_actions loadActions ("
208 print " \"pmchart *\"",
209 print "\t\"pmchart -c LoadAvg%s\" _default" % (PCPARGS
)
210 # original had IRIX gr_top here
212 print "_label %d %d \"Load\"" % (baseX
, baseY
+ FONTASCENT
)
213 print " _actions loadActions"
216 y
= VSPACE
+ baseY
+ FONTASCENT
222 print "_bargraph %d %d %d %d" % (baseX
, y
, LOADWIDTH
, LOADHEIGHT
)
223 print(" _update %f" % (LOADDELTA
)).rstrip('0').rstrip('.')
224 print " _metric kernel.all.load[\"1 minute\"]"
226 print " _actions loadActions"
228 # For more than one row, stack LoadAvg on top of Memory.
230 # Move baseX just after the right hand side of the memory, so
231 # we don't have to do anything special for the netifs. For the
232 # sake of argument, consider total width occupied by memory
233 # gauges equal to total width of loadavg graph
235 y
+= LOADHEIGHT
+ VSPACE
236 baseX
+= LOADWIDTH
+ HSPACE
239 baseX
+= LOADWIDTH
* 2 + HSPACE
* 2
241 # The memory gadgets and their label (platform-specific!)
242 x
= baseX
- LOADWIDTH
- HSPACE
244 print "_label %d %d \"Mem\"\n" % (x
, y
)
245 print "_colourlist memColours (cyan1 red yellow green)\n"
247 print "_multibar %d %d %d %d" % (x
, y
, MEMWIDTH
, MEMHEIGHT
)
250 print "\tmem.util.cached"
251 print "\tmem.util.bufmem"
252 print "\tmem.util.other"
253 print "\tmem.util.free"
255 print " _colourlist memColours"
256 x
+= HSPACE
+ MEMWIDTH
257 print "_bar %d %d %d %d" % (x
, y
, MEMWIDTH
, MEMHEIGHT
)
258 print " _metric swap.pagesout"
261 # Check for the max horizontal offset
266 # The network bars and their label
267 print "_colourlist netColours (aquamarine orange)"
268 print "_actions netActions ("
269 print " \"pmchart-packets *\"",
270 print "\t\"pmchart -c NetPackets%s\" _default" % (PCPARGS
)
271 print " \"pmchart-bytes\"",
272 print "\t\t\"pmchart -c NetBytes%s\"" % (PCPARGS
)
273 # original had netstat within an xterm here, next
276 y
= baseY
+ FONTASCENT
279 print "_label %d %d \"Net\"" % (x
, y
)
280 print " _actions netActions\n"
284 ncols
= int((ntiles
+ rows
- 1) / rows
)
287 while ni
< self
.niface
:
288 for nt
in range(0, ncols
):
290 while ni
< self
.niface
and tc
< 4:
291 print "_multibar %d %d %d %d" % (x
, y
, NETWIDTH
, NETHEIGHT
)
293 print "\tnetwork.interface.in.bytes[\"%s\"]" % (self
.ifaces
[ni
])
294 print "\tnetwork.interface.out.bytes[\"%s\"]" % (self
.ifaces
[ni
])
296 print "_colourlist netColours"
297 print " _actions netActions"
298 y
+= NETHEIGHT
+ VSPACE
305 y
-= (NETHEIGHT
+ VSPACE
) * tc
306 x
+= NETWIDTH
+ HSPACE
309 y
+= (NETHEIGHT
+ VSPACE
) * 4 + VSPACE
316 print "_actions diskActions ("
317 print " \"pmchart\"",
318 print "\t\t\"pmchart -c Disk%s\"" % (PCPARGS
)
319 print " \"dkvis *\"",
320 print "\t\t\"dkvis%s\" _default" % (PCPARGS
)
324 y
= maxY
+ FONTASCENT
+ 2 * VSPACE
325 print "_label %d %d \"Disk\"" % (x
, y
)
326 print " _actions diskActions\n"
327 print "_legend diskLegend ("
328 print " _default green3"
334 x
+= CPUWIDTH
+ HSPACE
335 # this only works if FONTASCENT >= ledSize
338 halfDiskSize
= int((DISKSIZE
- thickness
) / 2)
340 for i
in range(0, self
.ndiskmaps
):
341 mapping
= self
.diskmaps
[i
]
342 for j
in range(0, len(mappings
)):
344 if oldX
< x
: # moved to right
346 ly
= y
+ halfDiskSize
349 elif oldX
> x
: # moved to left
350 lx
= oldX
- VSPACE
- 1
351 ly
= y
+ halfDiskSize
355 lx
= x
+ halfDiskSize
356 ly
= oldY
+ DISKSIZE
- 1
359 print "_line %d %d %d %d" % (lx
, ly
, lw
, lh
)
360 print "_led %d %d %d %d" % (x
, y
, DISKSIZE
, DISKSIZE
)
361 print " _metric disk.dev.total[\"%s\"]" % (mapping
.name())
362 print " _legend diskLegend"
363 print " _actions diskActions"
366 xStep
= dir * (DISKSIZE
+ VSPACE
) # use VSPACE (tighter packing)
368 if x
> maxX
- DISKSIZE
or x
<= HSPACE
:
370 y
+= DISKSIZE
+ VSPACE
374 if __name__
== '__main__':
375 context
= pmapi
.pmContext()
376 machine
= Machine(context
)