#coding: utf-8
# +-------------------------------------------------------------------
# | 宝塔Windows面板
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2016 宝塔软件(http://www.bt.cn) All rights reserved.
# +-------------------------------------------------------------------
# | Author: 沐落 <cjx@bt.cn>
# +-------------------------------------------------------------------

import psutil,time,os,public,re,shutil,sys
panelPath = os.getenv('BT_PANEL')
os.chdir(panelPath)
sys.path.insert(0,panelPath + "/class/")

try:
    from BTPanel import session,cache
except :
    pass
class system:

    ssh = None
    shell = None
    setupPath = None

    def __init__(self):
        self.setupPath = public.GetConfigValue('setup_path');

    def GetConcifInfo(self,get = None):
        """
        取环境配置信息
        """
        if not 'config' in session:
            session['config'] = public.M('config').where("id=?",('1',)).field('webserver,sites_path,backup_path,status,mysql_root').find();

        if not 'email' in session['config']:
            session['config']['email'] = public.M('users').where("id=?",('1',)).getField('email')
        data = {}
        data = session['config']
        data['webserver'] = session['config']['webserver']

        #PHP版本
        phpVersions = ('52','53','54','55','56','70','71','72','73','74')
        data['php'] = []

        for version in phpVersions:
            tmp = {}
            tmp['setup'] = os.path.exists(self.setupPath + '/php/'+version+'/php.exe');
            if tmp['setup']:
                phpConfig = self.GetPHPConfig(version)
                tmp['version'] = version
                tmp['max'] = phpConfig['max']
                tmp['maxTime'] = phpConfig['maxTime']
                tmp['pathinfo'] = phpConfig['pathinfo']
                tmp['status'] = True
                data['php'].append(tmp)
        #mysql
        tmp = {'setup':False,'setup':False,'status':False,'version':''}
        server_status = public.get_server_status('mysql')
        if  server_status >= 0:
            tmp['setup'] = True
            try:
                tmp['version'] = re.search('([MySQL|MariaDB-]+\d+\.\d+)',public.get_server_path('mysql')).groups()[0]
            except :
                tmp['version'] = '未知版本'
            if server_status > 0: tmp['status'] = True
        data['mysql'] = tmp

        #sqlserver
        tmp = {'setup':False,'setup':False,'status':False,'version':''}
        server_status = public.get_server_status('MSSQLSERVER')
        if  server_status >= 0:
            tmp['setup'] = True
            tmp['version'] = public.readFile(self.setupPath  + '/sqlserver/version.pl')
            if server_status > 0: tmp['status'] = True
        data['sqlserver'] = tmp

        tmp = {'setup':False,'setup':False,'status':False,'version':''}
        server_status = public.get_server_status('FileZilla Server')
        if  server_status >= 0:
            tmp['setup'] = True
            tmp['version'] = public.readFile(self.setupPath  + '/ftpServer/version.pl')
            if server_status > 0: tmp['status'] = True
        data['ftp'] = tmp

        data['status'] = True
        data['panel'] = self.GetPanelInfo()
        data['systemdate'] =time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        data['show_workorder'] = not os.path.exists('data/not_workorder.pl')
        return data


    def GetPanelInfo(self,get=None):
        """
        取面板详细配置
        """
        address = public.GetLocalIp()
        try:
            port = public.GetHost(True)
        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')
        admin_path = '/'
        if os.path.exists('data/admin_path.pl'): admin_path = public.readFile('data/admin_path.pl').strip()

        templates = []
        template = public.GetConfigValue('template')

        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,'admin_path':admin_path}


    def GetPHPConfig(self,version):
        """
        取PHP配置
        """
        file = self.setupPath + "/php/"+version+"/php.ini"
        phpini = public.readFile(file)
        data = {}
        try:
            rep = "upload_max_filesize\s*=\s*([0-9]+)M"
            tmp = re.search(rep,phpini).groups()
            data['max'] = tmp[0]
        except:
            data['max'] = '50'

        data['maxTime'] = 0

        try:
            rep = r"\n;*\s*cgi\.fix_pathinfo\s*=\s*([0-9]+)\s*\n"
            tmp = re.search(rep,phpini).groups()
            if tmp[0] == '1':
                data['pathinfo'] = True
            else:
                data['pathinfo'] = False
        except:
            data['pathinfo'] = False

        return data


    def GetSystemTotal(self,get,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'] = public.version();
        return data

    """

    """
    def GetLoadAverage(self,get):
        """
        获取服务器负载状态
        """
        #c = os.getloadavg()
        data = {};
        data['one'] = 0;
        data['five'] = 0
        data['fifteen'] = 0
        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['cpu'] = self.GetCpuInfo(1);
        data['time'] = self.GetBootTime();
        data['system'] = self.GetSystemVersion();
        data['mem'] = self.GetMemInfo();
        data['version'] = session['version'];
        return data;

    def GetTitle(self):
        """
        获取面板标题
        """
        return public.GetConfigValue('title')

    def get_registry_value(self, key, subkey, value):
        """
        读取注册表信息
        @key 注册表类型
        @subkey 注册表路径
        @value 注册表具体key值
        """
        import winreg
        key = getattr(winreg, key)
        handle = winreg.OpenKey(key, subkey)
        (value, type) = winreg.QueryValueEx(handle, value)
        return value

    def GetSystemVersion(self):
        """
        取操作系统版本
        """
        try:
            import public,platform
            bit = 'x86';
            if public.is_64bitos(): bit = 'x64'

            def get(key):
                return self.get_registry_value("HKEY_LOCAL_MACHINE", "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", key)
            os = get("ProductName")
            build = get("CurrentBuildNumber")

            version = "%s (build %s) %s (Py%s)" % (os, build,bit,platform.python_version())
            return version
        except Exception as ex:
            public.WriteLog('获取系统版', '获取系统版本失败,注册表无法打开,错误详情 ->>' + str(ex));
            return '未知系统版本.'


    def GetBootTime(self):
        """
        取系统启动时间
        """
        import public,math

        tStr = time.time() - psutil.boot_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.getMsg('SYS_BOOT_TIME',(str(int(days)),str(int(hours)),str(int(min))))

    def get_cpu_percent_thead(self,interval):

        tmp = cache.get('cpu_used_write')
        if not tmp:
            used = psutil.cpu_percent(interval)
            cache.set('cpu_used_all',used,10)
            cache.set('cpu_used_write',1,1)
        else:
            used = cache.get('cpu_used_all')
        return used



    def GetCpuInfo(self,interval = 1):
        """
        取CPU详细信息
        """
        cpuCount = psutil.cpu_count()
        cpuNum = psutil.cpu_count(logical=False)

        import threading
        p = threading.Thread(target=self.get_cpu_percent_thead,args=(interval,))
        #p.setDaemon(True)
        p.start()

        used = cache.get('cpu_used_all')
        if not used: used = self.get_cpu_percent_thead(interval)

        used_all = psutil.cpu_percent(percpu=True)

        used_total = 0
        for x in used_all: used_total += x

        if used_total == cpuCount * 100:
            used_all = psutil.cpu_percent(percpu=True)

            used_total = 0
            for x in used_all: used_total += x

        ret = public.ExecShell('wmic cpu get NumberOfCores')[0]
        cpuW = 0

        arrs = ret.strip().split('\r\n')
        for x in arrs:
            val = x.strip()
            if not val: continue;
            try:
                val = int(val)
                cpuW += 1;
            except : pass
        try:
            cpu_name = '{} * {}'.format(public.ReadReg(r'HARDWARE\DESCRIPTION\System\CentralProcessor\0','ProcessorNameString').strip(),cpuW);
        except :
            cpu_name = ''

        tmp = 0
        if cpuW :tmp = cpuNum / cpuW

        used = 0
        if used_total:  used = round(used_total / cpuCount,2)
        return used,cpuCount,used_all,cpu_name,tmp ,cpuW

    def get_process_cpu_time(self):
        """
        获取进程cpu使用时间
        """
        pids = psutil.pids()
        cpu_time = 0.00;
        for pid in pids:
            try:
                cpu_times = psutil.Process(pid).cpu_times()
                for s in cpu_times: cpu_time += s
            except:continue;
        return cpu_time;

    def get_cpu_time(self):
        """
        获取CPU时间（计算cpu使用率）
        """
        cpu_time = 0.00
        cpu_times = psutil.cpu_times()
        for s in cpu_times: cpu_time += s
        return cpu_time;

    def GetMemInfo(self,get=None):
        """
        获取内存使用状态
        """
        mem = psutil.virtual_memory()
        memInfo = {'memTotal':int(mem.total/1024/1024),'memFree':int(mem.free/1024/1024),'memRealUsed':int(mem.used/1024/1024)}

        return memInfo

    def GetDiskInfo(self,get=None):
        """
        取磁盘分区信息
        """
        try:
            diskIo = psutil.disk_partitions();
        except :
            import string
            diskIo = []
            for c in string.ascii_uppercase:
                disk = c + ':'
                if os.path.isdir(disk):
                    data = public.dict_obj()
                    data['mountpoint'] = disk+ '/'
                    diskIo.append(data)

        diskInfo = []
        for disk in diskIo:
            try:
                tmp = {}
                tmp['path'] = disk.mountpoint.replace("\\","/")
                usage = psutil.disk_usage(disk.mountpoint)
                tmp['size'] = [public.to_size(usage.total),public.to_size(usage.used),public.to_size(usage.free),str(usage.percent) + '%']
                tmp['inodes'] = False
                diskInfo.append(tmp)
            except :pass
        return diskInfo

    def GetNetWork(self,get=None):

        cache_timeout = 86400
        otime = cache.get("otime")
        ntime = time.time()
        networkInfo = {}
        networkInfo['network'] = {}
        networkInfo['upTotal'] = 0
        networkInfo['downTotal'] = 0
        networkInfo['up'] = 0
        networkInfo['down'] = 0
        networkInfo['downPackets'] = 0
        networkInfo['upPackets'] = 0
        networkIo_list = psutil.net_io_counters(pernic = True)


        for net_key in networkIo_list.keys():
            if net_key.find('Loopback') >= 0 or net_key.find('Teredo') >= 0 or net_key.find('isatap') >= 0: continue

            networkIo = networkIo_list[net_key][:4]
            up_key = "{}_up".format(net_key)
            down_key = "{}_down".format(net_key)
            otime_key = "otime"

            if not otime:
                otime = time.time()

                cache.set(up_key,networkIo[0],cache_timeout)
                cache.set(down_key,networkIo[1],cache_timeout)
                cache.set(otime_key,otime ,cache_timeout)

            networkInfo['network'][net_key] = {}
            up = cache.get(up_key)
            down = cache.get(down_key)
            if not up:
                up = networkIo[0]
            if not down:
                down = networkIo[1]
            networkInfo['network'][net_key]['upTotal']   = networkIo[0]
            networkInfo['network'][net_key]['downTotal'] = networkIo[1]
            try:
                networkInfo['network'][net_key]['up']        = round(float(networkIo[0] -  up) / 1024 / (ntime - otime),2)
                networkInfo['network'][net_key]['down']      = round(float(networkIo[1] - down) / 1024 / (ntime -  otime),2)
            except :
                networkInfo['up'] = 0
                networkInfo['down'] = 0

                networkInfo['network'][net_key]['up'] = 0
                networkInfo['network'][net_key]['down'] = 0

            networkInfo['network'][net_key]['downPackets'] = networkIo[3]
            networkInfo['network'][net_key]['upPackets']   = networkIo[2]

            networkInfo['upTotal'] += networkInfo['network'][net_key]['upTotal']
            networkInfo['downTotal'] += networkInfo['network'][net_key]['downTotal']
            networkInfo['up'] += networkInfo['network'][net_key]['up']
            networkInfo['down'] += networkInfo['network'][net_key]['down']
            networkInfo['downPackets'] += networkInfo['network'][net_key]['downPackets']
            networkInfo['upPackets'] += networkInfo['network'][net_key]['upPackets']

            cache.set(up_key,networkIo[0],cache_timeout)
            cache.set(down_key,networkIo[1],cache_timeout)
            cache.set(otime_key, time.time(),cache_timeout)

        if get != False:
            networkInfo['cpu'] = self.GetCpuInfo(1)
            networkInfo['load'] = self.GetLoadAverage(get)
            networkInfo['mem'] = self.GetMemInfo(get)
            networkInfo['version'] = session['version']
            networkInfo['disk'] = self.GetDiskInfo()

        networkInfo['title'] = self.GetTitle()
        networkInfo['time'] = self.GetBootTime()
        networkInfo['site_total'] = public.M('sites').count()
        networkInfo['ftp_total'] = public.M('ftps').count()
        networkInfo['database_total'] = public.M('databases').count()
        networkInfo['system'] = self.GetSystemVersion()
        networkInfo['installed'] = self.CheckInstalled()
        import panelSSL
        networkInfo['user_info'] = panelSSL.panelSSL().GetUserInfo(None)
        networkInfo['up'] = round(float(networkInfo['up']),2)
        networkInfo['down'] = round(float(networkInfo['down']),2)

        return networkInfo

    #检查是否安装任何
    def CheckInstalled(self,get = None):
        checks = ['nginx','apache','W3SVC','FileZilla Server','mysql','MSSQLSERVER','phpmyadmin','php'];
        import os
        for name in checks:
            if name == 'phpmyadmin':
                filename = os.getenv("BT_SETUP") + '/' + name
                if os.path.exists(filename): return True;
            elif name == 'php':
                filename = os.getenv("BT_SETUP") + '/' + name
                if os.path.exists(filename):
                    dirs = os.listdir(filename)
                    if len(dirs) > 0: return True;
            else:
                status = public.get_server_status(name)
                if status >= 0:
                    if name == 'W3SVC':
                        if os.path.exists('data/iis.setup'):
                            return True;
                    else:
                        return True;
        return False;

    def GetNetWorkApi(self,get=None):
        return self.GetNetWork()

    def get_io_info(self,get = None):
        """
        取IO读写信息
        """
        io_disk = psutil.disk_io_counters()
        ioTotal = {}
        ioTotal['write'] = self.get_io_write(io_disk.write_bytes)
        ioTotal['read'] = self.get_io_read(io_disk.read_bytes)
        return ioTotal


    def get_io_write(self,io_write):
        """
         取IO写
        """
        disk_io_write = 0
        old_io_write = cache.get('io_write')
        if not old_io_write:
            cache.set('io_write',io_write)
            return disk_io_write;

        old_io_time = cache.get('io_time')
        new_io_time = time.time()
        if not old_io_time: old_io_time = new_io_time
        io_end = (io_write - old_io_write)
        time_end = (time.time() - old_io_time)
        if io_end > 0:
            if time_end < 1: time_end = 1;
            disk_io_write = io_end / time_end;
        cache.set('io_write',io_write)
        cache.set('io_time',new_io_time)
        if disk_io_write > 0: return int(disk_io_write)
        return 0


    def get_io_read(self,io_read):
        """
        取IO读
        """
        disk_io_read = 0
        old_io_read = cache.get('io_read')
        if not old_io_read:
            cache.set('io_read',io_read)
            return disk_io_read;
        old_io_time = cache.get('io_time')
        new_io_time = time.time()
        if not old_io_time: old_io_time = new_io_time
        io_end = (io_read - old_io_read)
        time_end = (time.time() - old_io_time)
        if io_end > 0:
            if time_end < 1: time_end = 1;
            disk_io_read = io_end / time_end;
        cache.set('io_read',io_read)
        if disk_io_read > 0: return int(disk_io_read)
        return 0

    def ServiceAdmin(self,get=None):
        """
        服务管理器
        @get.name 服务名称
        @get.type 操作（start/stop/reload/restart）
        """
        server_name = get.name
        server_type = get.type
        sdata = {"mysqld":"mysql","iis":"W3SVC","ftpserver":"FileZilla Server"}

        if server_name.find("php-") >=0:  server_name = public.get_webserver();

        if server_name == 'phpmyadmin':
            import ajax
            get.status = 'True';
            ajax.ajax().setPHPMyAdmin(get);
            return public.returnMsg(True,'SYS_EXEC_SUCCESS');
        elif server_name in sdata:
            server_name = sdata[server_name]

        if server_name in ['apache','nginx']:
            isError = public.checkWebConfig()
            if isError != True:
                if isError.find('0.0.0.0:80') >= 0:
                    os.system("net stop http /y")
                    isError = public.checkWebConfig()
                if isError != True:
                    return public.returnMsg(False,isError);

            if server_name == 'apache':
                os.system("net stop http /y")
                os.system("taskkill /f /im php-cgi.exe")
                if server_type in ['stop','restart','reload']:
                    ser = psutil.win_service_get(server_name)
                    if ser:
                        os.system('taskkill /pid {} -t -f'.format(ser.pid()))
                        time.sleep(0.2);

                if server_type == 'start':
                    os.system('net start apache')
                    if public.get_server_status(server_name) <= 0: return public.returnMsg(False,'启动失败，请通过cmd命令行启动：net start apache ');
                elif server_type == 'stop':
                    os.system('net stop apache')
                    if public.get_server_status(server_name) != 0: return public.returnMsg(False,'停止失败，请通过cmd命令行启动：net stop apache ');
                else:
                     os.system('net stop apache')
                     os.system('net start apache')
                     if public.get_server_status(server_name) <= 0: return public.returnMsg(False,'重启失败，请通过cmd命令行启动：net stop apache & net start apache');

                return public.returnMsg(True,'SYS_EXEC_SUCCESS');

        elif server_name == 'W3SVC':
            if server_type in ['restart','reload']:
                os.system("iisreset /restart")
            else:
                os.system("iisreset /{}".format(server_type))

            return public.returnMsg(True,'SYS_EXEC_SUCCESS');

        if server_name == 'sqlserver':
            import panelMssql
            sql_obj = panelMssql.panelMssql()
            server_name = sql_obj.get_sql_name()
            if public.get_server_status(server_name) >= 0:
                ret,error = public.set_server_status(server_name,server_type)
                if not ret: return public.returnMsg(False,error);
            else:
                return public.returnMsg(False,'未识别的SQL Server服务名');
        else:
            ret,error = public.set_server_status(server_name,server_type)
            if not ret: return public.returnMsg(False,error);

        return public.returnMsg(True,'SYS_EXEC_SUCCESS');


    def RestartServer(self,get):
        """
        重启系统
        """
        if not public.IsRestart(): return public.returnMsg(False,'EXEC_ERR_TASK');
        try:
            os.system("shutdown /r /f /t 0");
        except :pass

        return public.returnMsg(True,'SYS_REBOOT');


    def ReMemory(self,get):
        """
        释放内存
        """
        shell = '{}/script/BtTools clear_memory'.format(panelPath)
        os.system(shell)
        return self.GetMemInfo();


    def ReWeb(self,get):
        """
        重启面板
        """
        try:
            status,error = public.set_server_status("btTask","stop");
            if status:
                time.sleep(0.5)
                status,error = public.set_server_status("btTask","start");
        except : pass

        time.sleep(0.5)
        if public.get_server_status('btTask') > 0:
            public.writeFile('data/reload.pl','True')
            return public.returnMsg(True,'面板已重启')

        return public.returnMsg(False,'面板重启失败，请稍后重试.')


    def RepPanel(self,get):
        """
        修复面板
        """
        if not public.get_update_file():
            return public.returnMsg(False,"修复面板失败，更新脚本下载失败.");

        import panel_update
        update =  panel_update.panel_update()
        ret = update.UpdatePanel(public.version())
        if not ret['status']: return ret;

        ret = self.ReWeb(None)
        if ret['status']: return public.returnMsg(True,"修复面板成功，请按 Ctrl+F5 刷新缓存");

        return public.returnMsg(True,"修复面板成功，请手动重启面板后生效");


