#/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This file is part of Karesansui Core.
#
# Copyright (C) 2009 HDE, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#

""" 
<comment-ja>
ユーティリティ関数群を定義する
</comment-ja>
<comment-en>
Define the utility functions
</comment-en>

@file:   utils.py

@author: Taizo ITO <taizo@karesansui-project.info>

@copyright:    

"""
import string
import os
import os.path
import stat
import random
import subprocess
import shutil
import time
import datetime
import re
import pwd
import grp
import sys
import math

from xml.dom import minidom
from xml import xpath
import simplejson as json

from karesansui import KaresansuiLibException
from karesansui.lib.const import CHECK_DISK_QUOTA
from karesansui.lib.networkaddress import NetworkAddress

def dotsplit(val):
    """<comment-ja>
    ドット(.)区切りで文字列分割する。ドット(.)は)
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    rf = val.rfind('.')
    if rf == -1:
        return val, ''
    else:
        return val[:rf], val[rf+1:]

def toplist(val):
    """<comment-ja>
    リスト型に変換する。
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    if type(val) is list:
        return val
    else:
        return [val,]
        
def comma_split(s):
    """<comment-ja>
    カンマ(,)単位で文字列分割しリスト型に変換する。
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    ret = []
    for y in [x.strip() for x in s.split(',') if x]:
        ret.append(y)
    return ret

def uniq_sort(array):
    """<comment-ja>
    配列の要素をソートし重複した要素を取り除く
    @param array: 配列
    @return: 配列
    @rtype: list
    </comment-ja>
    <comment-en>
    run a unique sort and return an array of sorted

    @param array: list
    @return: list
    </comment-en>
    """
    array = sorted(array)
    array = [_x for _x, _y in zip(array, array[1:] + [None]) if _x != _y]
    return array

def dict_ksort(dt):
    """<comment-ja>
    辞書配列をキーを元にソートし重複する

    @param dt: 辞書
    @type dt: dict
    @return: ソート後の辞書配列
    @rtype: dict
    </comment-ja>
    <comment-en>
    run a key sort in dict
    </comment-en>
    """
    new_dict = {}
    for k,v in sorted(dt.iteritems(), lambda x,y : cmp(x[0], y[0])):
        new_dict[k] = v
    return new_dict

def dec2hex(num):
    """<comment-ja>
    整数値を１６進数の文字列に変換する
    @param num: 整数値
    @return: １６進数の文字列
    @rtype: str
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    return "%X" % num

def dec2oct(num):
    """<comment-ja>
    整数値を８進数の文字列に変換する
    @param num:整数値
    @return: ８進数の文字列
    @rtype: str
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    return "%o" % num

def hex2dec(s):
    """<comment-ja>
    １６進数の文字列を整数値に変換する
    @param string:１６進数の文字列
    @return int16
    @rtype: int
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    return int(s, 16)

def oct2dec(string):
    """<comment-ja>
    ８進数の文字列を整数値に変換する
    @param string:８進数の文字列
    @rtype: integer
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    return int(string, 8)

def next_number(min,max,exclude_numbers):
    """
    <comment-ja>
    指定範囲内における除外対象整数以外の次の整数を取得する
    
    @param min: 範囲中の最小の整数
    @param max: 範囲中の最大の整数
    @param exclude_numbers: 除外対象整数を要素にもつ配列
    @return: 整数
    @rtype: int
    </comment-ja>
    <comment-en>
    @param min: Minimum interger in specified range
    @param max: Maximum interger in specified range
    @param exclude_numbers: array that has the element of exclusive interger
    @return: Interger
    </comment-en>
    """
    for _x in range(min,max):
      if not _x in exclude_numbers:
        return _x

def is_uuid(uuid=None):
    """<comment-ja>
    KaresansuiのUUIDフォーマットに対応しているか。
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    uuid_regex = re.compile(r"""^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$""")
    if uuid != None and uuid_regex.match(uuid):
        return True

    return False

def generate_uuid():
    """
    <comment-ja>
    ランダムなUUIDを生成する

    @return: UUID用の16個のバイト要素を持つ配列
    </comment-ja>
    <comment-en>
    Generate UUID

    @return: Array UUID
    </comment-en>
    """
    uuid = []
    for _x in range(0, 16):
      uuid.append(random.randint(0x00,0xff))
    return uuid

def string_from_uuid(uuid):
    """
    <comment-ja>
    UUIDデータを文字列に変換する

    @param uuid: generate_uuid関数等で生成されたUUIDデータ
    @return: UUID文字列
    </comment-ja>
    <comment-en>
    Convert UUID data to string

    @param uuid: UUID data that was generated by certain function like as generate_uuid()
    @return: The string that stands for uuid
    </comment-en>
    """
    tuuid = tuple(uuid)
    return "-".join(["%02x"*4 % tuuid[0:4],
                    "%02x"*2 % tuuid[4:6],
                    "%02x"*2 % tuuid[6:8],
                    "%02x"*2 % tuuid[8:10],
                    "%02x"*6 % tuuid[10:16]
                   ]);

def string_to_uuid(string):
    """
    <comment-ja>
    文字列をUUIDデータに変換する

    @param string: UUID文字列
    @return: UUIDデータ
    </comment-ja>
    <comment-en>
    Convert string to UUID data

    @param string: The string that stands for uuid
    @return: UUID data
    </comment-en>
    """
    string = string.replace('-', '')
    return [ int(string[i : i + 2], 16) for i in range(0, 32, 2) ]

def generate_mac_address():
    """
    <comment-ja>
    ランダムなMACアドレスを生成する
    (00:16:3e:00:00:00 から 00:16:3e:7f:ff:ff の範囲で。)

    @return: MACアドレス
    </comment-ja>
    <comment-en>
    Generate random MAC address (between 00:16:3e:00:00:00 and 00:16:3e:7f:ff:ff)

    @return: The string that stands for MAC address
    </comment-en>
    """
    mac = [ 0x00, 0x16, 0x3e,
            random.randint(0x00, 0x7f),
            random.randint(0x00, 0xff),
            random.randint(0x00, 0xff) ]
    return ':'.join(map(lambda x: "%02x" % x, mac))

def generate_phrase(len):
    """<comment-ja>
    ランダムな文字列を生成する

    @param len: 文字列の長さ
    @return: ランダム文字列
    @rtype: str
    </comment-ja>
    <comment-en>
    Generate random string

    @param len: length of string
    @return: The generated string
    @rtype: str
    </comment-en>
    """
    letters = string.digits + string.letters + '-.'
    random.seed()
    return ''.join(random.choice(letters) for i in xrange(len))

def detect_encoding(string,encoding_list=None):
    """
    <comment-ja>
    文字エンコーディングを検出する

    @param string: 検出する文字列データ
    @param encoding_list: エンコーディングのリスト。エンコーディング検出の順番を配列で指定。省略時は、[ 'euc-jp', 'utf-8', 'shift-jis', 'iso2022-jp' ]
    @return: 検出した文字エンコーディング
    </comment-ja>
    <comment-en>
    Detect character encoding

    @param string: The string being detected
    @param encoding_list: list of character encoding. Encoding order will be specified by array. if it is omitted, detect order is [ 'euc-jp', 'utf-8', 'shift-jis', 'iso2022-jp' ]
    @return: The detected character encoding
    </comment-en>
    """
    func = lambda data,encoding: data.decode(encoding) and encoding

    if not encoding_list:
        encoding_list = [ 'euc-jp', 'utf-8', 'shift-jis', 'iso2022-jp' ]

    for encoding in encoding_list:
        try:
            return func(string, encoding)
        except:
            pass

    return None

def execute_command(command_args):
    """
    <comment-ja>
    コマンドを実行する

    @param command_args: 実行するコマンドとその引数を要素とする配列
    @return: 終了ステータスと実行時の出力結果の配列
    </comment-ja>
    <comment-en>
    Execute command

    @param command_args: The array that consists of command name and its arguments.
    @return: The return status of the executed command
    </comment-en>
    """
    ret = -1
    res = []

    subproc_args = { 'stdin': subprocess.PIPE,
                     'stdout': subprocess.PIPE,
                     'stderr': subprocess.STDOUT,
#                     'shell': True,
                     'cwd': os.getcwd(),
                     'close_fds': True,
                   }
    try:
        pp = subprocess.Popen(command_args, **subproc_args)
    except OSError:
        #raise "Failed to execute command: %s" % command_args[0]
        return [ret,res]

    (stdouterr, stdin) = (pp.stdout, pp.stdin)
    while True:
        line = stdouterr.readline()
        if not line:
            break
        line = line.rstrip()
        res.append(unicode(line, detect_encoding(line)).encode("utf-8"))
    ret = pp.wait()
    return [ret,res]


def create_file(file, value) :
    """
    <comment-ja>
    ファイルを作成する。

    @param file: 作成するファイル名
    @param value: 書き込む値
    @return: なし
    </comment-ja>
    <comment-en>
    create file

    @param file: The name of generated file
    @param value: The value of generated file
    @return: None
    </comment-en>
    """
    if os.path.exists(file):
        raise "Error: %s already exists" % file

    fd = open(file, 'w')
    try:
        try:
            fd.write(value)
        except IOError, err:
            raise "IOError: %s" % str(err)
    finally:
        fd.close()

def remove_file(file) :
    """
    <comment-ja>
    ファイルを削除する。

    @param file: 削除するファイル名
    @return: なし
    </comment-ja>
    <comment-en>
    remove file

    @param file: The name of removed file
    @return: None
    </comment-en>
    """
    if not os.path.exists(file):
        raise "Error: %s not exists" % file

    try:
        os.remove(file)
    except OSError, err:
        raise "OSError: %s" % str(err)

def create_sparse_file(file,size,is_sparse=True) :
    """
    <comment-ja>
    スパースファイルを生成する

    @param file: 生成するファイル名
    @param size: ファイルサイズ
    @param sparse: スパースファイル？
    @return: なし
    </comment-ja>
    <comment-en>
    Create sparse file

    @param file: The name of generated file
    @param size: The size of generated file
    @return: None
    </comment-en>
    """
    if is_sparse is True:
        command_args = [
            "dd",
            "if=/dev/zero",
            "of=%s" % file,
            "seek=%s" % str(size),
            "bs=1M",
            "count=0",
            ]
    else:
        command_args = [
            "dd",
            "if=/dev/zero",
            "of=%s" % file,
            "bs=1M",
            "count=%s" % str(size) ,
            ]

    (rc,res) = execute_command(command_args)
    if rc != 0:
        return None
    return rc

#    if os.path.exists(file):
#        raise "Error: %s already exists" % file
#
#    try:
#        fd = open(file, 'w')
#        try:
#            fd.truncate(1024L * 1024L * size)
#        except IOError, err:
#            raise "IOError: %s" % str(err)
#    finally:
#        fd.close()

def copy_file(src_file,dest_file):
    """
    <comment-ja>
    ファイルをコピーする

    @param src_file: コピー元ファイル名
    @param dest_file: コピー先ファイル名
    @return: コピー先ファイル名
    </comment-ja>
    <comment-en>
    Copy file

    @param src_file: Path to the source file
    @param dest_file: The destination path
    @return: The destination path
    </comment-en>
    """
    #ret = shutil.copy2(src_file, dest_file)
    ret = False
    if os.path.exists(src_file):
        try:
            if dest_file[0] != "/":
                dest_path = "%s/%s" % (os.getcwd(),os.path.dirname(dest_file),)
            else:
                dest_path = os.path.dirname(dest_file)
            if not os.path.exists(dest_path):
                os.makedirs(dest_path)

            (ret, res) = execute_command(["cp",src_file,dest_file])
            if ret == 0:
                ret = True
        except:
            return False
    else:
        return False

    return ret


def get_xml_parse(file):
    if os.path.exists(file):
        document = minidom.parse(file)
    else:
        document = minidom.parseString(file)

    return document

def get_xml_xpath(document, expression):
    """
    <comment-ja>
    XPathロケーションパスを評価する

    @param document: xml.dom.minidom.Document
    @param expression: 実行する XPath 式
    @return: 与えられた XPath 式 にマッチするすべてのノードを含む ノード一覧
    </comment-ja>
    <comment-en>
    Evaluates the XPath Location Path in the given string

    @param file: Path to XML file
    @param expression: The XPath expression to execute
    @return: Returns node list containing all nodes matching the given XPath expression
    </comment-en>
    """
    result = None
    for i in xpath.Evaluate(expression, document.documentElement):
        result = i.nodeValue

    return result

def get_nums_xml_xpath(document, expression):
    """
    <comment-ja>
    XPathロケーションパスを評価する

    @param file: XMLファイルパス、または、XMLデータそのもの
    @param expression: 実行する XPath 式
    @return: 与えられた XPath 式 にマッチするすべてのノードを含む ノード数
    </comment-ja>
    <comment-en>
    Evaluates the XPath Location Path in the given string

    @param file: Path to XML file
    @param expression: The XPath expression to execute
    @return: Returns the number of node containing all nodes matching the given XPath expression
    </comment-en>
    """
    result = None
    return xpath.Evaluate('count(%s)' % expression, document.documentElement)

def gettimeofday():
    """
    <comment-ja>
    現在の時刻を取得する

    @return: 紀元 (the Epoch: time(2) を参照) からの秒とマイクロ秒
    </comment-ja>
    <comment-en>
    Get current time

    @return: the number of seconds and microseconds since the  Epoch  (see time(2))
    </comment-en>
    """
    d = datetime.datetime.now()
    return int(time.mktime(d.timetuple())),d.microsecond


def load_locale():

    import karesansui
    import gettext
    try:
      t = gettext.translation('messages', karesansui.dirname + "/locale")
    except IOError, err:
      old_lang = os.environ['LANG']
      os.environ['LANG'] = 'en'
      t = gettext.translation('messages', karesansui.dirname + "/locale")
      os.environ['LANG'] = old_lang

    return t.gettext

def get_no_overlap_list(target_list):
    """
    <comment-ja>
    リストから重複要素を取り除く

    @param target_list: 重複要素を取り除きたいリスト
    @return: 重複が取り除かれたリスト(順番は保存されない)
    </comment-ja>
    <comment-en>
    delete overlap element in list

    @param target_list: list that has overlap element
    @return: list that delete overlap element (not keep original number)
    </comment-en>
    """
    return list(set(target_list))

def is_int(val):
    """<comment-ja>
    int型かどうか。
    @return: bool
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    try:
        ret = int(val)
        return True
    except (TypeError, ValueError):
        return False

def is_param(input, name, empty=False):
    """
    <comment-ja>
    リクエストデータ(self.input or web.input)に指定したパラメタが存在しているか。
    @param input: 
    @type input 
    @param
    @type
    @return: bool
    </comment-ja>
    <comment-en>
    TODO: English
    </comment-en>
    """
    try:
        if input.has_key(name) is True:
            if empty is True:
                if is_empty(input[name]) is True:
                    return False
                else: # has (name)key and input[name] is not empty
                    return True
            else: # has (name)key and empty arg is False
                return True
        else: # does not have (name)key
            return False
    except:
        return False

def is_ascii(value):
    for x in xrange(len(value)):
        # Printable characters ASCII is between 0x20(SP) and 0x7e(~)
        if ord(value[x]) < 0x20 or 0x7e < ord(value[x]):
            return False
    return True

def str2datetime(src, format):
    """<comment-ja>
    フォーマット(format)に一致した文字列(src)をdatetime型にキャストして
    M D Y のみのdatetimeを取得します。
    
    @param src: 変換文字列
    @type src: str
    @param format: 文字列フォーマット
    @type format: str
    @return: datetime型
    @rtype: datetime
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    _t = time.strptime(src, format)
    target = datetime.datetime(_t.tm_year, _t.tm_mon, _t.tm_mday)
    return target

def unixtime():
    """<comment-ja>
    UTCのエポックタイムを返却します。
    @rtype: float
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    return time.time()

def unixtime_str():
    """<comment-ja>
    UTCのエポックタイムを文字列として返却します。
    @rtype: str
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    return "%f" % unixtime()

def getfilesize(filepath):
    """<comment-ja>
    指定されたファイルのサイズを返却します。
    @rtype: long
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    return os.stat(filepath)[stat.ST_SIZE]

def getfilesize_str(filepath):
    """<comment-ja>
    指定されたファイルのサイズを文字列で返却します。
    @rtype: str
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    return "%ld" % getfilesize(filepath)

def get_filesize_MB(size):
    """
    <comment-ja>
    サイズ(str)をMBに変換する。
    @param size: サイズ
    @type size: str
    @return: MB
    @rtype: long
    </comment-ja>
    <comment-en>
    English Comment
    </comment-en>
    """
    return long(math.ceil(float(size) / 1024 / 1024))

def replace_None(obj, replace_item):
    """<comment-ja>
    __dict__から要素がNone, あるいは空文字('')のものを指定の要素に置き換えます
    @param __dict__をもつオブジェクト
    @rtype: object
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    for k,v in obj.__dict__.iteritems():
        if v == None or v == '':
            obj.__dict__[k] = replace_item
    return obj

def is_readable(path):
    """<comment-ja>
    指定されたパスが読み込み可能かどうか判定する
    @param path:ファイルパス
    @return: 可能ならTrue、不可能ならFalse
    @rtype: bool
    </comment-ja>
    <comment-en>
    test the readability of path
    </comment-en>
    """
    return os.access(path, os.R_OK)

def is_writable(path):
    """<comment-ja>
    指定されたパスが書き込み可能かどうか判定する
    @param path:ファイルパス
    @return: 可能ならTrue、不可能ならFalse
    @rtype: bool
    </comment-ja>
    <comment-en>
    test the readability of path
    </comment-en>
    """
    return os.access(path, os.W_OK)

def is_executable(path):
    """<comment-ja>
    指定されたパスが実行可能かどうか判定する
    @param path:ファイルパス
    @return: 可能ならTrue、不可能ならFalse
    @rtype: bool
    </comment-ja>
    <comment-en>
    test the readability of path
    </comment-en>
    """
    return os.access(path, os.X_OK)

def r_chown(path,owner):
    """<comment-ja>
    指定されたパス配下のディレクトリのオーナーを再帰的に変更する
    @param path:オーナーを変更したいパス
    @param owner:ユーザー名もしくがユーザーID、「:」で続けてグループを指定可能
    @return: 成功ならTrue、失敗ならFalse
    @rtype: bool
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    owner = str(owner)
    if not os.path.exists(path):
        return False

    if ':' in owner:
        user, group = owner.split(':')
    else:
        user, group = [owner,None ]

    if is_int(user) is not True:
        try:
            pw = pwd.getpwnam(user)
        except:
            return False
    else:
        try:
            pw = pwd.getpwuid(int(user))
        except:
            return False
    uid = pw[2]

    if group == None:
        statinfo = os.stat(path)
        gid = statinfo.st_gid
    else:
        if is_int(group) is not True:
            try:
                gr = grp.getgrnam(group)
            except:
                return False
        else:
            try:
                gr = grp.getgrgid(int(group))
            except:
                return False
        gid = gr[2]

    if os.path.isfile(path) or os.path.islink(path):
        try:
            os.chown(path,uid,gid)
        except:
            return False

    elif os.path.isdir(path):
        try:
            os.chown(path,uid,gid)
        except:
            return False

        for name in os.listdir(path):
            sub_path = os.path.join(path, name)
            r_chown(sub_path,owner)

    return True

def r_chgrp(path,group):
    """<comment-ja>
    指定されたパス配下のディレクトリのグループを再帰的に変更する
    @param path:グループを変更したいパス
    @param group:グループ名もしくがグループID
    @return: 成功ならTrue、失敗ならFalse
    @rtype: bool
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    group = str(group)
    if not os.path.exists(path):
        return False

    statinfo = os.stat(path)
    uid = statinfo.st_uid

    if is_int(group) is not True:
        try:
            gr = grp.getgrnam(group)
        except:
            return False
    else:
        try:
            gr = grp.getgrgid(int(group))
        except:
            return False
    gid = gr[2]

    if os.path.isfile(path) or os.path.islink(path):
        try:
            os.chown(path,uid,gid)
        except:
            return False

    elif os.path.isdir(path):
        try:
            os.chown(path,uid,gid)
        except:
            return False

        for name in os.listdir(path):
            sub_path = os.path.join(path, name)
            r_chgrp(sub_path,group)

    return True

def r_chmod(path,perm):
    """<comment-ja>
    指定されたパス配下のディレクトリのグループを再帰的に変更する
    @param path:グループを変更したいパス
    @param perm:パーミッション
    @return: 成功ならTrue、失敗ならFalse
    @rtype: bool
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """

    perm_regex = re.compile(r"""^(?P<user>[ugo]{0,3})(?P<action>[\+\-])(?P<value>[rwxst]{1,3})$""")

    user_table = {"u":"USR","g":"GRP","o":"OTH"}
    perm_table = {"r":"R","w":"W","x":"X"}

    if not os.path.exists(path):
        return False

    original_perm = perm
    if is_int(perm):
        if type(perm) == str:
            perm = oct2dec(perm)
        new_perm = perm
    else:
        s = os.lstat(path)
        new_perm = stat.S_IMODE(s.st_mode)

        m = perm_regex.match(perm)
        if m:
            user = m.group('user')
            action = m.group('action')
            value = m.group('value')
            if user == "":
                user = "ugo"

            mask_perm = 0
            for k,v in user_table.iteritems():
                if k in user:
                    for k2,v2 in perm_table.iteritems():
                        if k2 in value:
                            exec("bit = stat.S_I%s%s" % (v2,v,))
                            mask_perm = mask_perm | bit


            if "t" in value:
                bit = stat.S_ISVTX
                mask_perm = mask_perm | bit

            if "s" in value:
                if "u" in user:
                    bit = stat.S_ISUID
                    mask_perm = mask_perm | bit
                if "g" in user:
                    bit = stat.S_ISGID
                    mask_perm = mask_perm | bit

            #print "new_perm1:" + dec2oct(new_perm)
            #print "mask_perm:" + dec2oct(mask_perm)
            if action == "-":
                new_perm = new_perm & (~ mask_perm)
            elif action == "+":
                new_perm = new_perm | mask_perm
            #print "new_perm2:" + dec2oct(new_perm)

        else:
            return False

    if os.path.isfile(path) or os.path.islink(path):
        try:
            os.chmod(path,new_perm)
        except:
            return False

    elif os.path.isdir(path):
        try:
            os.chmod(path,new_perm)
        except:
            return False

        for name in os.listdir(path):
            sub_path = os.path.join(path, name)
            r_chmod(sub_path,original_perm)

    return True

def is_dict_value(value, dic):
    """<comment-ja>
    指定された値が辞書にあるか調べる
    @param value:調べたい値
    @param dic:辞書
    @return: 辞書にあればTrue、ないならFalse
    @rtype: bool
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    for key in dic.keys():
        if value == dic[key]:
            return True
    return False

#def is_number(string):
#    """<comment-ja>
#    文字列が数字のみで構成されているか調べる
#    @param string:調べたい文字列
#    @return: 数字のみならTrue、そうでないならFalse
#    @rtype: bool
#    </comment-ja>
#    <comment-en>
#    TODO: English Comment
#    </comment-en>
#    """
#    pattern = re.compile('^[\d]+$')
#    if pattern.match(string):
#        return True
#    return False

def is_empty(string):
    """<comment-ja>
    文字列が空かどうか調べる
    @param string: 調べたい文字列
    @return: 文字列が空ならTrue、そうでないならFalse
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    if string and 0 < len(string.strip()):
        return False
    else:
        return True

def uniq_filename():
    """<comment-ja>
    ユニークなファイル名を返却する。
    @param filename: 既存のファイル名
    @return: ユニークなファイル名
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    return unixtime_str()

def get_model_name(model):
    """<comment-ja>
    モデル名を返却する。
    @param model: モデル
    @return: モデル名
    </comment-ja>
    <comment-en>
    TODO: English Comment
    </comment-en>
    """
    if hasattr(model, "__repr__"):
        return repr(model).split("<")[0]
    else:
        return None


def chk_create_disk(target, disk_size):
    """<comment-ja>
    指定されたフォルダ/ファイルが属するパーティションに、指定したサイズのファイルが作成できるか。

    @param target: 調べるファイル/フォルダパス
    @type target: str 
    @param disk_size:
    @return: OK=True | NG=False
    @rtype: bool
    </comment-ja>
    <comment-en>
    English Comment
    </comment-en>
    """
    partition = get_partition_info(target, header=False)
    available = long(partition[3][0])
    if 0 < (available * CHECK_DISK_QUOTA - float(disk_size)):
        return True
    else:
        return False

def get_partition_info(target, header=False):
    """<comment-ja>
    指定したファイル/フォルダパスのパーティション情報(dfコマンドの結果)を取得する。
     - return
       - 0: 'Filesystem' デバイス名
       - 1: '1048576-blocks' 最大サイズ
       - 2: 'Used' 使用サイズ
       - 3: 'Available' ディスク残量
       - 4: 'Capacity' 使用率
       - 5: 'Mounted' マウント先
    値はすべてMB

    @param target: 調べるファイル/フォルダパス
    @type target: str
    @param header: ヘッダ情報を表示するか
    @type header: bool
    @rtype: dict
    </comment-ja>
    <comment-en>
    English Comment
    </comment-en>
    """
    if os.path.exists(target) is True:
        ret = {}
        if header is True:
            pipe = os.popen("LANG=C /bin/df -P -m " + target)
            try:
                data = []
                for line in pipe.readlines():
                    data.append(re.sub(r'[ \t]', ' ', line).split())
            finally:
                pipe.close()

            for i in range(0,6):
                ret[i] = (data[0][i], data[1][i])
        else:
            pipe = os.popen("LANG=C /bin/df -P -m %s | /bin/sed -n 2p" % target)
            try:
                line = pipe.read()
            finally:
                pipe.close()

            data = re.sub(r'[ \t]', ' ', line).split()

            for i in range(0,6):
                ret[i] = (data[i],)

        return ret
    else:
        return None

def uni_force(string, system="utf-8"):
    """
    <comment-ja>
    systemで指定された文字コードもしくは、["ascii", "utf-8", "euc-jp", "cp932", "shift-jis"]で強制的にunicodeへ変換します。

    @param string: 変換文字列
    @type string: str
    @param system: 文字コード
    @type system: str
    @return: Unicode文字列
    @rtype: str
    </comment-ja>
    <comment-en>
    English Comment
    </comment-en>
    """
    if isinstance(string, unicode) is True:
        return string
    else:
        try:
            return unicode(string, system)
        except:
            encodings = ["ascii", "utf-8", "euc-jp", "cp932", "shift-jis"]
            for encode in encodings:
                try:
                    return unicode(string, encode)
                except:
                    pass
            raise KaresansuiLibException("Character code that can be converted to unicode.")

def get_ifconfig_info(name=None):
    """
    <comment-ja>
    ネットワークデバイス情報を取得する

    @param name: 取得したいデバイス名(省略時は全デバイス情報が指定されたとみなす) 「regex:^eth」のようにプレフィックスにregex:を付けると正規表現にマッチしたデバイス名の情報を全て取得できる。
    @return:    デバイス情報が格納されたdict配列
                配列の内容例
                {'eth0': {   'bcast': '172.16.0.255',
                             'device': 'eth0',
                             'hwaddr': '00:1D:09:D7:30:4B',
                             'ipaddr': '172.16.0.10',
                             'ipv6addr': 'fe80::21d:9ff:fed7:304b/64',
                             'link': 'Ethernet',
                             'mask': '255.255.255.0',
                             'metric': '1',
                             'mtu': '1500',
                             'running': True,
                             'scope': 'Link',
                             'up': True,
                             'use_bcast': 'BROADCAST',
                             'use_mcast': 'MULTICAST'}}

    </comment-ja>
    <comment-en>
    Get computer's network interface information

    @param name: network device name
    @return: a dict with: ipaddr, hwaddr, bcast, mask etc...
    @rtype: dict
    </comment-en>
    """
    info = {}

    _ifconfig = '/sbin/ifconfig'
    command_args = [_ifconfig,'-a']

    old_lang = os.environ['LANG']
    os.environ['LANG'] = 'C'
    (ret,res) = execute_command(command_args)
    os.environ['LANG'] = old_lang

    if ret != 0:
        return info

    device_regex = re.compile(r"""^(?P<device>[\S\:]+)\s+Link encap:(?P<link>(\S+ ?\S+))(\s+HWaddr (?P<hwaddr>[0-9a-fA-F:]+))?""")
    ipv4_regex = re.compile(r"""^\s+inet addr:\s*(?P<ipaddr>[0-9\.]+)(\s+Bcast:(?P<bcast>[0-9\.]+))?\s+Mask:(?P<mask>[0-9\.]+)""")
    ipv6_regex = re.compile(r"""^\s+inet6 addr:\s*(?P<ipv6addr>[0-9a-fA-F\:\/]+)\s+Scope:(?P<scope>\S+)""")
    status_regex = re.compile(r"""^\s+((?P<up>UP)\s+)?((?P<use_bcast>(BROADCAST|LOOPBACK))\s+)?((?P<running>RUNNING)\s+)?((?P<use_mcast>(MULTICAST|NOARP))\s+)?MTU:(?P<mtu>[0-9]+)\s+Metric:(?P<metric>[0-9]+)""")

    _info = {}
    for aline in res:
        if aline.strip() == "":

            cidr = None
            netlen = None

            if ipaddr is not None and mask is not None:
                netaddr = NetworkAddress("%s/%s" % (ipaddr,mask,))
                cidr = netaddr.get('cidr')
                netlen = netaddr.get('netlen')

            _info[device] = {
                           "device":device,
                           "link":link,
                           "hwaddr":hwaddr,
                           "ipaddr":ipaddr,
                           "bcast":bcast,
                           "mask":mask,
                           "ipv6addr":ipv6addr,
                           "scope":scope,
                           "up":up,
                           "running":running,
                           "use_bcast":use_bcast,
                           "use_mcast":use_mcast,
                           "mtu":mtu,
                           "metric":metric,
                           "cidr":cidr,
                           "netlen":netlen,
                           }
        m = device_regex.match(aline)
        if m:
            device = m.group('device')
            link = m.group('link')
            hwaddr = m.group('hwaddr')
            ipaddr = None
            bcast = None
            mask = None
            ipv6addr = None
            scope = None
            up = False
            running = False
            use_bcast = None
            use_mcast = None
            mtu = None
            metric = None

        m = ipv4_regex.match(aline)
        if m:
            ipaddr = m.group('ipaddr')
            bcast = m.group('bcast')
            mask = m.group('mask')

        m = ipv6_regex.match(aline)
        if m:
            ipv6addr = m.group('ipv6addr')
            scope = m.group('scope')

        m = status_regex.match(aline)
        if m:
            if m.group('up') == 'UP':
                up = True
            use_bcast = m.group('use_bcast')
            if m.group('running') == 'RUNNING':
                running = True
            use_mcast = m.group('use_mcast')
            mtu = m.group('mtu')
            metric = m.group('metric')

    all_info = dict_ksort(_info)
    #import pprint
    #pp = pprint.PrettyPrinter(indent=4)
    #pp.pprint(all_info)

    if name == None:
        return all_info

    regex_regex = re.compile(r"""^regex:(?P<regex>.*)""")
    m = regex_regex.match(name)

    for dev,value in all_info.iteritems():
        if m == None:
            if dev == name:
                info[dev] = value
                return info
        else:
            regex = m.group('regex')
            query_regex = re.compile(r""+regex+"")
            n = query_regex.search(dev)
            if n != None:
                info[dev] = value
    return info

def get_proc_meminfo(path="/proc/meminfo"):
    if os.path.isfile(path) is False:
        return None

    fp = open(path, "r")
    try:
        ret = {}
        for line in fp.readlines():
            val = line.split(":")
            key = re.sub(r'[\t\n]', '', val[0].strip())
            value = re.sub(r'[\t\n]', '', val[1]).strip()
            invalue = value.split(" ")
            ret[key] = (invalue[0], invalue[1])

        return ret
    except:
        return None

def get_proc_cpuinfo(path="/proc/cpuinfo"):
    if os.path.isfile(path) is False:
        return None

    fp = open(path, "r")
    try:
        ret = {}
        i = 0
        ret[i] = {}
        for line in fp.readlines():
            if len(line.strip()) <= 0:
                i += 1
                ret[i] = {}
            else:
                val = line.split(":")
                key = re.sub(r'[\t\n]', '', val[0].strip())
                value = re.sub(r'[\t\n]', '', val[1]).strip()
                ret[i][key] = value

        if len(ret[len(ret)-1]) <= 0:
            ret.pop(len(ret)-1)

        return ret
        
    except:
        return None

def json_dumps(obj, **kw):
    """
    <comment-ja>
    PythonオブジェクトをJSONオブジェクトに変換する。

    @param obj: Pythonオブジェクト
    @type obj: str or dict or list or tuple or None or bool
    @param kw: 追加引数
    @type kw: str or dict or list or tuple or None or bool
    @return: JSONオブジェクト
    @rtype: str
    </comment-ja>
    <comment-en>
    English Comment
    </comment-en>
    """
    return json.dumps(obj, ensure_ascii=False, encoding="utf-8", **kw)

def is_path(target):
    """
    <comment-ja>
    指定された値が、パス名かどうか

    @param target: 調べるパス名
    @type target: str 
    @return: OK=True | NG=False
    @rtype: bool
    </comment-ja>
    <comment-en>
    English Comment
    </comment-en>
    """
    _path, _file = os.path.split(target)
    if _path != "":
        return True
    else:
        return False

def get_keymaps(dir_path='/usr/share/xen/qemu/keymaps'):
    """<comment-ja>
    指定されたKeymapフォルダのファイル名からKeymapのリストを取得する。
    </comment-ja>
    <comment-en>
    English Comment
    </comment-en>
    """
    ret = []
    if os.path.isdir(dir_path) is True:
        for _file in os.listdir(dir_path):
            if len(_file)==2 or (len(_file)==5 and _file[2:3]=='-'):
                ret.append(_file)
    return sorted(ret)
