# -*- coding:utf-8 -*-
"""
Function: upgrade_driver.py moudle. This moudle mainly involves the
Upgrading an In-Band Driver function.
Copyright Information: xFusion Digital Technologies Co.,
 Ltd. All Rights Reserved © 2017-2020
"""
import sys
import time
from scripts import common_function


def upgradedriver_init(parser, parser_dict):
    """
    Function Description:initializing the Command for
    Upgrading an In-Band Driver
    Parameter:parser object: action object
    parser_dict dict: dictionary format of subcommand and ArgumentParser
    Return Value: subcommand
    """
    sub_parser = parser.add_parser('upgradedriver', help='''upgrade driver''')
    sub_parser.add_argument('-i', dest='imageuri', required=True,
                            help='path of the driver upgrade package. On a'
                                 ' local file server, the package can be'
                                 ' stored only in the /tmp directory of iBMC,'
                                 ' and the path format is /tmp/file name.'
                                 ' On a remote file server, the path format'
                                 ' is server IP address/directory/'
                                 'package name')
    sub_parser.add_argument('-si', dest='signatureuri', required=True,
                            help='path of the digital signature certificate'
                                 ' of the driver upgrade package. On a local'
                                 ' file server, the certificate can be stored'
                                 ' only in the /tmp directory of iBMC, and the'
                                 ' path format is /tmp/file name. On a remote'
                                 ' file server, the path format is server IP'
                                 ' address/directory/file name.')
    sub_parser.add_argument('-ci', dest='crluri', required=False,
                            help="path of the cms digital signature certificate's "
                                 'public key of the driver upgrade package. On a '
                                 'local file server, the certificate can be stored'
                                 ' only in the /tmp directory of iBMC, and the'
                                 ' path format is /tmp/file name. On a remote'
                                 ' file server, the path format is server IP'
                                 ' address/directory/file name.')
    sub_parser.add_argument('-PRO', dest='protocol', required=True,
                            choices=['SFTP'],
                            help='protocol used to download the '
                                 'upgrade package')
    sub_parser.add_argument('-U', dest='accessuser', required=True,
                            help='''user name for upgrade package access''')
    sub_parser.add_argument('-P', dest='accesspassword', required=False,
                            default=None,
                            help='''password for upgrade package access''')
    sub_parser.add_argument('-PARM', dest='parameter', required=True,
                            help='\'all\' indicates the entire upgrade '
                                 'package or a specific upgrade '
                                 'package (for example package1.rpm)')
    sub_parser.add_argument('-ACT', dest='activemethod',
                            choices=['OSRestart', 'ServerRestart'],
                            help='''how does the upgrade take effect''')

    parser_dict['upgradedriver'] = sub_parser

    return 'upgradedriver'


def upgradedriver(client, args):
    """
    Function Description:Upgrading an In-Band Driver
    Parameter:client refishClient: class object
    args object:CLI command
    """
    url = "/redfish/v1/Sms/1/UpdateService/Actions/UpdateService.SimpleUpdate"

    payload = init_payload(args)

    resp = client.create_resource(url, payload,
                                  timeout=common_function.TIMEOUT_THIRTY)
    if resp is None:
        return None

    # If the upgrade is successful, query the upgrade progress.
    if resp['status_code'] == common_function.REDFISH_STATUS_CODE_200:
        resp = get_upgrade_progress(client)
    elif resp['status_code'] == common_function.REDFISH_STATUS_CODE_400:
        err_400_proc(resp)
    elif resp['status_code'] == common_function.REDFISH_STATUS_CODE_404:
        print('Failure: failed to obtain iBMA information')

    return resp


def init_payload(args):
    """
    Function Description:init payload
    Parameter:args object:CLI command
    Return Value: payload dict
    """
    if args.accesspassword is not None:
        password = args.accesspassword
    else:
        password = common_function.subcommand_pwd("URI Access", True)
    payload = {'ImageURI': args.imageuri, 'SignalURI': args.signatureuri,
               'CrlURI': args.crluri, 'ImageType': 'Driver',
               'TransferProtocol': args.protocol, 'User': args.accessuser,
               'Password': password, 'Parameter': args.parameter}
    if args.activemethod is None:
        payload['ActiveMethod'] = ''
    else:
        payload['ActiveMethod'] = args.activemethod
    return payload


def get_upgrade_progress(client):
    """
    Function Description:Obtain the in-band upgrade progress
    Parameter:client refishClient: class object
    """
    url = "/redfish/v1/Sms/1/UpdateService/Progress"

    resp = client.get_resource(url)
    if resp is None:
        return None

    if resp['status_code'] != common_function.REDFISH_STATUS_CODE_200:
        common_function.get_error_info(resp)
        return resp

    status = resp['resource']['Status']
    while status in ['done', 'uploading', 'tobeupgraded', 'preprocessing']:
        sys.stdout.write("Progress: 0%\r")
        sys.stdout.flush()
        time.sleep(1)

        resp = client.get_resource(url)

        if resp is None:
            return None

        if resp['status_code'] != common_function.REDFISH_STATUS_CODE_200:
            common_function.get_error_info(resp)
            return resp

        status = resp['resource']['Status']

    while status == 'upgrading':
        progress = resp['resource']['Progress']
        sys.stdout.write("Progress: %d%%\r" % int(float(progress)))
        sys.stdout.flush()
        time.sleep(1)

        resp = client.get_resource(url)
        if resp is None:
            return None

        if resp['status_code'] != common_function.REDFISH_STATUS_CODE_200:
            common_function.get_error_info(resp)
            return resp

        status = resp['resource']['Status']

    flag = suc_or_fail_proc(status, resp)
    if not flag:
        sys.exit(common_function.UREST_STATUS_CODE_144)

    return resp


def suc_or_fail_proc(status, resp):
    """
    Function Description:Display final success or failure messages
    Parameter:status str: current upgrade status
    resp dict: response results
    """
    flag = False
    # Modify: 2018.12.21 activing Indicates success
    if status == 'activing':
        print("Success: successfully completed request")
        sys.exit(common_function.UREST_STATUS_CODE_0)

    if status == 'tobeactived':
        # Modify: 2020.01.10 Modifying the log is a "" problem.
        if resp['resource']['Log'] == "":
            print("No device matches the file of the driver to be upgraded.")
            return flag
        failure_items = []
        for item in list(resp['resource']['Log'].values()):
            if item['Result'] != 'success':
                failure_items.append(item['File'])

        len_items = len(failure_items)
        if len_items == 0:
            flag = True
            print('Success: successfully completed request')
        else:
            print('Failure: failed to executing some upgrade packages')
            for item in failure_items:
                print('         %s' % item)

    if status == 'error':
        message_info = resp['resource']['Message']

        if message_info.startswith('ImageURI or SingleURI') is True:
            message_info = message_info.replace('ImageURI or SingleURI',
                                                'image uri or signature uri')
        elif message_info[0].isupper() is True:
            message_info = message_info[0].lower() + message_info[1:]

        if message_info[-1] == '.':
            message_info = message_info[0:-1]
        print('Failure: %s' % message_info)

    return flag


def err_400_proc(resp):
    """
    Function Description:When the response code is 400, process error messages
    Parameter:resp dict: response results
    """
    error_message_dic = {'ImageURI': 'image uri specified by -i',
                         'SignalURI': 'signature uri specified by -si',
                         'Server IP address': 'server ip address '
                                              'specified by -i or -si',
                         'ServerIP:': 'server ip address '
                                      'specified by -i or -si',
                         'Server port': 'server port specified by -i or -si',
                         'Parameter': 'parameter specified by -PARM',
                         'User or Password': 'user name or password'}

    mess_info = resp.get(common_function.BMA_ERROR_KEY)
    mess_info = (mess_info.get(common_function.BMA_ERROR_KEY)
                 if mess_info
                 else "status code %s" % resp.get('status_code'))
    for message_id in error_message_dic:
        if mess_info.startswith(message_id):
            mess_info = mess_info.replace(message_id,
                                          error_message_dic.get(message_id))
            break

    print('Failure: %s' % mess_info)
