# -*- coding:utf-8 -*-
"""
Function: set_devirtualization_service.py moudle. This moudle mainly
 involves the Configuring the Communication Service Between
  uREST and iBMC function.
Copyright Information: xFusion Digital Technologies Co.,
 Ltd. All Rights Reserved © 2020-2021
"""

import os
import sys
import platform
import time

from scripts import common_function

cur_path = os.path.split(os.path.realpath(__file__))[0]
urest_path = os.path.dirname(cur_path)
TOOLS_PATH = os.path.join(urest_path, 'tools/manager')
CDEV_PATH = os.path.join(urest_path, 'tools/cdevserver')
TCP_PATH = os.path.join(urest_path, 'tools/tcpserver')
DRIVER_EDMA_PATH = os.path.join(urest_path, 'driver/host_edma_drv.ko')
DRIVER_CDEV_PATH = os.path.join(urest_path, 'driver/host_cdev_drv.ko')
DRIVER_VETH_PATH = os.path.join(urest_path, 'driver/cdev_veth_drv.ko')
BMA_TOOL_PATH = os.path.join(common_function.BMA_BIN_PATH, "manager")
IBMA_INI_PATH = os.path.join(common_function.BMA_PATH, "config/iBMA.ini")
DEV_NAME = "/dev/hwibmc0"
CDEV_SERVER = "cdevserver"
TCP_SERVER = "tcpserver"
HOST_EDMA_DRV = "host_edma_drv"
HOST_CDEV_DRV = "host_cdev_drv"
CDEV_VETH_DRV = "cdev_veth_drv"
DRIVER_INFO_LIST = {CDEV_VETH_DRV: DRIVER_VETH_PATH,
                    HOST_CDEV_DRV: DRIVER_CDEV_PATH,
                    HOST_EDMA_DRV: DRIVER_EDMA_PATH}
SERVER_INFO_LIST = {CDEV_SERVER: CDEV_PATH, TCP_SERVER: TCP_PATH}


def setdevirtualizationservice_init(parser, parser_dict):
    """
    Function Description:initializing the Command for
    Configuring the Communication Service Between uREST and iBMC.
    Parameter:parser object: action object
    parser_dict dict: dictionary format of subcommand and ArgumentParser
    Return Value: subcommand
    """
    sub_parser = parser.add_parser('setcdevservice',
                                   help='configure the communication service'
                                        ' between the uREST and iBMC')
    sub_parser.add_argument('-E', dest='enable',
                            choices=['True', 'False'],
                            action=common_function.UniqueStore,
                            required=False,
                            help="enable the devirtualization communication"
                                 " service, or disable the devirtualization"
                                 " and USB communication services")
    sub_parser.add_argument('-T', dest='service_type',
                            choices=[common_function.USB_DEVICE],
                            action=common_function.UniqueStore,
                            required=False,
                            help="enable the %s communication"
                                 " service" % common_function.USB_DEVICE)
    parser_dict['setcdevservice'] = sub_parser

    return 'setcdevservice'


def install_all_driver():
    """
    Function Description:install driver.
    """
    DRIVER_DCIT = {HOST_EDMA_DRV: DRIVER_EDMA_PATH,
                   HOST_CDEV_DRV: DRIVER_CDEV_PATH,
                   CDEV_VETH_DRV: DRIVER_VETH_PATH}
    for key, value in DRIVER_DCIT.items():
        try:
            if is_driver(key):
                continue

            if not os.path.exists(value):
                os.system("./../driver/install.sh")
            if not os.path.exists(value):
                print("Failure: the driver %s is not exit." % value)
                sys.exit(common_function.UREST_STATUS_CODE_127)

            command = "insmod %s" % value
            insmod_info = common_function.get_cmd_value(command=command)

            flag = common_function.is_success(insmod_info)
            if not flag:
                print("Failure: %s" % insmod_info.get(common_function.ERROR))
                sys.exit(common_function.UREST_STATUS_CODE_127)

        except common_function.CustomError as e:
            print("Failure: %s" % str(e))
            sys.exit(common_function.UREST_STATUS_CODE_127)


def start_service(service_type=None):
    """
    Function Description:start service
    Modify: 2020.02.24 Receive exception and exit.
    """
    if not os.path.exists(TOOLS_PATH) or not os.path.exists(
            CDEV_PATH) or not os.path.exists(TCP_PATH):
        print("Failure: the tool %s or %s or %s"
              " is not exit." % (TOOLS_PATH, CDEV_PATH, TCP_PATH))
        sys.exit(common_function.UREST_STATUS_CODE_127)

    try:
        service_type = " %s" % service_type if service_type else ""
        command = "%s start 40443 tcp%s" % (TOOLS_PATH, service_type.lower())
        info = common_function.get_cmd_value(command=command)
        if not common_function.is_success(info):
            print("Failure: %s" % info.get(common_function.ERROR))
            sys.exit(common_function.UREST_STATUS_CODE_127)
    except common_function.CustomError as e:
        # Modify: 2020.02.24 Added failure tips.
        print("Failure: %s" % str(e))
        sys.exit(common_function.UREST_STATUS_CODE_127)


def inband_request(client):
    """
    Function Description:inband check
    client refishClient: class object
    """
    url = "/redfish/v1"
    resp = client.get_resource(url)
    if resp is None:
        return None

    if resp['status_code'] == 200:
        print('Success: successfully completed request.')
    else:
        print('Failure: In-band request failed. please confirm'
              ' whether the driver and service are enabled.')
    return resp


def is_manager_server(manager_path):
    """
    Function Description:Check whether the bma server exists.
    Parameter:manager_path str:manager path
    """
    if not os.path.exists(manager_path):
        return False

    if is_server(manager_path, TCP_SERVER) \
            and is_server(manager_path, CDEV_SERVER):
        return True

    return False


def is_server(path, key):
    """
    Function Description:Check whether the tcp server or cdev server exists.
    Parameter:path str:file path
    key str: cdevserver or tcpserver
    Return Value:result bool
    """
    if not os.path.exists(path):
        print("Failure: the %s is not exit." % path)
        return False

    try:
        command = "%s list | grep %s" % (path, key)
        sever_info = common_function.get_cmd_value(command=command)
        if common_function.is_success(sever_info) \
                and key in sever_info.get(common_function.RESULT):
            return True
    except common_function.CustomError as e:
        # Modify: 2020.02.24 Added failure tips.
        print("Failure: %s" % str(e))
        sys.exit(common_function.UREST_STATUS_CODE_127)

    return False


def shutdown_service():
    """
    Function Description:shutdown service
    """
    try:
        if not os.path.exists(TOOLS_PATH):
            print("Failure: the tool %s is not exit." % TOOLS_PATH)
            sys.exit(common_function.UREST_STATUS_CODE_127)
        command = "%s stop 40443" % TOOLS_PATH
        tool_info = common_function.get_cmd_value(command=command)
        flag = common_function.is_success(tool_info)
        if not flag:
            print("Failure: %s" % tool_info.get(common_function.ERROR))
            sys.exit(common_function.UREST_STATUS_CODE_127)

        if is_manager_server(TOOLS_PATH):
            print("Failure: failed to shutdown service.")
            sys.exit(common_function.UREST_STATUS_CODE_127)
    except common_function.CustomError as e:
        print("Failure: %s" % str(e))
        sys.exit(common_function.UREST_STATUS_CODE_127)


def remove_driver(driver_name):
    """
    Function Description:remove driver
    Parameter:driver_name str:driver name
    """
    try:
        if is_driver(driver_name):
            command = "rmmod %s" % driver_name
            info = common_function.get_cmd_value(command=command)
            flag = common_function.is_success(info)
            if not flag:
                print("Failure: %s" % info.get(common_function.ERROR))
                sys.exit(common_function.UREST_STATUS_CODE_127)
    except common_function.CustomError as e:
        # Modify: 2020.02.24 Added failure tips.
        print("Failure: %s" % str(e))
        sys.exit(common_function.UREST_STATUS_CODE_127)


def close_server():
    """
    Function Description:close server and nninstall the driver
    """
    if is_manager_server(BMA_TOOL_PATH):
        print("Failure: the path %s already exists. "
              "please use it to close manually." % BMA_TOOL_PATH)
        return False
    if is_manager_server(TOOLS_PATH):
        shutdown_service()
    # Modify: 2020.02.24 Receive exception and exit.
    for key, _ in DRIVER_INFO_LIST.items():
        remove_driver(key)
    return True


def register_ibmc():
    """
    Function Description:register_ibmc
    """
    data = [0x01, 0x08, 0xc0, 0x00, 0x93, 0x14, 0xe3, 0x00, 0x51, 0x01]
    data[5:8] = [int(ipmi_id, 16) for ipmi_id in common_function.VENDOR_IPMI_ID]
    hex_data = format_byte_to_hex_stream(data)
    if os.path.exists(DEV_NAME):
        try:
            OPEN_FILE_FLAGS = os.O_RDWR | os.O_APPEND | os.O_EXCL
            common_function.write_file(file_path=DEV_NAME,
                                       file_content=hex_data,
                                       text_flag=False,
                                       flags=OPEN_FILE_FLAGS)
        except (IOError, OSError):
            print("Failure: failed to send registration command to iBMC.")
            sys.exit(common_function.UREST_STATUS_CODE_127)
    else:
        print("Failure: failed to send registration command to iBMC. "
              "The device does not exist.")
        sys.exit(common_function.UREST_STATUS_CODE_127)


def format_byte_to_hex_stream(byte_list):
    """
    Function Description:Turn ASCII code array into a
     command byteList is converted to a Hex string
    Parameter:byteList list:ASCII
    Return Value:cmdString str:command
    """
    hex_string = ''.join(
        ['{0:02x}'.format(int(cByte)) for cByte in byte_list]).strip()
    # 需要先转换成 codecs 的bytes类型
    return bytes.fromhex(hex_string)


def is_driver(driver_name):
    """
    Function Description:Check whether the driver exists.
    Parameter:driver_name str:driver name
    Return Value:result bool:Whether the driver exists
    """
    command = "lsmod | grep %s" % driver_name
    try:
        lsmod_info = common_function.get_cmd_value(command=command)
        flag = common_function.is_success(lsmod_info)
        result = lsmod_info.get(common_function.RESULT)
        if flag and driver_name in result:
            return True
    except common_function.CustomError:
        print("Failure: failed to run the \'%s\' command." % command)
        sys.exit(common_function.UREST_STATUS_CODE_127)
    return False


def check_parameter(parser, args):
    if args.service_type is None and args.enable is None:
        parser.error("specify at least one of -E and -T.")

    if args.service_type and args.enable:
        parser.error("specify at most one of -E or -T.")


def start_server_result(args):
    """
    Function Description:Check the service startup result.
    args object:CLI command
    parser object:subcommand ArgumentParser object
    """
    if common_function.PLATFORM_TYPE not in platform.platform().lower():
        print("Failure: your os does not support the setting of the"
              " communication service between the uREST and iBMC.")
        sys.exit(common_function.UREST_STATUS_CODE_127)

    # Whether iBMA cdev is installed.
    if os.path.exists(IBMA_INI_PATH):
        print("Failure: the BMA has been installed. please use the BMA.")
        sys.exit(common_function.UREST_STATUS_CODE_127)

    if args.service_type and not os.path.exists(common_function.USB_DEV_FILE):
        print("Failure: enable the USB character device channel"
              " for communication between the uREST and iBMC.")
        return False

    close_server()
    if args.service_type:
        start_service(args.service_type)
        return True

    install_all_driver()
    from client_main import get_manufacturer_ipmi_id
    get_manufacturer_ipmi_id()
    register_ibmc()
    # wait until register_ibmc is finished
    time.sleep(6)
    start_service()
    return True


def setdevirtualizationservice(client, args):
    """
    Function Description:set cdev service server open
    Parameter:client refishClient: class object
    args object:CLI command
    """
    if client is None:
        return None

    if args.enable == "True" or args.service_type:
        # Check whether the virtualization function is enabled.
        resp = inband_request(client)
        return resp

    result = {'status_code': 200}
    return result
