# 北向映射器检查工具 MapperCheck

## 一、设计背景
iBMC V3通过映射器配置来完成redfish、web-rest、cli、snmp接口的开发，接口适配主要是json文件，存在代码量大、人工检视难、出现问题难定位等困难。因此开发了映射器配置检查工具，该工具可以为映射器配置的门禁检查工具，可以对映射器语法、接口逻辑进行检查，拦截低级错误和常见问题，提升调试效率，同时规范映射器用法。

## 二、配置及检查范围
```
rackmount
├── ...
├── interface_config
│   ├── cli                      # cli命令配置
│   │   ├── ipmcget              # cli get类型命令json配置
│   │   ├── ipmcset              # cli set类型命令json配置
│   │   ├── plugins              # cli命令实现调用的插件
│   │   └── script               # cli命令实现调用的脚本
│   ├── redfish                  # redfish命令配置
│   │   ├── mapping_config       # redfish命令json配置
│   │   ├── plugins              # redfish命令实现调用的插件
│   │   └── script               # redfish命令实现调用的脚本
│   ├── snmp                     # snmp命令配置
│   │   ├── mapping_config       # snmp命令json配置
│   │   ├── plugins              # snmp命令实现调用的插件
│   │   └── script               # snmp命令实现调用的脚本
│   └── web_backend              # web_backend命令配置
│       ├── mapping_config       # web_backend命令json配置
│       ├── plugins              # web_backend命令实现调用的插件
│       └── script               # web_backend命令实现调用的脚本
├── mapper_check                 # 北向映射器检查工具
│   ├── PrivilegeMapUriChecker   # 检查工具拓展组件-PrivilegeMap检查类
│   ├── data_mapping_schema.json # 语法检查json配置
|   ├── ErrorlogPrinter.py       # 检查工具错误信息打印类
|   └── MapperCheck.py           # 检查工具核心代码
└── build.py                     # rackmount构建脚本
```

### 运行方法
检查工具脚本已集成至构建脚本`build.py`中，执行`python3 build.py`构建时会自动先运行检查工具。若检查失败报错，会终止构建；若无报错，则开始构建。

### 文件结构
执行检查及构建时，请按照上方的文件结构，尽量不要做改动！！

`build.py`从`mapper_check`文件下引入了`MapperCheck`模块，而`MapperCheck`模块检查时会依赖于`data_mapping_schema.json`语法检查配置文件，代码中规定了该配置文件的相对位置在`mapper_check`文件下，因此请勿移动`build.py`、`MapperCheck.py`、`data_mapping_schema.json`的位置。

同样的，在检查命令json配置、命令调用的插件、脚本合法性过程中，依赖于各组件`mapping_config`（cli的为`ipmcget`和`ipmcset`）下的配置文件、`plugins`下的插件和`script`下的脚本，而在代码中规定了这些文件的相对位置都在`interface_config`下的各自组件下，因此请勿移动这些文件的相对位置。

此外，获取绝对位置时会搜索`/rackmount`所在位置，因此也请不要将代码移出`rackmount`仓。

### 检查范围

<table>
	<tr>
		<th>分类</th>
		<th>编号</th>
		<th>名称</th>
		<th>规则描述</th>
		<th>状态</th>
	</tr>
	<tr>
		<td>语法检查</td>
		<td>1-1</td>
		<td>映射器配置文件语法检查</td>
		<td>映射器配置必须满足数据映射器规定的语法</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td rowspan="10">引用合法性</td>
		<td>2-1</td>
		<td>被引用数据来源检查</td>
		<td>数据引用来源只能是Uri、ReqBody、Query、ProcessingFlow[].Destination、Statements、Context</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>2-2</td>
		<td>数据引用位置检查</td>
		<td>数据引用只能在以下配置项中使用：Statements、ResourceExist、RspBody、ProcessingFlow[].Path、ProcessingFlow[].Interface、ProcessingFlow[].Name、ProcessingFlow[].Params、ProcessingFlow[].Source、ProcessingFlow[].CallIf、ProcessingFlow[].Foreach</td>
		<td>未支持</td>
	</tr>
	<tr>
		<td>2-3</td>
		<td>Uri动态槽位号的名称大小写检查</td>
		<td>Uri中表示动态槽位号必须是冒号+全小写字符，不能包括大写字符，否则引用的时候拿不到正确值。</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>2-4</td>
		<td>Uri被引成员存在性检查</td>
		<td>被引用的Uri槽位号必须在Uri中声明了，例如Uri声明如下，则只能使用${Uri/managerid}和${Uri/id}，不能有其他形式的引用/redfish/v1/Managers/:managerid/NetworkProtocol/:id</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>2-5</td>
		<td>ReqBody被引成员存在性检查</td>
		<td>被引用的ReqBody成员必须在ReqBody中声明了</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>2-6</td>
		<td>Query被引成员存在性检查</td>
		<td>被引用的Query成员必须在Query中声明了</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>2-7</td>
		<td>ProcessingFlow被引成员存在性检查</td>
		<td>1.被引用的ProcessingFlow数组下标必须合法<br>
            2.被引用的成员必须在对应Destination下有定义</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>2-8</td>
		<td>Statements被引成员存在性检查</td>
		<td>被引用的Statements方法必须在Statements中有定义</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>2-9</td>
		<td>Context被引成员存在性检查</td>
		<td>1.CLI支持的字段有：Interface(固定取值CLI)、UserName、ClientIp
2.Redfish支持的字段有：Interface(固定取值Redfish)、UserName、ClientIp、Privilege、AccountId。以上为用户名密码方式支持的字段，如果是会话Token鉴权，还额外支持以下字段RoleId、AuthType、Token
3.web_backend支持的字段有：Interface(固定取值WEB)、UserName、ClientIp、Privilege、AccountId、RoleId、AuthType、Token</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>2-10</td>
		<td>全局变量有效性检查失败</td>
		<td>被引用的全局变量必须在config.json中有定义</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td rowspan="8">功能检查</td>
		<td>3-1</td>
		<td>Uri合法性校验存在性检查</td>
		<td>如果Uri中声明了动态槽位号，则必须定义ResourceExist关键字进行Uri合法性检查</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>3-2</td>
		<td>@odata.context属性格式检查</td>
		<td>V3 @odata.context必须使用规范新格式，参考如下：
		
"@odata.context": "/redfish/v1/$metadata#SessionCollection.SessionCollection"

 "@odata.context": "/redfish/v1/$metadata#ServiceRoot.ServiceRoot"</td>
 		<td>已支持</td>
	</tr>
	<tr>
		<td>3-3</td>
		<td>Uri重复性检查</td>
		<td>某类接口（redfish、web-rest）映射器配置中声明的Uri必须全局唯一，不能重复声明</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>3-4</td>
		<td>Uri动态槽位号合法性检查</td>
		<td>Uri中表示Managers、Systems、Chassis的动态槽位号必须是冒号+全小写字符，不能将其写死为固定数值。</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>3-5</td>
		<td>厂商定制正确性检查</td>
		<td>redfish接口厂商名须统一用{{OemIdentifier}}表示，snmp接口中oid的厂商名需统一替换为{{SnmpOemIdentifier}}</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>3-6</td>
		<td>ActionResponseBody字段配置协议检查</td>
		<td>ActionResponseBody字段只有redfish协议支持配置</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>3-7</td>
		<td>ActionResponseBody字段配置对应URI检查</td>
		<td>只有Actions类型的UrI才允许配置ActionResponseBody字段</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>3-8</td>
		<td>ActionResponse与RspBody冲突检查</td>
		<td>ActionResponse不能与RspBody共同使用</td>
		<td>已支持</td>
	</tr>	
	<tr>
		<td rowspan="4">脚本和插件检查</td>
		<td>4-1</td>
		<td>被调用的脚本存在性检查</td>
		<td>映射器中Script关键字下引用的脚本文件必须存在</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>4-2</td>
		<td>被调用的插件存在性检查</td>
		<td>映射器中Plugin关键字下引用的插件代码文件必须存在</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>4-3</td>
		<td>被调用的插件函数存在性检查</td>
		<td>映射器中Plugin关键字下引用的插件函数必须存在</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>4-4</td>
		<td>被调用的插件函数入参个数检查</td>
		<td>映射器中Plugin关键字下引用的插件函数所传入的参数必须与插件中定义的函数入参个数相同</td>
		<td>已支持</td>
	</tr>
	<tr>
		<td>mdb资源树定义检查</td>
		<td>5-1</td>
		<td>被引用的资源树定义检查</td>
		<td>映射器中引用的资源树Path、Interface、Property、Method必须已定义</td>
		<td>未支持</td>
	</tr>
	<tr>
		<td>接口配套资源校验</td>
		<td>6-1</td>
		<td>redfish接口PrivlegeMap校验</td>
		<td>redfish接口必须在PrivlegeMap中添加权限声明</td>
		<td>已支持</td>
	</tr>
</table>

## 三、修改建议
### 问题打印
`MapperCheck.py`中定义了`errorlog_printer`类用于打印错误信息，成员函数`print_errorlog2file()`将错误信息打印至`interface_config/error_log.txt`，每次运行都会删除上一次的错误信息文件；成员函数`print_errorlog2tmn()`将错误信息打印至终端。

若仅需要其中一种打印方式，在类中注释掉该函数对应调用即可。

错误信息会按照
```
=============reference errors found in file: <文件所在路径>
[Rule x-x]: 错误提示，错误值，错误位置: URI, Type, location
```
的格式打印。

### 问题定位

在定位错误位置时，可以**直接粘贴文件所在路径找到出错位置所在文件->粘贴错误值索引到错误内容**。若在同文件中定位到多处错误值匹配，可根据错误位置精细定位。

### 常见错误
本节仅对扫描、定位出的常见问题举例，提供快速查找定位的参考。实际排查过程中请具体问题具体分析。

#### [Rule 2-1]: 被引用数据来源检查失败
错误提示示例：
```
=============reference errors found in file: <path>
[Rule 2-1]: 被引用数据来源检查失败, 错误值:${ProcessingFlow[2].Destination.NetMode}, 出错位置:xxx
```
数据引用中的索引应用斜杠`/`分隔，其余符号都会无法解析导致引用失败。修改示例：

```
- ${ProcessingFlow[2].Destination.NetMode}
+ ${ProcessingFlow[2]/Destination/NetMode}
```

#### [Rule 2-x]: xxx引用有效性检查失败
错误提示示例：
```
=============reference errors found in file: <path>
[Rule 2-4]: Uri引用有效性检查失败, 错误值:${Uri/controllerid}, 错误位置: URI:/redfish/v1/Systems/:systemid/EthernetInterfaces/:ethernetinterfaceid/VLANs/:vlanid/xxx
```
URI中的动态槽位号可以自行命名，请在引用时检查对应数据体中是否含有需要引用的数据名。修改示例：

```
- ${Uri/controllerid}
+ ${Uri/systemid}
```

当引用`ProcessingFlow`中的返回值时，引用有效性检查失败还有一种比较经典的错误：错误认为`ProcessingFlow`索引从数组下标0开始。资源树是用Lua代码实现的，Lua中数组是从1开始索引的，因此请检查是否是`ProcessingFlow`索引下标出错。修改示例：

```
- ${ProcessingFlow[0]/Destination/Value}
+ ${ProcessingFlow[1]/Destination/Value}

```

#### [Rule 3-4]: URI中的动态槽位号合法性检查失败
错误提示示例：
```
=============reference errors found in file: <path>
[Rule 3-4]: URI中的动态槽位号合法性检查失败, 错误值:/redfish/v1/Chassis/1/xxx, 错误位置:xxx
```
`Systems`/`Chassis`/`Managers`后不能绑定数字，需绑定动态槽位号。修改示例：

```
- /redfish/v1/Chassis/1/xxx
+ /redfish/v1/Chassis/:chassisid/xxx
```

#### [Rule 3-5]: 厂商定制正确性检查失败
错误提示示例：
```
=============reference errors found in file: <path>
[Rule 3-5]: 厂商定制正确性检查失败, 错误值:xxx, 错误位置: xxx
```
请检查对应文件中是否未用{{OemIdentifier}}替换redfish接口中的Huawei字段，以及是否未用{{SnmpOemIdentifier}}替换snmp接口中oid中的Huawei厂商名2011.2.235.1.1。修改示例：

```
- "Uri": "/redfish/v1/Chassis/:chassisid/PowerSubsystem/Oem/Huawei/PowerConverters",
+ "Uri": "/redfish/v1/Chassis/:chassisid/PowerSubsystem/Oem/{{OemIdentifier}}/PowerConverters", 
```

#### [Rule 3-6]: ActionResponseBody只有redfish协议支持配置
错误提示示例：
```
=============reference errors found in file: <path>
[Rule 3-6]:ActionResponseBody只有redfish协议支持配置, 错误值:xxx, 错误位置: xxx
```
请检查错误位置对应URI是否为redfish接口，只有redfish协议支持配置ActionResponseBody字段。修改示例：
```
- "ActionResponseBody": {},
+ "RspBody": {},
```

#### [Rule 3-7]: ActionResponseBody只允许配置到Actions的URI中
错误提示示例：
```
=============reference errors found in file: <path>
[Rule 3-7]: ActionResponseBody只允许配置到Actions的URI中, 错误值:xxx, 错误位置: xxx
```
请检查Url是否为Actions类型。修改示例：

```
- "Uri": "/redfish/v1/CertificateService/CertificateService.GenerateCSR",
+ "Uri": "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR", 
```

#### [Rule 3-8]: 不允许同时定义ActionResponseBody和RspBody
错误提示示例：
```
=============reference errors found in file: <path>
[Rule 3-8]: 不允许同时定义ActionResponseBody和RspBody, 错误值:xxx, 错误位置: xxx
```
请检查该接口是否同时定义了RspBody和ActionResponseBody。修改示例：

```
- "RspBody": {},
```

#### [Rule 4-3]: 被调用的插件函数存在性检查失败
错误提示示例：
```
=============reference errors found in file: <path>
[Rule 4-3]: 被调用的插件函数存在性检查失败, 错误值:orchestrator.systems.get_Volume_health(xxx), 出错位置:xxx
```
调用插件中的函数名注意需全部小写。修改示例：#### [Rule 4-3]: 被调用的插件函数存在性检查失败

```
- orchestrator.systems.get_Volume_health(xxx)
+ orchestrator.systems.get_volume_health(xxx)
```

#### [Rule 4-4]: 被调用的插件函数入参个数检查失败
错误提示示例：
```
=============reference errors found in file: <path>
[Rule 4-4]: 被调用的插件函数入参个数检查失败, 错误值:orchestrator.thermal.liquid_cooling_level_set_supported(Input, ReqBody.Oem.Huawei.LiquidCoolingUnitsLevel), 出错位置:xxx
```
请检查对应文件中的函数定义的入参个数与实际入参是否一致。修改示例：

```
- orchestrator.thermal.liquid_cooling_level_set_supported(Input, ReqBody.Oem.Huawei.LiquidCoolingUnitsLevel)
+ orchestrator.thermal.liquid_cooling_level_set_supported(ReqBody.Oem.Huawei.LiquidCoolingUnitsLevel) # 调用函数只有1个入参
```

#### [Rule 6-1]: redfish接口PrivlegeMap校验检查失败
错误提示示例：
```

==================================================
PrivilegeMap检查不通过，错误详情如下：
==================================================
PATCH接口：xxx，类型：xxx 未配置PrivilegeMap
==================================================

=============reference errors found in file: NULL
[Rule 6-1]: redfish接口PrivlegeMap校验检查失败, 错误值:NULL, 出错位置:NULL
```
请检查对应接口权限是否在PrivilegeMap中配置。

## 四、语法更新
### Time: 2024-07-20 ReqBody格式整改
更新`ReqBody`的声明：
```
"ReqBody": {
	"Type": "object",
	"Required": true,
	"Properties": {
		"Name": {
			"Required": xxx,
			"Type": "xxx",
			"Validator": [
				{
					xxx
				}
			],
			"Sensitive": xxx,
			"Description": "xxx"
		}
	}
}
```
主要整改方向：

- `ReqBody`类型更改为`object`
- `ReqBody`中需要填写的属性都移动至`"Properties"`属性下，类型为`object`

整改前`ReqBody`声明方式（供参考对照）：
```
"ReqBody": [
	{
		"Name": "xxx",
		"Type": "xxx",
		"Required": xxx,
		"Validator": [
			{
				xxx
			}
		]
		"Sensitive": xxx,
		"Description": "xxx"
	}
]
```