Merge branch 'v2/kcompile-by-nodes' into v2/master
diff --git a/rteval/sysinfo/systopology.py b/rteval/sysinfo/systopology.py
new file mode 100644
index 0000000..a6e5c1a
--- /dev/null
+++ b/rteval/sysinfo/systopology.py
@@ -0,0 +1,237 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2016 - Clark Williams <williams@redhat.com>
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# For the avoidance of doubt the "preferred form" of this code is one which
+# is in an open unpatent encumbered format. Where cryptographic key signing
+# forms part of the process of creating an executable the information
+# including keys needed to generate an equivalently functional executable
+# are deemed to be part of the source code.
+#
+
+import os, sys
+import os.path
+import glob
+
+def _sysread(path, obj):
+ fp = open(os.path.join(path,obj), "r")
+ return fp.readline().strip()
+
+#
+# class to provide access to a list of cpus
+#
+
+class CpuList(object):
+ "Object that represents a group of system cpus"
+
+ cpupath = '/sys/devices/system/cpu'
+
+ def __init__(self, cpulist):
+ if type(cpulist) is list:
+ self.cpulist = cpulist
+ elif type(cpulist) is str:
+ self.cpulist = self.__expand_cpulist(cpulist)
+ self.cpulist.sort()
+
+ def __str__(self):
+ return self.__collapse_cpulist(self.cpulist)
+
+ def __contains__(self, cpu):
+ return cpu in self.cpulist
+
+ def __len__(self):
+ return len(self.cpulist)
+
+
+ # return the index of the last element of a sequence
+ # that steps by one
+ def __longest_sequence(self, cpulist):
+ lim = len(cpulist)
+ for idx,val in enumerate(cpulist):
+ if idx+1 == lim:
+ break
+ if int(cpulist[idx+1]) != (int(cpulist[idx])+1):
+ return idx
+ return lim - 1
+
+
+ #
+ # collapse a list of cpu numbers into a string range
+ # of cpus (e.g. 0-5, 7, 9)
+ #
+ def __collapse_cpulist(self, cpulist):
+ if len(cpulist) == 0:
+ return ""
+ idx = self.__longest_sequence(cpulist)
+ if idx == 0:
+ seq = str(cpulist[0])
+ else:
+ if idx == 1:
+ seq = "%d,%d" % (cpulist[0], cpulist[idx])
+ else:
+ seq = "%d-%d" % (cpulist[0], cpulist[idx])
+
+ rest = self.__collapse_cpulist(cpulist[idx+1:])
+ if rest == "":
+ return seq
+ return ",".join((seq, rest))
+
+ # expand a string range into a list
+ # don't error check against online cpus
+ def __expand_cpulist(self, cpulist):
+ '''expand a range string into an array of cpu numbers'''
+ result = []
+ for part in cpulist.split(','):
+ if '-' in part:
+ a, b = part.split('-')
+ a, b = int(a), int(b)
+ result.extend(range(a, b + 1))
+ else:
+ a = int(part)
+ result.append(a)
+ return [ int(i) for i in list(set(result)) ]
+
+ # returns the list of cpus tracked
+ def getcpulist(self):
+ return self.cpulist
+
+ # check whether cpu n is online
+ def isonline(self, n):
+ if n not in self.cpulist:
+ raise RuntimeError, "invalid cpu number %d" % n
+ if n == 0:
+ return True
+ path = os.path.join(CpuList.cpupath,'cpu%d' % n)
+ if os.path.exists(path):
+ return _sysread(path, "online") == 1
+ return False
+
+#
+# class to abstract access to NUMA nodes in /sys filesystem
+#
+
+class NumaNode(object):
+ "class representing a system NUMA node"
+
+ # constructor argument is the full path to the /sys node file
+ # e.g. /sys/devices/system/node/node0
+ def __init__(self, path):
+ self.path = path
+ self.nodeid = int(os.path.basename(path)[4:].strip())
+ self.cpus = CpuList(_sysread(self.path, "cpulist"))
+ self.getmeminfo()
+
+ # function for the 'in' operator
+ def __contains__(self, cpu):
+ return cpu in self.cpus
+
+ # allow the 'len' builtin
+ def __len__(self):
+ return len(self.cpus)
+
+ # string representation of the cpus for this node
+ def __str__(self):
+ return self.getcpustr()
+
+ # read info about memory attached to this node
+ def getmeminfo(self):
+ self.meminfo = {}
+ for l in open(os.path.join(self.path, "meminfo"), "r"):
+ elements = l.split()
+ key=elements[2][0:-1]
+ val=int(elements[3])
+ if len(elements) == 5 and elements[4] == "kB":
+ val *= 1024
+ self.meminfo[key] = val
+
+ # return list of cpus for this node as a string
+ def getcpustr(self):
+ return str(self.cpus)
+
+ # return list of cpus for this node
+ def getcpulist(self):
+ return self.cpus.getcpulist()
+
+#
+# Class to abstract the system topology of numa nodes and cpus
+#
+class SysTopology(object):
+ "Object that represents the system's NUMA-node/cpu topology"
+
+ cpupath = '/sys/devices/system/cpu'
+ nodepath = '/sys/devices/system/node'
+
+ def __init__(self):
+ self.nodes = {}
+ self.getinfo()
+
+ def __len__(self):
+ return len(self.nodes.keys())
+
+ # inplement the 'in' function
+ def __contains__(self, node):
+ for n in self.nodes:
+ if self.nodes[n].nodeid == node:
+ return True
+ return False
+
+ # allow indexing for the nodes
+ def __getitem__(self, key):
+ return self.nodes[key]
+
+ # allow iteration over the cpus for the node
+ def __iter__(self):
+ self.current = 0
+ return self
+
+ # iterator function
+ def next(self):
+ if self.current >= len(self.nodes):
+ raise StopIteration
+ n = self.nodes[self.current]
+ self.current += 1
+ return n
+
+ def getinfo(self):
+ nodes = glob.glob(os.path.join(SysTopology.nodepath, 'node[0-9]*'))
+ if not nodes:
+ raise RuntimeError, "No valid nodes found in %s!" % SysTopology.nodepath
+ nodes.sort()
+ for n in nodes:
+ node = int(os.path.basename(n)[4:])
+ self.nodes[node] = NumaNode(n)
+
+ def getnodes(self):
+ return self.nodes.keys()
+
+ def getcpus(self, node):
+ return self.nodes[node]
+
+
+
+if __name__ == "__main__":
+
+ def unit_test():
+ s = SysTopology()
+ print "number of nodes: %d" % len(s)
+ for n in s:
+ print "node[%d]: %s" % (n.nodeid, n)
+ print "system has numa node 0: %s" % (0 in s)
+ print "system has numa node 2: %s" % (2 in s)
+ print "system has numa node 24: %s" % (24 in s)
+
+ unit_test()