#!/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()
