#!/usr/bin/env python
# coding: utf-8
# 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.

import os
import json
import re
from collections import defaultdict
from mapper_check.ErrorlogPrinter import ErrorlogPrinter
from mapper_check.PrivilegeMapUriChecker import PrivilegeMapChecker
from jsonschema import Draft202012Validator

TargetDirs = {'web_backend': ['interface_config/web_backend/mapping_config'],
              'redfish': ['interface_config/redfish/mapping_config'],
              'cli': ['interface_config/cli/ipmcget', 'interface_config/cli/ipmcset'],
              'snmp': ['interface_config/snmp/mapping_config']}
ScriptTargetDic = {'web_backend': 'interface_config/web_backend/script',
                   'redfish': 'interface_config/redfish/script',
                   'cli': 'interface_config/cli/script',
                   'snmp': 'interface_config/snmp/script'}
PluginTargetDic = {'web_backend': 'interface_config/web_backend/plugins',
                   'redfish': 'interface_config/redfish/plugins',
                   'cli': 'interface_config/cli/plugins',
                   'snmp': 'interface_config/snmp/plugins'}
TargetUriPrefix = {'web_backend': '/UI/Rest',
                   'redfish': '/redfish/v1',
                   'cli': '/cli/v1',
                   'snmp': '/snmp'}
# 过滤构建时会删除的文件
FilterFilename = {'web_backend': [],
                  'redfish': [],
                  'cli': ['dft.json'],
                  'snmp': []}
# 存储全局替换符
ReplaceDict = {'web_backend': {},
              'redfish': {},
              'cli': {},
              'snmp': {}}

UriDic = {}
ScriptDic = {}
PluginDic = {}
PluginFuncDic = {}

ErrorlogPath = 'tools/mapper_check/error_log.txt'
ep = ErrorlogPrinter()


def load_json(path):
    with open(path, 'r', encoding='utf-8') as fp:
        return json.load(fp)


def get_abspath_under_rackmount(path):
    return os.path.join(os.getcwd(), path)


def load_custom_config():
    # 读取批量替换字段的关键字，在检查时进行替换避免校验失败
    for interface_name in ReplaceDict:
        replace_config_path = 'interface_config/{}/config.json'.format(interface_name)
        if os.path.exists(replace_config_path):
            with open(replace_config_path, 'r') as f:
                config = json.load(f)
            if config and isinstance(config.get('GlobalVariable'), dict):
                ReplaceDict[interface_name] = config.get('GlobalVariable')


def remove_array_subscript(member):
    ret = re.search(r'(\[\d{1,}\]$)', member)
    if ret is None:
        ret = re.search(r'(\[#INDEX\]$)', member)
    if ret is None:
        ret = re.search(r'\[#INDEX\]', member)
    if ret is not None:
        index = ret.span()
        member = member[:index[0]]
    return member


def is_member_in_dict(dic, member):
    # 如果member末尾有数组下标,则去除
    if not isinstance(dic, dict):
        return False
    origin_member = member
    if origin_member in dic.values():
        return True
    member = remove_array_subscript(origin_member)
    if member in dic.values():
        return True
    for value in dic.values():
        if value.startswith(member):
            return True
    return False


# 从URI中解析动态槽位号
def parse_uri_slots(uri):
    if not uri.endswith('/'):
        uri = uri + '/'
    # 匹配所有:和/之间的字符串并提取到ids
    ids = re.findall(r':(.+?)/', uri)
    if ids == '' or not ids:
        ids = re.findall(r'.:(.+?)/', uri)
    return ids


# 获取函数入参个数
def get_para_count(para):
    single_quote_count = 0
    double_quote_count = 0
    curly_brac_count = 0
    comma_count = 0
    for char in para:
        if char == '\'':
            single_quote_count ^= 1
        if char == '"':
            double_quote_count ^= 1
        if char == '{':
            curly_brac_count += 1
        if char == '}':
            curly_brac_count -= 1
        if char == ',' and single_quote_count == 0 and double_quote_count == 0 and curly_brac_count == 0:
            comma_count += 1
    return comma_count + 1


class SyntaxChecker:
    def __init__(self, schema_json, target_json, path, is_success=True):
        self.schema_json = schema_json
        self.target_json = target_json
        self.path = path
        self.is_success = is_success

    def check(self):
        v = Draft202012Validator(self.schema_json)
        errors = sorted(v.iter_errors(self.target_json), key=lambda e: e.path)
        if errors:
            self.is_success = False
            errorlog_file = open(ErrorlogPath, 'a+')
            start_index = self.path.find('interface_config')
            print('=============syntax errors found in file:', self.path[start_index:], file=errorlog_file)
            print('=============syntax errors found in file:', self.path[start_index:])
            for error in errors:
                print('[Rule 1-1]: 映射器语法检查失败, 详细信息如下=============', file=errorlog_file)
                print('错误信息: ', error.message, file=errorlog_file)
                print('语法要求: ', error.validator_value, file=errorlog_file)
                print('错误位置: ', error.json_path, file=errorlog_file)
                print('错误内容: ', error.instance, file=errorlog_file)
                print('[Rule 1-1]: 映射器语法检查失败, 详细信息如下=============')
                print('错误信息: ', error.message)
                print('语法要求: ', error.validator_value)
                print('错误位置: ', error.json_path)
                print('错误内容: ', error.instance)
            errorlog_file.close()
        return self.is_success


class IntfRefChecker:
    def __init__(self, intf_json, obj_path, file_path, uri, intf_type, check_component_name):
        self.intf_json = intf_json
        self.obj_path = obj_path
        self.path = file_path
        self.uri = uri
        self.slots = parse_uri_slots(uri)
        self.intf_type = intf_type
        self.check_component_name = check_component_name
        self.is_success = True
        self.check_ref_source_table = defaultdict(self.default_ref_func)
        self.check_ref_source_table['Uri'] = self.check_ref_uri
        self.check_ref_source_table['ReqBody'] = self.check_ref_reqbody
        self.check_ref_source_table['Query'] = self.check_ref_query
        self.check_ref_source_table['Context'] = self.check_ref_context
        self.check_ref_source_table['Statements'] = self.check_ref_statements
        self.check_ref_source_table['#INDEX'] = self.check_ref_index

    def default_ref_func(self):
        return self.check_ref_processingflow

    def get_obj_path(self, key):
        return self.obj_path + '.' + key
    
    # 将Uri引用内容与从Uri中提取到的动态槽位号比较
    def check_ref_uri(self, members, content, location):
        if len(members) != 2:
            ep.print_errorlog('2-4', 
                              self.path, 
                              content, 
                              self.uri, 
                              self.intf_type, 
                              location)
            self.is_success = False
            return
        for slot in self.slots:
            if slot == members[1]:
                return
        ep.print_errorlog('2-4', 
                          self.path, 
                          content, 
                          self.uri, 
                          self.intf_type, 
                          location)
        self.is_success = False

    def check_ref_reqbody(self, members, content, location):
        if len(members) < 2:
            if members[0] == 'ReqBody':
                return
            ep.print_errorlog('2-5', self.path, content, self.uri, self.intf_type, location)
            self.is_success = False
            return
        try:
            pros = self.req_body.get('Properties', [])
            # 跳过ReqBody, 从引用的参数路径开始搜索
            for i in range(1, len(members)):
                found = False
                for name, pro_def in pros.items():
                    if members[i].startswith(name) is False:
                        continue
                    if name == members[i]:
                        found = True
                        if pro_def.get('Type', '') == 'array':
                            pros = pro_def.get('Items', [])
                        elif pro_def.get('Type', '') == 'object':
                            pros = pro_def.get('Properties', [])
                        break
                    ret = re.search(r'(\[\d{1,}\]$)|(\[#INDEX\]$)', members[i])
                    found = ret is not None and (len(members[i]) == (len(name) + ret.span()[1] - ret.span()[0]))
                    if not found:
                        continue
                    items = pro_def.get('Items', [])
                    if isinstance(items, dict):
                        pros = items.get('Properties', [])
                    elif isinstance(items, list):
                        index = int(members[i][-2]) - 1
                        pros = items[index].get('Properties', [])
                if not found:
                    ep.print_errorlog('2-5', self.path, content, self.uri, self.intf_type, location, members[i])
                    self.is_success = False
                    return
            return
        except:
            ep.print_errorlog('2-5', self.path, content, self.uri, self.intf_type, location)
            self.is_success = False

    def check_ref_query(self, members, content, location):
        if len(members) != 2:
            ep.print_errorlog('2-6', 
                              self.path, 
                              content, 
                              self.uri, 
                              self.intf_type, 
                              location)
            self.is_success = False
            return
        # 检查引用的成员是否在Query对象中定义了
        for k in self.query:
            if k == members[1]:
                return
        ep.print_errorlog('2-6', 
                          self.path, 
                          content, 
                          self.uri, 
                          self.intf_type, 
                          location)
        self.is_success = False
    
    def check_ref_context(self, members, content, location):
        if len(members) != 2:
            ep.print_errorlog('2-9', 
                              self.path, 
                              content, 
                              self.uri, 
                              self.intf_type, 
                              location)
            self.is_success = False
            return
        # context字段允许引用的成员
        redfish_ctx = {'Interface', 'UserName', 'ClientIp', 'Privilege', 'AccountId',
            'RoleId', 'AuthType', 'Token', 'SessionId'}
        for ctx in redfish_ctx:
            if ctx == members[1]:
                return
        ep.print_errorlog('2-9', 
                          self.path, 
                          content, 
                          self.uri, 
                          self.intf_type, 
                          location)
        self.is_success = False

    def check_ref_statements(self, members, content, location):
        if len(members) < 2:
            ep.print_errorlog('2-8', 
                              self.path, 
                              content, 
                              self.uri, 
                              self.intf_type, 
                              location)
            self.is_success = False
            return
        for statement in self.statements:
            if is_member_in_dict({'statement': statement + '()'}, members[1]) is True:
                return
        ep.print_errorlog('2-8', 
                          self.path, 
                          content, 
                          self.uri, 
                          self.intf_type, 
                          location)
        self.is_success = False

    def check_ref_index(self, members, content, location):
        return
    
    def check_ref_processingflow(self, members, content, location):
        if len(members) < 2:
            ep.print_errorlog('2-7', self.path, content, self.uri, self.intf_type, location)
            self.is_success = False
            return
        if members[1] != 'Destination':
            if members[1] == 'Errors':
                return
            ep.print_errorlog('2-7', 
                              self.path, 
                              content, 
                              self.uri, 
                              self.intf_type, 
                              location, 
                              '错误原因:未识别到Destination关键字')
            self.is_success = False
            return
        index_str = re.findall('\[(.+?)\]', members[0])
        if len(index_str) > 1:
            ep.print_errorlog('2-7', self.path, content, self.uri, self.intf_type, location)
            self.is_success = False
            return
        # 检查引用的ProcessingFlow数组下标合法性
        try:
            index = int(index_str[0])
            process = self.processing_flow[index - 1]
            # 检查引用的Desctionation成员是否存在
            if process.get('Destination') is None or (len(members) == 3 and is_member_in_dict(process['Destination'], members[2]) is False):
                ep.print_errorlog('2-7', 
                                  self.path, 
                                  content, 
                                  self.uri, 
                                  self.intf_type, 
                                  location, 
                                  '错误原因:未定义的Destination成员:{}'.format(members[2]))
                self.is_success = False
            return
        except:
            ep.print_errorlog('2-7', self.path, content, self.uri, self.intf_type, location, '错误原因:无效数组下标')
            self.is_success = False

    # 检查数据引用来源
    def check_ref_source(self, content, location):
        if not isinstance(content, str):
            return
        patterns =  r'(ReqBody$)|(Uri$)|(Query$)|(Context$)|(Statements$)|(ProcessingFlow\[\d{1,}\]$|(#INDEX$))'
        # 兼容多层级定制，用'*'替换定制化字段，如{{OemIdentifier}}
        content_without_replace = content
        for k in ReplaceDict[self.check_component_name]:
            content_without_replace = content_without_replace.replace("{{" + k + "}}", '*')
        ref_list = re.findall(r'\${(.*?)}', content_without_replace)
        for v in ref_list:
            for k in ReplaceDict[self.check_component_name]:
                v = v.replace('*', "{{" + k + "}}")
            ss = v.split('/')
            if re.match(patterns, ss[0]) is None:
                if ss[0] == 'ReqHeader':
                    continue
                ep.print_errorlog('2-1', 
                                  self.path, 
                                  content, 
                                  self.uri, 
                                  self.intf_type, 
                                  location)
                self.is_success = False
            else:
                # 继续检查引用值的有效性
                self.check_ref_source_table[ss[0]](ss, content, location)

    def check_resource_exist(self):
        if len(self.resource_exist) == 0:
            return
        # ResourceExist对象下,key和value均可以引用外部数据
        for k, v in self.resource_exist.items():
            self.check_ref_source(k, self.get_obj_path('.ResourceExist.' + k))
            self.check_ref_source(v, self.get_obj_path('.ResourceExist.' + k))

    def check_req_body(self):
        return

    def check_odata_context(self, odata_context, location):
        ret = re.search(r'/redfish/v1/\$metadata\#[A-Za-z]*(\.[A-Za-z])*', odata_context)
        if ret is None:
            ep.print_errorlog('3-2', 
                              self.path, 
                              odata_context, 
                              self.uri, 
                              self.intf_type, 
                              location)
            self.is_success = False

    def check_rsp_body(self, target, parent):
        if isinstance(target, str):
            self.check_ref_source(target, parent)
            return
        for key in target:
            if key == '@odata.context':
                self.check_odata_context(target[key], parent)
            if isinstance(target[key], dict):
                new_parent = parent + '.' + key
                self.check_rsp_body(target[key], new_parent)
            elif isinstance(target[key], list):
                lists = target[key]
                for i in range(len(lists)):
                    location = parent + '.' + key + '[{}]'.format(i + 1)
                    if isinstance(lists[i], dict):
                        self.check_rsp_body(lists[i], location)
                    else:
                        self.check_ref_source(lists[i], location)
            else:
                location = parent + '.' + key
                self.check_ref_source(target[key], location)
    
    def check_processing_flow(self):
        for i in range(len(self.processing_flow)):
            process = self.processing_flow[i]
            location = self.get_obj_path('ProcessingFlow[{}].'.format(i + 1))
            self.check_ref_source(process['Path'], location + 'Path')
            self.check_ref_source(process.get('Interface'), location + 'Interface')
            self.check_ref_source(process.get('Name'), location + 'Name')
            self.check_ref_source(process.get('Foreach'), location + 'Foreach')
            for p in range(len(process.get('Params', {}))):
                param = process['Params'][p]
                self.check_ref_source(param, location + 'Params[{}]'.format(p + 1))
            for key in process.get('Source', {}):
                self.check_ref_source(process['Source'][key], location + 'Source.' + key)
            callif = process.get('CallIf', {})
            if isinstance(callif, dict):
                for key in callif:
                    # CallIf的key和val均可引用
                    self.check_ref_source(callif[key], location + 'CallIf.' + key)
                    self.check_ref_source(key, location + 'CallIf.' + key)

    def check_script_existence(self, step, step_location):
        if ScriptDic.get(step) is None:
            self.is_success = False
            ep.print_errorlog('4-1', 
                              self.path, 
                              step, 
                              self.uri, 
                              self.intf_type, 
                              step_location)

    def check_plugin_existence(self, step, step_location):
        func_path = re.findall(r'(.*)\(.*\)', step)[0]
        file_path = re.findall(r'(.*)\..*', func_path)[0]
        if PluginDic.get(file_path) is None:
            self.is_success = False
            ep.print_errorlog('4-2', 
                              self.path, 
                              step, 
                              self.uri, 
                              self.intf_type, 
                              step_location)

    def check_plugin_func_existence(self, step, step_location):
        func_path = re.findall(r'(.*)\(.*\)', step)[0]
        if func_path == 'orchestrator.sp.get_file_list':
            return
        if PluginFuncDic.get(func_path) is None:
            self.is_success = False
            ep.print_errorlog('4-3', 
                              self.path, 
                              step, 
                              self.uri, 
                              self.intf_type, 
                              step_location)

    def check_plugin_func_para_cnt(self, step, step_location):
        para_list = re.findall(r'.*\((.*)\)', step)[0]
        func_path = re.findall(r'(.*)\(.*\)', step)[0]
        if para_list == '':
            count = 0
        else:
            count = get_para_count(para_list)
        if PluginFuncDic.get(func_path) is not None:
            if PluginFuncDic[func_path] < count:
                self.is_success = False
                ep.print_errorlog('4-4', 
                                  self.path, 
                                  step, 
                                  self.uri, 
                                  self.intf_type, 
                                  step_location)

    def check_statements(self):
        for key in self.statements:
            statement = self.statements[key]
            location = self.get_obj_path('Statements.{}.'.format(key))
            if statement.get('Input') is not None:
                self.check_ref_source(statement['Input'], location + 'Input')
            for i in range(len(statement.get('Steps', {}))):
                step_location = location + 'Steps[{}].Formula'.format(i + 1)
                step = statement['Steps'][i].get('Formula', {})
                self.check_ref_source(step, step_location)
                type = statement['Steps'][i].get('Type', {})
                if type == 'Script' and re.search(r'\.lua', step) is not None:
                    self.check_script_existence(step, step_location)
                elif type == 'Plugin':
                    self.check_plugin_existence(step, step_location)
                    self.check_plugin_func_existence(step, step_location)
                    self.check_plugin_func_para_cnt(step, step_location)
    
    def check_action_rsp_body(self, parent):
        if 'ActionResponseBody' in self.intf_json:
            if self.check_component_name != 'redfish':
                self.is_success = False
                ep.print_errorlog('3-6', 
                                  self.path, 
                                  self.check_component_name, 
                                  self.uri, 
                                  self.intf_type, 
                                  parent)
            elif '/Actions/' not in self.uri:
                self.is_success = False
                ep.print_errorlog('3-7', 
                                  self.path, 
                                  'ActionResponseBody', 
                                  self.uri, 
                                  self.intf_type, 
                                  parent)

            elif 'RspBody' in self.intf_json:
                self.is_success = False
                ep.print_errorlog('3-8', 
                                  self.path, 
                                  'RspBody', 
                                  self.uri, 
                                  self.intf_type, 
                                  self.get_obj_path('RspBody'))

    # check Interfaces字段下的合法性
    def check(self):
        self.intf_type = self.intf_json['Type']
        self.resource_exist = self.intf_json.get('ResourceExist', {})
        self.req_body = self.intf_json.get('ReqBody', {})
        self.rsp_body = self.intf_json.get('RspBody', {})
        self.statements = self.intf_json.get('Statements', {})
        self.processing_flow = self.intf_json.get('ProcessingFlow', {})
        self.query = self.intf_json.get('Query', {})
        self.check_resource_exist()
        self.check_action_rsp_body(self.get_obj_path('ActionResponseBody'))
        self.check_rsp_body(self.rsp_body, self.get_obj_path('RspBody'))
        self.check_req_body()
        self.check_statements()
        self.check_processing_flow()
        return self.is_success


class RscRefChecker:
    def __init__(self, rscs_json, index, path, check_component_name):
        self.rsc_json = rscs_json   # uri下所有resource
        self.rsc_index = index
        self.path = path
        self.check_component_name = check_component_name
        self.is_success = True
        # 加载 web_rest DELETE 白名单（只加载一次）
        if not hasattr(RscRefChecker, '_web_rest_delete_whitelist'):
            RscRefChecker._web_rest_delete_whitelist = self.load_web_rest_delete_whitelist()

    @classmethod
    def load_web_rest_delete_whitelist(cls):
        """加载 web_rest DELETE 接口白名单"""
        whitelist_path = get_abspath_under_rackmount('tools/mapper_check/web_rest_delete_whitelist.json')
        whitelist = set()
        if os.path.exists(whitelist_path):
            with open(whitelist_path, 'r', encoding='utf-8') as f:
                config = json.load(f)
                whitelist = set(config.get('whitelist', []))
        return whitelist

    @classmethod
    def get_uri_no_slots(cls, uri):
        if not uri.endswith('/'):
            uri = uri + '/'
        # 匹配所有:和/之间的字符串并提取到ids
        ret = re.search(r':(.+?)/', uri)
        while ret is not None:
            index = ret.span()
            # 区别于不含有动态字段的Uri，动态字段提取后用'*'替代
            uri = uri[:index[0]] + '*' + uri[index[1]:]
            ret = re.search(r':(.+?)/', uri)
        return uri

    def get_uri_location(self):
        return 'Resources[{}].Uri'.format(self.rsc_index + 1)
                
    def check_snmp_oem_identifier(self):
        if self.check_component_name == 'snmp':
            if self.uri.find('1.3.6.1.4.1.2011.2.235.1.1.') != -1:
                ep.print_errorlog('3-5',
                                  self.path,
                                  self.uri,
                                  self.uri,
                                  'None',
                                  self.get_uri_location())
                self.is_success = False

    def check_web_rest_delete_whitelist(self):
        """检查 web_rest DELETE 接口是否在白名单中"""
        if self.check_component_name == 'web_backend':
            for intf in self.intfs:
                if intf.get('Type', '').upper() == 'DELETE':
                    # 检查是否在白名单中
                    if self.uri not in self._web_rest_delete_whitelist:
                        ep.print_errorlog('6-2',
                                        self.path,
                                        self.uri,
                                        self.uri,
                                        'DELETE',
                                        self.get_uri_location())
                        self.is_success = False

    # 槽位号是否全小写
    def check_uri_slot_format(self):
        self.slots = parse_uri_slots(self.uri)
        for slot in self.slots:
            if slot.islower() is False:
                if (self.check_component_name == 'redfish') or (self.check_component_name == 'web_backend'):
                    ep.print_errorlog('2-3', 
                                      self.path, 
                                      slot, 
                                      self.uri, 
                                      self.intfs[0]['Type'],
                                      self.get_uri_location())
                    self.is_success = False

    def check_uri_slot_validated(self):
        ret = re.search(r'^((/UI/Rest)|(/redfish/v1))', self.uri)
        if ret and self.slots:
            for intf in self.intfs:
                if intf.get('ResourceExist') is None and intf['Type'].upper() != 'PATCH':
                    ep.print_errorlog('3-1', 
                                      self.path, 
                                      self.uri, 
                                      self.uri, 
                                      intf['Type'], 
                                      self.get_uri_location())
                    self.is_success = False

    def check_if_uri_duplicate(self):
        uri_type = self.intfs[0]['Type']
        uri_type = uri_type.upper()
        uri_no_slots = self.get_uri_no_slots(self.uri)
        if UriDic.get(uri_no_slots) is None:
            UriDic[uri_no_slots] = []
            UriDic[uri_no_slots].append(uri_type)
        else:
            if uri_type in UriDic[uri_no_slots]:
                self.is_success = False
                ep.print_errorlog('3-3', 
                                    self.path, 
                                    self.uri, 
                                    self.uri, 
                                    self.intfs[0]['Type'], 
                                    self.get_uri_location())
            else:
                UriDic[uri_no_slots].append(uri_type)

    def check_uri_slot_legitimacy(self):
        ret = re.search(r'(/UI/Rest)|(/redfish/v1)|(/cli/v1)|(/snmp)', self.uri)
        if ret is not None:
            if re.search(r'Managers/\d+|Systems/\d+|Chassis/\d+', self.uri) is not None:
                self.is_success = False
                ep.print_errorlog('3-4', 
                                  self.path, 
                                  self.uri, 
                                  self.uri, 
                                  self.intfs[0]['Type'], 
                                  self.get_uri_location())

    def check_interfaces(self):
        for i in range(len(self.intfs)):
            location = 'Resources[{}].Interfaces[{}]'.format(self.rsc_index + 1, i + 1)
            c = IntfRefChecker(self.intfs[i], location, self.path, self.uri, 
                               self.intfs[0]['Type'], self.check_component_name)
            self.is_success = c.check() and self.is_success

    def check(self):
        self.uri = self.rsc_json['Uri']
        self.intfs = self.rsc_json['Interfaces']
        self.check_snmp_oem_identifier()
        self.check_web_rest_delete_whitelist()
        self.check_uri_slot_format()
        self.check_uri_slot_validated()
        self.check_if_uri_duplicate()
        self.check_uri_slot_legitimacy()
        self.check_interfaces()
        return self.is_success


class ReferenceChecker:
    def __init__(self, target_json, path, check_component_name):
        self.target_json = target_json
        self.path = path
        self.check_component_name = check_component_name
        self.is_success = True

    def check(self):
        rscs = self.target_json['Resources']
        # 遍历文件下所有uri下的resource
        for i in range(len(rscs)):
            c = RscRefChecker(rscs[i], i, self.path, self.check_component_name)
            if c.check() is False:
                self.is_success = False
        return self.is_success


class MapperChecker:
    def __init__(self):
        self.schema_path = os.path.abspath('tools/mapper_check/data_mapping_schema.json')
        self.is_success = True

    @staticmethod
    def get_target_dir(check_component_name, dir_type):
        if dir_type == 'script':
            dir_path = ScriptTargetDic[check_component_name]
        elif dir_type == 'plugin':
            dir_path = PluginTargetDic[check_component_name]
        return get_abspath_under_rackmount(dir_path)

    def check_oem_identifier(self, text, check_component_name, filename):
        if check_component_name == 'redfish':
            # 整改完毕后，校验规则增强为r"[\'\"\.\/][A-Za-z\_]*Huawei[A-Za-z\_]*.|.[A-Za-z\_]*Huawei[A-Za-z\_]*[\'\"\.\/]"
            find = re.search(r"[\'\"\.\/]Huawei[\'\"\.\/]", text)
            if find:
                self.is_success = False
                ep.print_errorlog('3-5', 
                                  filename, 
                                  '[' + find.group(0) + ']',
                                  filename, 
                                  'None', 
                                  'None')

    def get_script_file(self, check_component_name):
        target_dir = self.get_target_dir(check_component_name, 'script')
        for parent, _, filenames in os.walk(target_dir):
            for filename in filenames:
                path = os.path.join(parent, filename)
                script_file = re.findall(r'.*/script/(.*)', path)[0]
                ScriptDic[script_file] = True
                with open(path, 'r') as lua_file:
                    lua_code = lua_file.read()
                self.check_oem_identifier(lua_code, check_component_name, filename)

    def get_plugin_file(self, check_component_name):
        target_dir = self.get_target_dir(check_component_name, 'plugin')
        for parent, _, filenames in os.walk(target_dir):
            for filename in filenames:
                # 获取全量插件文件列表
                path = os.path.join(parent, filename)
                plugin_file = re.findall(r'.*/plugins(/[^\.]*)\.lua', path)[0]
                plugin_path_list = re.findall(r'/([^/]+)', plugin_file)
                plugin_path = '.'.join(plugin_path_list)
                PluginDic[plugin_path] = True
                # 获取全量插件文件中的函数列表
                with open(path, 'r') as lua_file:
                    lua_code = lua_file.read()
                self.check_oem_identifier(lua_code, check_component_name, filename)
                plugin_func_list = re.findall(r'function .*\.(.*)\(.*\)', lua_code)
                plugin_para_list = re.findall(r'function .*\..*\((.*)\)', lua_code)
                for i in range(len(plugin_para_list)):
                    func_path = "{}.{}".format(plugin_path, plugin_func_list[i])
                    if plugin_para_list[i] == '':
                        PluginFuncDic[func_path] = 0
                        continue
                    else:
                        PluginFuncDic[func_path] = get_para_count(plugin_para_list[i])

    def check_replace_config(self, text, check_component_name, filename):
        rep_list = re.findall(r'{{([a-zA-Z0-9]+)}}', text)
        for k in rep_list:
            if k not in ReplaceDict[check_component_name]:
                self.is_success = False
                ep.print_errorlog('2-10', 
                                  filename, 
                                  k,
                                  filename, 
                                  'NULL', 
                                  'NULL')

    def check_component(self, check_component_name):
        # 各类型接口的plugins和script记录相互独立
        ScriptDic.clear()
        self.get_script_file(check_component_name)
        PluginDic.clear()
        PluginFuncDic.clear()
        self.get_plugin_file(check_component_name)

        schema_json = load_json(self.schema_path)
        for target_dir in TargetDirs[check_component_name]:
            # 各类型接口的Uri记录相互独立，包括ipmcget和ipmcset
            UriDic.clear()
            target_dir = get_abspath_under_rackmount(target_dir)
            for parent, _, filenames in os.walk(target_dir):
                for filename in filenames:
                    # 跳过过滤文件不校验
                    if filename in FilterFilename[check_component_name]:
                        continue
                    path = os.path.join(parent, filename)
                    with open(path, 'r') as json_file:
                        json_txt = json_file.read()
                    self.check_replace_config(json_txt, check_component_name, filename)
                    self.check_oem_identifier(json_txt, check_component_name, filename)
                    target_json = load_json(path)
                    # 先进行语法校验,如果失败,直接退出当前文件检查
                    c = SyntaxChecker(schema_json, target_json, path, is_success=self.is_success)
                    ret = c.check()
                    self.is_success = ret
                    if not ret:
                        continue

                    c = ReferenceChecker(target_json, path, check_component_name)
                    self.is_success = c.check()

    def check_privilege_map(self):
        privilege_map_checker = PrivilegeMapChecker()
        if not privilege_map_checker.run():
            self.is_success = False
            ep.print_errorlog('6-1',
                            'NULL',
                            'NULL',
                            'NULL',
                            'NULL',
                            'NULL')


def check():
    global ErrorlogPath 
    ErrorlogPath = get_abspath_under_rackmount(ErrorlogPath)
    if os.path.exists(ErrorlogPath):
        os.remove(ErrorlogPath)
    # 加载定制文件
    load_custom_config()
    checker = MapperChecker()
    checker.check_component('web_backend')
    checker.check_component('redfish')
    checker.check_component('cli')
    checker.check_component('snmp')
    checker.check_privilege_map()  # 屏蔽PrivilegeMap校验请注释此行
    if checker.is_success:
        print('=========映射器配置检查成功!=========')
    else:
        print('映射器配置检查失败, 构建终止')
        print('检查规则说明及修复方法参考: ', 'mapper_check/README.md')
        raise Exception("build failed, because errors are found in Data mapping files")