diff --git a/graph.py b/graph.py index 00a7c0b..52ca5e2 100755 --- a/graph.py +++ b/graph.py @@ -19,6 +19,39 @@ global gnuplot GNUPLOT_VERSION_EXPECTED = 5.0 +START_DATE = "" +END_DATE = "" +AVERAGE_LOAD = 0.0 +MAX_USED_RAM = 0 +MAX_USED_FS = 0 +TOTAL_RAM = 0 +TOTAL_FS = 0 +NAME_FS = "unknown" + +UNAME="unknown" +CPUS=0 +CPU_NAME="unknown" +DURATION = 0.0 + +HOST = socket.gethostname() + +# The number of plots on the graph +NUMBER_OF_PLOTS = 3 + +# The default format +OUTPUT_TYPE="pngcairo" +OUTPUT_EXT="png" + +labels = [] + + +# Check if the avaliable gnuplot has a required version +p = run_process("gnuplot", "--version", stdout=subprocess.PIPE) +version = scan(r"gnuplot (\S+)", float, p.stdout.readline().decode()) +if version < GNUPLOT_VERSION_EXPECTED: + print(f"Error: Gnuplot version too low. Need at least {GNUPLOT_VERSION_EXPECTED} found {version[0]}") + sys.exit(1) + # Run a command in a running gnuplot process def g(command): @@ -41,100 +74,88 @@ def g(command): # Plot a single column of values from data.txt -def plot(ylabel, title, column): +def plot(ylabel, title, name, column): g(f"set ylabel '{ylabel}'") g(f"set title '{title}'") - g(f"plot 'data.txt' using 1:{column}:{column} title 'cpu' with boxes palette") - - -# Check if the avaliable gnuplot has a required version -p = run_process("gnuplot", "--version", stdout=subprocess.PIPE) -version = scan(r"gnuplot (\S+)", float, p.stdout.readline().decode()) -if version < GNUPLOT_VERSION_EXPECTED: - print(f"Error: Gnuplot version too low. Need at least {GNUPLOT_VERSION_EXPECTED} found {version[0]}") - sys.exit(1) - - -START_DATE = "" -END_DATE = "" -AVERAGE_LOAD = 0.0 -MAX_USED_RAM = 0 -MAX_USED_FS = 0 -TOTAL_RAM = 0 -TOTAL_FS = 0 -NAME_FS = "unknown" + g(f"plot '{name}.txt' using 1:{column}:{column} title 'cpu' with boxes palette") -uname="unknown" -cpus=0 -cpu_name="unknown" -duration = 0.0 +# Read additional information from 'data.txt' comments +def read_comments(name): + global START_DATE + global END_DATE + global AVERAGE_LOAD + global MAX_USED_RAM + global MAX_USED_FS + global TOTAL_RAM + global TOTAL_FS + global NAME_FS + global UNAME + global CPUS + global CPU_NAME + global DURATION -labels = [] -with open("data.txt", "r") as f: - for line in f: - value = None + with open(f"{name}.txt", "r") as f: + for line in f: + value = None - if len(line) <= 0: - continue + if len(line) <= 0: + continue - if line[0] != '#': - if not START_DATE: - START_DATE = scan("^(\S+)", str, line) - END_DATE = scan("^(\S+)", str, line) + if line[0] != '#': + if not START_DATE: + START_DATE = scan("^(\S+)", str, line) + END_DATE = scan("^(\S+)", str, line) - value = scan("label: (.+)", str, line) - if value is not None: - key = scan("(\S+) label:", str, line) - labels.append([key, value]) + value = scan("label: (.+)", str, line) + if value is not None: + key = scan("(\S+) label:", str, line) + labels.append([key, value]) - # Comments are not mixed with anything else, so skip - continue + # Comments are not mixed with anything else, so skip + continue - value = scan("machine: ([^,]+)", str, line) - if value is not None: - uname = value + value = scan("machine: ([^,]+)", str, line) + if value is not None: + UNAME = value - value = scan("cpu count: ([^,]+)", int, line) - if value is not None: - cpus = value + value = scan("cpu count: ([^,]+)", int, line) + if value is not None: + CPUS = value - value = scan("cpu: ([^,\n]+)", str, line) - if value is not None: - cpu_name = value + value = scan("cpu: ([^,\n]+)", str, line) + if value is not None: + CPU_NAME = value - value = scan("observed disk: ([^,]+)", str, line) - if value is not None: - NAME_FS = value + value = scan("observed disk: ([^,]+)", str, line) + if value is not None: + NAME_FS = value - value = scan("total ram: (\S+)", stof, line) - if value is not None: - TOTAL_RAM = value + value = scan("total ram: (\S+)", stof, line) + if value is not None: + TOTAL_RAM = value - value = scan("max ram used: (\S+)", stof, line) - if value is not None: - MAX_USED_RAM = value + value = scan("max ram used: (\S+)", stof, line) + if value is not None: + MAX_USED_RAM = value - value = scan("total disk space: (\S+)", stof, line) - if value is not None: - TOTAL_FS = value + value = scan("total disk space: (\S+)", stof, line) + if value is not None: + TOTAL_FS = value - value = scan("duration: (\S+)", stof, line) - if value is not None: - duration = value + value = scan("duration: (\S+)", stof, line) + if value is not None: + DURATION = value - value = scan("max disk used: (\S+)", stof, line) - if value is not None: - MAX_USED_FS = value + value = scan("max disk used: (\S+)", stof, line) + if value is not None: + MAX_USED_FS = value - value = scan("average load: (\S+)", stof, line) - if value is not None: - AVERAGE_LOAD = value + value = scan("average load: (\S+)", stof, line) + if value is not None: + AVERAGE_LOAD = value -# Initialize the plot -OUTPUT_TYPE="pngcairo" -OUTPUT_EXT="png" try: if os.environ["SARGRAPH_OUTPUT_TYPE"] == "svg": OUTPUT_TYPE="svg" @@ -142,77 +163,78 @@ try: except: pass -# The number of plots on the graph -NUMBER_OF_PLOTS = 3 -gnuplot = run_process("gnuplot", stdin=subprocess.PIPE, stdout=subprocess.PIPE) +def graph(name): + global gnuplot + + read_comments(name) -sdt = datetime.datetime.strptime(START_DATE, '%Y-%m-%d-%H:%M:%S') -edt = datetime.datetime.strptime(END_DATE, '%Y-%m-%d-%H:%M:%S') + gnuplot = run_process("gnuplot", stdin=subprocess.PIPE, stdout=subprocess.PIPE) -seconds_between = (edt - sdt).total_seconds() -if seconds_between < 100: - seconds_between = 100 + sdt = datetime.datetime.strptime(START_DATE, '%Y-%m-%d-%H:%M:%S') + edt = datetime.datetime.strptime(END_DATE, '%Y-%m-%d-%H:%M:%S') -nsdt = sdt - datetime.timedelta(seconds = (seconds_between * 0.01)) -nedt = edt + datetime.timedelta(seconds = (seconds_between * 0.01)) + seconds_between = (edt - sdt).total_seconds() + if seconds_between < 100: + seconds_between = 100 -host = socket.gethostname() + nsdt = sdt - datetime.timedelta(seconds = (seconds_between * 0.01)) + nedt = edt + datetime.timedelta(seconds = (seconds_between * 0.01)) -g("set ylabel tc rgb 'white' font 'Courier-New,8'") + g("set ylabel tc rgb 'white' font 'Courier-New,8'") -g("set datafile commentschars '#'") + g("set datafile commentschars '#'") -g("set timefmt '%s'") -g("set xdata time") -g("set border lc rgb 'white'") -g("set key tc rgb 'white'") -g("set timefmt '%Y-%m-%d-%H:%M:%S'") -g("set xtics format '%H:%M:%S'") -g("set xtics font 'Courier-New,8' tc rgb 'white'") -g("set ytics font 'Courier-New,8' tc rgb 'white'") -g("set grid xtics ytics ls 12 lc rgb '#444444'") -g("set style fill solid") -g("set palette defined ( 0.2 '#00ff00', 0.8 '#ff0000' )") -g("set cbrange [0:100]") -g("unset colorbox") -g("unset key") -g("set rmargin 6") + g("set timefmt '%s'") + g("set xdata time") + g("set border lc rgb 'white'") + g("set key tc rgb 'white'") + g("set timefmt '%Y-%m-%d-%H:%M:%S'") + g("set xtics format '%H:%M:%S'") + g("set xtics font 'Courier-New,8' tc rgb 'white'") + g("set ytics font 'Courier-New,8' tc rgb 'white'") + g("set grid xtics ytics ls 12 lc rgb '#444444'") + g("set style fill solid") + g("set palette defined ( 0.2 '#00ff00', 0.8 '#ff0000' )") + g("set cbrange [0:100]") + g("unset colorbox") + g("unset key") + g("set rmargin 6") -g(f"set terminal {OUTPUT_TYPE} size 1200,800 background '#222222' font 'Courier-New,8'") + g(f"set terminal {OUTPUT_TYPE} size 1200,800 background '#222222' font 'Courier-New,8'") -g(f"set output 'plot.{OUTPUT_EXT}'") + g(f"set output '{name}.{OUTPUT_EXT}'") -g(f"set multiplot layout {NUMBER_OF_PLOTS},1 title \"\\n\\n\\n\"") + g(f"set multiplot layout {NUMBER_OF_PLOTS},1 title \"\\n\\n\\n\"") -g("set title tc rgb 'white' font 'Courier-New,8'") + g("set title tc rgb 'white' font 'Courier-New,8'") -g(f"set xrange ['{nsdt.strftime('%Y-%m-%d-%H:%M:%S')}':'{nedt.strftime('%Y-%m-%d-%H:%M:%S')}']"); + g(f"set xrange ['{nsdt.strftime('%Y-%m-%d-%H:%M:%S')}':'{nedt.strftime('%Y-%m-%d-%H:%M:%S')}']"); -g(f"set label 101 at screen 0.02, screen 0.95 'Running on {{/:Bold {host}}} \@ {{/:Bold {uname}}}, {{/:Bold {cpus}}} threads x {{/:Bold {cpu_name}}}, total ram: {{/:Bold {TOTAL_RAM:.2f} GB}}, total disk space: {{/:Bold {TOTAL_FS:.2f} GB}}' tc rgb 'white'") -g(f"set label 102 at screen 0.02, screen 0.93 'duration: {{/:Bold {START_DATE}}} .. {{/:Bold {END_DATE}}} ({duration:.2f} minutes)' tc rgb 'white'") + g(f"set label 101 at screen 0.02, screen 0.95 'Running on {{/:Bold {HOST}}} \@ {{/:Bold {UNAME}}}, {{/:Bold {CPUS}}} threads x {{/:Bold {CPU_NAME}}}, total ram: {{/:Bold {TOTAL_RAM:.2f} GB}}, total disk space: {{/:Bold {TOTAL_FS:.2f} GB}}' tc rgb 'white'") + g(f"set label 102 at screen 0.02, screen 0.93 'Duration: {{/:Bold {START_DATE}}} .. {{/:Bold {END_DATE}}} ({DURATION:.2f} minutes)' tc rgb 'white'") -i = 0 -for label in labels: - i = i + 1 - g(f"set arrow nohead from '{label[0]}', graph 0.01 to '{label[0]}', graph 0.87 front lc rgb 'red' dt 2") - g(f"set object rect at '{label[0]}', graph 0.90 size char {len('%d' % i)+1}, char 1.5 fc rgb 'red'") - g(f"set object rect at '{label[0]}', graph 0.0 size char 0.5, char 0.5 front fc rgb 'red'") - g(f"set label at '{label[0]}', graph 0.90 '{i}' center tc rgb 'black' font 'Courier-New,7'") - g(f"set label at '{label[0]}', graph 0.95 '{label[1][0:20]}' center tc rgb 'white' font 'Courier-New,7'") + i = 0 + for label in labels: + i = i + 1 + g(f"set arrow nohead from '{label[0]}', graph 0.01 to '{label[0]}', graph 0.87 front lc rgb 'red' dt 2") + g(f"set object rect at '{label[0]}', graph 0.90 size char {len('%d' % i)+1}, char 1.5 fc rgb 'red'") + g(f"set object rect at '{label[0]}', graph 0.0 size char 0.5, char 0.5 front fc rgb 'red'") + g(f"set label at '{label[0]}', graph 0.90 '{i}' center tc rgb 'black' font 'Courier-New,7'") + g(f"set label at '{label[0]}', graph 0.95 '{label[1][0:20]}' center tc rgb 'white' font 'Courier-New,7'") -if i > 0: - g("set yrange [0:119]") -else: - g("set yrange [0:100]") + if i > 0: + g("set yrange [0:119]") + else: + g("set yrange [0:100]") -g("set object rectangle from graph 0, graph 0 to graph 2, graph 2 behind fillcolor rgb '#111111' fillstyle solid noborder") -g(f"set object rectangle from '{START_DATE.replace(' ', '-')}', 0 to '{END_DATE.replace(' ', '-')}', 100 behind fillcolor rgb '#000000' fillstyle solid noborder") + g("set object rectangle from graph 0, graph 0 to graph 2, graph 2 behind fillcolor rgb '#111111' fillstyle solid noborder") + g(f"set object rectangle from '{START_DATE.replace(' ', '-')}', 0 to '{END_DATE.replace(' ', '-')}', 100 behind fillcolor rgb '#000000' fillstyle solid noborder") -plot("cpu % load (user)", f"cpu load (average = {AVERAGE_LOAD:.2f} %)", 2) -plot("ram % usage", f"ram usage (max = {MAX_USED_RAM:.2f} GB)", 3) -plot(f"{NAME_FS}'", f"{NAME_FS} usage (max = {MAX_USED_FS:.2f} MB)", 4) + plot("cpu % load (user)", f"cpu load (average = {AVERAGE_LOAD:.2f} %)", name, 2) + plot("ram % usage", f"ram usage (max = {MAX_USED_RAM:.2f} GB)", name, 3) + plot(f"{NAME_FS}'", f"{NAME_FS} usage (max = {MAX_USED_FS:.2f} MB)", name, 4) -g("unset multiplot") -g("unset output") -g("quit") + g("unset multiplot") + g("unset output") + g("quit") diff --git a/sargraph.py b/sargraph.py index 08de32c..0783e35 100755 --- a/sargraph.py +++ b/sargraph.py @@ -7,16 +7,18 @@ import argparse import sys +import time + from common import * # Declare and parse command line flags parser = argparse.ArgumentParser() -parser.add_argument('session', metavar='SESSION-NAME', type=str, nargs='?', default=None, help='sargraph session name') -parser.add_argument('command', metavar='COMMAND', type=str, nargs='*', help='send command') -parser.add_argument('-f', metavar='DEVICE-NAME', type=str, nargs='?', default=None, dest='fsdev', help='observe a chosen filesystem') -parser.add_argument('-m', metavar='MOUNT-DIR', type=str, nargs='?', default=None, dest='fspath', help='observe a chosen filesystem') -parser.add_argument('-t', metavar='GRAPH-TYPE', type=str, nargs='?', default='png', dest='type', help='output graph type or \'none\'') +parser.add_argument('session', metavar='SESSION-NAME', type=str, nargs='?', default=None, help='sargraph session name') +parser.add_argument('command', metavar='COMMAND', type=str, nargs='*', help='send command') +parser.add_argument('-f', metavar='DEVICE-NAME', type=str, nargs='?', default=None, dest='fsdev', help='observe a chosen filesystem') +parser.add_argument('-m', metavar='MOUNT-DIR', type=str, nargs='?', default=None, dest='fspath', help='observe a chosen filesystem') +parser.add_argument('-o', metavar='OUTPUT-NAME', type=str, nargs='?', default='data', dest='name', help='set output base names') args = parser.parse_args() # Check if sar is available @@ -32,6 +34,18 @@ if version is None: # If the script was run with no parameters, run in background and gather data if args.session is None: import watch + + # Find requested disk device + if args.fspath: + args.fspath = os.path.realpath(args.fspath) + with open("/proc/self/mounts", "r") as f: + while args.fsdev is None: + args.fsdev = scan(f"^(/dev/\S+)\s+{re.escape(args.fspath)}\s+", str, f.readline()) + if not args.fsdev: + print(f"Error: no device is mounted on {args.fspath}") + sys.exit(1) + + watch.watch(args.name, args.fsdev) sys.exit(0) # Now handle the commands @@ -49,7 +63,7 @@ if cmd[0] == "start": print(f"Starting sargraph session '{sid}'") # Spawn watcher process, *sys.argv[3:] is all arguments after 'chart start' - p = subprocess.Popen(["screen", "-dmSL", sid, os.path.realpath(__main__.__file__), *sys.argv[3:]]) + p = subprocess.Popen(["screen", "-dmSL", sid, os.path.realpath(__file__), *sys.argv[3:], '-o', sid]) while p.poll() is None: time.sleep(0.1) @@ -75,6 +89,9 @@ elif cmd[0] == "stop": #print("Waiting for pid %d" % gpid) while pid_running(gpid): time.sleep(0.25) + + import graph + graph.graph(sid) elif cmd[0] == "label": # Check if the label name was provided if len(cmd) < 2: @@ -87,8 +104,8 @@ elif cmd[0] == "label": while p.poll() is None: time.sleep(0.1) elif cmd[0] == 'plot': - # Plot the result - import graph + # TODO: plot the result + pass else: print(f"Error: Unknown command '{cmd[0]}'") sys.exit(1) diff --git a/watch.py b/watch.py index 3ec1251..c304f44 100755 --- a/watch.py +++ b/watch.py @@ -16,24 +16,21 @@ import subprocess import sys import time -# Get access to main module information -import __main__ - from common import * -global die - die = 0 # Initialize summary variables +SAMPLE_NUMBER = 0 TOTAL_RAM = 0 START_DATE = "" END_DATE = "" -AVERAGE_LOAD = 0.0 +TOTAL_LOAD = 0.0 MAX_USED_RAM = 0 MAX_USED_FS = 0 TOTAL_FS = 0 +FS_NAME = None FS_SAR_INDEX = None @@ -70,140 +67,151 @@ def read_table(f): return table -my_env = os.environ -my_env["S_TIME_FORMAT"] = "ISO" - -with open("/proc/meminfo") as f: - TOTAL_RAM = int(scan("MemTotal:\s+(\d+)", float, f.read())/1024/1024) - - -p = run_process("sar", "-F", "-u", "-r", "1", stdout=subprocess.PIPE, env=my_env) - -print(os.getpid()) +# Initialize 'data.txt' where the data is dumped +def initialize(name, machine): + global TOTAL_RAM + + with open("/proc/meminfo") as f: + TOTAL_RAM = int(scan("MemTotal:\s+(\d+)", float, f.read())/1024/1024) + + uname = machine.split(" ")[0:2] + uname = f"{uname[0]} {uname[1]}" + + cpus = int(machine.split(" CPU)")[0].split("(")[-1]) + + cpu_name = "unknown" + + with open("/proc/cpuinfo") as f: + for line in f: + if "model name" in line: + cpu_name = line.replace("\n", "").split(": ")[1] + break + + with open(f"{name}.txt", "w") as f: + print(f"# pid: {os.getpid()}", + f"machine: {uname}", + f"cpu count: {cpus}", + f"cpu: {cpu_name}", + sep=", ", file=f) + + +# Add a summary comment to 'data.txt' +def summarize(name): + average_load = TOTAL_LOAD / float(SAMPLE_NUMBER) + max_used_ram = MAX_USED_RAM / 1024.0 / 1024.0 + max_used_fs = MAX_USED_FS / 1024.0 + + sdt = datetime.datetime.strptime(START_DATE, '%Y-%m-%d %H:%M:%S') + edt = datetime.datetime.strptime(END_DATE, '%Y-%m-%d %H:%M:%S') + delta_t = ((edt - sdt).total_seconds()) / 60.0 + + with open(f"{name}.txt", "a") as f: + print(f"# total ram: {TOTAL_RAM:.2f} GB", + f"total disk space: {TOTAL_FS:.2f} GB", + f"max ram used: {max_used_ram:.2f} GB", + f"max disk used: {max_used_fs:.2f} GB", + f"average load: {average_load:.2f} %", + f"observed disk: {FS_NAME}", + f"duration: {delta_t:.2f} minutes", + sep=", ", file=f) + + +# Run sar and gather data from it +def watch(name, fsdev): + global SAMPLE_NUMBER + global START_DATE + global END_DATE + global TOTAL_LOAD + global MAX_USED_RAM + global MAX_USED_FS + global TOTAL_FS + global FS_SAR_INDEX + global TOTAL_RAM + global FS_NAME -machine = p.stdout.readline().decode() - -uname = machine.split(" ")[0:2] -uname = f"{uname[0]} {uname[1]}" - -cpus = int(machine.split(" CPU)")[0].split("(")[-1]) - -cpu_name = "unknown" + global die -with open("/proc/cpuinfo") as f: - for line in f: - if "model name" in line: - cpu_name = line.replace("\n", "").split(": ")[1] + my_env = os.environ + my_env["S_TIME_FORMAT"] = "ISO" + p = run_process("sar", "-F", "-u", "-r", "1", stdout=subprocess.PIPE, env=my_env) + + machine = p.stdout.readline().decode() + initialize(name, machine) + p.stdout.readline() + + signal.signal(signal.SIGTERM, kill_handler) + + flags = fcntl.fcntl(sys.stdin, fcntl.F_GETFL) + fcntl.fcntl(sys.stdin, fcntl.F_SETFL, flags | os.O_NONBLOCK) + + # Gather data from sar output + while 1: + # Await sar output or a command sent from command handler in sargraph.py + rlist, _, _ = select.select([p.stdout, sys.stdin], [], [], 0.25) + now = datetime.datetime.now() + if sys.stdin in rlist: + label_line = sys.stdin.readline().replace("\n", "") + if label_line == "q": + die = 1 + break + + with open(f"{name}.txt", "a") as f: + timestamp = now.strftime("%Y-%m-%d-%H:%M:%S") + print(f"# {timestamp} label: {label_line}", file=f) + if (p.stdout not in rlist): + continue + + date = now.strftime("%Y-%m-%d") + daytime = now.strftime("%H:%M:%S") + + # Read and process CPU data + cpu_data = read_table(p.stdout) + if START_DATE == "": + START_DATE = date + " " + daytime + TOTAL_LOAD += stof(cpu_data["%user"][0]) + SAMPLE_NUMBER += 1 + + # Read and process RAM data + ram_data = read_table(p.stdout) + if TOTAL_RAM == 0: + TOTAL_RAM = (int(ram_data['kbmemused'][0]) + int(ram_data['kbmemfree'][0])) / 1024.0 / 1024.0 + if MAX_USED_RAM < int(ram_data['kbmemused'][0]): + MAX_USED_RAM = int(ram_data['kbmemused'][0]) + + # Read and process FS data + fs_data = read_table(p.stdout) + if FS_SAR_INDEX is None: + if fsdev: + FS_SAR_INDEX = fs_data['FILESYSTEM'].index(fsdev) + else: + maxj, maxv = 0, 0 + for j, free in enumerate(fs_data['MBfsfree']): + v = stof(fs_data['MBfsfree'][j]) + stof(fs_data['MBfsused'][j]) + if maxv < v: + maxj, maxv = j, v + FS_SAR_INDEX = maxj + if FS_NAME is None: + FS_NAME = fs_data["FILESYSTEM"][FS_SAR_INDEX] + if TOTAL_FS == 0: + TOTAL_FS = (stof(fs_data['MBfsused'][FS_SAR_INDEX]) + stof(fs_data['MBfsfree'][FS_SAR_INDEX])) / 1024.0 + if MAX_USED_FS < int(fs_data['MBfsused'][FS_SAR_INDEX]): + MAX_USED_FS = int(fs_data['MBfsused'][FS_SAR_INDEX]) + + END_DATE = date + " " + daytime + timestamp = date + "-" + daytime + + with open(f"{name}.txt", "a") as f: + print(timestamp, + cpu_data['%user'][0], + ram_data['%memused'][0], + fs_data['%fsused'][FS_SAR_INDEX], + file=f) + + if die: break -with open("data.txt", "w") as f: - print(f"# pid: {os.getpid()}", - f"machine: {uname}", - f"cpu count: {cpus}", - f"cpu: {cpu_name}", - sep=", ", file=f) - -p.stdout.readline() - -if args.fspath: - args.fspath = os.path.realpath(args.fspath) - with open("/proc/self/mounts", "r") as f: - while args.fsdev is None: - args.fsdev = scan(f"^(/dev/\S+)\s+{re.escape(args.fspath)}\s+", str, f.readline()) - if not args.fsdev: - print(f"Error: no device is mounted on {args.fspath}") - sys.exit(1) - -signal.signal(signal.SIGTERM, kill_handler) -i = 0 - -flags = fcntl.fcntl(sys.stdin, fcntl.F_GETFL) -fcntl.fcntl(sys.stdin, fcntl.F_SETFL, flags | os.O_NONBLOCK) - -# Gather data from sar output -while 1: - # Await sar output or a command sent from command handler in sargraph.py - rlist, _, _ = select.select([p.stdout, sys.stdin], [], [], 0.25) - now = datetime.datetime.now() - if sys.stdin in rlist: - label_line = sys.stdin.readline().replace("\n", "") - if label_line == "q": - die = 1 - break + if SAMPLE_NUMBER == 0: + time.sleep(1) + sys.exit(0) - with open("data.txt", "a") as f: - timestamp = now.strftime("%Y-%m-%d-%H:%M:%S") - print(f"# {timestamp} label: {label_line}", file=f) - if (p.stdout not in rlist): - continue - - date = now.strftime("%Y-%m-%d") - daytime = now.strftime("%H:%M:%S") - - # Read and process CPU data - cpu_data = read_table(p.stdout) - if START_DATE == "": - START_DATE = date + " " + daytime - AVERAGE_LOAD += stof(cpu_data["%user"][0]) - i = i + 1 - - # Read and process RAM data - ram_data = read_table(p.stdout) - if TOTAL_RAM == 0: - TOTAL_RAM = (int(ram_data['kbmemused'][0]) + int(ram_data['kbmemfree'][0])) / 1024.0 / 1024.0 - if MAX_USED_RAM < int(ram_data['kbmemused'][0]): - MAX_USED_RAM = int(ram_data['kbmemused'][0]) - - # Read and process FS data - fs_data = read_table(p.stdout) - if FS_SAR_INDEX is None: - if args.fsdev: - FS_SAR_INDEX = fs_data['FILESYSTEM'].index(args.fsdev) - else: - maxj, maxv = 0, 0 - for j, free in enumerate(fs_data['MBfsfree']): - v = stof(fs_data['MBfsfree'][j]) + stof(fs_data['MBfsused'][j]) - if maxv < v: - maxj, maxv = j, v - FS_SAR_INDEX = maxj - if TOTAL_FS == 0: - TOTAL_FS = (stof(fs_data['MBfsused'][FS_SAR_INDEX]) + stof(fs_data['MBfsfree'][FS_SAR_INDEX])) / 1024.0 - if MAX_USED_FS < int(fs_data['MBfsused'][FS_SAR_INDEX]): - MAX_USED_FS = int(fs_data['MBfsused'][FS_SAR_INDEX]) - - END_DATE = date + " " + daytime - timestamp = date + "-" + daytime - - with open("data.txt", "a") as f: - print(timestamp, - cpu_data['%user'][0], - ram_data['%memused'][0], - fs_data['%fsused'][FS_SAR_INDEX], - file=f) - - if die: - break - -if i == 0: - time.sleep(1) - sys.exit(0) - -FS_NAME = fs_data["FILESYSTEM"][FS_SAR_INDEX] - -AVERAGE_LOAD = AVERAGE_LOAD / float(i) -MAX_USED_RAM = MAX_USED_RAM / 1024.0 / 1024.0 -MAX_USED_FS /= 1024.0 - -sdt = datetime.datetime.strptime(START_DATE, '%Y-%m-%d %H:%M:%S') -edt = datetime.datetime.strptime(END_DATE, '%Y-%m-%d %H:%M:%S') -delta_t = ((edt - sdt).total_seconds()) / 60.0 - -with open("data.txt", "a") as f: - print(f"# total ram: {TOTAL_RAM:.2f} GB", - f"total disk space: {TOTAL_FS:.2f} GB", - f"max ram used: {MAX_USED_RAM:.2f} GB", - f"max disk used: {MAX_USED_FS:.2f} GB", - f"average load: {AVERAGE_LOAD:.2f} %", - f"observed disk: {FS_NAME}", - f"duration: {delta_t:.2f} minutes", - sep=", ", file=f) + summarize(name)