diff options
Diffstat (limited to 'src/pmgadgets/pmgsys.py')
-rwxr-xr-x | src/pmgadgets/pmgsys.py | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/src/pmgadgets/pmgsys.py b/src/pmgadgets/pmgsys.py new file mode 100755 index 0000000..5f8a1df --- /dev/null +++ b/src/pmgadgets/pmgsys.py @@ -0,0 +1,380 @@ +#!/usr/bin/python +# +# Copyright (C) 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +""" Rewrite of pmgsys (originally a C++ application) in python + + Needs to handle the following options at some point: + -h (host), -cpudelta (interval), -l (label), -v (verbose), + -c (config), -zoom (factor), ... rest through to pmgadgets. +""" + +from sys import argv +from pcp import pmapi +from math import sqrt +from cpmapi import PM_TYPE_U32 + +PCPARGS = "" # for pmgadgets-launched child processes to use + +# magic numbers, from original pmgsys algorithms +CPUDELTA = 0.5 +LOADDELTA = 5 +FONTASCENT = 7 +DOLABEL = 1 +HSPACE = 5 +VSPACE = 2 +CPUWIDTH = 30 +CPUHEIGHT = 4 +LOADWIDTH = CPUWIDTH +LOADHEIGHT = 4 * CPUHEIGHT + 3 * VSPACE +MEMWIDTH = CPUHEIGHT +MEMHEIGHT = LOADHEIGHT +DISKSIZE = 6 +NETWIDTH = CPUWIDTH +NETHEIGHT = CPUHEIGHT + +class Machine(object): + def __init__(self, context): + # pcp context + self.context = context + # hardware bits + self.ncpu = 0 + self.ndisk = 0 + self.niface = 0 + self.ndiskmaps = 0 + self.memory = 0 + # external names + self.cpus = [] + self.disks = [] + self.parts = [] + self.ifaces = [] + self.diskmaps = [] + + def get_hinv(self): + """ Extract counts of CPUs, disks, interfaces and memory size + """ + hinv = ('hinv.ncpu', 'hinv.ndisk', 'hinv.ninterface', 'hinv.physmem') + pmids = self.context.pmLookupName(hinv) + descs = self.context.pmLookupDescs(pmids) + result = self.context.pmFetch(pmids) + hardware = [0, 0, 0, 0] + for x in xrange(4): + atom = self.context.pmExtractValue( + result.contents.get_valfmt(x), + result.contents.get_vlist(x, 0), + descs[x].contents.type, PM_TYPE_U32) + hardware[x] = atom.ul + context.pmFreeResult(result) + self.ncpu = hardware[0] + self.ndisk = hardware[1] + self.niface = hardware[2] + self.memory = hardware[3] + + def get_names(self): + """ Extract names of CPUs, disks and network interfaces. + """ + inst = ('kernel.percpu.cpu.user', # expand CPU names + 'disk.dev.total', # expand disk names + 'disk.partitions.total', # expand disk partition names + 'network.interface.total.bytes') # expand network interface names + pmids = self.context.pmLookupName(inst) + descs = self.context.pmLookupDescs(pmids) + + (inst, self.cpus) = self.context.pmGetInDom(descs[0]) + (inst, self.disks) = self.context.pmGetInDom(descs[1]) + (inst, self.parts) = self.context.pmGetInDom(descs[2]) + (inst, self.ifaces) = self.context.pmGetInDom(descs[3]) + + def details(self): + print "CPUs: ", self.ncpu + print "CPU names: ", self.cpus + print "Disks: ", self.ndisk + print "Disk names: ", self.disks + print "Partition names: ", self.parts + print "Interfaces: ", self.niface + print "Interface names: ", self.ifaces + print "Memory: ", self.memory + + def get_diskmaps(self): + """ Produce disk -> partition mappings + This means grouping "sda1 sda2 sda3 sdb1" into two mappings + - "sda" -> (sda1, sda2, sda3) and "sdb" -> (sdb1); done via + the disk.dev.total and disk.partitions.total metrics. + (original: controller -> disk mappings, but ENODATA) + """ + return 0 + + def inventory(self): + """ Wrap calls to getting counts and subsystem names + """ + self.get_hinv() + self.get_names() + self.get_diskmaps() + + def gadgetize(self): + """ Generate a pmgadgets configuration for this host + """ + print "pmgadgets 1", # follow with command line + for arg in argv: + print "\"%s\"" % (arg), + print + + rows = 1 + ctiles = int((self.ncpu - 1) / 4 + 1) # always at least one cpu + ntiles = int((self.niface - 1) / 4 + 1) + if ctiles > 3: + cr = int(math.sqrt(ctiles)) + if cr > rows: + rows = cr + if ntiles > 3: + nr = int(math.sqrt(ntiles)) + if nr > rows: + rows = nr + + baseY = VSPACE + if DOLABEL == 1: + y = FONTASCENT + VSPACE + hostname = self.context.pmGetContextHostName() + print "_label %d %d \"%s\"" % (HSPACE, y, hostname) + baseY += y + baseX = maxX = HSPACE + maxY = baseY + + print "_actions cpuActions (" + print " \"pmchart\"\t\t\"pmchart -c CPU%s\"" % (PCPARGS) + print " \"mpvis *\"\t\t\"mpvis%s\" _default" % (PCPARGS) + # original had IRIX gr_top and gr_osview tools next; + # perhaps some fine day we could implement these as + # pmgadgets front-end tools (certainly the latter) + print ")" + + y = baseY + FONTASCENT + x = baseX + print "_label %d %d \"CPU\"" % (x, y) + print " _actions cpuActions\n" + # original: "these should match the colours in mpvis" + print "_colourlist cpuColours (blue3 red3 yellow3 cyan3 green3)" + + # place the CPU bars + y += VSPACE + ccols = int((ctiles + rows - 1) / rows) + + cpu = 0 + for rc in range(0, self.ncpu): + for ct in range(0, ccols): + tc = 0 + while (cpu < self.ncpu and tc < 4): + print "_multibar %d %d %d %d" % (x, y, CPUWIDTH, CPUHEIGHT) + print(" _update %f" % (CPUDELTA)).rstrip('0').rstrip('.') + print " _metrics (" + print "\tkernel.percpu.cpu.user[\"%s\"]" % (self.cpus[cpu]) + print "\tkernel.percpu.cpu.sys[\"%s\"]" % (self.cpus[cpu]) + print "\tkernel.percpu.cpu.intr[\"%s\"]" % (self.cpus[cpu]) + print "\tkernel.percpu.cpu.wait.total[\"%s\"]" % (self.cpus[cpu]) + print "\tkernel.percpu.cpu.idle[\"%s\"]" % (self.cpus[cpu]) + print " )" + print " _maximum 0.0\n" + print " _colourlist cpuColours" + print " _actions cpuActions\n" + cpu += 1 + tc += 1 + y += VSPACE + CPUHEIGHT + + if maxY < y: + maxY = y + y -= (VSPACE + CPUHEIGHT) * tc + x += HSPACE + CPUWIDTH + + y += (CPUHEIGHT + VSPACE) * 4 + VSPACE + if (maxX < x): + maxX = x + x = baseX + + baseX += (HSPACE + CPUWIDTH) * ccols + + # The load gadget and its label + print "_actions loadActions (" + print " \"pmchart *\"", + print "\t\"pmchart -c LoadAvg%s\" _default" % (PCPARGS) + # original had IRIX gr_top here + print ")" + print "_label %d %d \"Load\"" % (baseX, baseY + FONTASCENT) + print " _actions loadActions" + print + + y = VSPACE + baseY + FONTASCENT + + i = y + LOADHEIGHT + if (i > maxY): + maxY = i + + print "_bargraph %d %d %d %d" % (baseX, y, LOADWIDTH, LOADHEIGHT) + print(" _update %f" % (LOADDELTA)).rstrip('0').rstrip('.') + print " _metric kernel.all.load[\"1 minute\"]" + print " _max 1.0" + print " _actions loadActions" + + # For more than one row, stack LoadAvg on top of Memory. + # + # Move baseX just after the right hand side of the memory, so + # we don't have to do anything special for the netifs. For the + # sake of argument, consider total width occupied by memory + # gauges equal to total width of loadavg graph + if (rows > 1): + y += LOADHEIGHT + VSPACE + baseX += LOADWIDTH + HSPACE + else: + y += baseY + baseX += LOADWIDTH * 2 + HSPACE * 2 + + # The memory gadgets and their label (platform-specific!) + x = baseX - LOADWIDTH - HSPACE + y += FONTASCENT + print "_label %d %d \"Mem\"\n" % (x, y) + print "_colourlist memColours (cyan1 red yellow green)\n" + y += VSPACE + print "_multibar %d %d %d %d" % (x, y, MEMWIDTH, MEMHEIGHT) + print " _update 0.5" + print " _metrics (" + print "\tmem.util.cached" + print "\tmem.util.bufmem" + print "\tmem.util.other" + print "\tmem.util.free" + print " )" + print " _colourlist memColours" + x += HSPACE + MEMWIDTH + print "_bar %d %d %d %d" % (x, y, MEMWIDTH, MEMHEIGHT) + print " _metric swap.pagesout" + print " _vertical" + + # Check for the max horizontal offset + i = y + MEMHEIGHT + if (i > maxY): + maxY = i + + # The network bars and their label + print "_colourlist netColours (aquamarine orange)" + print "_actions netActions (" + print " \"pmchart-packets *\"", + print "\t\"pmchart -c NetPackets%s\" _default" % (PCPARGS) + print " \"pmchart-bytes\"", + print "\t\t\"pmchart -c NetBytes%s\"" % (PCPARGS) + # original had netstat within an xterm here, next + print ")" + + y = baseY + FONTASCENT + x = baseX + + print "_label %d %d \"Net\"" % (x, y) + print " _actions netActions\n" + + y += VSPACE + + ncols = int((ntiles + rows - 1) / rows) + + ni = 0 + while ni < self.niface: + for nt in range(0, ncols): + tc = 0 + while ni < self.niface and tc < 4: + print "_multibar %d %d %d %d" % (x, y, NETWIDTH, NETHEIGHT) + print " _metrics (" + print "\tnetwork.interface.in.bytes[\"%s\"]" % (self.ifaces[ni]) + print "\tnetwork.interface.out.bytes[\"%s\"]" % (self.ifaces[ni]) + print " )" + print "_colourlist netColours" + print " _actions netActions" + y += NETHEIGHT + VSPACE + + tc += 1 + ni += 1 + + if maxY < y: + maxY = y + y -= (NETHEIGHT + VSPACE) * tc + x += NETWIDTH + HSPACE + if maxX < x: + maxX = x + y += (NETHEIGHT + VSPACE) * 4 + VSPACE + x = baseX + print + + # Disks + dir = 1 + + print "_actions diskActions (" + print " \"pmchart\"", + print "\t\t\"pmchart -c Disk%s\"" % (PCPARGS) + print " \"dkvis *\"", + print "\t\t\"dkvis%s\" _default" % (PCPARGS) + print ")" + + x = HSPACE + y = maxY + FONTASCENT + 2 * VSPACE + print "_label %d %d \"Disk\"" % (x, y) + print " _actions diskActions\n" + print "_legend diskLegend (" + print " _default green3" + print " 15 yellow" + print " 40 orange" + print " 75 red" + print ")" + + x += CPUWIDTH + HSPACE + # this only works if FONTASCENT >= ledSize + y -= DISKSIZE + thickness = 4 + halfDiskSize = int((DISKSIZE - thickness) / 2) + + for i in range(0, self.ndiskmaps): + mapping = self.diskmaps[i] + for j in range(0, len(mappings)): + if j > 0: + if oldX < x: # moved to right + lx = x - VSPACE - 1 + ly = y + halfDiskSize + lw = VSPACE + 2 + lh = thickness + elif oldX > x: # moved to left + lx = oldX - VSPACE - 1 + ly = y + halfDiskSize + lw = VSPACE + 2 + lh = thickness + else: # moved down + lx = x + halfDiskSize + ly = oldY + DISKSIZE - 1 + lw = thickness + lh = VSPACE + 2 + print "_line %d %d %d %d" % (lx, ly, lw, lh) + print "_led %d %d %d %d" % (x, y, DISKSIZE, DISKSIZE) + print " _metric disk.dev.total[\"%s\"]" % (mapping.name()) + print " _legend diskLegend" + print " _actions diskActions" + oldX = x + oldY = y + xStep = dir * (DISKSIZE + VSPACE) # use VSPACE (tighter packing) + x += xStep + if x > maxX - DISKSIZE or x <= HSPACE: + x -= xStep + y += DISKSIZE + VSPACE + dir = -dir + + +if __name__ == '__main__': + context = pmapi.pmContext() + machine = Machine(context) + machine.inventory() + # machine.details() + machine.gadgetize() + |