#!/usr/bin/python
#encoding:UTF-8

import os, smtplib, shutil, syslog, subprocess, cgi, commands, re, MySQLdb
from datetime import date, timedelta
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from htmlentitydefs import codepoint2name


def toNotationUnit(value, base=10):
    units = [ 'y','z','a','f','p','n','u','m',' ', 'k','M','G','T','P','E','Z','Y'] 
    counter = 8
    if base==10:
        val = value if value > 0 else -value
        if val < 1:
            while ( val < 1.00) and not (counter == 0):
                counter = counter - 1
                val = val * 1000.0
        else:
            while ( val >= 1000 ) and not (counter == 16):
                counter = counter + 1
                val = val / 1000.0
        return ( (val if value > 0 else -val) , units[counter])
    elif base == 2:
        val = value if value > 0 else -value
        if val < 1:
            while ( val < 1.00) and not (counter == 0):
                counter = counter - 1
                val = val * 1024.0
        else:
            while ( val >= 1024 ) and not (counter == 16):
                counter = counter + 1
                val = val / 1024.0
        return ( (val if value > 0 else -val) , units[counter])
  

def htmlentities(text):
    text = (text).decode('utf-8')

    from htmlentitydefs import codepoint2name
    d = dict((unichr(code), u'&%s;' % name) for code,name in codepoint2name.iteritems() if code!=38) # exclude "&"    
    if u"&" in text:
        text = text.replace(u"&", u"&amp;")
    for key, value in d.iteritems():
        if key in text:
            text = text.replace(key, value)
    return text

def LOG(msg):
    syslog.syslog(syslog.LOG_INFO, 'TVS Backup System: %s' %msg)
    
def WARN(msg):
    syslog.syslog(syslog.LOG_WARNING, 'TVS Backup System: %s' %msg)

def ExecuteShell(cmd):
    return    subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True).stdout.read()

def GetDiskList():
    disks = filter(None, ExecuteShell('ls /sys/block/ |grep "sd\|xvd\|drbd"').split('\n'))
    return [ ("/dev/%s" %d) for d in disks ]

def GetSmartData(disk):
    try :
        smartok                =    commands.getstatusoutput('smartctl -a %s' %disk)[0]
        if smartok ==    0:
            ModelFamily         =     ExecuteShell('smartctl -a %s | grep "Model Family" | cut -d: -f2' %disk).strip()
            DeviceModel            =    ExecuteShell('smartctl -a %s | grep "Device Model" | cut -d: -f2' %disk).strip()
            UserCapacity        =    ExecuteShell('smartctl -a %s | grep "User Capacity" | cut -d: -f2' %disk).strip()
            DiskHealth            =    ExecuteShell('smartctl -a %s | grep "SMART overall-health" | cut -d: -f2' %disk).strip()
            PowerOnHours        =    ExecuteShell('smartctl -a %s | grep "Power_On_Hours"' %disk).strip().split(' ')
            PowerOnHours        =    PowerOnHours[len(PowerOnHours)-1]
            PowerCycleCount        =    ExecuteShell('smartctl -a %s | grep "Power_Cycle_Count"' %disk).strip().split(' ')
            PowerCycleCount        =    PowerCycleCount[len(PowerCycleCount)-1]
            ReadErrorRate        =    ExecuteShell('smartctl -a %s | grep "Raw_Read_Error_Rate"' %disk).strip().split(' ')
            ReadErrorRate        =    ReadErrorRate[len(ReadErrorRate)-1]    
            ReallocatedSector    =    ExecuteShell('smartctl -a %s | grep "Reallocated_Sector_Ct"' %disk).strip().split(' ')    
            ReallocatedSector    =    ReallocatedSector[len(ReallocatedSector)-1]

            return { "Device": disk, "ModelFamily" : ModelFamily, "DeviceModel" : DeviceModel, "UserCapacity" : UserCapacity, "DiskHealth": DiskHealth, "PowerOnHours" : PowerOnHours, "PowerCycleCount" : PowerCycleCount, "ReadErrorRate" : ReadErrorRate, "ReallocatedSector" : ReallocatedSector }
    except Exception, e:
        WARN("Erro ao ler SMART (%s): %s" %(disk,e))
        return None

def Init3WareSmart():
    try:
        smartok =   commands.getstatusoutput('smartctl -d 3ware,0 /dev/twa0')
    except:
        pass
        
        
def Get3WareSmartData():
    Init3WareSmart()
    smlist = []
    try :
        for disk   in  range(0,12):
            smartok                 =    commands.getstatusoutput('smartctl -a -d 3ware,%s /dev/twa0' %disk)[0]
            if smartok ==    0:
                ModelFamily         =    ExecuteShell('smartctl -a -d 3ware,%s /dev/twa0 | grep "Model Family" | cut -d: -f2' %disk).strip()
                DeviceModel         =    ExecuteShell('smartctl -a -d 3ware,%s /dev/twa0 | grep "Device Model" | cut -d: -f2' %disk).strip()
                UserCapacity        =    ExecuteShell('smartctl -a -d 3ware,%s /dev/twa0 | grep "User Capacity" | cut -d: -f2' %disk).strip()
                DiskHealth          =    ExecuteShell('smartctl -a -d 3ware,%s /dev/twa0 | grep "SMART overall-health" | cut -d: -f2' %disk).strip()
                PowerOnHours        =    ExecuteShell('smartctl -a -d 3ware,%s /dev/twa0 | grep "Power_On_Hours"' %disk).strip().split(' ')
                PowerOnHours        =    PowerOnHours[len(PowerOnHours)-1]
                PowerCycleCount     =    ExecuteShell('smartctl -a -d 3ware,%s /dev/twa0 | grep "Power_Cycle_Count"' %disk).strip().split(' ')
                PowerCycleCount     =    PowerCycleCount[len(PowerCycleCount)-1]
                ReadErrorRate       =    ExecuteShell('smartctl -a -d 3ware,%s /dev/twa0 | grep "Raw_Read_Error_Rate"' %disk).strip().split(' ')
                ReadErrorRate       =    ReadErrorRate[len(ReadErrorRate)-1]    
                ReallocatedSector   =    ExecuteShell('smartctl -a -d 3ware,%s /dev/twa0 | grep "Reallocated_Sector_Ct"' %disk).strip().split(' ')    
                ReallocatedSector   =    ReallocatedSector[len(ReallocatedSector)-1]

                smlist.append({ "Device": "3ware:%s" %disk, "ModelFamily" : ModelFamily, "DeviceModel" : DeviceModel, "UserCapacity" : UserCapacity, "DiskHealth": DiskHealth, "PowerOnHours" : PowerOnHours, "PowerCycleCount" : PowerCycleCount, "ReadErrorRate" : ReadErrorRate, "ReallocatedSector" : ReallocatedSector })
        return smlist
    except Exception, e:
        WARN("Erro ao ler SMART: %s" %(e))
        return None
        
             
def GetDiskUsage():
    disks = []
    try:
        disk_data = filter(None, ExecuteShell('df -h |grep "/dev/sd\|/dev/xvd\|/dev/mapper/\|/dev/drbd"').split('\n'))
        for disk in disk_data:
            dsk = filter(None, disk.split(' '))
            dk = { 'Device' : dsk[0], 'Size' : dsk[1], 'Used' : dsk[2], 'Free' : dsk[3], 'UsedPercent' : dsk[4], 'MountPoint' : dsk[5] }     
            disks.append(dk)
    except Exception, e:
        WARN("Erro ao ler dados de disco : %s" %(e))
    return disks

def GetNetDevData(dev):
    devdata = { 'Device' : dev, "TX" : "(0.0 B)", "RX" : "(0.0 B)", "IP" : "Unknown", "Broadcast" : "Unknown", "NetMask" : "Unknown" }
    try:
        netstring = ExecuteShell('LANG="en_US.UTF-8" LANGUAGE="en" ifconfig %s' %dev)
        devdata["IP"]             =    '.'.join(re.findall("inet addr:([0-9]*).([0-9]*).([0-9]*).([0-9]*)", netstring)[0])
        devdata["Broadcast"]     =    '.'.join(re.findall("Bcast:([0-9]*).([0-9]*).([0-9]*).([0-9]*)", netstring)[0])
        devdata["NetMask"]         =    '.'.join(re.findall("Mask:([0-9]*).([0-9]*).([0-9]*).([0-9]*)", netstring)[0])
        tx                        =    toNotationUnit(int(re.findall("TX bytes:([0-9]*)", netstring)[0]))
        rx                        =    toNotationUnit(int(re.findall("RX bytes:([0-9]*)", netstring)[0]))
        devdata["TX"]             =    '(%s %sB)' %(round(tx[0]*100)/100.0, tx[1])
        devdata["RX"]             =    '(%s %sB)' %(round(rx[0]*100)/100.0, rx[1])
    except Exception, e:
        WARN("Erro ao ler dados da rede (%s) : %s" %(dev, e))
    return devdata

def GetNetworkDevices():
    devices = []
    try:
        data = filter(None, ExecuteShell('cat /proc/net/dev').split('\n'))
        data.pop(0)
        data.pop(0)
        for i in data:
            devices.append(i.split(':', 1)[0].strip())
    except Exception, e:
        WARN("Erro ao ler interfaces de rede : %s" %(e))
    return devices

def GetNetworkUsage():
    devices = GetNetworkDevices()
    usages = []
    for device in devices:
        usages.append( GetNetDevData(device) )

    return usages

def GetDevicesInfo():
    devs = filter(None, ExecuteShell("lspci").split('\n'))
    devs = [ d[8:] for d in devs ] 
    return devs

def GetMemInfo():
    '''
        Retorna informações sobre a memória
    '''
    total            =    toNotationUnit(int(commands.getstatusoutput('cat /proc/meminfo |grep MemTotal | cut -d: -f2')[1].strip()[:-3])        *    1000)
    free            =    toNotationUnit(int(commands.getstatusoutput('cat /proc/meminfo |grep MemFree | cut -d: -f2')[1].strip()[:-3])        *    1000)
    swaptotal        =    toNotationUnit(int(commands.getstatusoutput('cat /proc/meminfo |grep SwapTotal | cut -d: -f2')[1].strip()[:-3])        *    1000)
    swapfree        =    toNotationUnit(int(commands.getstatusoutput('cat /proc/meminfo |grep SwapFree | cut -d: -f2')[1].strip()[:-3])        *    1000)
    return { "total" : "%s %sB" % (round(total[0]*100)/100, total[1]) , "free" : "%s %sB" % (round(free[0]*100)/100, free[1]), "swaptotal" : "%s %sB" % (round(swaptotal[0]*100)/100, swaptotal[1]), "swapfree" : "%s %sB" % (round(swapfree[0]*100)/100, swapfree[1]) } 

def GetUpTime():
    uptime            =    float(commands.getstatusoutput('cat /proc/uptime')[1].split(' ')[0])
    return str( timedelta(seconds=round(uptime)))

def GetCPUInfo():
    '''
        Retorna os dados do processador
    '''
    cpu_family         =    commands.getstatusoutput('cat /proc/cpuinfo |grep "cpu family" |head -n1 |cut -d: -f2')[1].strip()
    cpu_model         =    commands.getstatusoutput('cat /proc/cpuinfo |grep "model" |head -n1 |cut -d: -f2')[1].strip()
    cpu_model_name     =    commands.getstatusoutput('cat /proc/cpuinfo |grep "model name" |head -n1 |cut -d: -f2')[1].strip()
    cpu_stepping    =    commands.getstatusoutput('cat /proc/cpuinfo |grep "stepping" |head -n1 |cut -d: -f2')[1].strip()
    cpu_cores        =    commands.getstatusoutput('cat /proc/cpuinfo |grep "cpu cores" |head -n1 |cut -d: -f2')[1].strip()
    cpu_bogomips    =    commands.getstatusoutput('cat /proc/cpuinfo |grep "bogomips" |head -n1 |cut -d: -f2')[1].strip()
    return { 'cpu_family' : cpu_family, 'cpu_model' : cpu_model, 'cpu_model_name' : cpu_model_name, 'cpu_stepping' : cpu_stepping, 'cpu_cores' : cpu_cores, 'cpu_bogomips' : cpu_bogomips }
    
def GetDRBDInfo():
    '''
        Retorna informações sobre DRBD
    '''
    drbdinfo = {"conn" : {}}
    try:
        f = open("/proc/drbd")
        data = f.read()
        f.close()
        drbdinfo["has"] = True
        drbdinfo["drbdfile"] = data
        data = filter(None, data.split("\n"))
        lastdigit = None
        for line in data:
            block = line.split(":",1)
            block[0] = block[0].strip()
            block[1] = block[1].strip()
            if 'srcversion'  in block[0]:    
                drbdinfo["srcversion"] = block[1]
            elif 'version' in block[0]:
                drbdinfo["version"] = block[1]
            elif block[0].isdigit():
                lastdigit = int(block[0])
                drbdinfo["conn"][lastdigit] = {}
                d2 = filter(None, block[1].split(" "))
                for d in d2:
                    o = d.split(":")
                    if len(o) > 1:
                        drbdinfo["conn"][lastdigit][o[0]] = o[1]    
            else:
                if lastdigit == None:
                    WARN("Problemas ao ler partes do DRBD! - Linha: \n %s \n\n /proc/drbd : \n %s" %(line, "\n".join(data)))
                else:
                    drbdinfo["conn"][lastdigit][block[0]] = block[1]
    except:
        drbdinfo["has"] = False
    return drbdinfo
    
def GetMySQLSlave(hostname,username,password):
    '''
        Retorna informações da sincronia do MySQL
    '''
    con = MySQLdb.connect(hostname,username,password)
    cursor = con.cursor()
    cursor.execute("SHOW SLAVE STATUS")
    data = cursor.fetchall()[0]
    desc = cursor.description

    con.close()

    output = {}
    c = 0
    for dc in desc:
        output[dc[0]] = data[c]
        c = c + 1
    
    return output
    
    