This initial edition contains only "--raw" command.master
parent
34a7659f3d
commit
7957b28a05
1 changed files with 164 additions and 0 deletions
@ -0,0 +1,164 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
# |
||||||
|
# Created: 20160830 |
||||||
|
# Wirawan Purwanto |
||||||
|
|
||||||
|
""" |
||||||
|
show-node-status.py |
||||||
|
--------------------- |
||||||
|
|
||||||
|
Various tools to investigate node status in an SGE cluster. |
||||||
|
This tool is a replacement and upgrade of the shell version of the tool |
||||||
|
`node-slot-status.sh`. |
||||||
|
|
||||||
|
Usage: |
||||||
|
|
||||||
|
""" |
||||||
|
|
||||||
|
import os |
||||||
|
import re |
||||||
|
import subprocess |
||||||
|
import sys |
||||||
|
|
||||||
|
#----------------------- UNDER CONSTRUCTION ----------------------- |
||||||
|
#Nothing was done yet |
||||||
|
|
||||||
|
def node_slot_stats_raw(qstat_f, show_disabled_nodes=True): |
||||||
|
"""Prints the node stats from `qstat -f' in raw format: |
||||||
|
- not printing disabled nodes |
||||||
|
- not showing the computational jobs that are running on these nodes |
||||||
|
""" |
||||||
|
FNR = 0 |
||||||
|
for L in qstat_f: |
||||||
|
FNR += 1 |
||||||
|
FLDS = L.split() |
||||||
|
status_flags = FLDS[5] if (len(FLDS) > 5) else "" |
||||||
|
|
||||||
|
if FNR == 1 and FLDS[0] == "queuename": |
||||||
|
print(L) |
||||||
|
continue |
||||||
|
|
||||||
|
# Valid host status field |
||||||
|
if re.search(r'^[A-Za-z]', L) and len(FLDS) in (5,6) \ |
||||||
|
and (show_disabled_nodes or ("d" not in status_flags)): |
||||||
|
print(L) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def help(): |
||||||
|
msg = """\ |
||||||
|
show-node-status.py - Showing node status from SGE information |
||||||
|
|
||||||
|
The information is mainly drawn from `qstat -f` output. |
||||||
|
|
||||||
|
Usage: one of the following: |
||||||
|
|
||||||
|
--raw |
||||||
|
raw |
||||||
|
Shows the raw queue/node status |
||||||
|
|
||||||
|
--stats |
||||||
|
stats |
||||||
|
(no argument) |
||||||
|
Shows the statistic summary per node type |
||||||
|
""" |
||||||
|
|
||||||
|
|
||||||
|
def main_default(argv, save_qstat=True): |
||||||
|
"""Main default function: |
||||||
|
- By default we invoke qstat -f and prints the analysis. |
||||||
|
- If argv[1] is given, then we read in the file and |
||||||
|
use that for the analysis. |
||||||
|
""" |
||||||
|
from time import localtime, strftime |
||||||
|
|
||||||
|
dtime = localtime() |
||||||
|
dtimestr = strftime("%Y%m%d-%H%M", dtime) |
||||||
|
|
||||||
|
# Read the command first--what do we want to do |
||||||
|
if len(argv) < 2: |
||||||
|
cmd = "stats" |
||||||
|
elif argv[1] in ('--raw', 'raw'): |
||||||
|
cmd = "raw" |
||||||
|
elif argv[1] in ('--stats', 'stats'): |
||||||
|
cmd = "stats" |
||||||
|
else: |
||||||
|
raise ValueError, "Unknown action: "+argv[1] |
||||||
|
|
||||||
|
# Skip program name and first command: |
||||||
|
cmdargs = argv[2:] |
||||||
|
|
||||||
|
# Default options |
||||||
|
show_disabled_nodes = False |
||||||
|
|
||||||
|
if len(cmdargs) > 0: |
||||||
|
qstat_f_current = open(cmdargs[0], "r").read().splitlines() |
||||||
|
else: |
||||||
|
qstat_f_current = pipe_out(('qstat', '-f'), split=True) |
||||||
|
if save_qstat: |
||||||
|
with open("qstat-f-%s.txt" % dtimestr, "w") as F: |
||||||
|
F.write("\n".join(qstat_f_current)) |
||||||
|
F.write("\n") |
||||||
|
|
||||||
|
if cmd == "raw": |
||||||
|
node_slot_stats_raw(qstat_f_current, |
||||||
|
show_disabled_nodes=show_disabled_nodes, |
||||||
|
) |
||||||
|
elif cmd == "stats": |
||||||
|
node_slots_stats_per_node_type(qstat_f_current, |
||||||
|
show_disabled_nodes=show_disabled_nodes, |
||||||
|
) |
||||||
|
else: |
||||||
|
raise "Missing support for command: "+cmd |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- |
||||||
|
# Support tools below |
||||||
|
# --------------------------------------------------------------------------- |
||||||
|
|
||||||
|
def pipe_out(args, split=False, shell=False): |
||||||
|
"""Executes a shell command, piping out the stdout to python for parsing. |
||||||
|
This is my customary shortcut for backtick operator. |
||||||
|
The result is either a single string (if split==False) or a list of strings |
||||||
|
with EOLs removed (if split==True).""" |
||||||
|
retval = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell).communicate()[0] |
||||||
|
if not split: |
||||||
|
return retval |
||||||
|
else: |
||||||
|
return retval.splitlines() |
||||||
|
|
||||||
|
|
||||||
|
# Internal variable: don't mess! |
||||||
|
_str_fmt_heading_rx = None |
||||||
|
def str_fmt_heading(fmt): |
||||||
|
"""Replaces a printf-style formatting with one suitable for table heading: |
||||||
|
all non-string conversions are replaced with string conversions, |
||||||
|
preserving the minimum widths.""" |
||||||
|
# Originally from: $PWQMC77/scripts/cost.py and later Cr2_analysis_cbs.py . |
||||||
|
# |
||||||
|
#_str_fmt_heading_rx = None # only for development purposes |
||||||
|
import re |
||||||
|
global _str_fmt_heading_rx |
||||||
|
if _str_fmt_heading_rx is None: |
||||||
|
# Because of complicated regex, I verbosely write it out here: |
||||||
|
_str_fmt_heading_rx = re.compile(r""" |
||||||
|
( |
||||||
|
% # % sign |
||||||
|
(?:\([^)]+\))? # optional '(keyname)' mapping key |
||||||
|
[-+#0 hlL]* # optional conversion flag |
||||||
|
[0-9*]* # optional minimum field width |
||||||
|
) |
||||||
|
((?:\.[0-9]*)?) # optional precision |
||||||
|
[^-+#*0 hlL0-9.%s] # not conv flag, dimensions, nor literal '%', |
||||||
|
# nor 's' conversion specifiers |
||||||
|
""", re.VERBOSE) |
||||||
|
return _str_fmt_heading_rx.sub(r'\1s', fmt) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# stub main code |
||||||
|
|
||||||
|
if __name__ == "__main__" and not "get_ipython" in globals(): |
||||||
|
main_default(sys.argv) |
Loading…
Reference in new issue