#!/usr/bin/env python

import MySQLdb
import sys
import socket
import os
import datetime
import hashlib
try:
    from ipp.lifetime_templates import Template
except ImportError:
    sys.path.append('/home/panstarrs/schastel/local/share/python/')
    sys.stderr.write('ImportError')
    for folder in sys.path:
        sys.stderr.write('\t%s\n' % folder)
    from ipp.lifetime_templates import Template

def md5sum(filename):
    f = open(filename, "rb")
    content = f.read()
    f.close()
    sum = hashlib.md5()
    sum.update(content)
    return sum.hexdigest()

class DbEntry:
    verbose = False
    def __init__(self, db_key, full_filename):
        self.db_key = db_key
        self.full_filename = full_filename

    def __str__(self):
        if not DbEntry.verbose and len(self.db_key) > 10:
            db_key = '%s...' % self.db_key[:10]
            full_filename = '...%s' % self.full_filename[-10:]
        else:
            db_key = self.db_key
            full_filename = self.full_filename
        if self.role != 'deleted':
            return '[%s]: %s (%s/%s/%s), %d, %s, %s' % (db_key, 
                                                        full_filename, 
                                                        self.stage,
                                                        self.role, 
                                                        self.lifetime,
                                                        self.size,
                                                        self.md5sum,
                                                        self.last_check)
        else:
            return '[%s]: %s (%s)' % (db_key, 
                                      full_filename, 
                                      self.role)

    def check(self):
        try:
            self.stage, self.role, self.lifetime = Template.classify(self.db_key)
            stat_values = os.stat(self.full_filename)
            self.size = stat_values.st_size
            try:
                self.md5sum = md5sum(self.full_filename)
                self.status = 'ok'
            except IOError:
                self.status = 'error'
                self.md5sum = None
        except OSError:
            self.status = 'deleted'
            self.stage, self.role, self.lifetime = Template.classify(self.db_key)
        self.last_check = datetime.datetime.now()

    def update(self, table):
        if self.status == 'deleted':
            self.stage, self.role, self.lifetime = Template.classify(self.db_key)
            stmt = """UPDATE %s 
SET status=\'%s\', 
    last_check=\'%s\', 
    role=\'%s\',
    stage=\'%s\', 
    lifetime=\'%s\' WHERE filename=\'%s\';""" % ( table,
                                                  self.status,
                                                  self.last_check,
                                                  self.role,
                                                  self.stage,
                                                  self.lifetime,
                                                  self.db_key )
            return stmt
        stmt = """
UPDATE %s 
SET 
  size=%d, 
  md5sum=\'%s\',
  role=\'%s\',
  lifetime=\'%s\',
  last_check=\'%s\',
  stage=\'%s\',
  status=\'%s\'
WHERE filename=\'%s\';
""" % ( table,
        self.size,
        self.md5sum,
        self.role,
        self.lifetime,
        self.last_check,
        self.stage,
        self.status,
        self.db_key )
        return stmt

def usage():
    print
    print 'Usage: %s [-v] [-l <limit> | -limit <limit>] [-disk <disk>] <nebulous directory>' % sys.argv[0]
    print '     Update the 10 last checked entries of FilesMonitoring <host>_<directory> table'
    print '     where <host> is the name of the host where this command is run and'
    print '           <directory> is a nebulous directory name (range: 00 - ff)' 
    print 
    print '   * -h: display help'
    print '   * -v: be verbose'
    print '   *  -l <limit>, -limit <limit>: process no more than <limit> entries (default 10)'
    print '   *  -disk <disk>: process disk /export/<node>.<disk> (default: 0)'
    print 
    print '     e.g.: %s [-v] 00' % sys.argv[0]
    print '     e.g.: %s [-v] 01' % sys.argv[0]
    print '     e.g.: ..'
    print '     e.g.: %s [-v] ff' % sys.argv[0]
    print

def process_options(argv):
    limit = 10
    directory = None
    argc = 1
    disk = "0"
    while argc < len(argv):
        if sys.argv[argc] == '-h':
            usage()
            sys.exit(0)
        elif sys.argv[argc] == '-v':
            DbEntry.verbose = True
            argc += 1
        elif sys.argv[argc] == '-limit' or sys.argv[argc] == '-l':
            limit = int(sys.argv[argc+1])
            argc += 2
        elif sys.argv[argc] == '-disk':
            disk = sys.argv[argc+1]
            argc += 2
        else:
            directory = sys.argv[argc]
            argc += 1
    if directory is None:
        usage()
        sys.exit(1)
    return limit, directory, disk

def process_batch(filesMonitoring, disk, table_name, limit):
    cursor = filesMonitoring.cursor()
    query = 'SELECT filename, subdirname FROM %s WHERE status=\'undefined\' LIMIT %d' % ( table_name,
                                                                                          limit)
    if DbEntry.verbose:
        sys.stderr.write('%s\n' % query)
    cursor.execute(query)
    rows = cursor.fetchall()
    cursor.close()
    entries = [ ]
    for row in rows:
        filename = row[0]
        subdirname = row[1]
        full_filename = '/export/%s.%s/nebulous/%s/%s/%s' % ( hostname,
                                                              disk,
                                                              directory,
                                                              subdirname,
                                                              filename)
        entries.append(DbEntry(filename, full_filename))
    for entry in entries:
        entry.check()
    count_updates = 0
    cursor = filesMonitoring.cursor()
    for entry in entries:
        query = entry.update(table_name)
        count_updates += 1
        cursor.execute(query)
        if DbEntry.verbose:
            print query
    cursor.close()
    filesMonitoring.commit()
    sys.stderr.write('\t# of updates: %d\n' % count_updates)
    return count_updates

#######################################################################################
if __name__ == '__main__':
    if len(sys.argv) == 1:
        usage()
        sys.exit(1)
    userlimit, directory, disk = process_options(sys.argv)
    hostname = socket.gethostbyaddr(socket.gethostname())[0].split('.')[0]
    table_name = 'neb_%s_%s' % (disk, directory)
    sys.stderr.write('Updating: %s %s %s\n' % (hostname, directory, table_name))
    if hostname == 'neverland':
        filesMonitoring = MySQLdb.connect( 'localhost',
                                           'root', 'root',
                                           'DisksMonitoring' )
    else:
        filesMonitoring = MySQLdb.connect( 'localhost',
                                           'ipp', 'ipp',
                                           'DisksMonitoring' )
    max_batch_size = 2500
    if userlimit > max_batch_size:
        sys.stderr.write('Batching by sets of %d\n' % max_batch_size)
        count = 0
        while count < userlimit:
            sys.stderr.write('\tProcessing: %d out of %d\n' % (count, userlimit) )
            if process_batch(filesMonitoring, disk, table_name, max_batch_size) == 0:
                count = userlimit
            count += max_batch_size
    else:
        process_batch(filesMonitoring, disk, table_name, userlimit)

    filesMonitoring.close()
