# -*- coding:utf-8 -*-
"""
Function: client_main.py moudle. This moudle mainly involves the
init CLI command and Enabling Inband and Outband Channels function.
Copyright Information: xFusion Digital Technologies Co.,
 Ltd. All Rights Reserved © 2017-2021
"""
import os
import socket
import subprocess
import sys
import argparse
import signal
import re
import platform
import time
import traceback
from xml.etree import ElementTree
from scripts import common_function
from scripts import set_devirtualization_service

PROTOCOL_COMMAND = "setprotocol"
SET_CDEV_CHANNEL = "setcdevchannel"
SET_CDEV_COMMAND = "setcdevservice"
# add new interface
new_command_list = ["request", "addspdiagnose", "getspdiagnose", "collectsel",
                    "installlicense", "getlicenseinfo",
                    "restorefactory", "getsession",
                    "delsession", "resetraid", "addspdriveerasetask",
                    "getspdriveerasetask", SET_CDEV_COMMAND, "getcdevchannel",
                    SET_CDEV_CHANNEL, "delscreenshot", "getscreenshot",
                    "getprocessor", 'setuser',
                    common_function.HTTPS_SUB_COMMAND,
                    'delremotehttpsservercert', 'getsecurityservice',
                    'setsecurityservice']
# urest version
__version__ = '2.7.30'
inner_commands = [SET_CDEV_COMMAND, PROTOCOL_COMMAND]
# urest coordinate position of the help information.
POSITION_LENGTH = 30
# urest Inband and outband command flag
# (True indicates outband and False indicates inband).
flag = False
# subcommand and subcommand_init dict
met_dict = {}
# subcommand and subcommand's ArgumentParser dict
subparser_dic = {}
# subcommand  and submoudle's parameter_check dict
sub_parameter_check_dict = {}
# subcommand and submodule's is_interactive dict
sub_is_interactive_method_dict = {}
# CLI command args's object
args = None
parser = None

new_script = ["get_processor.py", "get_cpu.py", "set_user.py",
              "import_remote_https_server_cert.py",
              "del_remote_https_server_cert.py",
              "get_security_service.py",
              "set_security_service.py"]

CERT_HELP = 'client ca certificate'
cur_path = os.path.split(os.path.realpath(__file__))[0]
root_path = os.path.dirname(cur_path)
ROOT_NAME = "Equipment Root CA.pem"
ROOT_NAME_XFUSION = "xFusionRootCA.crt"
IT_NAME = "IT Product CA.pem"
IT_NAME_XFUSION = "xfusion_ca.crt"
CA_DIR = "ibmc_client"
CLIENT_CA_NAME = "xfusion_it.pem"
UREST_CONFIG_PATH = os.path.join(root_path, 'urest_config.xml')
DRIVER_PATH = os.path.join(root_path, 'driver')
IBMA_WINDOWS_DRIVER = 'ibmadriver_v22.06.30.100_2016_x86_64.msi'
IBMA_WINDOWS_DRIVER_FILE = os.path.join(DRIVER_PATH, IBMA_WINDOWS_DRIVER)
IBMA_VMWARE_DRIVER = 'ibma-driver-0.3.9-1OEM.700.1.0.15843807.x86_64.vib'
IBMA_VMWARE_SCRIPT = 'iBMA.sh'
IBMA_VMWARE_DRIVER_FILE = os.path.join(DRIVER_PATH, IBMA_VMWARE_DRIVER)
IBMA_VMWARE_SCRIPT_FILE = os.path.join(DRIVER_PATH, IBMA_VMWARE_SCRIPT)


class CHelpFormatter(argparse.HelpFormatter):
    """
    Function Description:Custom help formatter
    """

    def _iter_indented_subactions(self, action):
        self._max_help_position = POSITION_LENGTH
        self._action_max_length = POSITION_LENGTH

        try:
            get_subactions = action._get_subactions
        except AttributeError:
            pass
        else:
            self._indent()
            if isinstance(action, argparse._SubParsersAction):
                for subaction in sorted(get_subactions(),
                                        key=lambda x: x.dest):
                    yield subaction
            else:
                for subaction in get_subactions():
                    yield subaction
            self._dedent()

    def _format_actions_usage(self, actions, groups):
        """
        Function Description:Change '--timeout= TIMEOUT'
        in the help information to '--timeout=TIMEOUT'.
        Parameter:actions: helpactions
        groups: groups
        Return Value: text
        """
        text = super(CHelpFormatter, self)._format_actions_usage(actions,
                                                                 groups)
        if '--timeout= TIMEOUT' in text:
            text = text.replace('--timeout= TIMEOUT', '--timeout=TIMEOUT', 1)
        return text

    def _format_action(self, action):
        """
        Function Description:: Change '--timeout= TIMEOUT' in optional arguments
        in the help information to '--timeout=TIMEOUT '.
        Parameter: action: helpaction
        Return Value: text
        """
        text = super(CHelpFormatter, self)._format_action(action)
        if '--timeout= TIMEOUT' in text:
            text = text.replace('--timeout= TIMEOUT', '--timeout=TIMEOUT ', 1)
        return text


def set_cdev_service():
    """
    Function Description:set cdev service
    """
    if args.enable == "False":
        service_flag = set_devirtualization_service.close_server()
        if service_flag:
            print("Success: successfully completed request.")
            sys.exit(common_function.UREST_STATUS_CODE_0)
    else:
        service_flag = set_devirtualization_service.start_server_result(args)

    if not service_flag:
        sys.exit(common_function.UREST_STATUS_CODE_127)


def set_vmware_driver():
    cmd = "esxcli software vib list | grep ibma-driver"
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
    stdout, _ = p.communicate(timeout=60)
    result = common_function.handle_stdout(stdout).strip()
    if not result:
        cmd = 'localcli software vib install -v %s --no-sig-check --maintenance-mode' % IBMA_VMWARE_DRIVER_FILE
        common_function.check_file_exists(IBMA_VMWARE_DRIVER_FILE)
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
        stdout, _ = p.communicate(timeout=120)
        if p.returncode != 0:
            print('Failure: load driver failed.')
            common_function.exception_exit(common_function.UREST_STATUS_CODE_127)
    set_nddk_driver_mac_cmd = ['sh', IBMA_VMWARE_SCRIPT_FILE, 'set_nddk_driver_mac']
    common_function.check_file_exists(IBMA_VMWARE_SCRIPT_FILE)
    p = subprocess.Popen(set_nddk_driver_mac_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False)
    stdout, _ = p.communicate(timeout=60)
    if p.returncode != 0:
        print('Failure: set nddk driver mac failed.')
        common_function.exception_exit(common_function.UREST_STATUS_CODE_127)
    print('Success: load driver successfully, it need to restart system to take effect.')
    common_function.exception_exit(common_function.UREST_STATUS_CODE_0)


def close_vmware_driver():
    """
    Function Description:close vmware service, it need to restart system to take effect.
    """
    cmd = "esxcli software vib list | grep ibma-driver"
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
    stdout, _ = p.communicate(timeout=60)
    result = common_function.handle_stdout(stdout).strip()
    if not result:
        print('Failure: the driver not found.')
        common_function.exception_exit(common_function.UREST_STATUS_CODE_127)
    cmd = 'esxcli software vib remove -n ibma-driver --maintenance-mode'
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
    stdout, _ = p.communicate(timeout=60)
    if p.returncode != 0:
        print('Failure: close driver failed.')
        common_function.exception_exit(common_function.UREST_STATUS_CODE_127)
    print('Success: close driver successfully, it need to restart system to take effect.')
    common_function.exception_exit(common_function.UREST_STATUS_CODE_0)


def set_vmware_service():
    """
    Function Description:set vmware service, it need to restart system to take effect.
    """
    ibma = "/etc/init.d/iBMA"
    if os.path.exists(ibma):
        print("loading or close driver manually is not allowed when iBMA has been installed.")
        common_function.exception_exit(common_function.UREST_STATUS_CODE_127)
    if args.enable == "False":
        close_vmware_driver()
    else:
        set_vmware_driver()


def set_routing_and_virtual_network_ports():
    cmd = ['sh', IBMA_VMWARE_SCRIPT_FILE, 'virtual_netport_enable']
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False)
    stdout, _ = p.communicate(timeout=60)
    if p.returncode != 0:
        print('Failure: set virtual netports enable failed.')
        common_function.exception_exit(common_function.UREST_STATUS_CODE_127)


def config_firewall_rules():
    cmd = ['sh', IBMA_VMWARE_SCRIPT_FILE, 'config_firewall']
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False)
    stdout, _ = p.communicate(timeout=60)
    if p.returncode != 0:
        print('Failure: config firewall rules failed.')
        common_function.exception_exit(common_function.UREST_STATUS_CODE_127)


def set_windows_service():
    """
    Function Description:set service
    """
    cmd = 'chcp 437 & sc query iBMA'
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
    stdout, _ = p.communicate(timeout=60)
    stdout = common_function.handle_stdout(stdout)
    if 'SERVICE_NAME: iBMA' in stdout.strip():
        print("loading or close driver manually is not allowed when iBMA has been installed.")
        common_function.exception_exit(common_function.UREST_STATUS_CODE_127)
    if args.enable == "False":
        is_success = close_windows_driver()
        if is_success:
            print('Success: close driver successfully.')
            common_function.exception_exit(common_function.UREST_STATUS_CODE_0)
        else:
            print('Failure: close driver failed.')
            common_function.exception_exit(common_function.UREST_STATUS_CODE_127)
    else:
        set_windows_driver()


def set_windows_driver():
    is_success = close_windows_driver()
    if not is_success:
        print('Failure: close driver failed.')
        sys.exit(common_function.UREST_STATUS_CODE_127)
    log_file = os.path.join(DRIVER_PATH, 'tmp.log')
    cmd = ['MSIEXEC', '/I', IBMA_WINDOWS_DRIVER_FILE, '/LOG', log_file, '/QUIET', '/NORESTART']
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False)
    stdout, _ = p.communicate(timeout=60)
    if p.returncode != 0:
        print('Failure: load driver failed.')
        sys.exit(common_function.UREST_STATUS_CODE_127)
    for i in range(10):
        ip_strs_list = get_ips_info()
        port = get_virtual_port(ip_strs_list)
        if port:
            cmd = [
                'netsh', 'Interface', 'ipv6', 'set', 'address',
                'interface=%s' % port, 'fe80::9e7d:a3ff:fe28:6ff9'
            ]
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False)
            stdout, _ = p.communicate(timeout=60)
            if p.returncode != 0:
                print('Failure: set virtual network port failed')
                common_function.exception_exit(common_function.UREST_STATUS_CODE_127)
            else:
                print('Success: load driver successfully.')
                common_function.exception_exit(common_function.UREST_STATUS_CODE_0)
        if i >= 9:
            print('Failure: load driver timeout.')
            common_function.exception_exit(common_function.UREST_STATUS_CODE_127)
        time.sleep(2)


def close_windows_driver():
    cmd = 'wmic product where name="ibmadriver 22.06.30.100" call uninstall'
    is_success = True
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
    stdout, _ = p.communicate(timeout=60)
    if p.returncode != 0:
        is_success = False
    return is_success


def reset_mode_ip():
    """
    Function Description:Restore the default
    server communication mode and IP address.
    """
    if not old_mode_ip or len(old_mode_ip) < 2:
        return

    old_mode = old_mode_ip[0]
    old_ip = old_mode_ip[1]
    if args is not None and args.subcommand == PROTOCOL_COMMAND:
        if old_mode is not None and old_ip is not None:
            from scripts import set_protocol
            set_protocol.edit_code(old_mode, old_ip)


def is_shutdown_service():
    # 判断是否是linux系统
    if platform.system() == 'Linux' and args.subcommand != SET_CDEV_COMMAND and not flag:
        try:
            # 执行命令并获取输出结果
            subprocess.check_output("service iBMA status > /dev/null 2>&1", shell=True)
        except subprocess.CalledProcessError:
            # 未安装BMA
            if platform.system() == 'Linux' and is_driver_installed():
                set_devirtualization_service.shutdown_service()
            else:
                common_function.print_info(common_function.CDEVICE_NOT_EXIT_MESSAGE)
                sys.exit(127)


def is_start_service():
    # 判断是否是linux系统
    if platform.system() == 'Linux' and args.subcommand != SET_CDEV_COMMAND and not flag:
        try:
            # 执行命令并获取输出结果
            subprocess.check_output("service iBMA status > /dev/null 2>&1", shell=True)
        except subprocess.CalledProcessError:
            # 未安装BMA
            if platform.system() == 'Linux' and is_driver_installed():
                set_devirtualization_service.start_service()
            else:
                common_function.print_info(common_function.CDEVICE_NOT_EXIT_MESSAGE)
                sys.exit(127)


def is_driver_installed():
    """
    Function Description:Check whether the driver exists.
    Parameter:driver_name str:driver name
    Return Value:result bool:Whether the driver exists
    """
    command = "lsmod | grep host"
    try:
        lsmod_info = common_function.get_cmd_value(command=command)
        result = lsmod_info.get(common_function.RESULT)
        if 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 execute_subcommand(client):
    """
    Function Description:execute subcommand
    Parameter:client refishClient: class object
    """
    met = met_dict.get(args.subcommand)
    if met is not None:
        try:
            # 判断redifish是否可以正常访问
            client.get_common_key()
            redfish_client.sub_command = args.subcommand
            result = met(client, args)
            is_shutdown_service()
            common_function.set_exit_code(result)
        except common_function.CustomError as e:
            result = e.get_dict()
            common_function.get_error_info(result)
            common_function.set_exit_code(result)
        except KeyError as e:
            print(common_function.KEYERROR_FORMATTER.format("%s" % (e)))
            sys.exit(common_function.UREST_STATUS_CODE_127)


def set_service():
    os_type = platform.system()
    if os_type == 'Windows':
        # check Windows version, only 2019 and 2022 support setcdevservice command.
        # the kernel of Windows 2019 and 2022 is Windows 10.
        if platform.release() != "10":
            print('Failure: only windows 2019 and windows 2022 support setting windows drivers.')
            common_function.exception_exit(common_function.UREST_STATUS_CODE_127)
        set_windows_service()
    elif os_type == 'Linux':
        set_cdev_service()
    elif os_type == 'VMkernel':
        # check vmware version, only 7.x and 8.x support setcdevservice command.
        if platform.release().split('.')[0] not in common_function.VMWARE_VERSIONS_SUPPORTED_SETCDEVSERVICE:
            print('Failure: only vmware 7.x and vmware 8.x support setting vmware drivers.')
            common_function.exception_exit(common_function.UREST_STATUS_CODE_127)
        set_vmware_service()
    else:
        print('Failure: %s os does not support the setcdevservice command.' % os_type)
        common_function.exception_exit(common_function.UREST_STATUS_CODE_127)


def init_communication_info(client):
    """
    Function Description:init communication channel
    Parameter:client refishClient: class object
    """
    if flag:
        client.set_auth()
    else:
        if args.subcommand == SET_CDEV_COMMAND:
            # 加载驱动实现带内通信
            set_service()
        if platform.system() == 'VMkernel' and not os.path.exists("/etc/init.d/iBMA") \
                and platform.release().split('.')[0] in common_function.VMWARE_VERSIONS_SUPPORTED_SETCDEVSERVICE:
            set_routing_and_virtual_network_ports()
            config_firewall_rules()
        resp = client.set_inner_bmcinfo()
        if not resp:
            sys.exit(common_function.UREST_STATUS_CODE_127)


def exit_code_exit(error_info):
    """
    Function Description:Exit the program according to the exit code.
    Parameter:error int: exit code
    """
    if str(error_info) != "0":
        reset_mode_ip()
    if get_command_flag():
        sys.exit(error_info)
    elif "126" in str(error_info):
        if "Windows" in platform.system():
            sys.exit(common_function.UREST_STATUS_CODE_0)
        else:
            sys.exit(common_function.UREST_STATUS_CODE_2)
    else:
        sys.exit(common_function.UREST_STATUS_CODE_0)


def init_flag():
    """
    Function Description:initialization flag. The value can be False in inband
    and True in outband. Inband sessions are created.
    Modify: 2018.12.13 Modify the prompt information.
    2020.6.18 failed to create the in-band token. flag is True
    """
    global flag
    if "-H" in sys.argv and "setdns" not in sys.argv:
        flag = True
    elif "-H" in sys.argv and "setdns" in sys.argv \
            and sys.argv.index("-H") < sys.argv.index("setdns"):
        flag = True
    else:
        flag = False


def check_args():
    """
    Function Description:check inband and outband command
    """
    check_inband_command()
    check_outband_command()
    if args.subcommand is None:
        sys.exit(common_function.UREST_STATUS_CODE_0)
    check_subcommand()


def check_inband_command():
    """
    Function Description:check inband command
    """
    if not flag:
        if args.subcommand == SET_CDEV_CHANNEL:
            print('Failure: set the enabling status of the character device'
                  ' channel for communication between the uREST and iBMC.'
                  ' Use out-of-band commands only.')
            common_function.exception_exit(common_function.UREST_STATUS_CODE_127)

        if args.host is not None or \
                args.username is not None or \
                args.port is not None:
            # exit status code:126
            parser.error('-H, -U and -p are not required for local access.'
                         ' -H and -U are mandatory for remote access')


def check_outband_command():
    """
    Function Description:check outband command
    """
    if flag:
        if args.subcommand in inner_commands:
            parser.error('The subcommand only supports in-band operation')


def init_password(client):
    """
    Function Description:input bmc password
    Parameter:client refishClient: class object
    """
    if flag:
        if args.password is not None:
            password = args.password
        else:
            password = common_function.subcommand_pwd("BMC", True)
        client.setpassword(password)


def get_cert():
    """
    Function Description:get cert
    """
    try:
        ca_dir_path = os.path.join(root_path, CA_DIR)
        root_ca_path = os.path.join(ca_dir_path, ROOT_NAME)
        root_xfusion_ca_path = os.path.join(ca_dir_path, ROOT_NAME_XFUSION)
        it_ca_path = os.path.join(ca_dir_path, IT_NAME)
        it_xfusion_ca_path = os.path.join(ca_dir_path, IT_NAME_XFUSION)
        if not os.path.exists(root_xfusion_ca_path) or not os.path.exists(it_xfusion_ca_path) or \
                not os.path.exists(root_ca_path) or not os.path.exists(it_ca_path):
            print("%s%s" % (common_function.ERROR_KEY,
                            common_function.CERT_NOT_EXIT_MESSAGE.format(
                                ROOT_NAME_XFUSION, IT_NAME_XFUSION)))
            common_function.exception_exit(common_function.UREST_STATUS_CODE_127)

        client_ca_path = os.path.join(ca_dir_path, CLIENT_CA_NAME)
        client_xfusion_ca_content = ""
        if os.path.exists(root_xfusion_ca_path) and os.path.exists(it_xfusion_ca_path):
            with open(root_xfusion_ca_path, 'r', encoding='utf-8') as root_xfusion_file:
                root_xfusion_ca_content = root_xfusion_file.read()
            with open(it_xfusion_ca_path, 'r', encoding='utf-8') as it_xfusion_file:
                it_xfusion_ca_content = it_xfusion_file.read()
            client_xfusion_ca_content = "".join((it_xfusion_ca_content, root_xfusion_ca_content))

        client_ca_content = ""
        if os.path.exists(root_ca_path) and os.path.exists(it_ca_path):
            with open(root_ca_path, 'r', encoding='utf-8') as root_file:
                root_ca_content = root_file.read()
            with open(it_ca_path, 'r', encoding='utf-8') as it_file:
                it_ca_content = it_file.read()
            client_ca_content = "".join((it_ca_content, root_ca_content))

        content = "".join((client_xfusion_ca_content, client_ca_content))
        common_function.write_file(file_path=client_ca_path, file_content=content)
        return client_ca_path
    except OSError as message:
        print("%s%s" % (common_function.ERROR_KEY, message))
        common_function.exception_exit(common_function.UREST_STATUS_CODE_127)


def init_cert():
    """
    Function Description:CA certificate path for out-of-band initialization
    """
    if not flag or args.ignoreCert:
        args.cert = False
    elif args.cert is None:
        args.cert = get_cert()


def get_command_flag():
    """
    Function Description:get command_flag
    Parameter:subcommand str: subcommand
    code bool: check CLI command flag
    """
    if (args and (
            args.subcommand in new_command_list or args.code)) \
            or "--error-code" in sys.argv \
            or [sub_command for sub_command in new_command_list
                if sub_command in sys.argv]:
        return True

    return False


def init_met_subparser():
    """
    Function Description:init met_dict and subparser_dic
    """
    from importlib import import_module
    global met_dict
    global subparser_dic
    global sub_parameter_check_dict
    global sub_is_interactive_method_dict

    subparsers = parser.add_subparsers(title='sub commands',
                                       dest='subcommand',
                                       help='sub-command help',
                                       metavar="sub command")
    # Traverse the current folder, and add subcommands and parameters
    # based on the subcommand Python file names.
    for fil in os.listdir(cur_path):
        # 判断是否是命令文件
        if is_command_file(fil):
            mod = os.path.splitext(fil)[0]
            if fil in new_script:
                mod_split = mod.split(".py")
                mod_join = mod_split[-1]
            else:
                mod_split = mod.split("_")
                mod_join = ''.join(mod_split)
            mod_init = "%s%s" % (mod_join, '_init')
            mod_im = import_module(mod)
            met = getattr(mod_im, mod_join)
            met_init = getattr(mod_im, mod_init)
            sub_parameter_check = getattr(mod_im, 'check_parameter', None)
            sub_cmd = met_init(subparsers, subparser_dic)
            met_dict[sub_cmd] = met
            sub_parameter_check_dict[sub_cmd] = sub_parameter_check
            sub_is_interactive_method_dict[sub_cmd] = getattr(mod_im, 'is_interactive', None)


def is_command_file(file):
    return (not os.path.isdir(file)) \
           and (os.path.splitext(file)[1] == '.py') \
           and (file != '__init__.py') \
           and (file != 'client_main.py') \
           and (file != 'common_function.py') \
           and (file != 'version_comparator.py')


def check_timeout(timeout):
    """
    Function Description:check CLI --timeout
    Parameter:subcommand str: subcommand
    timeout int: time out
    Return Value:timeout int: time out
    """
    time_out_object = re.search(r'(?P<timeout>^[\d]+$)', timeout)
    if time_out_object is None:
        message = 'invalid int value:\'%s\'' % str(timeout)
        parser.error(message)
    timeout_dic = time_out_object.groupdict()
    timeout = int(timeout_dic.get('timeout'))
    if timeout is not None:
        if '--timeout=%d' % timeout not in sys.argv \
                or timeout < 10 or timeout > common_function.MAX_TIMEOUT:
            message = 'Parameter timeout is invalid.' \
                      ' The timeout range 10-%s seconds.' \
                      % common_function.MAX_TIMEOUT
            parser.error(message)
    return timeout


def check_host(host):
    """
    Function Description:check CLI --H
    Parameter:host str: service's IP
    Return Value:host str: service's IP
    """
    if host:
        if host[-1] in ('#', '?') or ('/' in host):
            message = 'invalid BMC address.'
            parser.error(message)
    return host


def check_certificate(cert_path):
    """
    Function Description:check CLI --cert
    Parameter:cert_path str: CA's path
    """
    if cert_path is None or not os.path.exists(cert_path):
        parser.error("the certificate file %s does not exist." % cert_path)
    elif not os.path.isabs(cert_path):
        cert_path = os.path.abspath(cert_path)
    suffix = os.path.splitext(cert_path)[-1]
    if suffix not in (".crt", ".pem", ".cer"):
        parser.error('the certificate file format should '
                     'be .crt or .pem. or .cer')
    return cert_path


def parser_to_args():
    """
    Function Description:parser to args
    """
    global args
    try:
        args = parser.parse_args()
    except SystemExit as error:
        sys.exit(error)


def check_subcommand():
    """
    Function Description:check subcommand
    """
    sub_parameter_check = sub_parameter_check_dict.get(args.subcommand)
    if sub_parameter_check is not None:
        sub_parameter_check(subparser_dic[args.subcommand], args)
    sub_is_interactive = sub_is_interactive_method_dict.get(args.subcommand)
    if args.subcommand in common_function.SUBCOMMAND_RL_SERVER_LIST and sub_is_interactive(args):
        common_function.check_real_path_parameter(subparser_dic[args.subcommand], args)


def init_args():
    """
    Function Description:init CLI command
    Modify: 2017.8.11 -h and -V respectively display the version number.
    2017.8.7 adjust the sequence of the -p and -P parameters.
    2018.12.20 translation-timeout help information
    """
    global parser
    # 定义主命令参数
    parser = argparse.ArgumentParser(prog='urest', add_help=True,
                                     formatter_class=CHelpFormatter,
                                     description='urest version '
                                                 '%s' % __version__)
    parser.add_argument('-V', '--version', action='version',
                        version="uREST version %s" % __version__)
    parser.add_argument('--error-code', dest='code',
                        action='store_true',
                        help='exit code. When an error occurs, '
                             'the exit code is not 0.')
    parser.add_argument('-I', '--ignore-cert', dest='ignoreCert',
                        default=False, required=False, action='store_true',
                        help='ignore certificate verification.')
    parser.add_argument('-H', dest='host', required=flag,
                        type=check_host,
                        help='domain name, IPv4 address, '
                             'or [IPv6 address]')
    parser.add_argument('-p', dest='port', type=int,
                        help='port')
    parser.add_argument('-U', dest='username', required=flag,
                        help='local or LDAP username')
    parser.add_argument('-P', dest='password', required=False,
                        default=None, help='iBMC password')
    parser.add_argument('--timeout=', dest='timeout', type=check_timeout,
                        help='timeout interval (seconds) for '
                             'requesting the iBMC Redfish '
                             'interface [default=10].'
                             ' Upgrade commands [default=30] / '
                             'File commands [default=120].'
                             ' Other different timeouts are explained '
                             'in the specific commands.')
    # Modify: 2021.3.2 Added a prompt message.
    parser.add_argument('--cert', dest='cert',
                        type=check_certificate,
                        help=CERT_HELP)
    # 初始化子命令
    init_met_subparser()
    # 参数实例化
    parser_to_args()
    # 检查输入参数
    check_args()
    # 初始化证书
    init_cert()


def get_ips_info():
    ip_str_list = []
    cmd = 'chcp 437 & ipconfig /all'
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
    stdout, _ = p.communicate(timeout=60)
    stdout_str = common_function.handle_stdout(stdout)
    lines = stdout_str.split('\n')
    ip_infos = []
    for line in lines:
        if not line.strip():
            ip_str_list.append(ip_infos)
            ip_infos = []
            continue
        ip_infos.append(line)
    return ip_str_list


def check_ibma_description(ip_strs):
    is_find = False
    description = 'iBMA Virtual Network Adapter'
    for line in ip_strs:
        if description in line:
            is_find = True
    return is_find


def get_virtual_port(ip_strs_list):
    port = ''
    link_local_ipv6_regex = re.compile(r".*Link-local.*IPv6.*Address.*:.*%([0-9a-zA-Z]+)\(.*")
    for ip_strs in ip_strs_list:
        if not check_ibma_description(ip_strs):
            continue
        for line in ip_strs:
            ret = link_local_ipv6_regex.findall(line)
            if ret:
                return str(ret[0])
    return port


def clientmain():
    """
    Function Description:CLI entry function
    Exception Description: SystemExit catch sys.exit()
    """
    client = None
    is_start_service()
    try:
        client = redfish_client.RedfishClient()
        # 初始化数据
        init_data(client)
        # 初始化通信鉴权信息
        init_communication_info(client)
        # 命令执行
        execute_subcommand(client)
    except KeyboardInterrupt:
        signal.signal(signal.SIGINT,
                      exit_code_exit(common_function.UREST_STATUS_CODE_130))
    except (AttributeError, IOError, ImportError, IndexError, NameError,
            TypeError,
            ValueError, SystemError, KeyError) as e:
        print("Failure: %s" % traceback.format_exc())
        exit_code_exit(common_function.UREST_STATUS_CODE_127)
    except SystemExit as error:
        exit_code_exit(error)
    finally:
        delete_inner_session(client)


def delete_inner_session(client):
    """
    Function Description:delete inband session
    Parameter:client refishClient: class object
    """
    if not flag and client is not None:
        client.delete_inner_session()


def init_data(client):
    """
    Function Description:init data
    Parameter:client refishClient: class object
    """
    if not flag and args.subcommand not in inner_commands:
        # 获取厂商ID
        get_manufacturer_ipmi_id()
        #  获取token
        client.create_inner_session()

    # RedfishClient初始化
    client.setself(args)
    # 初始化密码，如果密码为空表示隐藏密码
    init_password(client)


def edit_protocol():
    """
    Function Description:Modify the urest and bmc communication code
    """
    check_result = []
    if PROTOCOL_COMMAND != args.subcommand:
        return check_result

    from scripts import set_protocol
    if args.CommunicationMethod == common_function.IPV6:
        check_result = set_protocol.edit_code(common_function.IPV6, '')
    else:
        check_result = set_protocol.edit_code(common_function.IPV4,
                                              args.iBMCIp)

    return check_result


def get_manufacturer_ipmi_id():
    """
    Function Description: get vendor ipmi info of the manufacturer
    """
    import getbmctoken
    try:
        # 获取制造商ID的IPMI命令
        request_cmd = ["0x01", "0x03", "0x18", "0x00", "0x01"]
        res = getbmctoken.get_info_by_ipmi((common_function.format_byte_to_hex_stream(request_cmd)))
        if res is None or len(res) < 16:
            return

        # 12到14个byte是厂商ID
        common_function.VENDOR_IPMI_ID = [hex(i) for i in res[12:15]]
    except common_function.CustomError as error:
        print(error)
        common_function.exception_exit(common_function.UREST_STATUS_CODE_127)
    finally:
        pass


if __name__ == '__main__':
    # Modify: 2021/10/21 modifying the Maximum Recursion Depth of Python
    sys.setrecursionlimit(common_function.DEPTH_PYTHON)
    # Modify redfish_client.py's ibmcmode and ibmcip
    old_mode_ip = None
    try:
        # 判断使用带内命令还是带外
        init_flag()
        # 初始化参数
        init_args()
        # 设置与iBMC通信协议(ipv4, ipv6)
        old_mode_ip = edit_protocol()
        import redfish_client
    except KeyboardInterrupt:
        signal.signal(signal.SIGINT,
                      exit_code_exit(common_function.UREST_STATUS_CODE_130))
    except SystemExit as error:
        exit_code_exit(error)
    except (TypeError, ValueError, ImportError) as e:
        print("Failure: %s" % e)
        exit_code_exit(common_function.UREST_STATUS_CODE_127)

    # Added urest exception handling mechanism.
    # Shielding the effects of SIGCHLD signals on programs in Linux systems
    try:
        old_handler = signal.signal(signal.SIGCHLD, signal.SIG_DFL)
    except AttributeError:
        pass
    # 命令执行
    clientmain()
    try:
        signal.signal(signal.SIGCHLD, old_handler)
    except AttributeError:
        pass
