-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local utils_core = require 'utils.core'
local common_def = require 'common_def'
local method_misc = require 'method_misc'
local error_engine = require 'error_engine'
local sync_task = require 'sync_task'
local skynet = require 'skynet'

local sml = require 'sml'
local utils = require 'mc.utils'
local class = require 'mc.class'
local bs = require 'mc.bitstring'
local singleton = require 'mc.singleton'
local task_prop = require 'mc.mdb.task_mgmt'
local log = require 'mc.logging'

local config_util = require 'handler.config_util'
local base_messages = require 'messages.base'
local custom_messages = require 'messages.custom'
local c_tasks = require 'mc.orm.tasks'

local ctrl_operation_cmd = require 'controller.ctrl_operation_cmd'
local controller_collection = require 'controller.controller_collection'
local drive_collection = require 'drive.drive_collection'
local volume_collection = require 'volume.volume_collection'
local volume_param_check = require 'volume.volume_param_check'
local array_collection = require 'array.array_collection'
local rpc_service_drive = require 'rpc_services.rpc_service_drive'
local rpc_service_volume = require 'rpc_services.rpc_service_volume'
local c_storageconfig = require 'storageconfig.storageconfig_object'
local storage_bus = require 'storage_bus'

local TASK_IMPORT<const> = 'wait_import_config'
local WAIT_IMPORT_FLAG = 0
local task_state<const> = task_prop.state
local task_status<const> = task_prop.status
local SM_CODE_E<const> = error_engine.SM_CODE_E
local SML_ERR_CODE_E<const> = error_engine.SML_ERR_CODE_E
local SML_MAX_RAID_CONTROLLER <const> = 8 -- SML库管理的RAID控制器的最大支持数
local dump_flag_table = {}
local log_phase_table = {}
local CACHED_IO_FIRMWARE_VERSION<const> = '5.140.00-3515' -- 博通卡固件版本小于该版本时支持Cached IO策略
local CACHED_IO_POLICY<const> = 0

local rpc_service_controller = class()

function rpc_service_controller:ctor(bus)
    self.bus = bus
end

-- 设置控制器前的检查
local function prep_set_properties(ctx, obj)
    if obj.OOBSupport ~= 1 then
        log:error(string.format('[Storage]Controller%s does not support OOB management.', obj.Id))
        error_engine.raise_controller_error(ctx, SML_ERR_CODE_E.SML_ERR_CTRL_STATUS_INVALID)
    end
    if obj.is_working then
        log:error(string.format('[Storage]Controller%s is busy.', obj.Id))
        error_engine.raise_controller_error(ctx, SM_CODE_E.SM_CODE_OPERATION_IN_PROGRESS)
    end
end

local function get_operated_id_list(old_volume_list, new_volume_list)
    if not old_volume_list or not new_volume_list then
        return
    end
    local old_volume_id_list = {}
    -- 获取删除的ID
    for _, volume in ipairs(old_volume_list) do
        if not volume_collection.get_instance():get_ctrl_volume_by_id(volume.controller_id, volume.volume_id) then
           return volume.volume_id
        end
        old_volume_id_list[volume.volume_id] = true
    end
    -- 获取新增的ID
    for _, new_volume in ipairs(new_volume_list) do
        if not old_volume_id_list[new_volume.volume_id] then
            return new_volume.volume_id
        end
    end
end

local function check_ld_data_created_in_existing_array(controller_id, array_id, name, capacity, capacity_unit,
    strip_size, read_policy, write_policy, io_policy, access_policy, disk_cache_policy, init_type)

    local array_obj = array_collection.get_instance():get_ctrl_array_by_id(controller_id, array_id)
    if not array_obj then
        log:error(string.format('[Storage]Invalid ArrayId %s.', array_id))
        error({SML_ERR_CODE_E.SML_ERR_CTRL_STATUS_INVALID, nil, nil})
    end
    local check_ld_params_retval = volume_param_check:check_ld_params(controller_id, name, capacity, capacity_unit,
        strip_size, read_policy, write_policy, io_policy, access_policy, disk_cache_policy, init_type)
    return check_ld_params_retval
end

-- 根据错误码返回有问题的是哪个属性
local function decode_error_codes(retval, args)
    local switch_error_code_param = {
        [common_def.SM_CODE_CAPACITY_OUT_OF_RANGE] = {'%Capacity', args[1]},
        [common_def.SM_CODE_STRIP_SIZE_OUT_OF_RANGE] = {'%StripSize', args[2]},
        [common_def.SM_CODE_READ_POLCIY_OUT_OF_RANGE] = {'%ReadPolicy', args[3]},
        [common_def.SM_CODE_WRITE_POLICY_OUT_OF_RANGE] = {'%WritePolicy', args[4]},
        [common_def.SM_CODE_IO_POLICY_OUT_OF_RANGE] = {'%IOPolicy', args[5]},
        [common_def.SM_CODE_ACCESS_POLICY_OUT_OF_RANGE] = {'%AccessPolicy', args[6]},
        [common_def.SM_CODE_DISK_CAHCE_POLICY_OUT_OF_RANGE] = {'%DiskCachePolicy', args[7]},
        [common_def.SM_CODE_INIT_TYPE_OUT_OF_RANGE] = {'%InitType', args[8]},
        [common_def.SM_CODE_LD_NAME_EXCEED_MAX_LEN] = {'%Name', args[9]},
        [common_def.SM_CODE_LD_NAME_INVALID_ASCII] = {'%Name', args[9]},
        [common_def.SM_CODE_INVALID_RAID_LEVEL] = {'%RaidType', args[10]},
        [common_def.SM_CODE_INVALID_SPAN_DEPTH] = {'%SpanDepth', args[11]},
        [common_def.SM_CODE_INVALID_PD_COUNT] = {'%DriveLists', args[12]},
        [common_def.SML_ERR_CONFIG_BLOCK_SIZE_NOT_SAME] = {'', ''},
    }
    return switch_error_code_param[retval]
end

local function check_io_policy_allowed(controller, io_policy)
    if method_misc:test_controller_vendor(controller.TypeId, common_def.VENDER_LSI) and
    controller.FirmwareVersion >= CACHED_IO_FIRMWARE_VERSION and
    io_policy == CACHED_IO_POLICY then
        return false
    end
    return true
end

local function check_io_policy(ctx, operate_name, controller, ...)
    local io_policy
    if operate_name == 'CreateVolumeInExistingArray' then
        io_policy = select(11, ...)
        if not check_io_policy_allowed(controller, io_policy) then
            local array_id = select(1, ...)
            log:operation(ctx:get_initiator(), 'storage', 'Add RAID controller %s logical drive on Array %s failed',
                controller.Id, array_id)
            error(custom_messages.InvalidIOPolicy())
        end
    end
    if operate_name == 'CreateVolumeInNewArray' then
        io_policy = select(10, ...)
        if not check_io_policy_allowed(controller, io_policy) then
            local raid_type = select(2, ...)
            local raid_level = common_def.RAID_LEVEL_DESC[raid_type] or 'Unknown'
            log:operation(ctx:get_initiator(), 'storage', 'Create RAID controller %s logical drive %s failed',
                controller.Id, raid_level)
            error(custom_messages.InvalidIOPolicy())
        end
    end
end

local function check_params_of_create_volume_in_existing_array(obj, array_id, block_index, raid_type,
    span_depth, name, capacity, capacity_unit, strip_size, read_policy, write_policy, io_policy, access_policy,
    disk_cache_policy, init_type, accelerator)
    if not obj.CreateVolumeSupported then
        log:error('[Storage]Creating volume is not supported in current state')
        error({SML_ERR_CODE_E.SML_ERR_CTRL_STATUS_INVALID, nil, nil})
    end
    local create_retval = check_ld_data_created_in_existing_array(obj.Id, array_id, name, capacity,
        capacity_unit, strip_size, read_policy, write_policy, io_policy, access_policy, disk_cache_policy, init_type)
    if create_retval ~= common_def.SM_CODE_SUCCESS then
        log:error(string.format('[Storage]Invalid param, and return: %s', create_retval))
        local prop = decode_error_codes(create_retval,
            {tostring(capacity), common_def.LD_STRIP_SIZE_STR[strip_size], tostring(read_policy),
            tostring(write_policy), tostring(io_policy), tostring(access_policy), tostring(disk_cache_policy),
            tostring(init_type), name, nil, nil, nil})
        error({create_retval, prop[1], prop[2]})
    end
end

local function check_params_of_create_volume_in_new_array(obj, drive_lists, raid_type, span_depth, name,
    capacity, capacity_unit, strip_size, read_policy, write_policy, io_policy, access_policy, disk_cache_policy,
    init_type, accelerator)
    if not obj.CreateVolumeSupported then
        log:error('[Storage]Creating volume is not supported in current state')
        error({SML_ERR_CODE_E.SML_ERR_CTRL_STATUS_INVALID, nil, nil})
    end

    -- 检查输入的硬盘是否都是受RAID卡管理的硬盘
    local trans_list = {string.byte(drive_lists, 1, #drive_lists)}
    for k, v in pairs(trans_list) do
        if not drive_collection.get_instance():get_drive_by_id(obj.Id, v) then
            error({SM_CODE_E.SM_CODE_INVALID_PD_ID, '%DriveLists', v})
        end
    end
    local check_ld_data_retval = volume_param_check:check_ld_data_created_in_new_array(obj.Id, drive_lists, raid_type,
        span_depth, name, capacity, capacity_unit, strip_size, read_policy, write_policy, io_policy, access_policy,
        disk_cache_policy, init_type)
    if check_ld_data_retval ~= common_def.SM_CODE_SUCCESS then
        log:error(string.format('[Storage]Invalid param, and return: %s', check_ld_data_retval))
        local prop = decode_error_codes(check_ld_data_retval,
            {tostring(capacity), common_def.LD_STRIP_SIZE_STR[strip_size], tostring(read_policy),
            tostring(write_policy), tostring(io_policy), tostring(access_policy), tostring(disk_cache_policy),
            tostring(init_type), name, tostring(raid_type), tostring(span_depth),
            string.format('[%s]', table.concat(trans_list, ", "))})
        error({check_ld_data_retval, prop[1], prop[2]})
    end
end

local function check_volume_status(obj, volume_id)

    local volume_obj = volume_collection.get_instance():get_ctrl_volume_by_id(obj.Id, volume_id)
    if not volume_obj then
        error({SML_ERR_CODE_E.SML_ERR_LD_INVALID_TARGET_ID, nil, nil})
    end
    if volume_obj.CurrentForegroundInitState == 1 then
        error({SM_CODE_E.SM_CODE_OPERATION_IN_PROGRESS, nil, nil})
    end
end

function rpc_service_controller:check_params_before_task(operate_name, ctx, ...)
    local ok, ret
    if operate_name == 'CreateVolumeInExistingArray' then
        ok, ret = pcall(check_params_of_create_volume_in_existing_array, ...)
    elseif operate_name == 'CreateVolumeInNewArray' then
        ok, ret = pcall(check_params_of_create_volume_in_new_array, ...)
    elseif operate_name == 'DeleteVolume' then
        ok, ret = pcall(check_volume_status, ...)
    else
        return
    end
    if not ok and ret then
        log:error('[Storage]Failed to %s, and return: %s', operate_name, ret[1])
        error_engine.raise_controller_error(ctx, ret[1], ret[2], ret[3])
    end
end

local function controller_log_operation(ctx, err_code, prop_name, controller_id, set_state)
    local log_operation_ret = 'successfully'
    if err_code ~= common_def.RET_OK and err_code ~= common_def.SML_ERR_REBOOT_REQUIRED then
        log_operation_ret =
            err_code == common_def.SML_ERR_CTRL_OPERATION_NOT_SUPPORT and 'not supported' or 'failed'
    end
    if err_code == common_def.SML_ERR_REBOOT_REQUIRED then
        log_operation_ret = 'successfully, please reboot OS to take effect'
    end
    if ctx and ctx.get_initiator then
        log:operation(ctx:get_initiator(), 'storage', 'Set RAID controller %s %s %s %s', controller_id,
            prop_name, set_state, log_operation_ret)
    end
end

-- 设置属性的入口
function rpc_service_controller:ctrl_task_operate(operate_name, controller_path, controller_id, ctx, ...)
    local operations = {
        ["CreateVolumeInExistingArray"] = self.create_volume_in_existing_array,
        ["DeleteVolume"] = self.delete_volume,
        ["CreateVolumeInNewArray"] = self.create_volume_in_new_array,
        ["CreateCacheCadeVolume"] = self.create_ld_as_cachecade
    }
    log:notice('[Storage]Start %s of Controller%s', operate_name, controller_id)
    local controller = controller_collection:get_by_controller_id(controller_id)
    if not controller then
        error_engine.raise_controller_error(ctx, SML_ERR_CODE_E.SML_ERR_CTRL_INDEX_INVALID)
    end
    prep_set_properties(ctx, controller)
    self:check_params_before_task(operate_name, ctx, controller, ...)
    check_io_policy(ctx, operate_name, controller, ...)
    local task_name = string.format('%s:C%s', operate_name, controller_id)
    local task_id = sync_task.create_task(self.bus, task_name, controller_path)
    skynet.fork(function(cb, cb_name, ...)
        sync_task.update_task_prop(task_id, {State = task_state.Running, Progress = 10})
        local old_volume_list = controller:get_ctrl_volume_list()
        local ok, ret = pcall(cb, controller, ctx, ...)
        controller.on_update_volume_array:emit()
        if not ok then
            if ret[1] ~= common_def.SML_ERR_REBOOT_REQUIRED then
                log:error('[Storage]Failed to %s, and return: %s', cb_name, ret[1])
                local message_id, message_args = error_engine.get_controller_error_message(ret[1], ret[2], ret[3])
                sync_task.update_task_prop(task_id, {State = task_state.Exception, Status = task_status.Error,
                    MessageId = message_id, MessageArgs = message_args})
                return
            end
            log:notice('[Storage]Successfully %s of Controller%s, please restart OS.', cb_name, controller_id)
        else
            log:notice('[Storage]Successfully %s of Controller%s.', cb_name, controller_id)
        end
        local new_volume_list = controller:get_ctrl_volume_list()

        local id = get_operated_id_list(old_volume_list, new_volume_list)
        local url = id and string.format('/redfish/v1/Systems/1/Storages/RAIDStorage%s/Volumes/LogicalDrive%s',
            controller_id, id) or ''
        if string.find(cb_name, 'Create') ~= nil then
            sync_task.update_task_prop(task_id, {Progress = 100,
                MessageId = 'VolumeCreationSuccess', MessageArgs = {url}})
        else
            sync_task.update_task_prop(task_id, {Progress = 100,
                MessageId = 'VolumeDeletionSuccess', MessageArgs = {url}})
        end
    end, operations[operate_name], operate_name, ...)

    return task_id
end

-- 设置属性的入口
function rpc_service_controller:ctrl_operate(operate_name, controller_id, ctx, ...)
    local operations = {
        ["SetCopyBackState"] = self.set_copyback_state,
        ["SetSmarterCopyBackState"] = self.set_smart_copyback_state,
        ["SetJBODState"] = self.set_jbod_state,
        ["SetMaintainPDFailHistoryState"] = self.set_maintain_pd_fail_history_state,
        ["RestoreDefaultSettings"] = self.restore_default_settings,
        ["Retirement"] = self.retirement,
        ["SetBootDevices"] = self.set_boot_devices,
        ["DisableCCheck"] = self.disable_consistency_check,
        ["EnableCCheck"] = self.enable_consistency_check,
        ["SetCCheck"] = self.set_consistency_check,
        ["SetWorkMode"] = self.set_work_mode,
        ["ClearForeignConfig"] = self.clear_foreign_config,
        ["ImportForeignConfig"] = self.import_foreign_config,
        ["CreateVolumeInExistingArray"] = self.create_volume_in_existing_array,
        ["DeleteVolume"] = self.delete_volume,
        ["CreateVolumeInNewArray"] = self.create_volume_in_new_array,
        ["CreateCacheCadeVolume"] = self.create_ld_as_cachecade,
        ["DumpLog"] = self.dump_log,
        ["SetNoBatteryWriteCache"] = self.set_no_battery_write_cache,
        ["SetReadCachePercent"] = self.set_read_cache_percent,
        ["SetWriteCachePolicy"] = self.set_write_cache_policy
    }
    log:notice('[Storage]Start %s of Controller%s', operate_name, controller_id)

    local controller = controller_collection:get_by_controller_id(controller_id)
    if not controller then
        error_engine.raise_controller_error(ctx, SML_ERR_CODE_E.SML_ERR_CTRL_INDEX_INVALID)
    end

    prep_set_properties(ctx, controller)

    local ok, ret = pcall(operations[operate_name], controller, ctx, ...)
    if not ok and ret then
        if ret[1] ~= common_def.SML_ERR_REBOOT_REQUIRED then
            log:error('[Storage]Failed to %s, and return: %s', operate_name, ret[1])
        else
            log:notice('[Storage]Successfully %s of Controller%s, please restart OS.', operate_name, controller_id)
        end
        error_engine.raise_controller_error(ctx, ret[1], ret[2], ret[3])
    else
        log:notice('[Storage]Successfully %s of Controller%s.', operate_name, controller_id)
    end

    -- 设置成功后刷新资源树上的信息
    local ctrl_info
    ok, ctrl_info = pcall(sml.get_ctrl_info, {Priority = 'High'}, controller.Id)
    if ok then
        controller.on_update:emit(ctrl_info)
    end

    if operate_name == 'CreateVolumeInNewArray' or operate_name == 'CreateVolumeInExistingArray' or
       operate_name == 'DeleteVolume' or operate_name == 'CreateCacheCadeVolume'then
        controller.on_update_volume_array:emit()
    end
    return ret
end

-- 设置控制器的回拷开关
function rpc_service_controller.set_copyback_state(obj, ctx, copyback_state)
    -- 0: 关闭；1: 打开
    if copyback_state ~= 0 and copyback_state ~= 1 then
        log:error(string.format('[Storage]The input %s is invalid.', copyback_state))
        error({common_def.SM_CODE_PARA_DATA_ILLEGAL, 'CopybackState', tostring(copyback_state)})
    end

    if obj.CopyBackState == copyback_state then
        return
    end

    -- 命令是设置Copyback是否禁止，因此copyback_state要取反
    local param = (copyback_state == 0) and 1 or 0
    obj.is_working = true
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_DISABLE_COPYBACK, string.pack('B', param))
    end)
    obj.is_working = false
    local set_state = copyback_state == 1 and 'enabled' or 'disabled'
    controller_log_operation(ctx, ret, 'Copyback', obj.Id, set_state)
    if ret ~= common_def.RET_OK then
        log:error(string.format('[Storage]Failed to set the CopybackState and ret: %s.', ret))
        error({ret, 'CopybackState', tostring(copyback_state)})
    else
        obj.CopyBackState = copyback_state
    end
end

-- 设置控制器的Smart信息错误回拷开关
function rpc_service_controller.set_smart_copyback_state(obj, ctx, smart_copyback_state)
    -- 0: 关闭；1: 打开
    if smart_copyback_state ~= 0 and smart_copyback_state ~= 1 then
        log:error(string.format('[Storage]The input %s is invalid.', smart_copyback_state))
        error({common_def.SM_CODE_PARA_DATA_ILLEGAL, 'SmarterCopyBackState', tostring(smart_copyback_state)})
    end

    if obj.SmarterCopyBackState == smart_copyback_state then
        return
    end

    obj.is_working = true
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_ENABLE_SMART_COPYBACK,
            string.pack('B', smart_copyback_state))
    end)
    obj.is_working = false
    local set_state = smart_copyback_state == 1 and 'enabled' or 'disabled'
    controller_log_operation(ctx, ret, 'Copyback on SMART error', obj.Id, set_state)
    if ret ~= common_def.RET_OK then
        log:error(string.format('[Storage]Failed to set the SmarterCopybackState and ret: %s.', ret))
        error({ret, 'SmarterCopyBackState', tostring(smart_copyback_state)})
    else
        obj.SmarterCopyBackState = smart_copyback_state
    end
end

-- 设置控制器的JBOD开关
function rpc_service_controller.set_jbod_state(obj, ctx, jbod_state)
    -- 0: 关闭；1: 打开
    if jbod_state ~= 0 and jbod_state ~= 1 then
        log:error(string.format('[Storage]The input %s is invalid.', jbod_state))
        error({common_def.SM_CODE_PARA_DATA_ILLEGAL, 'JBODState', tostring(jbod_state)})
    end

    if obj.JBODState == jbod_state then
        return
    end

    obj.is_working = true
    local set_state = jbod_state == 1 and 'enabled' or 'disabled'
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_ENABLE_JBOD, string.pack('B', jbod_state))
    end)
    controller_log_operation(ctx, ret, 'JBOD', obj.Id, set_state)
    obj.is_working = false
    if ret ~= common_def.RET_OK then
        log:error(string.format('[Storage]Failed to set the JBODState and ret: %s.', ret))
        error({ret, 'JBODState', tostring(jbod_state)})
    else
        obj.JBODState = jbod_state
    end
end

function rpc_service_controller.set_maintain_pd_fail_history_state(obj, ctx, maintainpdfailhistory_state)
    -- 0: 关闭；1: 打开
    if maintainpdfailhistory_state ~= 0 and maintainpdfailhistory_state ~= 1 then
        log:error('[Storage]The input %s is invalid.', maintainpdfailhistory_state)
        error({common_def.SM_CODE_PARA_DATA_ILLEGAL, 'MaintainPDFailHistoryState',
            tostring(maintainpdfailhistory_state)})
    end

    if obj.MaintainPDFailHistrory == maintainpdfailhistory_state then
        return
    end

    obj.is_working = true
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_SET_MAINTAIN_PDFAIL_HISTORY,
            string.pack('B', maintainpdfailhistory_state))
    end)
    obj.is_working = false

    local set_state = maintainpdfailhistory_state == 1 and 'enabled' or 'disabled'
    local operation_status = ret == common_def.RET_OK and 'Successfully' or 'Failed to'
    if ctx and ctx.get_initiator then
        log:operation(ctx:get_initiator(), '%s %s maintpdfailhistory for RAID controller %s', operation_status,
            set_state, controller_id)
    end
    if ret ~= common_def.RET_OK then
        log:error('[Storage]Failed to set the MaintainPDFailHistoryState and ret: %s.', ret)
        error({ret, 'MaintainPDFailHistoryState', tostring(maintainpdfailhistory_state)})
    else
        obj.MaintainPDFailHistrory = maintainpdfailhistory_state
    end
end

-- 恢复控制器的默认配置
function rpc_service_controller.restore_default_settings(obj, ctx)
    obj.is_working = true
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_RESTORE_DEFAULT, '')
    end)
    obj.is_working = false
    -- 博通卡需要重启默认配置才能生效
    local log_operation_ret = (ret == 0 or ret == common_def.SML_ERR_REBOOT_REQUIRED) and
        'successfully' or 'failed'
    if ctx and ctx.get_initiator then
        log:operation(ctx:get_initiator(), 'storage', 'Restore RAID controller %s settings %s', obj.Id,
            log_operation_ret)
    end
    if ret ~= common_def.RET_OK and ret ~= common_def.SML_ERR_REBOOT_REQUIRED then
        log:error(string.format('[Storage]Failed to restore the default settings and ret: %s.', ret))
        error({ret, nil, nil})
    end
end

-- 单板报废场景，执行数据报废，恢复控制器默认配置
local function update_retirement(obj, result)
    obj.Media = 'Flash'
    obj.Source = 'Huawei'
    obj.Description = 'Clear'
    obj.Method = 'BlockErase'
    obj.Result = result
    obj.Progress = 100
    obj.RetirementState = 'Finish'
end

local function retirement_data_wipe(obj)
    obj.is_working = true
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_RESTORE_DEFAULT, '')
    end)
    obj.is_working = false
    -- 博通卡需要重启默认配置才能生效
    local result = (ret == 0 or ret == common_def.SML_ERR_REBOOT_REQUIRED)  and 'Successful' or 'Failed'

    if result == 'Failed' then
        log:error(string.format('[Storage]Failed to restore the default settings and ret: %s.', ret))
    else
        log:notice('[Storage]RAID controller %s data wipe %s', obj.Id, result)
    end
    update_retirement(obj, result)
end

local function retirement_get_report(obj)
    local report = {}
    report.Manufacturer = obj.ChipManufacturer
    report.Model = obj.Model
    report.SerialNumber = obj.SerialNumber
    report.Media = "Flash"
    report.Source = "Huawei"
    report.Description = "Clear"
    report.Method = "BlockErase"
    report.Result = obj.Result
    return report
end

function rpc_service_controller.retirement(obj, op)
    if op == "DataWipe" then
        -- 初始化资源树状态
        obj.RetirementState = "Idle"
        obj.Progress = 0
        -- 启动后台任务执行数据销毁
        skynet.fork_once(function ()
            return retirement_data_wipe(obj)
        end)
    elseif op == "GetReport" then
        return retirement_get_report(obj)
    end
end

-- 判断输入的设备类型与序号是否正确
local function check_input_device_name(controller_id, device_name)
    if device_name == 'None' then
        return 'None'
    end

    -- 检查输入的设备名格式是否正确
    if string.len(string.match(device_name, '%a+%d+')) ~= string.len(device_name) then
        log:error('[Storage]Illegal device name!')
        error({common_def.SM_CODE_PARA_DATA_ILLEGAL, 'DeviceName', device_name})
    end

    local device_type = string.match(device_name, '%a+')
    local device_id = tonumber(string.match(device_name, '%d+'))

    -- 检查设备类型是否支持
    if device_type ~= 'Volume' and device_type ~= 'Disk' then
        log:error('[Storage]Unsupported device type!')
        error({common_def.SM_CODE_PARA_DATA_ILLEGAL, 'DeviceName', device_name})
    end

    -- 检查设备序号是否有效
    if (device_type == 'Volume' and volume_collection.get_instance():get_ctrl_volume_by_id(controller_id, device_id)) or
        (device_type == 'Disk' and drive_collection.get_instance():get_drive_by_drive_id(device_id)) then
        return device_name
    else
        log:error('[Storage]Invalid device id!')
        error({common_def.SM_CODE_PARA_DATA_ILLEGAL, 'DeviceName', device_name})
    end
end

-- 解析设置启动设备的操作类型
local function get_operation_list(obj, primary_device_name, secondary_device_name)
    local operation_list = {}
    if primary_device_name == secondary_device_name then
        if primary_device_name == 'None' then
            if obj.BootDevices[1] == obj.BootDevices[2] then
                local operation = {device_name = obj.BootDevices[1],
                                   operation_cmd = ctrl_operation_cmd.BOOT_PRIORITY_NONE}
                table.insert(operation_list, operation)
            else
                local operation_1 = {device_name = obj.BootDevices[1],
                                     operation_cmd = ctrl_operation_cmd.BOOT_PRIORITY_NONE}
                local operation_2 = {device_name = obj.BootDevices[2],
                                     operation_cmd = ctrl_operation_cmd.BOOT_PRIORITY_NONE}
                table.insert(operation_list, operation_1)
                table.insert(operation_list, operation_2)
            end
        else
            local operation = {device_name = primary_device_name,
                               operation_cmd = ctrl_operation_cmd.BOOT_PRIORITY_ALL}
            table.insert(operation_list, operation)
        end
    else
        if primary_device_name ~= 'None' and secondary_device_name ~= 'None' then
            local operation_1 = {device_name = primary_device_name,
                                 operation_cmd = ctrl_operation_cmd.BOOT_PRIORITY_PRIMARY}
            local operation_2 = {device_name = secondary_device_name,
                                 operation_cmd = ctrl_operation_cmd.BOOT_PRIORITY_SECONDARY}
            table.insert(operation_list, operation_1)
            table.insert(operation_list, operation_2)
        else
            if primary_device_name == 'None' then
                local operation_1 = {device_name = obj.BootDevices[1],
                                     operation_cmd = ctrl_operation_cmd.BOOT_PRIORITY_NONE}
                local operation_2 = {device_name = secondary_device_name,
                                     operation_cmd = ctrl_operation_cmd.BOOT_PRIORITY_SECONDARY}
                table.insert(operation_list, operation_1)
                table.insert(operation_list, operation_2)
            else
                local operation_1 = {device_name = obj.BootDevices[2],
                                     operation_cmd = ctrl_operation_cmd.BOOT_PRIORITY_NONE}
                local operation_2 = {device_name = primary_device_name,
                                     operation_cmd = ctrl_operation_cmd.BOOT_PRIORITY_PRIMARY}
                table.insert(operation_list, operation_1)
                table.insert(operation_list, operation_2)
            end
        end
    end
    return operation_list
end

-- 设置控制器的启动设备
function rpc_service_controller.set_boot_devices(obj, ctx, primary_device_name, secondary_device_name)
    primary_device_name = check_input_device_name(obj.Id, primary_device_name)
    secondary_device_name = check_input_device_name(obj.Id, secondary_device_name)
    if obj.TypeId >= common_def.HI1880_SP186_M_16i and obj.TypeId <= common_def.HI1880_SP186_M_8i then
        if secondary_device_name ~= 'None' then
            log:error('[Storage]The secondary_device must be None!')
            error({common_def.SM_CODE_PARA_DATA_ILLEGAL, 'SecondaryDeviceName', secondary_device_name})
        end
    end
    local operation_list = get_operation_list(obj, primary_device_name, secondary_device_name)
    local ret = common_def.RET_OK
    for _, operation in pairs(operation_list) do
        if string.match(operation.device_name,'%a+') == 'Disk' then
            rpc_service_drive.get_instance():drive_operate('SetBootPriority', operation.device_name,
                ctx, operation.operation_cmd)
        elseif string.match(operation.device_name,'%a+') == 'Volume' then
            local volume_id = tonumber(string.match(operation.device_name,'%d+'))
            rpc_service_volume.get_instance():volume_operate('SetBootable', obj.Id, volume_id,
                ctx, operation.operation_cmd)
        elseif operation.device_name ~= 'None' then
            ret = common_def.RET_ERR
        end
        if ret ~= common_def.RET_OK then
            log:error('[Storage]Failed to set boot devices, and ret: %s', ret)
            local device_list = string.format('{%s, %s}', primary_device_name, secondary_device_name)
            error({ret, 'DeviceNames', device_list})
        end
    end
end

-- 打包一致性校验参数
local work_mode_param = bs.new([[<<mode:8,profile_id:8>>]])
local get_work_mode_param = function(mode, profile_id)
    local param = work_mode_param.new()
    param.mode = mode
    param.profile_id = profile_id
    return work_mode_param:pack(param)
end

local work_mode = {'RAID', 'HBA', 'JBOD', 'Mixed'}
-- 将值作为key，方便查找值是否存在
local function revert_table(t)
    local revt = {}
    for _, v in pairs(t) do
        revt[v] = true
    end
    return revt
end
-- 设置控制器的工作模式
function rpc_service_controller.set_work_mode(obj, ctx, mode, profile_id)
    if profile_id == nil then
        profile_id = 0
    end
    -- 查看ctrl是否支持设置mode
    if method_misc:test_controller_vendor(obj.TypeId, common_def.VENDER_HUAWEI) then
        log:error('[Storage]RAID Controller do not support set work mode.')
        error({SML_ERR_CODE_E.SML_ERR_CTRL_OPERATION_NOT_SUPPORT, 'WorkMode', nil})
    end

    -- 查看ctrl是否支持该工作模式
    local rev_obj_workmode = revert_table(obj.SupportedMode)
    if not rev_obj_workmode[work_mode[mode + 1]] then
        log:error(string.format('[Storage]RAID Controller do not support work mode: %s.', work_mode[mode + 1]))
        error({SM_CODE_E.SM_CODE_PARA_DATA_ILLEGAL, 'WorkMode', work_mode[mode + 1]})
    end

    obj.is_working = true
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_SET_PERSONALITY_MODE,
            get_work_mode_param(mode, profile_id))
    end)
    obj.is_working = false
    local set_state = work_mode[mode + 1] or 'Unknown'
    controller_log_operation(ctx, ret, 'mode to', obj.Id, set_state)
    if ret ~= common_def.RET_OK then
        log:error(string.format('[Storage]Failed to set work mode and ret: %s.', ret))
        error({ret, 'WorkMode', work_mode[mode + 1]})
    end
end


local function check_consistency_period(period)
    local mask = 0
    if period ~= 0xFFFF then
        if period < ctrl_operation_cmd.CTRL_CC_PERIOD_MIN or period > ctrl_operation_cmd.CTRL_CC_PERIOD_MAX or
            period % ctrl_operation_cmd.CTRL_CC_PERIOD_MIN ~= 0 then
            log:error('[Storage]The period is invalid')
            return false, 0
        else
            mask = mask | ctrl_operation_cmd.CTRL_CC_MASK_PERIOD
        end
    end
    return true, mask
end

local function check_consistency_rate(rate, mask)
    if rate ~= 0xFF then
        if rate ~= ctrl_operation_cmd.CTRL_CCHECK_RATE_LOW and rate ~= ctrl_operation_cmd.CTRL_CCHECK_RATE_MIDDLE and
            rate ~= ctrl_operation_cmd.CTRL_CCHECK_RATE_HIGH then
            log:error('[Storage]The rate is invalid')
            return false, 0
        else
            mask = mask | ctrl_operation_cmd.CTRL_CC_MASK_RATE
        end
    end
    return true, mask
end

local function check_consistency_auto_repaired(auto_repaired, mask)
    if auto_repaired ~= 0xFF then
        if auto_repaired ~= ctrl_operation_cmd.CTRL_CC_ENABLE and
            auto_repaired ~= ctrl_operation_cmd.CTRL_CC_DISABLE then
            log:error('[Storage]The auto repaired enable is invalid')
            return false, 0
        else
            mask = mask | ctrl_operation_cmd.CTRL_CC_MASK_REPAIR
        end
    end
    return true, mask
end

local function check_consistency_delay(delay, mask)
    if delay and delay ~= 0xFFFFFFFF then
        if delay > ctrl_operation_cmd.CTRL_CC_DELAY_MAX then
            log:error('[Storage]The delay is invalid')
            return false, 0
        else
            mask = mask | ctrl_operation_cmd.CTRL_CC_MASK_DELAY
        end
    end
    return true, mask
end


-- 检查输入的控制器的一致性校验功能参数是否正常
local function check_consistency_parameters(period, rate, auto_repaired, delay)
    local ret, mask = check_consistency_period(period)
    if not ret then
        return false, {"Period", period}
    end
    ret, mask = check_consistency_rate(rate, mask)
    if not ret then
        return false, {"Rate", rate}
    end
    ret, mask = check_consistency_auto_repaired(auto_repaired, mask)
    if not ret then
        return false, {"AutoRepaired", auto_repaired}
    end
    ret, mask = check_consistency_delay(delay, mask)
    if not ret then
        return false, {"Delay", delay}
    end

    return true, mask
end

-- 一致性校验设置属性值
local function set_consistency_parameters(obj, state, period, rate, auto_repaired, delay, mask)
    local current_state = obj.ConsistencyCheckState
    local rate_array = {'Low', 'Medium', 'High'}
    local running_state = {'Off', 'On'}
    if state ~= ctrl_operation_cmd.CTRL_CC_SET and state ~= current_state then
        obj.ConsistencyCheckState = state
        obj.RunningStatus = running_state[state + 1]
    end
    if state == ctrl_operation_cmd.CTRL_CC_DISABLE then
        obj.DelayToStart = 0
        return
    end
    if mask & ctrl_operation_cmd.CTRL_CC_MASK_PERIOD ~= 0 then
        obj.PeriodOfHours = period
    end
    if mask & ctrl_operation_cmd.CTRL_CC_MASK_RATE ~= 0 then
        obj.Rate = rate_array[rate]
    end
    if mask & ctrl_operation_cmd.CTRL_CC_MASK_REPAIR ~= 0 then
        obj.AutoRepairEnabled = auto_repaired
    end
    if mask & ctrl_operation_cmd.CTRL_CC_MASK_DELAY ~= 0 then
        obj.DelayToStart = delay
        if obj.PeriodOfHours > 0 then
            obj.RunningStatus = running_state[ctrl_operation_cmd.CTRL_CC_DISABLE]
        end
    end
    if obj.DelayToStart == 0xFFFFFFFF then
        obj.DelayToStart = 0
    end
end

-- 获取一致性校验操作日志参数值
local function get_consistency_log_parameters(obj, state, period, rate, auto_repaired, delay, mask)
    local rate_array = {'Low', 'Medium', 'High'}
    local running_state = {'Off', 'On'}
    local params = {
        operation = state,
        data = ''
    }
    local used_datas = {}
    if state == ctrl_operation_cmd.CTRL_CC_DISABLE then
        return params
    end
    if mask & ctrl_operation_cmd.CTRL_CC_MASK_PERIOD ~= 0 then
        params.data = string.format('Period : %s hours', period)
        used_datas['PeriodOfHours'] = period
    else
        used_datas['PeriodOfHours'] = obj.PeriodOfHours
    end

    if mask & ctrl_operation_cmd.CTRL_CC_MASK_RATE ~= 0 then
        if string.len(params.data) > 0 then
            params.data = string.format('%s, Rate : %s', params.data, rate_array[rate])
        else
            params.data = string.format('Rate : %s', rate_array[rate])
        end
        used_datas['Rate'] = rate_array[rate]
    else
        used_datas['Rate'] = obj.Rate
    end

    if mask & ctrl_operation_cmd.CTRL_CC_MASK_REPAIR ~= 0 then
        if string.len(params.data) > 0 then
            params.data = string.format('%s, AutoRepair : %s', params.data, running_state[auto_repaired + 1])
        else
            params.data = string.format('AutoRepair : %s', running_state[auto_repaired + 1])
        end
        used_datas['AutoRepairEnabled'] = running_state[auto_repaired + 1]
    else
        used_datas['AutoRepairEnabled'] = running_state[obj.AutoRepairEnabled + 1]
    end

    if mask & ctrl_operation_cmd.CTRL_CC_MASK_DELAY == 0 then
        delay = obj.DelayToStart
    end
    used_datas['DelayToStart'] = delay

    if state == ctrl_operation_cmd.CTRL_CC_ENABLE then
        params.data = string.format('Period : %s hours, Rate : %s, AutoRepair : %s, Delay : %s hours',
        used_datas.PeriodOfHours, used_datas.Rate, used_datas.AutoRepairEnabled, used_datas.DelayToStart)
    end

    return params
end

-- 打包一致性校验参数
local consistency_check_param = bs.new([[<<sw:8,period:16,rate:8,auto_repaired:8,delay:32,mask:8>>]])
local get_consistency_check_param = function(sw, period, rate, auto_repaired, delay, mask)
    local param = consistency_check_param.new()
    param.sw = sw
    param.period = period
    param.rate = rate
    param.auto_repaired = auto_repaired
    param.delay = delay
    param.mask = mask
    return consistency_check_param:pack(param)
end

-- 打印一致性校验操作日志
local function controller_ccheck_operation_log(ctx, err_code, controller_id, params)
    if not ctx or not ctx.get_initiator then
        return
    end

    local log_operation_ret = 'successfully'
    if err_code ~= common_def.RET_OK and err_code ~= common_def.SML_ERR_REBOOT_REQUIRED then
        log_operation_ret =
            err_code == common_def.SML_ERR_CTRL_OPERATION_NOT_SUPPORT and 'not supported' or 'failed'
    end
    if err_code == common_def.SML_ERR_REBOOT_REQUIRED then
        log_operation_ret = 'successfully, please reboot OS to take effect'
    end

    local operation_names = {
        [ctrl_operation_cmd.CTRL_CC_DISABLE] = "Disable",
        [ctrl_operation_cmd.CTRL_CC_ENABLE] = "Start",
        [ctrl_operation_cmd.CTRL_CC_SET] = "Set"
    }

    local operation_name = operation_names[params.operation]

    if params.operation == ctrl_operation_cmd.CTRL_CC_DISABLE then
        log:operation(ctx:get_initiator(), 'storage', '%s the consistency check of RAID controller %s %s',
            operation_name, controller_id, log_operation_ret)
        return
    end
    log:operation(ctx:get_initiator(), 'storage', '%s the consistency check of RAID controller %s with [%s] %s',
        operation_name, controller_id, params.data, log_operation_ret)
end


-- 打开控制器的一致性校验功能
function rpc_service_controller.enable_consistency_check(obj, ctx, period, rate, auto_repaired, delay)
    -- 查看ctrl是否支持设置一致性校验
    if not method_misc:test_controller_vendor(obj.TypeId, common_def.VENDER_HUAWEI) then
        log:error('[Storage]RAID Controller do not support consistency check.')
        error({SML_ERR_CODE_E.SML_ERR_CTRL_OPERATION_NOT_SUPPORT, 'ConsistencyCheck', nil})
    end
    -- 如果重复开启，并且有参数，那就当作set处理
    if obj.ConsistencyCheckState == 1 and delay == 0xFFFFFFFF then
        rpc_service_controller.set_consistency_check(obj, period, rate, auto_repaired)
        return
    end

    local sw = ctrl_operation_cmd.CTRL_CC_ENABLE
    local ok, mask = check_consistency_parameters(period, rate, auto_repaired, delay)
    if not ok then
        log:error('[Storage]Invalid input param')
        error({SM_CODE_E.SM_CODE_PARA_DATA_ILLEGAL, mask[1], mask[2]})
    end
    obj.is_working = true
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_ENABLE_CC,
            get_consistency_check_param(sw, period, rate, auto_repaired, delay, mask))
    end)
    obj.is_working = false
    local log_params = get_consistency_log_parameters(obj, sw, period, rate, auto_repaired, delay, mask)
    controller_ccheck_operation_log(ctx, ret, obj.Id, log_params)
    if ret ~= common_def.RET_OK then
        log:error(string.format('[Storage]Failed to enable the consistency check and ret: %s.', ret))
        error({ret, 'ConsistencyCheck', nil})
    end
    set_consistency_parameters(obj, sw, period, rate, auto_repaired, delay, mask)
end

-- 关闭控制器的一致性校验功能
function rpc_service_controller.disable_consistency_check(obj, ctx)
    -- 查看ctrl是否支持设置一致性校验
    if not method_misc:test_controller_vendor(obj.TypeId, common_def.VENDER_HUAWEI) then
        log:error('[Storage]RAID Controller do not support consistency check.')
        error({SML_ERR_CODE_E.SML_ERR_CTRL_OPERATION_NOT_SUPPORT, 'ConsistencyCheck', nil})
    end
    local sw = ctrl_operation_cmd.CTRL_CC_DISABLE
    obj.is_working = true
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_ENABLE_CC,
            get_consistency_check_param(sw, 0, 0, 0, 0, 0))
    end)
    obj.is_working = false
    local log_params = get_consistency_log_parameters(obj, sw, 0, 0, 0, 0, 0)
    controller_ccheck_operation_log(ctx, ret, obj.Id, log_params)
    if ret ~= common_def.RET_OK then
        log:error(string.format('[Storage]Failed to disable the consistency check and ret: %s.', ret))
        error({ret, 'ConsistencyCheck', nil})
    end
    set_consistency_parameters(obj, sw, 0, 0, 0, 0, 0)
end

-- 设置控制器的一致性校验功能
function rpc_service_controller.set_consistency_check(obj, ctx, period, rate, auto_repaired)
    -- 查看ctrl是否支持设置一致性校验
    if not method_misc:test_controller_vendor(obj.TypeId, common_def.VENDER_HUAWEI) then
        log:error('[Storage]RAID Controller do not support consistency check.')
        error({SML_ERR_CODE_E.SML_ERR_CTRL_OPERATION_NOT_SUPPORT, 'ConsistencyCheck', nil})
    end
    local sw = ctrl_operation_cmd.CTRL_CC_SET
    local ok, mask = check_consistency_parameters(period, rate, auto_repaired, nil)
    if not ok then
        log:error('[Storage]Invalid input param')
        error({SM_CODE_E.SM_CODE_PARA_DATA_ILLEGAL, mask[1], mask[2]})
    end
    obj.is_working = true
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_ENABLE_CC,
            get_consistency_check_param(sw, period, rate, auto_repaired, 0, mask))
    end)
    obj.is_working = false
    local log_params = get_consistency_log_parameters(obj, sw, period, rate, auto_repaired, nil, mask)
    controller_ccheck_operation_log(ctx, ret, obj.Id, log_params)
    if ret ~= common_def.RET_OK then
        log:error(string.format('[Storage]Failed to set the consistency check and ret: %s.', ret))
        error({ret, 'ConsistencyCheck', nil})
    end
    set_consistency_parameters(obj, sw, period, rate, auto_repaired, nil, mask)
end

-- 清除硬盘内的外部RAID配置
function rpc_service_controller.clear_foreign_config(obj, ctx)
    obj.is_working = true
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_CLEAR_FOREIGN_CONFIG, '')
    end)
    obj.is_working = false
    if ret ~= common_def.RET_OK then
        if ctx and ctx.get_initiator then
            log:operation(ctx:get_initiator(), 'storage',
                'RAID controller %s failed to clear foreign configuration', obj.Id)
        end
        log:error(string.format('[Storage]Failed to clear foreign config and ret: %s.', ret))
        error({ret, 'ClearForeignConfig', nil})
    end
    if ctx and ctx.get_initiator then
        log:operation(ctx:get_initiator(), 'storage',
                'RAID controller %s clear foreign configuration successfully', obj.Id)
    end
end

-- 导入硬盘内的外部RAID配置
function rpc_service_controller.import_foreign_config(obj, ctx)
    obj.is_working = true
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_IMPORT_FOREIGN_CONFIG, '')
    end)
    obj.is_working = false
    if ret ~= common_def.RET_OK then
        if ctx and ctx.get_initiator then
            log:operation(ctx:get_initiator(), 'storage',
                'RAID controller %s failed to imported foreign configuration', obj.Id)
        end
        error({ret, 'ImportForeignConfig', nil})
    end
    if ctx and ctx.get_initiator then
        log:operation(ctx:get_initiator(), 'storage',
                'RAID controller %s imported foreign configuration successfully', obj.Id)
    end
end

-- 在已有阵列上创建逻辑盘
function rpc_service_controller.create_volume_in_existing_array(obj, ctx, array_id, block_index, raid_type, span_depth,
    name, capacity, capacity_unit, strip_size, read_policy, write_policy, io_policy, access_policy, disk_cache_policy,
    init_type, accelerator)
    check_params_of_create_volume_in_existing_array(obj, array_id, block_index, raid_type,
        span_depth, name, capacity, capacity_unit, strip_size, read_policy, write_policy, io_policy, access_policy,
        disk_cache_policy, init_type, accelerator)
    local convert_capacity = volume_param_check:convert_ld_capacity_in_array(capacity, capacity_unit)
    span_depth = volume_param_check:calc_pmc_span_depth(obj.Id, raid_type, span_depth)
    obj.is_working = true
    local _, ret, _ = pcall(function()
        return sml.add_ld_on_exist_array(obj.Id, array_id, block_index,
            raid_type, span_depth, name, convert_capacity, strip_size, read_policy, write_policy,
            io_policy, access_policy, disk_cache_policy, init_type, accelerator)
    end)
    obj.is_working = false
    local log_operation_ret = ret == 0 and 'successfully' or 'failed'
    if ctx and ctx.get_initiator then
        log:operation(ctx:get_initiator(), 'storage', 'Add RAID controller %s logical drive on Array %s %s', obj.Id,
            array_id, log_operation_ret)
    end
    if ret == common_def.SML_ERR_PD_CLEAR_IN_PROGRESS then
        error({ret, 'CreateVolumeInNewArray', 'PdClearInProgress'})
    end

    if ret ~= common_def.SM_CODE_SUCCESS then
        error({ret, 'CreateVolumeInExisingArray', nil})
    end
end

-- 删除指定逻辑盘
function rpc_service_controller.delete_volume(obj, ctx, volume_id)
    log:notice('[Storage]Volume %s of Controller%s will be deleted', volume_id, obj.Id)
    obj.is_working = true
    local _, ret = pcall(function()
        return sml.delete_volume(obj.Id, volume_id)
    end)
    obj.is_working = false
    local log_operation_ret = ret == 0 and 'successfully' or 'failed'
    if ctx and ctx.get_initiator then
        log:operation(ctx:get_initiator(), 'storage', 'Delete RAID controller %s logical drive %s %s', obj.Id,
            volume_id, log_operation_ret)
    end
    if ret ~= common_def.SM_CODE_SUCCESS then
        error({ret, 'DeleteVolume', nil})
    end
end

-- 在新的阵列上创建逻辑盘
function rpc_service_controller.create_volume_in_new_array(obj, ctx, drive_lists, raid_type, span_depth, name,
    capacity, capacity_unit, strip_size, read_policy, write_policy, io_policy, access_policy, disk_cache_policy,
    init_type, accelerator)
    check_params_of_create_volume_in_new_array(obj, drive_lists, raid_type, span_depth, name,
        capacity, capacity_unit, strip_size, read_policy, write_policy, io_policy, access_policy, disk_cache_policy,
        init_type, accelerator)
    local convert_drive_lists = volume_param_check:convert_id_list_from_drive_to_pd(obj.Id, drive_lists)
    local convert_capacity = volume_param_check:convert_ld_capacity_in_array(capacity, capacity_unit)
    local convert_span_depth = volume_param_check:check_and_get_ld_span_depth(obj.Id, raid_type, #drive_lists,
        span_depth)

    obj.is_working = true
    local _, ret, _ = pcall(function()
        return sml.create_ld_on_new_array(obj.Id, convert_span_depth, #drive_lists,
            convert_drive_lists, convert_capacity, raid_type, name, strip_size, read_policy, write_policy,
            io_policy, access_policy, disk_cache_policy, init_type, accelerator)
    end)
    obj.is_working = false
    local log_operation_ret = ret == 0 and 'successfully' or 'failed'
    local raid_level = common_def.RAID_LEVEL_DESC[raid_type] or 'Unknown'
    if ctx and ctx.get_initiator then
        log:operation(ctx:get_initiator(), 'storage', 'Create RAID controller %s logical drive %s %s', obj.Id,
            raid_level, log_operation_ret)
    end
    if ret == common_def.SML_ERR_PD_CLEAR_IN_PROGRESS then
        error({ret, 'CreateVolumeInNewArray', 'PdClearInProgress'})
    end

    if ret ~= common_def.SM_CODE_SUCCESS then
        error({ret, 'CreateVolumeInNewArray', nil})
    end
end

-- 创建cachecade逻辑盘
function rpc_service_controller.create_ld_as_cachecade(obj, ctx, drive_lists, raid_type, name, write_policy,
    array_id, capacity, capacity_unit, associated_ld, cache_line_size)

    local count = #drive_lists
    if count == 1 and drive_lists[1] == common_def.STORAGE_INFO_INVALID_BYTE then
        count = 0
    end
    local ld_cache_ret = volume_param_check:check_ld_data_created_ssd_caching(obj.Id, raid_type, count, write_policy,
        name)
    if ld_cache_ret ~= common_def.SM_CODE_SUCCESS then
        log:error(string.format('[Storage]Invalid param, and return: %s', ld_cache_ret))
        local prop = decode_error_codes(ld_cache_ret,
            {nil, nil, nil, tostring(write_policy),
            nil, nil, nil, nil, name,
            tostring(raid_type), nil, tostring(count)})
        error({ld_cache_ret, prop[1], prop[2]})
    end
    local convert_drive_lists = volume_param_check:convert_id_list_from_drive_to_pd(obj.Id, drive_lists)
    local convert_capacity = volume_param_check:convert_ld_capacity_in_array(capacity, capacity_unit)

    obj.is_working = true
    local _, ret = pcall(function()
        return sml.create_ld_as_cachecade(obj.Id, count, convert_drive_lists, raid_type, convert_capacity, name,
            write_policy, array_id, associated_ld, cache_line_size)
    end) 
    obj.is_working = false
    local log_operation_ret = ret == 0 and 'successfully' or 'failed'
    local raid_level = common_def.RAID_LEVEL_DESC[raid_type] or 'Unknown'
    if ctx and ctx.get_initiator then
        log:operation(ctx:get_initiator(), 'storage', 'Create RAID controller %s CacheCade logical drive %s %s', obj.Id,
            raid_level, log_operation_ret)
    end
    if ret ~= common_def.SM_CODE_SUCCESS then
        error({ret, 'CreateCachecadeVolume', nil})
    end
end

function rpc_service_controller:import_controller_info(...)
    return self:ctrl_operate(...)
end

local function get_import_controller_id(obj_prop_list)
    if not obj_prop_list or not obj_prop_list["Id"] then
        return nil
    end
    return obj_prop_list["Id"]["Value"]
end

local prop_map = {
    ['Id'] = function()
    end,
    ['Type'] = function()
    end,
    ['CopybackEnabled'] = function(service, ...)
        service:import_controller_info("SetCopyBackState", ...)
    end,
    ['SMARTerCopybackEnabled'] = function(service, ...)
        service:import_controller_info("SetSmarterCopyBackState", ...)
    end,
    ['JBODEnabled'] = function(service, ...)
        service:import_controller_info("SetJBODState", ...)
    end,
    ['Mode'] = function(service, ...)
        service:import_controller_info("SetWorkMode", ...)
    end,
    ['NoBatteryWriteCache'] = function(service, ...)
        service:import_controller_info("SetNoBatteryWriteCache", ...)
    end,
    ['ReadCachePercent'] = function(service, ...)
        service:import_controller_info("SetReadCachePercent", ...)
    end,
}

function rpc_service_controller:import_when_power_on(ctx, obj_prop_list)
    log:info('[storage] start importing')
    local controller_id = get_import_controller_id(obj_prop_list)
    if not controller_id then
        log:error('[storage] import storage config, property(Id) no exist')
        log:operation(ctx:get_initiator(), 'storage', "Import RAID configuration failed")
        error(base_messages.PropertyUnknown("RaidController/Id"))
    end
    local service = controller_collection.get_instance()
    local controller = service:get_by_controller_id(controller_id)
    if not controller then
        log:error('[storage] import storage config: controller id(%u) invalid', controller_id)
        log:operation(ctx:get_initiator(), 'storage', "Import RAID configuration failed")
        error(custom_messages.CurrentStatusNotSupportOperation())
    end

    for prop, val_json in pairs(obj_prop_list) do
        local import_fun = prop_map[prop]
        if import_fun == nil then
            log:error('[storage] import storage config: get property(%s) fun fail', prop)
            goto continue
        end
        local current_bmc_form_val = controller[config_util.get_bmc_prop(prop)]
        local current_user_form_val = config_util.get_user_prop_val(prop, current_bmc_form_val)
        if config_util.is_need_import(val_json, current_user_form_val) then
            local user_import_val = config_util.get_bmc_prop_val(prop, val_json['Value'])
            local ok, err_info = pcall(function()
                return import_fun(self, controller_id, ctx, user_import_val)
            end)
            if not ok then
                log:operation(ctx:get_initiator(), 'storage', "Import RAID configuration failed")
                error(err_info)
            end
        end
        ::continue::
    end
    log:operation(ctx:get_initiator(), 'storage', "Import RAID configuration successfully")
end

function rpc_service_controller:import(ctx, obj_prop_list)
    log:info('[storage] start import config file, import flag is %s', WAIT_IMPORT_FLAG)
    if WAIT_IMPORT_FLAG ~= 0 then
        -- 已存在import task直接退出
        return
    end
    WAIT_IMPORT_FLAG = 1
    c_tasks.get_instance():new_task(TASK_IMPORT):loop(function(task)
        if c_storageconfig.get_instance().obj and
            c_storageconfig.get_instance().obj.StorageConfigReady == 1 then
            pcall(function ()
                return self:import_when_power_on(ctx, obj_prop_list)
            end)
            -- 执行一次, 不管执行成功还是失败都要退出task
            WAIT_IMPORT_FLAG = 0
            task:stop()
        end
    end):set_timeout_ms(10000)
end

local export_list = {
    ['Id'] = 'Id',
    ['Type'] = 'Type',
    ['CopybackEnabled'] = 'CopyBackState',
    ['SMARTerCopybackEnabled'] = 'SmarterCopyBackState',
    ['JBODEnabled'] = 'JBODState',
    ['Mode'] = 'WorkMode',
    ['NoBatteryWriteCache'] = 'NoBatteryWriteCacheEnabled',
    ['ReadCachePercent'] = 'ReadCachePercent'
}

function rpc_service_controller:export()
    log:info('[storage] start exporting')
    local controllers = {}
    local service = controller_collection.get_instance()
    local controller_set = service:get_all_controllers()
    for _, v in pairs(controller_set) do
        local export_data = {}
        for user_prop_name, bmc_prop_name in pairs(export_list) do
            local ok, _ = pcall(function()
                export_data[user_prop_name] =
                    config_util.get_user_prop_val(user_prop_name, v[bmc_prop_name]) or ''
            end)
            if not ok then
                log:error("[storage]export storage config: export property(%s) fail", bmc_prop_name)
                error(base_messages.InternalError())
            end
        end
        table.insert(controllers, export_data)
    end
    log:info('[storage] export finished')
    return controllers
end

local function check_dump_log_precondition(obj)
    local max_space = 30 * 1024 * 1024
    if obj.OOBSupport ~= 1 then
        log:error('RAID Controller don\'t support storage management.')
        return false
    end

    local upgrade_progress = false
    if upgrade_progress then
        log:error('dump RAID Controller log start failed because an upgrade is in progress.')
        return false
    end

    local free_space = max_space + 1
    if free_space < max_space then
        log:error('dump RAID Controller log start failed because directory has no enough sapce.')
        return false
    end

    return true
end

-- 打包压缩文件
local function tar_dump(obj)
    local ok, _ = pcall(function ()
        local tmp_file = '/tmp/' .. string.gsub(obj.DeviceName," ","_") .. '.tar.gz'
        return utils.tar_zip(common_def.CTRL_LOG_BASE_PATH, string.gsub(obj.DeviceName," ","_"), tmp_file)
    end)

    return ok
end

local function dump_ctrl_log(obj, src_dir, sync_task, task_id, ctx)
    local PROGRESS_BAR_MAX_PERCENT <const> = 100
    local PMC_LOG_OFFSET <const> = 9
    local start = 0
    local log_info_huawei = {
        [common_def.CTRL_AP_LOG_TYPE] = common_def.AP_FILE_BIN,
        [common_def.CTRL_IMU_LOG_TYPE] = common_def.IMU_FILE_BIN,
        [common_def.CTRL_AP_LOG_INDEX] = common_def.AP_INDEX_FILE_GZ,
        [common_def.CTRL_IMU_LOG_INDEX] = common_def.IMU_INDEX_FILE_GZ,
        [common_def.CTRL_AP_LASTWORD_TYPE] = common_def.AP_LASTWORD_FILE_BIN,
        [common_def.CTRL_DUMP_FILE_TYPE] = common_def.DUMP_FILE_BIN,
        [common_def.CTRL_FLASH_DUMP_FILE_TYPE] = common_def.FLASH_DUMP_FILE_BIN,
        [common_def.CTRL_NAND_LOG0_FILE_TYPE] = common_def.NAND_LOG0_FILE_BIN,
        [common_def.CTRL_NAND_LOG1_FILE_TYPE] = common_def.NAND_LOG1_FILE_BIN
    }

    local log_info_pmc = {
        [common_def.CTRL_LOG_LAST_CRASH_DUMP] = common_def.AP_LASTWORD_FILE_BIN,
        [common_def.CTRL_LOG_SERIAL_OUTPUT] = common_def.DUMP_FILE_BIN
    }

    local log_info = log_info_huawei
    if method_misc:test_controller_vendor(obj.TypeId, common_def.VENDER_PMC) then
        log_info = log_info_pmc
        start = start + PMC_LOG_OFFSET
    end
    local log_info_size = 0
    for _ in pairs(log_info) do
        log_info_size = log_info_size + 1
    end

    local success_count = 0
    local log_phase = log_phase_table[obj.Id]
    if not log_phase or log_phase ~= 0 then
        log_phase = 0
    end

    local ok, ret
    for i = start, start + log_info_size - 1 do
        log:notice('Begin collect the file %s', log_info[i])
        ok, ret = pcall(function ()
            return sml.dump_ctrl_single_log(obj.Id, src_dir, i, log_info[i])
        end)
        if not ok then
            log:error('sml error when collect %s', log_info_huawei[i])
        end
        log:notice('the file %s collect %s, the ret is %s', log_info[i], ret == common_def.RET_OK, ret)

        if ret == common_def.RET_OK then
            success_count = success_count + 1
            if method_misc:test_controller_vendor(obj.TypeId, common_def.VENDER_PMC) then
                log_phase = (PROGRESS_BAR_MAX_PERCENT * success_count) // log_info_size
            else
                log_phase = ((PROGRESS_BAR_MAX_PERCENT - 50) * success_count) // log_info_size
            end
        end
        sync_task.update_task_prop(task_id, {State = task_state.Running, Progress = log_phase})
    end
    if method_misc:test_controller_vendor(obj.TypeId, common_def.VENDER_HUAWEI) then
        ok, ret = pcall(sml.parse_controller_bin_log, obj.Id, src_dir, src_dir)
        if ok and ret == common_def.RET_OK then
            sync_task.update_task_prop(task_id, { State = task_state.Completed, Progress = PROGRESS_BAR_MAX_PERCENT })
        end
    end
    if success_count ~= 0 and tar_dump(obj) then
        log:operation(ctx:get_initiator(), 'storage', "Collect RAID controller %s log finished", obj.Id)
    else
        log:operation(ctx:get_initiator(), 'storage', "Collect RAID controller %s log failed", obj.Id)
    end

    if log_phase ~= PROGRESS_BAR_MAX_PERCENT then
        sync_task.update_task_prop(task_id, {State = task_state.Completed, Progress = PROGRESS_BAR_MAX_PERCENT})
    end
end

function rpc_service_controller.dump_log(obj, ctx)
    if not check_dump_log_precondition(obj) then
        error(custom_messages.CurrentStatusNotSupportOperation())
    end

    -- 检查控制器是否支持收集
    if method_misc:test_controller_vendor(obj.TypeId, common_def.VENDER_LSI) then
        log:error('%s is not supported', obj.ControllerName)
        error(custom_messages.CurrentStatusNotSupportOperation())
    end

    -- 检查控制器当前是否可以收集
    local config = c_storageconfig.get_instance().obj
    if not config and
        config.StorageConfigReady ~= common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_READY and
        config.StorageConfigReady ~= common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_CTRL_ABNOMAL then
        -- 允许在控制器异常的情况下收集日志
        error(custom_messages.CurrentStatusNotSupportOperation())
    end

    if obj.Id >= SML_MAX_RAID_CONTROLLER then
        error(custom_messages.CurrentStatusNotSupportOperation())
    end

    if not dump_flag_table[obj.Id] or dump_flag_table[obj.Id] ~= 0 then
        dump_flag_table[obj.Id] = 0
    end

    -- 是否有正在收集的task
    if dump_flag_table[obj.Id] ~= 0 then
        log:error('dump task has already started')
        error(custom_messages.CurrentStatusNotSupportOperation())
    end

    dump_flag_table[obj.Id] = 1
    local src_dir = common_def.CTRL_LOG_BASE_PATH .. '/' .. obj.DeviceName
    src_dir = string.gsub(src_dir," ","_") -- 将字符串中的空格替换为'_'
    local task_name = string.format('%s:C%s', 'dump_log', obj.Id)
    local ok, task_id = pcall(function ()
        if not utils_core.is_dir(src_dir) then
             if not utils.mkdir_with_parents(src_dir, utils.S_IRWXU | utils.S_IRGRP | utils.S_IXGRP) then
                log:error('create dump log dir failed')
             end
        end

        return sync_task.create_task(storage_bus.get_instance().bus, task_name, obj.path)
    end)

    log:operation(ctx:get_initiator(), 'storage', "Collect RAID controller %s log start %s",
        obj.Id, ok and 'successfully' or 'failed')
    if not ok then
        log:error('create dump_log task failed')
        error(custom_messages.CurrentStatusNotSupportOperation())
    end

    skynet.fork(function ()
        -- 启动收集任务
        pcall(function ()
            dump_ctrl_log(obj, src_dir, sync_task, task_id, ctx)
        end)
        dump_flag_table[obj.Id] = 0
    end)

    return task_id
end

function rpc_service_controller.set_no_battery_write_cache(obj, ctx, cache_state)
    -- 0: 关闭；1: 打开
    if cache_state ~= 0 and cache_state ~= 1 then
        log:error(string.format('[Storage]The input %s is invalid.', cache_state))
        error({common_def.SM_CODE_PARA_DATA_ILLEGAL, 'NoBatteryWriteCache', tostring(cache_state)})
    end

    if obj.NoBatteryWriteCacheEnabled == cache_state then
        return
    end

    obj.is_working = true
    local set_state = cache_state == 1 and 'enabled' or 'disabled'
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_ENABLE_NBWC, string.pack('B', cache_state))
    end)
    controller_log_operation(ctx, ret, 'No Battery Write Cache to', obj.Id, set_state)
    obj.is_working = false

    if ret ~= common_def.RET_OK then
        log:error(string.format('[Storage]Failed to set the NoBatteryWriteCache and ret: %s.', ret))
        error({ret, 'NoBatteryWriteCache', tostring(cache_state)})
    else
        obj.NoBatteryWriteCacheEnabled = cache_state
    end
end

-- percent取值范围0~100，且必须为5的倍数
function rpc_service_controller.set_read_cache_percent(obj, ctx, percent)
    if percent > common_def.PERCENTAGE_UPPER_LIMIT then
        log:error(string.format('[Storage]The input %s is invalid.', percent))
        error({common_def.SM_CODE_PARA_DATA_ILLEGAL, 'ReadCachePercent', tostring(percent)})
    end

    if obj.ReadCachePercent == percent then
        return
    end

    obj.is_working = true
    local set_state = tostring(percent)
    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_READ_CACHE_POLICY,
            string.pack('B', percent))
    end)
    controller_log_operation(ctx, ret, 'read cache percent to', obj.Id, set_state)
    obj.is_working = false

    if ret ~= common_def.RET_OK then
        log:error(string.format('[Storage]Failed to set the ReadCachePercent and ret: %s.', ret))
        error({ret, 'ReadCachePercent', tostring(percent)})
    else
        obj.ReadCachePercent = percent
    end
end

local wcp_map = {
    ['ConfiguredDriveWriteCachePolicy'] = 0,
    ['UnconfiguredDriveWriteCachePolicy'] = 1,
    ['HBADriveWriteCachePolicy'] = 2
}

local wcp_num_map_str = {
    [0] = 'ConfiguredDrive',
    [1] = 'UnconfiguredDrive',
    [2] = 'HBADrive'
}

-- 设置控制器硬盘写缓存策略
function rpc_service_controller.set_write_cache_policy(obj, ctx, type, write_cache_policy)
    local type_num = wcp_map[type]
    if not type_num then
        log:error(string.format('[Storage]The input type:%s is invalid.', type))
        error({common_def.SML_ERR_DATA_INVALID, 'Type', type})
    end

    local val = common_def.RAID_CTRL_WCP_NUM[write_cache_policy]
    if not val then
        log:error(string.format('[Storage]The input write_cache_policy:%s is invalid.', write_cache_policy))
        error({common_def.SM_CODE_PARA_DATA_ILLEGAL, type, write_cache_policy})
    end

    if obj[type] == write_cache_policy then
        return
    end

    obj.is_working = true

    local _, ret = pcall(function()
        return sml.ctrl_operation(obj.Id, ctrl_operation_cmd.CTRL_OPERATION_WCP_VALUE,
            string.pack('BB', type_num, val))
    end)
    controller_log_operation(ctx, ret, wcp_num_map_str[type_num] .. ' write cache policy' , obj.Id, write_cache_policy)
    obj.is_working = false

    if ret ~= common_def.RET_OK then
        log:error(string.format('[Storage]Failed to set the write cache policy and ret: %s.', ret))
        error({ret, type, write_cache_policy})
    else
        obj[type] = write_cache_policy
    end
end

-- 调试打印运行时RAID卡的数据
function rpc_service_controller:get_controller_info(controller_id)

    local details = {}
    local controller = controller_collection:get_by_controller_id(controller_id)
    if not controller then
        log:error('[Storage]Controller%u is invalid.', controller_id)
        return details
    end   

    details.DevBus = tostring(controller.DevBus)
    details.DevDevice = tostring(controller.DevDevice)
    details.DevFunction = tostring(controller.DevFunction)
    details.Eid = tostring(controller.Eid)
    details.Phyaddr = tostring(controller.Phyaddr)
    details.CtrlOption1	= tostring(controller.CtrlOption1)
    details.CtrlOption2 = tostring(controller.CtrlOption2)
    details.CtrlOption3 = tostring(controller.CtrlOption3)
    details.SlotId = tostring(controller.SlotId)

    log:notice('[Storage]Print Controller%u running info succsess!', controller_id)
    return details
end

return singleton(rpc_service_controller)
