#!/usr/bin/env python
# vim: tabstop=4 softtabstop=4 shiftwidth=4 textwidth=80 smarttab expandtab
"""
* Copyright (C) 2013  Sangoma Technologies Corp.
* All Rights Reserved.
*
* Author(s)
* Fenton Har <fhar@sangoma.com>
*
* This code is Sangoma Technologies Confidential Property.
* Use of and access to this code is covered by a previously executed
* non-disclosure agreement between Sangoma Technologies and the Recipient.
* This code is being supplied for evaluation purposes only and is not to be
* used for any other purpose.
"""

import sys
import os
import time
import logging
import subprocess
import re
import signal
import errno
import commands
import pickle
import shutil
from optparse import OptionParser
from datetime import datetime
from datetime import timedelta
from logging.handlers import SysLogHandler
from stat import *

class CoreLogger:
    formatter = logging.Formatter('[%(asctime)s] [%(name)s] [%(process)d] [%(levelname)s] %(message)s')
    logger = logging.getLogger('sng-cdr-rotator')
    logger.setLevel(logging.INFO)

    syslog_handler = SysLogHandler('/dev/log')
    syslog_handler.setFormatter(formatter)
    logger.addHandler(syslog_handler)

def rotatecdr(servicename):
    try:
        cmd = ['/usr/bin/pgrep', '^' + servicename + '$']
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
        out, err = p.communicate()

        #pidstr = subprocess.check_output('/usr/bin/pgrep ' + servicename)
        i = int(out)
        os.kill(i, signal.SIGHUP)
    except ValueError, CalledProcessError:
        CoreLogger.logger.error("Failed: " + servicename + " process does not exist.")
        sys.exit(1)

def getnewestdir(cdrpath):
    newest = ''
    for f in os.listdir(cdrpath):
        if not newest:
            newest = f;
        if f > newest:
            newest = f
    return newest

def getoldestdir(cdrpath):
    oldest = ''
    for f in os.listdir(cdrpath):
        if not oldest:
            oldest = f;
        if f < oldest:
            oldest = f
    return oldest

def gettotalsize(cdrpath):
    # determine the current CDR direction in use by the service
    newest = getnewestdir(cdrpath)
    CoreLogger.logger.info("Current CDR directory in use: %s" % newest)

    # loop through all CDR directories to obtain total disk usage. If directory size was determined
    # previously, obtain the size from file. Otherwise, calculate directory size
    # and save to file.
    total = 0
    for f in os.listdir(cdrpath):
        dirsize = 0
        pathname = os.path.join(cdrpath, f)
        if f != newest:
            mode = os.stat(pathname).st_mode
            if S_ISDIR(mode):
                conffile = os.path.join(pathname, 'cdrconfig.dat')
                if os.path.isfile(conffile):
                    sfile = open(conffile, 'r')
                    dirsize = pickle.load(sfile)
                    CoreLogger.logger.info('Directory size {!s} bytes obtained from file {!s}'.format(dirsize,conffile))
                else:
                    status, duoutput = commands.getstatusoutput('du -s -b {!s}'.format(pathname))
                    [dirsizestr, junk] = duoutput.split('\t')
                    try:
                        dirsize = int(dirsizestr)
                    except ValueError:
                        CoreLogger.logger.error('Failed: Cannot obtain size of directory: %s' % pathname)
                    sfile = open(conffile, 'w')
                    pickle.dump(dirsize, sfile)
                    CoreLogger.logger.info('Directory size {!s} bytes calculated and saved to {!s}'.format(dirsize, conffile))
            else:
                CoreLogger.logger.error('WARNING: Skipping unexpected file %s' % pathname)
        else:
            status, duoutput = commands.getstatusoutput('du -s -b {!s}'.format(pathname))
            [dirsizestr, junk] = duoutput.split('\t')
            try:
                dirsize = int(dirsizestr)
            except ValueError:
                CoreLogger.logger.error('Failed: Cannot obtain size of directory: %s' % pathname)
        total = total + dirsize
    total_mb = total/1024/1024
    CoreLogger.logger.info('Total CDR storage: %s MB' % total_mb)
    return total_mb

def onremtreeerr(function, path, excinfo):
    CoreLogger.logger.error('Failed on removing CDR directory: %s: Exiting...' % path)
    sys.exit(1)

def removecdrdir(cdrdir):
    CoreLogger.logger.info('Removing oldest CDR directory: %s' % cdrdir)
    ignore_errors = 0
    shutil.rmtree(cdrdir, ignore_errors, onremtreeerr)

def main():
    parser = OptionParser()
    parser.add_option('', '--service_name',
            dest='servicename',
            help='Service Name',
            metavar='SERVICE_NAME')
    parser.add_option('', '--path',
            dest='cdrpath',
            help='CDR path location',
            metavar='CDR_PATH')
    parser.add_option('', '--size',
            dest='size',
            help='CDR storage size limit in MB',
            metavar='CDR_SIZE')

    (options, args) = parser.parse_args()

    if not os.path.isdir(options.cdrpath):
        CoreLogger.logger.error('Failed: No such directory %s' % options.cdrpath)
        sys.exit(1)

    try:
        sizelimit = int(options.size)
    except ValueError:
        CoreLogger.logger.error('Failed: Incorrect CDR storage size %s' % options.size)
        sys.exit(1)

    while gettotalsize(options.cdrpath) > sizelimit:
        newest = getnewestdir(options.cdrpath)
        oldest = getoldestdir(options.cdrpath)
        if newest != oldest:
            pathname = os.path.join(options.cdrpath, oldest)
            removecdrdir(pathname)
        else:
            CoreLogger.logger.error('WARNING: Current CDR directory in used is oversized. It will be removed during the next scheduled cleanup time.')
            break

    # Send SIG HUP to the service to rotate CDR directory
    rotatecdr(options.servicename)

if __name__ == '__main__':
    main()
