import os, time
import threading
from StringIO import StringIO
import subprocess, shlex
import whatap.util.logging_util as logging_util
from whatap.agent.conf.configure import GetConfig
import whatap.util.thread_util as thread_util
import re
import psutil

from . import _DiskPerf

filesystems = { "btrfs": True, "ext2": True, "ext3": True, "ext4": True, "reiser": True, "xfs": True,
        "ffs": True, "ufs": True, "jfs": True, "jfs2": True, "vxfs": True, "hfs": True, "ntfs": True, "fat32": True,
        "zfs": True, "refs": True, "nfs": True, "nfs2": True, "nfs3": True, "nfs4": True, "cifs": True, "ocfs2": True }

zfsInfoCheck = False
_zfsUpdateStarted = False

class _PoolInfo:
    def __init__(self, name):
        self.name = name
        self.totalSpace = None
        self.diskList = []
        self.ioInfo = _IoInfo()
        self.oldTimeStamp = 0
        self.readIops = 0
        self.writeIops = 0
        self.readBps = 0
        self.writeBps = 0
        self.ioPercent = 0


    def setTotalSpace(self, totalSpace):
        self.totalSpace = totalSpace

    def setDisk(self, disk):
        self.diskList.append(disk)

    def initIoInfo(self):
        self.ioInfo.writeIoByte = 0
        self.ioInfo.writeIoCount = 0 
        self.ioInfo.readIoByte = 0
        self.ioInfo.readIoCount = 0 
        self.ioInfo.ioMillis = 0 

    def sumIoStats(self, newInfo):
        self.ioInfo.writeIoByte += newInfo.writeIoByte
        self.ioInfo.writeIoCount += newInfo.writeIoCount
        self.ioInfo.readIoByte += newInfo.readIoByte
        self.ioInfo.readIoCount += newInfo.readIoCount
        if self.ioInfo.ioMillis < newInfo.ioMillis:
            self.ioInfo.ioMillis = newInfo.ioMillis

    def calcIoStat(self, diffTime):
        self.writeBps = float(self.ioInfo.writeIoByte) / float(diffTime)
        self.writeIops = float(self.ioInfo.writeIoCount) / float(diffTime)
        self.readBps = float(self.ioInfo.readIoByte) / float(diffTime)
        self.readIops = float(self.ioInfo.readIoCount) / float(diffTime)
        self.ioPercent = float(self.ioInfo.ioMillis) / float(diffTime * 1000) * 100
        if self.ioPercent > float(100):
            self.ioPercent = float(100)
        self.initIoInfo()

            
 
class _DataSetInfo:
    def __init__(self, name):
        self.name = name
        self.pool = None
        self.used = 0
        self.avail = 0
        self.refer = 0
        self.quota = None
        self.refquota = None
        self.recordSize = 0

    def setDataSet(self, pool, used, avail, refer, quota, refquota, recordSize):
        self.pool = pool
        self.used = used
        self.avail = avail
        self.refer = refer
        self.quota = quota if quota != "none" and quota != "0" else None
        self.refquota = refquota if refquota != "none" and refquota != "0" else None
        self.recordSize = recordSize

    def setRecodeSize(self, recordSize):
        self.recordSize = recordSize


class _IoInfo:
    def __init__(self):
        self.writeIoByte= 0
        self.writeIoCount = 0
        self.readIoByte= 0
        self.readIoCount = 0
        self.readTime = 0
        self.writeTime = 0
        self.ioMillis = 0
        self.timestamp = 0

    def setIoInfo(self, writeIoByte, writeIoCount, readIoByte, readIoCount, ioMillis, timestamp):
        self.writeIoByte= writeIoByte
        self.writeIoCount = writeIoCount
        self.readIoByte= readIoByte
        self.readIoCount = readIoCount
        self.ioMillis = ioMillis
        self.timestamp = timestamp

def getDiskList():
    diskList = list(set(os.listdir("/dev/rdsk") + os.listdir("/dev/dsk")))
    return diskList

oldZfsDiskList = None
diskToPoolMap = None

def getZpoolInfo():
    global oldZfsDiskList
    diskList = getDiskList()

    if diskList == oldZfsDiskList:
        return None

    command = "zpool list -H -o name,size"
    try:
        process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        text = process.stdout.read()

        info_map = {}
        for line in text.strip('\n').split('\n'):
            name, size = line.split('\t')
            pool = _PoolInfo(name)
            pool.setTotalSpace(size)
            info_map[name] = pool

        info_map = getZpoolStatus(info_map, diskList)
        oldZfsDiskList = diskList
        return info_map
    except subprocess.CalledProcessError as e:
        logging_util.error("zpool list error  : {} - {}".format(type(e).__name__, e))
        return None
    except Exception as e:
        logging_util.error("zpool list error  : {} - {}".format(type(e).__name__, e))
        return None


globalDiskMap = None
def getDiskMap():
    global globalDiskMap
    if globalDiskMap:
        return globalDiskMap
    diskMap = {}
    #command = "paste -d, <(iostat -x | awk 'NR>2{print $1}') <(iostat -nx | awk 'NR>2{print $11}')"
    command = "tmp1=/tmp/tmpfile1 tmp2=/tmp/tmpfile2 && iostat -x | awk 'NR>2{print $1}' > $tmp1 && iostat -nx | awk 'NR>2{print $11}' > $tmp2 && paste -d, $tmp1 $tmp2 && rm $tmp1 $tmp2"
    try:
        process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        text = process.stdout.read()
        for line in text.strip('\n').split('\n'):
            deviceName, deviceRealName = line.split(',')
            diskMap[deviceRealName] = deviceName
    except subprocess.CalledProcessError as e:
        logging_util.error("iostat error  : {} - {}".format(type(e).__name__, e))
        return None
    except Exception as e:
        logging_util.error("iostat error  : {} - {}".format(type(e).__name__, e))
        return None



    globalDiskMap = diskMap
    return diskMap

def getZpoolStatus(info_map, diskList):
    poolMap = {}
    global diskToPoolMap

    diskMap = getDiskMap()

    command = "zpool status"
    try:
        process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        text = process.stdout.read().strip('\n')
        poolKey = ""
        
        for line in re.split(r'[\n\t]+', text):
            status = [value for value in line.split(' ') if value]
            if status[0] in info_map.keys():
                poolKey = status[0]
            elif status[0] in diskList and poolKey in info_map:
                if poolKey == "": continue
                else: 
                    diskName = re.sub(r's\d+', '', status[0])
                    if diskName in diskMap:
                        info_map[poolKey].setDisk(diskName)
                        poolMap[diskMap[diskName]] = poolKey
        diskToPoolMap = poolMap
        return info_map
    except subprocess.CalledProcessError as e:
        logging_util.error("zpool status error  : {} - {}".format(type(e).__name__, e))
        return None
    except Exception as e:
        logging_util.error("zpool status error  : {} - {}".format(type(e).__name__, e))
        return None



def diffIoInfo(total, old):
    if total == None or old == None:
        return None
    ret = _IoInfo()
    ret.setIoInfo(total.writeIoByte - old.writeIoByte, total.writeIoCount- old.writeIoCount, total.readIoByte - old.readIoByte, total.readIoCount - old.readIoCount, total.ioMillis - old.ioMillis, total.timestamp - old.timestamp)
    ret.readTime = total.readTime - old.readTime
    ret.writeTime = total.writeTime - old.writeTime
    return ret

oldIOTotal = None
def getDiskIoInfo():
    ioStats = psutil.disk_io_counters()
    info = _IoInfo()
    now = time.time() 

    info.setIoInfo(ioStats.write_bytes, ioStats.write_count, ioStats.read_bytes, ioStats.read_count, ioStats.write_time + ioStats.read_time, now)

    global oldIOTotal
    if not oldIOTotal:
        oldIOTotal = info
        return

    diffIo = diffIoInfo(info, oldIOTotal)
    
    ret = _DiskPerf()
    diffTime = diffIo.timestamp
    ret.readBps = float(diffIo.readIoByte / diffTime)
    ret.writeBps = float(diffIo.writeIoByte / diffTime)
    ret.readIops = float(diffIo.readIoCount / diffTime)
    ret.writeIops = float(diffIo.writeIoCount / diffTime)

    oldIOTotal = info
    return ret

def getTotalDisk():
    disk = getDiskIoInfo()

#infra - combined: aggregate IO + busyMax/diskName in one perdisk call
oldIOTotalPerDisk = None
def GetDiskTotalIO():
    ioStats = psutil.disk_io_counters(perdisk=True)
    now = time.time()

    global oldIOTotalPerDisk
    if not oldIOTotalPerDisk:
        oldIOTotalPerDisk = (ioStats, now)
        return None

    oldStats, oldTime = oldIOTotalPerDisk
    diffTime = float(now - oldTime)
    if diffTime <= 0:
        return None

    total_read_bytes = 0
    total_write_bytes = 0
    total_read_count = 0
    total_write_count = 0
    max_busy = 0.0
    max_disk_name = ""

    for k, v in ioStats.items():
        if k not in oldStats:
            continue
        oldv = oldStats[k]
        total_read_bytes += v.read_bytes - oldv.read_bytes
        total_write_bytes += v.write_bytes - oldv.write_bytes
        total_read_count += v.read_count - oldv.read_count
        total_write_count += v.write_count - oldv.write_count

        busy_diff = float((v.read_time + v.write_time) - (oldv.read_time + oldv.write_time))
        busy_pct = 100.0 * busy_diff / (diffTime * 1000)
        if busy_pct > max_busy:
            max_busy = busy_pct
            max_disk_name = k

    ret = _DiskPerf()
    ret.readBps   = float(total_read_bytes) / diffTime
    ret.writeBps  = float(total_write_bytes) / diffTime
    ret.readIops  = float(total_read_count) / diffTime
    ret.writeIops = float(total_write_count) / diffTime
    ret.busyMax   = min(max_busy, 100.0)
    ret.diskName  = max_disk_name

    oldIOTotalPerDisk = (ioStats, now)
    return ret

oldIoInfoMapForInfra = None
def GetDiskIO():
    ioStats = psutil.disk_io_counters(perdisk=True)
    ioInfoMap = {}
    now = time.time() 

    for k, v in ioStats.items():
        info = _IoInfo()
        info.setIoInfo(v.write_bytes, v.write_count, v.read_bytes, v.read_count, v.write_time+ v.read_time, now)
        info.readTime = v.read_time
        info.writeTime = v.write_time
        ioInfoMap[k] = info

    global oldIoInfoMapForInfra
    if not oldIoInfoMapForInfra:
        oldIoInfoMapForInfra = ioInfoMap
        return None
    
    retArr = []
    for k, v in ioInfoMap.items():
        diffIo = diffIoInfo(v, oldIoInfoMapForInfra[k])
        if not diffIo:
            continue

        diffTime = diffIo.timestamp
        ret = _DiskPerf()
        ret.deviceId = k
        ret.readBps = float(diffIo.readIoByte) / float(diffTime)
        ret.writeBps = float(diffIo.writeIoByte) / float(diffTime)
        ret.readIops = float(diffIo.readIoCount) / float(diffTime)
        ret.writeIops = float(diffIo.writeIoCount) / float(diffTime)

        if diffIo.readIoCount == 0:
            ret.readTime = 0
        else:
            ret.readTime = float(diffIo.readTime) / float(diffIo.readIoCount)

        if diffIo.writeIoCount == 0:
            ret.writeTime = 0
        else:
            ret.writeTime = float(diffIo.writeTime) / float(diffIo.writeIoCount)

        ret.ioPercent = float(diffIo.ioMillis) / float(diffTime * 1000) * 100
        if ret.ioPercent > float(100):
            ret.ioPercent = float(100)

        retArr.append(ret)

    oldIoInfoMapForInfra = ioInfoMap
    return retArr
         

#server
oldIoInfoMap = None
def getDiskIoInfoPerDisk(diff):
    ioStats = psutil.disk_io_counters(perdisk=True)
    ioInfoMap = {}
    now = time.time() 

    for k, v in ioStats.items():
        info  = _IoInfo()
        info.setIoInfo(v.write_bytes, v.write_count, v.read_bytes, v.read_count, v.write_time+ v.read_time, now)
        ioInfoMap[k] = info

    if zpoolInfoMap == None:
        return 

    global oldIoInfoMap 
    if not oldIoInfoMap:
        oldIoInfoMap = ioInfoMap
        return

    for k, v in ioInfoMap.items():
        diffIo = diffIoInfo(v, oldIoInfoMap[k])
        if not diffIo:
            continue
        try:
            zpoolInfoMap[diskToPoolMap[k]].sumIoStats(diffIo)
        except KeyError, e:
            logging_util.debug("disk to pool error  : {} - {}".format(type(e).__name__, e))
            continue
        except TypeError, e:
            logging_util.debug("disk to pool error  : {} - {}".format(type(e).__name__, e))
            continue

    oldIoInfoMap = ioInfoMap
    for k, v in zpoolInfoMap.items():
        v.calcIoStat(diff)



def getZfsInfo():
    try:
        command = "zfs list -H -o name,used,avail,refer,quota,refquota,recordsize"
        process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        text = process.stdout.read()

        info_map = {}
        for line in text.strip('\n').split('\n'):
            name, used, avail, refer, quota, refquota, recordSize = line.split('\t')
            pool = name.split('/')[0]
            dataSet = _DataSetInfo(name)
            dataSet.setDataSet(pool, used, avail, refer, quota, refquota, recordSize)
            info_map[name] = dataSet
        return info_map
    except subprocess.CalledProcessError as e:
        logging_util.error("zfs list error  : {} - {}".format(type(e).__name__, e))
        return None
    except Exception as e:
        logging_util.error("zfs list error  : {} - {}".format(type(e).__name__, e))
        return None




zpoolInfoMap = None 
zfsInfoMap = None
oldTime = time.time()

def setZfsInfo():
    global oldTime
    now = time.time()

    global zpoolInfoMap 
    poolCheck = getZpoolInfo()
    if poolCheck != None:
        zpoolInfoMap = poolCheck

    global zfsInfoMap 
    zfsInfoMap = getZfsInfo()
    diffTime = now - oldTime
    getDiskIoInfoPerDisk(diffTime)

    oldTime = now

def parse_byte_value(byte_str):
    if byte_str.endswith('K'):
        return int(float(byte_str[:-1]) * 1024)
    elif byte_str.endswith('M'):
        return int(float(byte_str[:-1]) * 1024 * 1024)
    elif byte_str.endswith('G'):
        return int(float(byte_str[:-1]) * 1024 * 1024 * 1024)
    elif byte_str.endswith('T'):
        return int(float(byte_str[:-1]) * 1024 * 1024 * 1024 * 1024)
    else:
        return int(byte_str)

def parse_count_value(count_str):
    if count_str.endswith('K'):
        return int(float(count_str[:-1]) * 1000)
    elif count_str.endswith('M'):
        return int(float(count_str[:-1]) * 1000 * 1000)
    elif count_str.endswith('G'):
        return int(float(count_str[:-1]) * 1000 * 1000 * 1000)
    elif count_str.endswith('T'):
        return int(float(count_str[:-1]) * 1000 * 1000 * 1000 * 1000)
    else:
        return int(count_str)



def getAncestorQuota(dataSetName, infoMap):
    parts = dataSetName.split('/')
    for i in range(len(parts) - 1, 0, -1):
        ancestor_name = '/'.join(parts[:i])
        if ancestor_name in infoMap:
            ancestor = infoMap[ancestor_name]
            if ancestor.quota:
                return ancestor.quota
    return None

def getZFSDisk(diskPerf):
    if zfsInfoMap is None or zpoolInfoMap is None:
        return diskPerf

    dataSetInfo = zfsInfoMap[diskPerf.deviceId]
    poolInfo = zpoolInfoMap[dataSetInfo.pool]

    conf = GetConfig()
    quotaEnabled = getattr(conf, 'SunosDiskQuotaEnabled', False)

    if quotaEnabled:
        availBytes = parse_byte_value(dataSetInfo.avail)

        if dataSetInfo.refquota:
            referBytes = parse_byte_value(dataSetInfo.refer)
            refquotaBytes = parse_byte_value(dataSetInfo.refquota)
            diskPerf.usedSpace = referBytes
            diskPerf.freeSpace = min(availBytes, refquotaBytes - referBytes)
            diskPerf.totalSpace = refquotaBytes
        elif dataSetInfo.quota:
            referBytes = parse_byte_value(dataSetInfo.refer)
            quotaBytes = parse_byte_value(dataSetInfo.quota)
            diskPerf.usedSpace = referBytes
            diskPerf.freeSpace = min(availBytes, quotaBytes - referBytes)
            diskPerf.totalSpace = quotaBytes
        else:
            ancestorQuota = getAncestorQuota(dataSetInfo.name, zfsInfoMap)
            if ancestorQuota:
                quotaBytes = parse_byte_value(ancestorQuota)
                diskPerf.usedSpace = parse_byte_value(dataSetInfo.refer)
                diskPerf.freeSpace = availBytes
                diskPerf.totalSpace = quotaBytes
            else:
                diskPerf.totalSpace = parse_byte_value(poolInfo.totalSpace)
                diskPerf.usedSpace = parse_byte_value(dataSetInfo.refer)
                diskPerf.freeSpace = availBytes
    else:
        diskPerf.totalSpace = parse_byte_value(poolInfo.totalSpace)
        diskPerf.usedSpace = parse_byte_value(dataSetInfo.refer)
        diskPerf.freeSpace = parse_byte_value(dataSetInfo.avail)

    if diskPerf.totalSpace > 0:
        diskPerf.usedPercent = float(100.0 * float(diskPerf.usedSpace) / float(diskPerf.totalSpace))
        diskPerf.freePercent = float(100.0 * float(diskPerf.freeSpace) / float(diskPerf.totalSpace))
    else:
        diskPerf.usedPercent = float(0)
        diskPerf.freePercent = float(0)
    diskPerf.blksize = int(parse_byte_value(dataSetInfo.recordSize))
    diskPerf.readBps = poolInfo.readBps
    diskPerf.writeBps = poolInfo.writeBps
    diskPerf.readIops = poolInfo.readIops
    diskPerf.writeIops = poolInfo.writeIops
    diskPerf.ioPercent = poolInfo.ioPercent
    return diskPerf

def getDisk():
    f = open('/etc/mnttab','r')
    if not f:
        return None
    try:
        global zfsInfoCheck
        diskQuotas = []
        for l in f.readlines():
            words = l.split()
            if words[2] in filesystems:
                deviceId = words[0]
                p = _DiskPerf()
                p.mountPoint = words[1]
                p.deviceId = deviceId
                p.fileSystem = words[2]

                if words[2] == 'zfs' or words[2] == 'vzfs':
                    if zfsInfoCheck == False:
                        zfsInfoCheck = True
                        setZfsInfo()
                    p = getZFSDisk(p)
                    diskQuotas.append(p)
            
                else:
                    f_bsize, f_frsize, f_blocks, f_bfree, f_bavail, f_files, f_ffree, f_favail, f_flag, f_namemax = os.statvfs(p.mountPoint)
                    if f_blocks == 0:
                        continue
                    p.totalSpace = int(f_frsize * f_blocks)
                    p.usedSpace = int(f_frsize * (f_blocks - f_bfree))
                    p.freeSpace = p.totalSpace - p.usedSpace
                    p.usedPercent = float(100.0 * float(f_blocks - f_bfree) / float(f_blocks))
                    p.freePercent = float(100.0 * f_bfree / f_blocks)
                    p.blksize = int(f_frsize)
                    diskQuotas.append(p)
    finally:
        f.close()

    zfsInfoCheck = False
    return diskQuotas

class zfsIoStat:
    def __init__(self):
        self.name = None
        self.zfsType = None
        self.readOperation = 0
        self.writeOperation = 0
        self.readBandwidth = 0
        self.writeBandwidth = 0


class ZpoolPerfStat:
    def __init__(self):
        self.name = None
        self.poolName = None
        self.devName = None
        self.devType = None
        self.allocated = 0
        self.free = 0
        self.total = 0
        self.pctUsed = 0.0
        self.readBytes = 0
        self.writeBytes = 0
        self.readOps = 0
        self.writeOps = 0


zpoolInfo = {}
lock = threading.Lock()

_zpoolPerfSnapshot = {}
_zpoolPerfSnapshotLock = threading.Lock()

def safe_add_to_dict(key, value):
    global zpoolInfo
    with lock:
        zpoolInfo[key] = value

def safe_get_from_dict():
    global zpoolInfo
    ret = {}
    with lock:
        for k, v in zpoolInfo.items():
            ret[k] = v
            del zpoolInfo[k]
    return ret




#
#                              capacity     operations    bandwidth
#pool                        alloc   free   read  write   read  write
#-------------------------   -----  -----  -----  -----  -----  -----
#data-pool                   16.9G   539G      0      0      0      0
#  mirror-0                  16.9G   539G      0      0      0      0
#    c0t5000C500773ADFC3d0       -      -      0      0      0      0
#    c0t5000C50077331803d0       -      -      0      0      0      0
#-------------------------   -----  -----  -----  -----  -----  -----
#                              capacity     operations    bandwidth
#pool                        alloc   free   read  write   read  write
#-------------------------   -----  -----  -----  -----  -----  -----
#rpool                       90.4G  45.6G      0     51      0   144K
#  mirror-0                  90.4G  45.6G      0     51      0   144K
#    c1t5000039488127D22d0       -      -      0     16      0   145K
#    c1t5000039498033186d0       -      -      0     15      0   145K
#-------------------------   -----  -----  -----  -----  -----  -----

def parse_zpool_output(output, pool_re, diskMap):
    lines = output.strip().splitlines()
    current_pool = None
    current_type = "data"
    isPool = True
    parsed_data = {}


    for line in lines:
        if line.startswith("-"):
            continue

        line = line.strip()
        match = pool_re.match(line) 
        type_match = re.match(r'^\s*(\S+)\s*$', line)

        if match:
            groups = match.groupdict()
            name, alloc, free, read, write, read_bw, write_bw = (groups[key] for key in ["name", "alloc", "free", "read", "write", "read_bw", "write_bw"])
            if name ==  "pool" and alloc == "alloc" and free == "free": #header
                isPool = True
                current_type = "data"
                continue
            if name.startswith("mirror"):
                continue


            zfs_stat = zfsIoStat()
            if isPool:
                current_pool = name
                isPool = False
                zfs_stat.name = name 
            else:
                diskName = re.sub(r's\d+', '', name)
                if diskName in diskMap:
                    zfs_stat.name = "{}-{}({})".format(current_pool, diskName, diskMap[diskName])
                else:
                    zfs_stat.name = "{}-{}(Unknown)".format(current_pool, diskName)


            zfs_stat.zfsType = current_type
            zfs_stat.readOperation = parse_count_value(match.group("read"))
            zfs_stat.writeOperation = parse_count_value(match.group("write"))
            zfs_stat.readBandwidth = parse_byte_value(match.group("read_bw"))
            zfs_stat.writeBandwidth = parse_byte_value(match.group("write_bw"))

            safe_add_to_dict(zfs_stat.name, zfs_stat)

        if type_match:
            current_type = type_match.group(1)


def _getZpoolCapacity():
    command = "zpool list -H -o name,alloc,free,size"
    try:
        process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        text = process.stdout.read()
        cap_map = {}
        for line in text.strip('\n').split('\n'):
            if not line.strip():
                continue
            parts = line.split('\t')
            if len(parts) < 4:
                continue
            pool_name = parts[0]
            try:
                alloc = parse_byte_value(parts[1])
                free_bytes = parse_byte_value(parts[2])
                total = parse_byte_value(parts[3])
                cap_map[pool_name] = (alloc, free_bytes, total)
            except Exception:
                pass
        return cap_map
    except Exception as e:
        logging_util.error("zpool list capacity error: {} - {}".format(type(e).__name__, e))
        return {}


def _refreshZpoolPerfSnapshot(output, pool_re, diskMap, capacity_map):
    lines = output.strip().splitlines()
    current_pool = None
    current_type = "data"
    isPool = True
    new_snapshot = {}

    for line in lines:
        if line.startswith("-"):
            continue

        line = line.strip()
        match = pool_re.match(line)
        type_match = re.match(r'^\s*(\S+)\s*$', line)

        if match:
            groups = match.groupdict()
            name = groups["name"]

            if name == "pool" and groups["alloc"] == "alloc" and groups["free"] == "free":
                isPool = True
                current_type = "data"
                continue
            if name.startswith("mirror"):
                continue

            perf = ZpoolPerfStat()
            perf.devType = current_type

            if isPool:
                current_pool = name
                isPool = False
                perf.name = name
                perf.poolName = name
                perf.devName = name
                cap = capacity_map.get(name)
                if cap:
                    perf.allocated = cap[0]
                    perf.free = cap[1]
                    perf.total = cap[2]
                    denom = float(perf.allocated + perf.free)
                    if denom > 0:
                        perf.pctUsed = float(perf.allocated) / denom * 100.0
            else:
                diskName = re.sub(r's\d+', '', name)
                if diskName in diskMap:
                    perf.name = "{}-{}({})".format(current_pool, diskName, diskMap[diskName])
                else:
                    perf.name = "{}-{}(Unknown)".format(current_pool, diskName)
                perf.poolName = current_pool if current_pool else ""
                perf.devName = diskName

            try:
                perf.readOps = parse_count_value(groups["read"])
                perf.writeOps = parse_count_value(groups["write"])
                perf.readBytes = parse_byte_value(groups["read_bw"])
                perf.writeBytes = parse_byte_value(groups["write_bw"])
            except Exception:
                pass

            if perf.name:
                new_snapshot[perf.name] = perf

        if type_match:
            current_type = type_match.group(1)

    global _zpoolPerfSnapshot
    with _zpoolPerfSnapshotLock:
        _zpoolPerfSnapshot = new_snapshot


def GetZpoolPerfInfo():
    with _zpoolPerfSnapshotLock:
        return list(_zpoolPerfSnapshot.values())


def updateZFSPerf():
    pool_re = re.compile(r'^(?P<name>\S+)\s+(?P<alloc>\S+)\s+(?P<free>\S+)\s+(?P<read>\S+)\s+(?P<write>\S+)\s+(?P<read_bw>\S+)\s+(?P<write_bw>\S+)$')

    diskMap = getDiskMap()

    while True:
        process = subprocess.Popen(["zpool", "iostat", "-v", "5", "2"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        output, err = process.communicate()

        if err:
            return err

        intervals = output.split("\n\n")
        if len(intervals) >= 2:
            output = intervals[1]

        parse_zpool_output(output, pool_re, diskMap)

        capacity_map = _getZpoolCapacity()
        _refreshZpoolPerfSnapshot(output, pool_re, diskMap, capacity_map)


def initUpdateZfs():
    global _zfsUpdateStarted
    if _zfsUpdateStarted:
        return True
    f = open('/etc/mnttab','r')
    if not f:
        return None
    try:
        for l in f.readlines():
            words = l.split()
            if words[2] == 'zfs' or words[2] == 'vzfs':
                _zfsUpdateStarted = True
                thread_util.async(updateZFSPerf)
                break
    finally:
        f.close()

    return True

def GetZfsInfo():
    return safe_get_from_dict()



def test():
    for a in getDisk():
        print a
    time.sleep(10)
    for a in getDisk():
        print a


if __name__ == '__main__':
    test()

