nes-proj/tools/chakana/linux.py

269 lines
7.6 KiB
Python

#
# Copyright (C) 2004-2007 Swedish Institute of Computer Science.
#
# Please refer to the file named LICENSE in the same directory as this
# file for licensing information.
#
# $Id: linux.py,v 1.1 2007/08/21 14:41:01 fros4943 Exp $
import errno
import glob
import os
import re
import signal
import string
import time
import chakana.command
import chakana.error
import chakana.utils
from debug import *
def numProcessors(physical = 0, cpuInfo = None):
return Host().CpuInfo(cpuInfo).count(physical = physical)
class CpuInfo:
"Parser for /proc/cpuinfo."
def __init__(self, cpuInfo = None):
if cpuInfo is None:
cpuInfo = chakana.utils.readFile("/proc/cpuinfo")
self.__values = [map(string.strip, l.split(":"))
for l in cpuInfo.splitlines()]
def values(self):
return self.__values
def find(self, name):
debug(Debug3, "Looking for " + name + " in cpuinfo:")
ret = [val[1] for val in self.values() if val[0] == name]
debug(Debug3, repr(ret))
return ret
class IfconfigInfo:
"Parser for ifconfig output."
def __init__(self, ifconfigOutput = None):
if ifconfigOutput is None:
self._ifconfigOutput = chakana.command.output("/sbin/ifconfig")
else:
self._ifconfigOutput = ifconfigOutput
def interfaces(self):
return map(InterfaceInfo,
re.compile("^\\w+", re.M).findall(self._ifconfigOutput))
class InterfaceInfo:
"Parser for ifconfig output for a single interface."
def __init__(self, interface = None, ifconfigOutput = None):
if interface is None:
self._interface = chakana.command.output("/sbin/ifconfig").split()[0]
else:
self._interface = interface
if ifconfigOutput is None:
self._ifconfigOutput = chakana.command.output("/sbin/ifconfig " +
self._interface)
else:
self._ifconfigOutput = ifconfigOutput
def name(self):
return self._ifconfigOutput.split()[0]
def address(self):
match = re.search("inet addr:((\d{1,3}\.){3}\d{1,3})",
self._ifconfigOutput)
if match:
return match.group(1)
else:
return None
__str__ = name
class MemInfo(dict):
"Parser for /proc/meminfo"
def __init__(self, memInfo = None):
if memInfo is None:
memInfo = chakana.utils.readFile("/proc/meminfo")
for line in memInfo.splitlines():
if len(line.split(":")) == 2:
key, value = line.split(":")
if len(value.strip().split()) <= 2:
if len(value.strip().split()) == 2:
unit = {"KB" : 1024, "MB" : 1024 * 1024,
"GB" : 1024 * 1024 * 1024 }[value.split()[-1].upper()]
else:
unit = 1
self[key] = long(value.strip().split()[0]) * unit
class UnameInfo:
"Parser for output from uname -a"
def __init__(self, unameInfo = None):
if unameInfo is None:
unameInfo = chakana.command.output("uname -a")
self._unameInfo = unameInfo.split()
def __getitem__(self, i):
return self._unameInfo[i]
def __len__(self):
return len(self._unameInfo)
def __getattr__(self, attr):
ret = self[self.indexMap()[attr]]
if (attr == "architecture") and \
(re.match(r"(.*intel|i[0-9]86)", ret, re.I)):
# Work around bogus uname output from e.g. Gentoo
return "i386"
else:
return ret
def smp(self):
return "SMP" in self._unameInfo
def indexMap(self):
return {
"os" : 0,
"host" : 1,
"version" : 2,
"architecture" : -2,
}
def __str__(self):
return repr(dict([(attr, getattr(self, attr))
for attr in self.indexMap()]))
def __repr__(self):
return repr(self._unameInfo)
class X86:
class CpuInfo(CpuInfo):
def count(self, physical = 0):
logical = len(self.find("processor"))
if physical:
siblings = self.find("siblings")
if len(siblings) > 0:
return logical / int(siblings[0])
return logical
def type(self, num = 0):
return re.match(r"Intel(\(R\))? (.*) processor [\d]+MHz",
self.find("model name")[num]).group(2)
class X86_64(X86):
pass
class Sparc:
class CpuInfo(CpuInfo):
def count(self):
return int(self.find("ncpus active")[0])
def type(self, num = 0):
return " ".join(self.find("cpu")[num].split()[1:3])
class UnameInfo(UnameInfo):
pass
def freeSpace(path):
"Return available disk space on device corresponding to path."
# If the device name is long, df may split the relevant line, so
# count lines and words from end.
return long(
chakana.command.output("df -k " + path).splitlines()[-1].split()[-3]) * 1024
def macAddresses():
"Return list of mac addresses for all network interfaces."
return re.findall('..:..:..:..:..:..', os.popen(
'/sbin/ifconfig -a | grep HWaddr', 'r').read().lower())
class Process:
"""Representation of a running process."""
def __init__(self, pid):
self._pid = pid
def pid(self):
return self._pid
def attribute(self, name):
try:
lines = chakana.utils.readFile("/proc/" + str(self._pid) +
"/status").splitlines()
for line in lines:
(key, value) = line.split(":", 1)
if key.strip().lower() == name.lower():
return value.strip()
except EnvironmentError, err:
if err.errno == errno.ENOENT:
raise chakana.error.errnoError(errno.ESRCH)
raise
def name(self):
return self.attribute("name")
def parent(self):
return Process(int(self.attribute("ppid")))
def children(self):
ret = []
for proc in self.all():
try:
if proc.parent().pid() == self.pid():
ret.append(proc)
except EnvironmentError, err:
if err.errno != errno.ESRCH:
raise
return ret
def reallyKill(self, graceTime = 1, sigKillTime = 3, debugLevel = Event):
"""Really kill process after waiting for graceTime. Send SIGKILL after
sigKillTime."""
try:
debug(debugLevel, "Killing process " + str(self.pid()) +
" (" + str(self.name()) + ")")
os.kill(self.pid(), 0)
time.sleep(graceTime)
debug(debugLevel + 1, "kill -TERM " + str(self.pid()))
os.kill(self.pid(), signal.SIGTERM)
for tick in range(sigKillTime):
os.kill(self.pid(), 0)
debug(debugLevel + 1, "kill -KILL " + str(self.pid()))
os.kill(self.pid(), signal.SIGKILL)
time.sleep(graceTime)
if self.attribute("state")[0] != "Z":
debug(debugLevel, "Process refused to die: " + str(self.pid()) +
" (" + self.name() + ")")
except EnvironmentError, err:
if err.errno != errno.ESRCH:
raise
debug(debugLevel + 2, "Pid " + str(self.pid()) + " no longer exists")
def killAllBelow(self, debugLevel = Event, ** kwArgs):
"""Kill all processes below (but not including) a process. Start from
bottom of tree."""
debug(debugLevel, "Killing all processes below " + str(self.pid()) +
" (" + self.name() + ")")
children = self.children()
debug(debugLevel + 2, "Pid " + str(self.pid()) + " has " +
str(len(children)) + " children")
for child in children:
child.killAllBelow(debugLevel = debugLevel, ** kwArgs)
child.reallyKill(debugLevel = debugLevel, ** kwArgs)
def all(cls):
ret = []
for statusFile in glob.glob("/proc/*/status"):
try:
ret.append(cls(int(os.path.basename(os.path.dirname(statusFile)))))
except ValueError:
continue
return ret
all = classmethod(all)
def Host():
architecture = chakana.command.output("uname -m").strip()
if re.match("i[0-9]86", architecture):
return X86
elif architecture == "x86_64":
return X86_64