# coding: utf-8

import psutil
import time
import os
import re
import math
import json

from flask import Flask, session
from flask import request

import db
import public
import config_api


from threading import Thread
from time import sleep


def async(f):
    def wrapper(*args, **kwargs):
        thr = Thread(target=f, args=args, kwargs=kwargs)
        thr.start()
    return wrapper


class system_api:
    setupPath = None
    pids = None

    def __init__(self):
        self.setupPath = public.getServerDir()

    ##### ----- start ----- ###
    def networkApi(self):
        data = self.getNetWork()
        return public.getJson(data)

    def updateServerApi(self):
        stype = request.args.get('type', 'check')
        version = request.args.get('version', '')
        return self.updateServer(stype, version)

    def systemTotalApi(self):
        data = self.getSystemTotal()
        return public.getJson(data)

    def diskInfoApi(self):
        diskInfo = self.getDiskInfo()
        return public.getJson(diskInfo)

    def setControlApi(self):
        stype = request.form.get('type', '')
        day = request.form.get('day', '')
        data = self.setControl(stype, day)
        return data

    def getLoadAverageApi(self):
        start = request.args.get('start', '')
        end = request.args.get('end', '')
        data = self.getLoadAverageData(start, end)
        return public.getJson(data)

    def getCpuIoApi(self):
        start = request.args.get('start', '')
        end = request.args.get('end', '')
        data = self.getCpuIoData(start, end)
        return public.getJson(data)

    def getDiskIoApi(self):
        start = request.args.get('start', '')
        end = request.args.get('end', '')
        data = self.getDiskIoData(start, end)
        return public.getJson(data)

    def getNetworkIoApi(self):
        start = request.args.get('start', '')
        end = request.args.get('end', '')
        data = self.getNetWorkIoData(start, end)
        return public.getJson(data)

    def rememoryApi(self):
        os.system('sync')
        scriptFile = public.getRunDir() + '/script/rememory.sh'
        public.execShell("/bin/bash " + scriptFile)
        data = self.getMemInfo()
        return public.getJson(data)

    # 重启面板
    def restartApi(self):
        self.restartMw()
        return public.returnJson(True, '面板已重启!')

    def restartServerApi(self):
        if public.isAppleSystem():
            return public.returnJson(False, "开发环境不可重起")
        self.restartServer()
        return public.returnJson(True, '正在重启服务器!')
    ##### ----- end ----- ###

    @async
    def restartMw(self):
        sleep(1)
        cmd = public.getRunDir() + '/scripts/init.d/mw reload'
        public.execShell(cmd)

    @async
    def restartServer(self):
        if not public.isRestart():
            return public.returnJson(False, '请等待所有安装任务完成再执行!')
        public.execShell("sync && init 6 &")
        return public.returnJson(True, '命令发送成功!')

        # 名取PID
    def getPid(self, pname):
        try:
            if not self.pids:
                self.pids = psutil.pids()
            for pid in self.pids:
                if psutil.Process(pid).name() == pname:
                    return True
            return False
        except:
            return False

    # 检查端口是否占用
    def isOpen(self, port):
        import socket
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.connect(('127.0.0.1', int(port)))
            s.shutdown(2)
            return True
        except:
            return False

    # 检测指定进程是否存活
    def checkProcess(self, pid):
        try:
            if not self.pids:
                self.pids = psutil.pids()
            if int(pid) in self.pids:
                return True
            return False
        except:
            return False

    def getPanelInfo(self, get=None):
        # 取面板配置
        address = public.GetLocalIp()
        try:
            try:
                port = web.ctx.host.split(':')[1]
            except:
                port = public.readFile('data/port.pl')
        except:
            port = '8888'
        domain = ''
        if os.path.exists('data/domain.conf'):
            domain = public.readFile('data/domain.conf')

        autoUpdate = ''
        if os.path.exists('data/autoUpdate.pl'):
            autoUpdate = 'checked'
        limitip = ''
        if os.path.exists('data/limitip.conf'):
            limitip = public.readFile('data/limitip.conf')

        templates = []
        for template in os.listdir('templates/'):
            if os.path.isdir('templates/' + template):
                templates.append(template)
        template = public.readFile('data/templates.pl')

        check502 = ''
        if os.path.exists('data/502Task.pl'):
            check502 = 'checked'
        return {'port': port, 'address': address, 'domain': domain, 'auto': autoUpdate, '502': check502, 'limitip': limitip, 'templates': templates, 'template': template}

    def getSystemTotal(self, interval=1):
        # 取系统统计信息
        data = self.getMemInfo()
        cpu = self.getCpuInfo(interval)
        data['cpuNum'] = cpu[1]
        data['cpuRealUsed'] = cpu[0]
        data['time'] = self.getBootTime()
        data['system'] = self.getSystemVersion()
        data['isuser'] = public.M('users').where(
            'username=?', ('admin',)).count()
        data['version'] = '0.0.1'
        return data

    def getLoadAverage(self):
        c = os.getloadavg()
        data = {}
        data['one'] = float(c[0])
        data['five'] = float(c[1])
        data['fifteen'] = float(c[2])
        data['max'] = psutil.cpu_count() * 2
        data['limit'] = data['max']
        data['safe'] = data['max'] * 0.75
        return data

    def getAllInfo(self, get):
        data = {}
        data['load_average'] = self.GetLoadAverage(get)
        data['title'] = self.GetTitle()
        data['network'] = self.GetNetWorkApi(get)
        data['panel_status'] = not os.path.exists(
            '/www/server/mdserver-web/data/close.pl')
        import firewalls
        ssh_info = firewalls.firewalls().GetSshInfo(None)
        data['enable_ssh_status'] = ssh_info['status']
        data['disable_ping_status'] = not ssh_info['ping']
        data['time'] = self.GetBootTime()
        # data['system'] = self.GetSystemVersion();
        # data['mem'] = self.GetMemInfo();
        data['version'] = web.ctx.session.version
        return data

    def getTitle(self):
        titlePl = 'data/title.pl'
        title = 'Linux面板'
        if os.path.exists(titlePl):
            title = public.readFile(titlePl).strip()
        return title

    def getSystemVersion(self):
        # 取操作系统版本
        if public.getOs() == 'darwin':
            data = public.execShell('sw_vers')[0]
            data_list = data.strip().split("\n")
            mac_version = ''
            for x in data_list:
                mac_version += x.split("\t")[1] + ' '
            return mac_version

        version = public.readFile('/etc/redhat-release')
        if not version:
            version = public.readFile(
                '/etc/issue').strip().split("\n")[0].replace('\\n', '').replace('\l', '').strip()
        else:
            version = version.replace('release ', '').strip()
        return version

    def getBootTime(self):
        # 取系统启动时间
        start_time = psutil.boot_time()
        run_time = time.time() - start_time
        # conf = public.readFile('/proc/uptime').split()
        tStr = float(run_time)
        min = tStr / 60
        hours = min / 60
        days = math.floor(hours / 24)
        hours = math.floor(hours - (days * 24))
        min = math.floor(min - (days * 60 * 24) - (hours * 60))
        return public.getInfo('已不间断运行: {1}天{2}小时{3}分钟', (str(int(days)), str(int(hours)), str(int(min))))

    def getCpuInfo(self, interval=1):
        # 取CPU信息
        cpuCount = psutil.cpu_count()
        used = psutil.cpu_percent(interval=interval)
        return used, cpuCount

    def getMemInfo(self):
        # 取内存信息
        mem = psutil.virtual_memory()
        if public.getOs() == 'darwin':
            memInfo = {
                'memTotal': mem.total / 1024 / 1024
            }
            memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100)
        else:
            memInfo = {
                'memTotal': mem.total / 1024 / 1024,
                'memFree': mem.free / 1024 / 1024,
                'memBuffers': mem.buffers / 1024 / 1024,
                'memCached': mem.cached / 1024 / 1024
            }

            memInfo['memRealUsed'] = memInfo['memTotal'] - \
                memInfo['memFree'] - memInfo['memBuffers'] - \
                memInfo['memCached']
        return memInfo

    def getMemUsed(self):
        # 取内存使用率
        try:
            import psutil
            mem = psutil.virtual_memory()

            if public.getOs() == 'darwin':
                return mem.percent

            memInfo = {'memTotal': mem.total / 1024 / 1024, 'memFree': mem.free / 1024 / 1024,
                       'memBuffers': mem.buffers / 1024 / 1024, 'memCached': mem.cached / 1024 / 1024}
            tmp = memInfo['memTotal'] - memInfo['memFree'] - \
                memInfo['memBuffers'] - memInfo['memCached']
            tmp1 = memInfo['memTotal'] / 100
            return (tmp / tmp1)
        except Exception, ex:
            return 1

    def getDiskInfo(self, get=None):
        return self.getDiskInfo2()
        # 取磁盘分区信息
        diskIo = psutil.disk_partitions()
        diskInfo = []

        for disk in diskIo:
            if disk[1] == '/mnt/cdrom':
                continue
            if disk[1] == '/boot':
                continue
            tmp = {}
            tmp['path'] = disk[1]
            tmp['size'] = psutil.disk_usage(disk[1])
            diskInfo.append(tmp)
        return diskInfo

    def getDiskInfo2(self):
        # 取磁盘分区信息
        temp = public.execShell(
            "df -h -P|grep '/'|grep -v tmpfs | grep -v devfs")[0]
        tempInodes = public.execShell(
            "df -i -P|grep '/'|grep -v tmpfs | grep -v devfs")[0]
        temp1 = temp.split('\n')
        tempInodes1 = tempInodes.split('\n')
        diskInfo = []
        n = 0
        cuts = ['/mnt/cdrom', '/boot', '/boot/efi', '/dev',
                '/dev/shm', '/run/lock', '/run', '/run/shm', '/run/user']
        for tmp in temp1:
            n += 1
            inodes = tempInodes1[n - 1].split()
            disk = tmp.split()
            if len(disk) < 5:
                continue
            if disk[1].find('M') != -1:
                continue
            if disk[1].find('K') != -1:
                continue
            if len(disk[5].split('/')) > 4:
                continue
            if disk[5] in cuts:
                continue
            arr = {}
            arr['path'] = disk[5]
            tmp1 = [disk[1], disk[2], disk[3], disk[4]]
            arr['size'] = tmp1
            arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]]
            if disk[5] == '/':
                bootLog = os.getcwd() + '/tmp/panelBoot.pl'
                if disk[2].find('M') != -1:
                    if os.path.exists(bootLog):
                        os.system('rm -f ' + bootLog)
                else:
                    if not os.path.exists(bootLog):
                        pass
            if inodes[2] != '0':
                diskInfo.append(arr)
        return diskInfo

    # 清理系统垃圾
    def clearSystem(self, get):
        count = total = 0
        tmp_total, tmp_count = self.ClearMail()
        count += tmp_count
        total += tmp_total
        tmp_total, tmp_count = self.ClearOther()
        count += tmp_count
        total += tmp_total
        return count, total

    # 清理邮件日志
    def clearMail(self):
        rpath = '/var/spool'
        total = count = 0
        import shutil
        con = ['cron', 'anacron', 'mail']
        for d in os.listdir(rpath):
            if d in con:
                continue
            dpath = rpath + '/' + d
            time.sleep(0.2)
            num = size = 0
            for n in os.listdir(dpath):
                filename = dpath + '/' + n
                fsize = os.path.getsize(filename)
                size += fsize
                if os.path.isdir(filename):
                    shutil.rmtree(filename)
                else:
                    os.remove(filename)
                print '\t\033[1;32m[OK]\033[0m'
                num += 1
            total += size
            count += num
        return total, count

    # 清理其它
    def clearOther(self):
        clearPath = [
            {'path': '/www/server/mdserver-web', 'find': 'testDisk_'},
            {'path': '/www/wwwlogs', 'find': 'log'},
            {'path': '/tmp', 'find': 'panelBoot.pl'},
            {'path': '/www/server/mdserver-web/install', 'find': '.rpm'}
        ]

        total = count = 0
        for c in clearPath:
            for d in os.listdir(c['path']):
                if d.find(c['find']) == -1:
                    continue
                filename = c['path'] + '/' + d
                fsize = os.path.getsize(filename)
                total += fsize
                if os.path.isdir(filename):
                    shutil.rmtree(filename)
                else:
                    os.remove(filename)
                count += 1
        public.serviceReload()
        os.system('echo > /tmp/panelBoot.pl')
        return total, count

    def getNetWork(self):
        # return self.GetNetWorkApi(get);
        # 取网络流量信息
        try:
            # 取网络流量信息
            networkIo = psutil.net_io_counters()[:4]
            if not "otime" in session:
                session['up'] = networkIo[0]
                session['down'] = networkIo[1]
                session['otime'] = time.time()

            ntime = time.time()
            networkInfo = {}
            networkInfo['upTotal'] = networkIo[0]
            networkInfo['downTotal'] = networkIo[1]
            networkInfo['up'] = round(float(
                networkIo[0] - session['up']) / 1024 / (ntime - session['otime']), 2)
            networkInfo['down'] = round(
                float(networkIo[1] - session['down']) / 1024 / (ntime - session['otime']), 2)
            networkInfo['downPackets'] = networkIo[3]
            networkInfo['upPackets'] = networkIo[2]

            # print networkIo[1], session['down'], ntime, session['otime']
            session['up'] = networkIo[0]
            session['down'] = networkIo[1]
            session['otime'] = time.time()

            networkInfo['cpu'] = self.getCpuInfo()
            networkInfo['load'] = self.getLoadAverage()
            networkInfo['mem'] = self.getMemInfo()

            return networkInfo
        except Exception, e:
            print e
            return None

    def getNetWorkApi(self):
        # 取网络流量信息
        try:
            tmpfile = 'data/network.temp'
            networkIo = psutil.net_io_counters()[:4]

            if not os.path.exists(tmpfile):
                public.writeFile(tmpfile, str(
                    networkIo[0]) + '|' + str(networkIo[1]) + '|' + str(int(time.time())))

            lastValue = public.readFile(tmpfile).split('|')

            ntime = time.time()
            networkInfo = {}
            networkInfo['upTotal'] = networkIo[0]
            networkInfo['downTotal'] = networkIo[1]
            networkInfo['up'] = round(
                float(networkIo[0] - int(lastValue[0])) / 1024 / (ntime - int(lastValue[2])), 2)
            networkInfo['down'] = round(
                float(networkIo[1] - int(lastValue[1])) / 1024 / (ntime - int(lastValue[2])), 2)
            networkInfo['downPackets'] = networkIo[3]
            networkInfo['upPackets'] = networkIo[2]

            public.writeFile(tmpfile, str(
                networkIo[0]) + '|' + str(networkIo[1]) + '|' + str(int(time.time())))

            # networkInfo['cpu'] = self.GetCpuInfo(0.1)
            return networkInfo
        except:
            return None

    def getNetWorkIoData(self, start, end):
        # 取指定时间段的网络Io
        data = public.M('network').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field(
            'id,up,down,total_up,total_down,down_packets,up_packets,addtime').order('id asc').select()
        return self.toAddtime(data)

    def getDiskIoData(self, start, end):
        # 取指定时间段的磁盘Io
        data = public.M('diskio').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field(
            'id,read_count,write_count,read_bytes,write_bytes,read_time,write_time,addtime').order('id asc').select()
        return self.toAddtime(data)

    def getCpuIoData(self, start, end):
        # 取指定时间段的CpuIo
        data = public.M('cpuio').dbfile('system').where("addtime>=? AND addtime<=?",
                                                        (start, end)).field('id,pro,mem,addtime').order('id asc').select()
        return self.toAddtime(data, True)

    def getLoadAverageData(self, start, end):
        data = public.M('load_average').dbfile('system').where("addtime>=? AND addtime<=?", (
            start, end)).field('id,pro,one,five,fifteen,addtime').order('id asc').select()
        return self.toAddtime(data)

    # 格式化addtime列
    def toAddtime(self, data, tomem=False):
        import time
        if tomem:
            import psutil
            mPre = (psutil.virtual_memory().total / 1024 / 1024) / 100
        length = len(data)
        he = 1
        if length > 100:
            he = 1
        if length > 1000:
            he = 3
        if length > 10000:
            he = 15
        if he == 1:
            for i in range(length):
                data[i]['addtime'] = time.strftime(
                    '%m/%d %H:%M', time.localtime(float(data[i]['addtime'])))
                if tomem and data[i]['mem'] > 100:
                    data[i]['mem'] = data[i]['mem'] / mPre

            return data
        else:
            count = 0
            tmp = []
            for value in data:
                if count < he:
                    count += 1
                    continue
                value['addtime'] = time.strftime(
                    '%m/%d %H:%M', time.localtime(float(value['addtime'])))
                if tomem and value['mem'] > 100:
                    value['mem'] = value['mem'] / mPre
                tmp.append(value)
                count = 0
            return tmp

    def setControl(self, stype, day):

        filename = 'data/control.conf'

        if stype == '0':
            public.execShell("rm -f " + filename)
        elif stype == '1':
            _day = int(day)
            if _day < 1:
                return public.returnJson(False, "设置失败!")
            public.writeFile(filename, day)
        elif stype == 'del':
            if not public.isRestart():
                return public.returnJson(False, '请等待所有安装任务完成再执行')
            os.remove("data/system.db")

            sql = db.Sql().dbfile('system')
            csql = public.readFile('data/sql/system.sql')
            csql_list = csql.split(';')
            for index in range(len(csql_list)):
                sql.execute(csql_list[index], ())
            return public.returnJson(True, "监控服务已关闭")
        else:
            data = {}
            if os.path.exists(filename):
                try:
                    data['day'] = int(public.readFile(filename))
                except:
                    data['day'] = 30
                data['status'] = True
            else:
                data['day'] = 30
                data['status'] = False
            return public.getJson(data)

        return public.returnJson(True, "设置成功!")

    def versionDiff(self, old, new):
        '''
            test 测试
            new 有新版本
            none 没有新版本
        '''
        new_list = new.split('.')
        if len(new_list) > 3:
            return 'test'

        old_list = old.split('.')
        ret = 'none'
        for i in range(len(old_list)):
            tm_new = int(new_list[i])
            tm_old = int(old_list[i])
            if tm_new > tm_old:
                return 'new'
        return ret

    def getServerInfo(self):
        upAddr = 'https://raw.githubusercontent.com/midoks/mdserver-web/master/version/info.json'
        try:
            version = public.httpGet(upAddr)
            version = json.loads(version)
            return version[0]
        except Exception as e:
            print 'getServerInfo', e
        return {}

    def updateServer(self, stype, version=''):
        # 更新服务
        try:
            if not public.isRestart():
                return public.returnJson(False, '请等待所有安装任务完成再执行!')

            if stype == 'check':
                version_now = config_api.config_api().getVersion()
                version_new_info = self.getServerInfo()
                if not 'version' in version_new_info:
                    return public.returnJson(False, '服务器数据有问题!')

                diff = self.versionDiff(
                    version_now, version_new_info['version'])
                if diff == 'new':
                    return public.returnJson(True, '有新版本!', version_new_info['version'])
                elif diff == 'test':
                    return public.returnJson(True, '有测试版本!', version_new_info['version'])
                else:
                    return public.returnJson(False, '已经是最新,无需更新!')

            if stype == 'info':
                version_new_info = self.getServerInfo()
                version_now = config_api.config_api().getVersion()

                if not 'version' in version_new_info:
                    return public.returnJson(False, '服务器数据有问题!')
                diff = self.versionDiff(
                    version_now, version_new_info['version'])
                return public.returnJson(True, '更新信息!', version_new_info)

            if stype == 'update':
                if version == '':
                    return public.returnJson(False, '缺少版本信息!')

                v_new_info = self.getServerInfo()
                if v_new_info['version'] != version:
                    return public.returnJson(False, '更新失败,请重试!')

                if not 'path' in v_new_info or v_new_info['path'] == '':
                    return public.returnJson(False, '下载地址不存在!')

                newUrl = v_new_info['path']
                toPath = public.getRootDir() + '/temp'
                if not os.path.exists(toPath):
                    public.execShell('mkdir -p ' + toPath)

                if not os.path.exists(toPath + '/mw.zip'):
                    public.execShell('wget -O ' + toPath + '/mw.zip ' + newUrl)

                public.execShell('unzip -o ' + toPath + '/mw.zip' + ' -d ./')
                public.execShell('unzip -o mdserver-web.zip -d ./')
                public.execShell('rm -f mdserver-web.zip')
                return public.returnJson(True, '安装更新成功!')

            return public.returnJson(False, '已经是最新,无需更新!')
        except Exception as ex:
            print 'updateServer', ex
            return public.returnJson(False, "连接服务器失败!")

    # 修复面板
    def repPanel(self, get):
        vp = ''
        if public.readFile('/www/server/mdserver-web/class/common.py').find('checkSafe') != -1:
            vp = '_pro'
        public.ExecShell("wget -O update.sh " + public.get_url() +
                         "/install/update" + vp + ".sh && bash update.sh")
        if hasattr(web.ctx.session, 'getCloudPlugin'):
            del(web.ctx.session['getCloudPlugin'])
        return True
