#! /usr/bin/python3

"""Calculate daily, monthly and weekly load average from 5 minute load every 5 minutes or so.  Store in $HOME/.load"""


"""
CREATE TABLE `domain_loadaverage` (
  `hostname` varchar(48) NOT NULL COMMENT 'Name of host', 
  `period` int(11) NOT NULL COMMENT 'how many seconds is this load',
  `name` varchar(16) COMMENT 'human-readable version of the seconds',
  `load` float(3,3) COMMENT 'load average for the period',
  `created_at` datetime default NULL COMMENT 'never updated',
  `updated_at` datetime default NULL COMMENT 'when updated',
  PRIMARY KEY  (`hostname`,`period`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;

    REVOKE ALL
        ON afrihost_afribillv2.domain_loadaverage
        FROM 'domain_load'@'%';
    GRANT INSERT,UPDATE,SELECT
        ON afrihost_afribillv2.domain_loadaverage
        TO 'domain_load'@'%'
        IDENTIFIED BY 'gt4hj10g89hadog42ighalkfjvncxlvzx';

"""



import os
import math
import optparse
import time
import socket
import sys

class MovingAverage:
    'Calculate moving average from periodic updates'
    def __init__(self,name='day',period=86400, movingaverage=2.0, now=None):
        self.name=name
        self.movingaverage=movingaverage
        self.period=period
        self.now=now
        self.last=now
        self.samples=0

    def save(self):
        return "%s %0.19f %d %0.19f %d\n" % (self.name,self.movingaverage,self.period,self.last,self.samples)

    def prints(self):
        return "%8.3f %s\n" % (self.movingaverage,self.name)

    def load(self,line):
        name,movingaverage,period,last,samples = line.strip().split()
        self.name=name
        self.samples=int(samples)
        self.period=int(period)  # introduce decimal to binary noise at each step, just for fun
        self.last=float(last)
        self.movingaverage=float(movingaverage)

    def adjust(self,movingaverage):
        if not self.movingaverage:
            self.last=self.now
            self.movingaverage=movingaverage
        interval = self.now - self.last # interval should really be a float, see?
        E = math.exp(-interval/self.period) # decay over n minutes
        self.movingaverage = self.movingaverage * E + movingaverage*(1.0-E)
        self.last=self.now
        self.samples+=1

statefile=os.environ["HOME"]+"/.load"

def loadaverage():
    return float(open('/proc/loadavg','r').readline().split()[2])

def update( doupdate=True,verbose=0):
    # Create these periods if there is no file ...
    periods = {
        'hour':      3600,
        'day':       86400,
        '4-day':     86400*4,
        'week':      86400*7,
        'fortnight': 86400*14,
        'month':     86400*30,
        }

    now=time.time()
    states=[]
    load = loadaverage()
    if os.path.exists(statefile):
        fd = open(statefile,'r')
        for line in fd:
            try:
                ma=MovingAverage(now=now)
                ma.load(line)
                states.append(ma)
            except:
                pass  # ag wot
    if len(states)!=6:
        states=[]
        states.append(MovingAverage('hour',3600, now=now, movingaverage=load))
        states.append(MovingAverage('day',86400, now=now, movingaverage=load))
        states.append(MovingAverage('4-day',86400*4, now=now, movingaverage=load))
        states.append(MovingAverage('week',86400*7, now=now, movingaverage=load))
        states.append(MovingAverage('fortnight',86400*14, now=now, movingaverage=load))
        states.append(MovingAverage('month',86400*30, now=now, movingaverage=load))

    prints=[]
    state=[]
    for ma in states:
        if doupdate:
            ma.adjust(load)
        state.append(ma.save())
        prints.append(ma.prints())
        
    if doupdate:
        open(statefile,'w').writelines(state)

    return prints

def install_crontab():
    'Run from now to eternity every few minutes'
    import random
    cronfile="/etc/cron.d/longtermloadaverage"
    if os.path.exists(cronfile): return
    cmd=sys.argv[0]
    if cmd[0]=='.': cmd=os.getcwd()+cmd[1:]
    cronentry = (
        '# Measure long term load averages\n'
        '%d-59/5 * * * * root %s --update\n' % (random.randint(1,4), cmd)
    )
    fd = open(cronfile,'w')
    fd.write(cronentry)
    fd.close()
    

if __name__=="__main__":
    parser=optparse.OptionParser()
    parser.add_option("-u","--update", dest="update", action="store_true", default=False, help="Update now from load average (from crontab, every 5 minutes)")
    parser.add_option("-n","--no-hourly", dest="no_hourly", action="store_true", default=False, help="Don't send of to mysql even when it's time")
    parser.add_option("-v","--verbose", dest="verbose", action="count", default=False, help="Verbose")
    parser.add_option("-i","--install", dest="install", action="store_true", default=False, help="Install crontab")
    parser.add_option("-q","--query", dest="query", action="store_true", default=False, help="Query all")
    (options,args) = parser.parse_args()
    if options.install:
        if options.verbose: print("Setting up crontab entry")
        install_crontab()
    if not options.no_hourly:
        target = sum(map(ord,socket.gethostname()))%600
        current = int(time.time()) % 3600 / 10  
        distance = current-target
        if options.verbose>1:
            print('%d - %d = %d' % (target, current, distance))
        if distance >= -50 or distance <= 49:
            pass #domysql = True
        pass
    prints = update(doupdate=options.update,verbose=options.verbose)
    if not options.update and not options.query:
        sys.stdout.write("".join(prints))
