#!/usr/bin/env python3 """ choosehost.py - Return the name of the local host with the lowest load average. This script can be used to decide on a remote host for running a resource-intensive job. Synopsis: choosehost.py [--showlist] @modified: December 7, 2017 @author: Brian Fristensky @contact: brian.fristensky@umanitoba.ca """ import os import subprocess import sys #optparse is deprecated in favor of argparse as of Python 2.7. However, # since 2.7 is not always present on many systems, at this writing, # it is safer to stick with optparse for now. It should be easy # to change later, since the syntax is very similar between argparse and optparse. from optparse import OptionParser PROGRAM = "choosehost.py : " USAGE = "\n\tUSAGE: choosehost.py [--showlist]" DEBUG = False class Parameters: """ Wrapper class for command line parameters """ def __init__(self): """ Initializes arguments: SHOWLIST=False Then calls read_args() to fill in their values from command line """ self.BIRCH=os.environ.get("BIRCH") self.RHOSTFILE=os.path.join(self.BIRCH,'local','admin','BIRCHrhosts') self.SHOWLIST=False self.HELP=False self.read_args() def read_args(self): """ Read command line arguments into a Parameter object """ parser = OptionParser() parser.add_option("--showlist", dest="showlist", action="store_true", default=False, help="Write tab-separated list of host/load avg. results") (options, args) = parser.parse_args() self.SHOWLIST = options.showlist if DEBUG : print('------------ Parameters from command line ------' + '\n') print(' BIRCH: ' + self.BIRCH) print(' RHOSTFILE: ' + self.RHOSTFILE) print(' SHOWLIST: ' + str(self.SHOWLIST)) class Hostlist : """ Class for working with information from RHOSTFILE """ def __init__(self): """ Initialize Hostlist """ self.HostData = {} def ReadRHostFile(self,RHOSTFILE) : f = open(RHOSTFILE,'r') lines = f.readlines() for l in lines: HostName=l.strip() #Initialize loadaverage to 0 self.HostData[HostName]=0.0 def GetLoadAvg(self,HOST): """ Returns the load average of a host as a floating point number. IMPLEMENTATION The output from the Unix uptime command can take two forms: 11:12 up 173 days, 20:40, 2 users, load averages: 0.18 0.08 0.01 11:25:18 up 85 days, 22:58, 8 users, load average: 0.81, 1.15, 1.30 If we split the lines using colon as a field separator, the rightmost field will have a list of space-separated real numbers. The only difference is that some systems will also have commas as separators. So, we just strip out all commas, and take the leftmost real number as the load avarage to be returned. """ COMMAND=['ssh', '-t', '-q', HOST,'uptime'] p = subprocess.Popen(COMMAND, stdin=None, stdout=subprocess.PIPE) p.wait() OUTPUT = p.communicate()[0] s1=OUTPUT.rpartition(':')[2] s2=s1.replace(',','') LdAvg=s2.split()[0] return float(LdAvg) def MinLoadHost(self): """ Return the name of the host with the lowest load average. """ FreeHost="" MinLoad=100000.0 # an impossibly high load average for key, val in sorted(self.HostData.items()) : if DEBUG : print(key + '\t' + str(val)) if val < MinLoad : FreeHost = key MinLoad = val return FreeHost def WriteRHostData(self) : for key, val in sorted(self.HostData.items()) : print (key + '\t' + str(val)) #======================== MAIN PROCEDURE ========================== def main(): """ Called when not in documentation mode. """ P = Parameters () H = Hostlist () if os.path.exists(P.RHOSTFILE) : H.ReadRHostFile(P.RHOSTFILE) for Host in H.HostData : H.HostData[Host] = H.GetLoadAvg(Host) FreeHost = H.MinLoadHost() if P.SHOWLIST : H.WriteRHostData() else : print(FreeHost) else: print('') if ("-test" in sys.argv): pass else: main()