Index: client/common_lib/utils.py |
diff --git a/client/common_lib/utils.py b/client/common_lib/utils.py |
index 8a34ef17a02856ded4fcd3be9dd341718f8eb5d3..47f3cb4c2e11d32a7331f6d12fbc448a6ee09b7f 100644 |
--- a/client/common_lib/utils.py |
+++ b/client/common_lib/utils.py |
@@ -4,6 +4,7 @@ |
import os, pickle, random, re, resource, select, shutil, signal, StringIO |
import socket, struct, subprocess, sys, time, textwrap, urlparse |
import warnings, smtplib, logging, urllib2 |
+from threading import Thread, Event |
try: |
import hashlib |
except ImportError: |
@@ -190,6 +191,30 @@ def read_file(filename): |
f.close() |
+def get_field(data, param, linestart="", sep=" "): |
+ """ |
+ Parse data from string. |
+ @param data: Data to parse. |
+ example: |
+ data: |
+ cpu 324 345 34 5 345 |
+ cpu0 34 11 34 34 33 |
+ ^^^^ |
+ start of line |
+ params 0 1 2 3 4 |
+ @param param: Position of parameter after linestart marker. |
+ @param linestart: String to which start line with parameters. |
+ @param sep: Separator between parameters regular expression. |
+ """ |
+ search = re.compile(r"(?<=^%s)\s*(.*)" % linestart, re.MULTILINE) |
+ find = search.search(data) |
+ if find != None: |
+ return re.split("%s" % sep, find.group(1))[param] |
+ else: |
+ print "There is no line which starts with %s in data." % linestart |
+ return None |
+ |
+ |
def write_one_line(filename, line): |
open_write_close(filename, line.rstrip('\n') + '\n') |
@@ -211,9 +236,14 @@ def matrix_to_string(matrix, header=None): |
in each column, and determining the format string dynamically. |
@param matrix: Matrix representation (list with n rows of m elements). |
- @param header: Optional tuple with header elements to be displayed. |
+ @param header: Optional tuple or list with header elements to be displayed. |
""" |
+ if type(header) is list: |
+ header = tuple(header) |
lengths = [] |
+ if header: |
+ for column in header: |
+ lengths.append(len(column)) |
for row in matrix: |
for column in row: |
i = row.index(column) |
@@ -294,6 +324,156 @@ def write_keyval(path, dictionary, type_tag=None): |
keyval.close() |
+class FileFieldMonitor(object): |
+ """ |
+ Monitors the information from the file and reports it's values. |
+ |
+ It gather the information at start and stop of the measurement or |
+ continuously during the measurement. |
+ """ |
+ class Monitor(Thread): |
+ """ |
+ Internal monitor class to ensure continuous monitor of monitored file. |
+ """ |
+ def __init__(self, master): |
+ """ |
+ @param master: Master class which control Monitor |
+ """ |
+ Thread.__init__(self) |
+ self.master = master |
+ |
+ def run(self): |
+ """ |
+ Start monitor in thread mode |
+ """ |
+ while not self.master.end_event.isSet(): |
+ self.master._get_value(self.master.logging) |
+ time.sleep(self.master.time_step) |
+ |
+ |
+ def __init__(self, status_file, data_to_read, mode_diff, continuously=False, |
+ contlogging=False, separator=" +", time_step=0.1): |
+ """ |
+ Initialize variables. |
+ @param status_file: File contain status. |
+ @param mode_diff: If True make a difference of value, else average. |
+ @param data_to_read: List of tuples with data position. |
+ format: [(start_of_line,position in params)] |
+ example: |
+ data: |
+ cpu 324 345 34 5 345 |
+ cpu0 34 11 34 34 33 |
+ ^^^^ |
+ start of line |
+ params 0 1 2 3 4 |
+ @param mode_diff: True to subtract old value from new value, |
+ False make average of the values. |
+ @parma continuously: Start the monitoring thread using the time_step |
+ as the measurement period. |
+ @param contlogging: Log data in continuous run. |
+ @param separator: Regular expression of separator. |
+ @param time_step: Time period of the monitoring value. |
+ """ |
+ self.end_event = Event() |
+ self.start_time = 0 |
+ self.end_time = 0 |
+ self.test_time = 0 |
+ |
+ self.status_file = status_file |
+ self.separator = separator |
+ self.data_to_read = data_to_read |
+ self.num_of_params = len(self.data_to_read) |
+ self.mode_diff = mode_diff |
+ self.continuously = continuously |
+ self.time_step = time_step |
+ |
+ self.value = [0 for i in range(self.num_of_params)] |
+ self.old_value = [0 for i in range(self.num_of_params)] |
+ self.log = [] |
+ self.logging = contlogging |
+ |
+ self.started = False |
+ self.num_of_get_value = 0 |
+ self.monitor = None |
+ |
+ |
+ def _get_value(self, logging=True): |
+ """ |
+ Return current values. |
+ @param logging: If true log value in memory. There can be problem |
+ with long run. |
+ """ |
+ data = read_file(self.status_file) |
+ value = [] |
+ for i in range(self.num_of_params): |
+ value.append(int(get_field(data, |
+ self.data_to_read[i][1], |
+ self.data_to_read[i][0], |
+ self.separator))) |
+ |
+ if logging: |
+ self.log.append(value) |
+ if not self.mode_diff: |
+ value = map(lambda x, y: x + y, value, self.old_value) |
+ |
+ self.old_value = value |
+ self.num_of_get_value += 1 |
+ return value |
+ |
+ |
+ def start(self): |
+ """ |
+ Start value monitor. |
+ """ |
+ if self.started: |
+ self.stop() |
+ self.old_value = [0 for i in range(self.num_of_params)] |
+ self.num_of_get_value = 0 |
+ self.log = [] |
+ self.end_event.clear() |
+ self.start_time = time.time() |
+ self._get_value() |
+ self.started = True |
+ if (self.continuously): |
+ self.monitor = FileFieldMonitor.Monitor(self) |
+ self.monitor.start() |
+ |
+ |
+ def stop(self): |
+ """ |
+ Stop value monitor. |
+ """ |
+ if self.started: |
+ self.started = False |
+ self.end_time = time.time() |
+ self.test_time = self.end_time - self.start_time |
+ self.value = self._get_value() |
+ if (self.continuously): |
+ self.end_event.set() |
+ self.monitor.join() |
+ if (self.mode_diff): |
+ self.value = map(lambda x, y: x - y, self.log[-1], self.log[0]) |
+ else: |
+ self.value = map(lambda x: x / self.num_of_get_value, |
+ self.value) |
+ |
+ |
+ def get_status(self): |
+ """ |
+ @return: Status of monitored process average value, |
+ time of test and array of monitored values and time step of |
+ continuous run. |
+ """ |
+ if self.started: |
+ self.stop() |
+ if self.mode_diff: |
+ for i in range(len(self.log) - 1): |
+ self.log[i] = (map(lambda x, y: x - y, |
+ self.log[i + 1], self.log[i])) |
+ self.log.pop() |
+ return (self.value, self.test_time, self.log, self.time_step) |
+ |
+ |
def is_url(path): |
"""Return true if path looks like a URL""" |
# for now, just handle http and ftp |
@@ -801,6 +981,226 @@ def get_cpu_percentage(function, *args, **dargs): |
return cpu_percent, to_return |
+class SystemLoad(object): |
+ """ |
+ Get system and/or process values and return average value of load. |
+ """ |
+ def __init__(self, pids, advanced=False, time_step=0.1, cpu_cont=False, |
+ use_log=False): |
+ """ |
+ @param pids: List of pids to be monitored. If pid = 0 whole system will |
+ be monitored. pid == 0 means whole system. |
+ @param advanced: monitor add value for system irq count and softirq |
+ for process minor and maior page fault |
+ @param time_step: Time step for continuous monitoring. |
+ @param cpu_cont: If True monitor CPU load continuously. |
+ @param use_log: If true every monitoring is logged for dump. |
+ """ |
+ self.pids = [] |
+ self.stats = {} |
+ for pid in pids: |
+ if pid == 0: |
+ cpu = FileFieldMonitor("/proc/stat", |
+ [("cpu", 0), # User Time |
+ ("cpu", 2), # System Time |
+ ("intr", 0), # IRQ Count |
+ ("softirq", 0)], # Soft IRQ Count |
+ True, |
+ cpu_cont, |
+ use_log, |
+ " +", |
+ time_step) |
+ mem = FileFieldMonitor("/proc/meminfo", |
+ [("MemTotal:", 0), # Mem Total |
+ ("MemFree:", 0), # Mem Free |
+ ("Buffers:", 0), # Buffers |
+ ("Cached:", 0)], # Cached |
+ False, |
+ True, |
+ use_log, |
+ " +", |
+ time_step) |
+ self.stats[pid] = ["TOTAL", cpu, mem] |
+ self.pids.append(pid) |
+ else: |
+ name = "" |
+ if (type(pid) is int): |
+ self.pids.append(pid) |
+ name = get_process_name(pid) |
+ else: |
+ self.pids.append(pid[0]) |
+ name = pid[1] |
+ |
+ cpu = FileFieldMonitor("/proc/%d/stat" % |
+ self.pids[-1], |
+ [("", 13), # User Time |
+ ("", 14), # System Time |
+ ("", 9), # Minority Page Fault |
+ ("", 11)], # Majority Page Fault |
+ True, |
+ cpu_cont, |
+ use_log, |
+ " +", |
+ time_step) |
+ mem = FileFieldMonitor("/proc/%d/status" % |
+ self.pids[-1], |
+ [("VmSize:", 0), # Virtual Memory Size |
+ ("VmRSS:", 0), # Resident Set Size |
+ ("VmPeak:", 0), # Peak VM Size |
+ ("VmSwap:", 0)], # VM in Swap |
+ False, |
+ True, |
+ use_log, |
+ " +", |
+ time_step) |
+ self.stats[self.pids[-1]] = [name, cpu, mem] |
+ |
+ self.advanced = advanced |
+ |
+ |
+ def __str__(self): |
+ """ |
+ Define format how to print |
+ """ |
+ out = "" |
+ for pid in self.pids: |
+ for stat in self.stats[pid][1:]: |
+ out += str(stat.get_status()) + "\n" |
+ return out |
+ |
+ |
+ def start(self, pids=[]): |
+ """ |
+ Start monitoring of the process system usage. |
+ @param pids: List of PIDs you intend to control. Use pids=[] to control |
+ all defined PIDs. |
+ """ |
+ if pids == []: |
+ pids = self.pids |
+ |
+ for pid in pids: |
+ for stat in self.stats[pid][1:]: |
+ stat.start() |
+ |
+ |
+ def stop(self, pids=[]): |
+ """ |
+ Stop monitoring of the process system usage. |
+ @param pids: List of PIDs you intend to control. Use pids=[] to control |
+ all defined PIDs. |
+ """ |
+ if pids == []: |
+ pids = self.pids |
+ |
+ for pid in pids: |
+ for stat in self.stats[pid][1:]: |
+ stat.stop() |
+ |
+ |
+ def dump(self, pids=[]): |
+ """ |
+ Get the status of monitoring. |
+ @param pids: List of PIDs you intend to control. Use pids=[] to control |
+ all defined PIDs. |
+ @return: |
+ tuple([cpu load], [memory load]): |
+ ([(PID1, (PID1_cpu_meas)), (PID2, (PID2_cpu_meas)), ...], |
+ [(PID1, (PID1_mem_meas)), (PID2, (PID2_mem_meas)), ...]) |
+ |
+ PID1_cpu_meas: |
+ average_values[], test_time, cont_meas_values[[]], time_step |
+ PID1_mem_meas: |
+ average_values[], test_time, cont_meas_values[[]], time_step |
+ where average_values[] are the measured values (mem_free,swap,...) |
+ which are described in SystemLoad.__init__()-FileFieldMonitor. |
+ cont_meas_values[[]] is a list of average_values in the sampling |
+ times. |
+ """ |
+ if pids == []: |
+ pids = self.pids |
+ |
+ cpus = [] |
+ memory = [] |
+ for pid in pids: |
+ stat = (pid, self.stats[pid][1].get_status()) |
+ cpus.append(stat) |
+ for pid in pids: |
+ stat = (pid, self.stats[pid][2].get_status()) |
+ memory.append(stat) |
+ |
+ return (cpus, memory) |
+ |
+ |
+ def get_cpu_status_string(self, pids=[]): |
+ """ |
+ Convert status to string array. |
+ @param pids: List of PIDs you intend to control. Use pids=[] to control |
+ all defined PIDs. |
+ @return: String format to table. |
+ """ |
+ if pids == []: |
+ pids = self.pids |
+ |
+ headers = ["NAME", |
+ ("%7s") % "PID", |
+ ("%5s") % "USER", |
+ ("%5s") % "SYS", |
+ ("%5s") % "SUM"] |
+ if self.advanced: |
+ headers.extend(["MINFLT/IRQC", |
+ "MAJFLT/SOFTIRQ"]) |
+ headers.append(("%11s") % "TIME") |
+ textstatus = [] |
+ for pid in pids: |
+ stat = self.stats[pid][1].get_status() |
+ time = stat[1] |
+ stat = stat[0] |
+ textstatus.append(["%s" % self.stats[pid][0], |
+ "%7s" % pid, |
+ "%4.0f%%" % (stat[0] / time), |
+ "%4.0f%%" % (stat[1] / time), |
+ "%4.0f%%" % ((stat[0] + stat[1]) / time), |
+ "%10.3fs" % time]) |
+ if self.advanced: |
+ textstatus[-1].insert(-1, "%11d" % stat[2]) |
+ textstatus[-1].insert(-1, "%14d" % stat[3]) |
+ |
+ return matrix_to_string(textstatus, tuple(headers)) |
+ |
+ |
+ def get_mem_status_string(self, pids=[]): |
+ """ |
+ Convert status to string array. |
+ @param pids: List of PIDs you intend to control. Use pids=[] to control |
+ all defined PIDs. |
+ @return: String format to table. |
+ """ |
+ if pids == []: |
+ pids = self.pids |
+ |
+ headers = ["NAME", |
+ ("%7s") % "PID", |
+ ("%8s") % "TOTAL/VMSIZE", |
+ ("%8s") % "FREE/VMRSS", |
+ ("%8s") % "BUFFERS/VMPEAK", |
+ ("%8s") % "CACHED/VMSWAP", |
+ ("%11s") % "TIME"] |
+ textstatus = [] |
+ for pid in pids: |
+ stat = self.stats[pid][2].get_status() |
+ time = stat[1] |
+ stat = stat[0] |
+ textstatus.append(["%s" % self.stats[pid][0], |
+ "%7s" % pid, |
+ "%10dMB" % (stat[0] / 1024), |
+ "%8dMB" % (stat[1] / 1024), |
+ "%12dMB" % (stat[2] / 1024), |
+ "%11dMB" % (stat[3] / 1024), |
+ "%10.3fs" % time]) |
+ |
+ return matrix_to_string(textstatus, tuple(headers)) |
+ |
+ |
def get_arch(run_function=run): |
""" |
Get the hardware architecture of the machine. |
@@ -1100,6 +1500,14 @@ def get_pid_from_file(program_name): |
return pid |
+def get_process_name(pid): |
+ """ |
+ Get process name from PID. |
+ @param pid: PID of process. |
+ """ |
+ return get_field(read_file("/proc/%d/stat" % pid), 1)[1:-1] |
+ |
+ |
def program_is_alive(program_name): |
""" |
Checks if the process is alive and not in Zombie state. |