openUBMC中北向接口(Redfish、Web-backend、CLI、SNMP)的设计,针对接口格式与资源协作接口的对接,提出配置、映射的概念,进而引入了映射接口配置的机制,框架解析接口配置,将接口请求映射到资源协作接口,拼装对应格式的返回数据。映射接口配置机制特点如下:
- 简易定制:封装接口协议,使用标准的资源协作接口实现接口配置
- 高效扩展:抽象接口数据模型,建立映射规则,扩展接口仅需修改配置
- 业务解耦:解耦接口与功能逻辑,避免接口错误调用影响业务功能
本文介绍公共接口映射配置规则,部分规则只在单一接口使用,可见其对应的接口配置指南。
通用格式
接口映射配置的通用格式:
{
"Resources": [
{
"Uri": "xxxx",
"Interfaces": [
{
"Type": "GET/PATCH/POST/DELETE",
"ResourceExist": {xxxx},
"ReqBody": {xxxx},
"RspBody": {xxxx},
"RspHeader": {xxxx},
"Statements": {xxxx},
"ProcessingFlow": [xxxx]
}
]
}
]
}
Resources
: Uri配置对象数组Interfaces
:Uri所对应的多种Type
的请求配置数组Type
:请求方法,支持GET、PATCH、POST、DELETE
ResourceExist
: Uri有效性校验ReqBody
:请求体配置RspBody
:响应体配置RspHeader
: 响应头配置Statements
: 数据处理ProcessingFlow
:资源协作接口映射配置
Uri有效性校验
在Uri配置中,需要利用动态字段指明同类资源中的某个具体资源。因此需要根据资源存在与否,校验访问Uri的有效性,相当于接口的入参校验。 例如"Uri": "/redfish/v1/Managers/:managersid"
中:managersid
就是动态参数。对于机架机型,一般有效取值为1,外部需要访问Uri:/redfish/v1/Managers/1
进行正确的接口访问
"ResourceExist": {
"${Uri/managersid}": "1",
"${Statements/GetName()}": "#WITH",
"${ProcessingFlow[1]/Destination/Valid}": true
}
当ResourceExist
字段中配置每个键值对中key==value
的判断均成立时,入参校验成功,资源存在条件全部满足,Uri校验成功。 上述例子中,除了当Uri/managersid
取值为"1"这个判断固定结果的条件外,还有另外两个条件需要满足。 其中#WITH
和#WITHOUT
来表示属性是否为nil;true和false作为bool类型值的判定规则。
只有当Uri/managersid
取值为"1"、Statements/GetName()
不为nil、ProcessingFlow[1]/Destination/Valid
取值为true,所有条件都满足时Uri校验成功。(ProcessingFlow的配置被ResourceExist直接或间接引用时,需要加上配置"CallIf": "CheckUri") 注:由于PATCH
接口在处理前会会调用一次对应GET
接口,所以PATCH
无需再配置ResourceExis
。
ReqBody声明
用户在发送请求时可能会携带请求体,由于请求体是用户输入,在实际使用中未必能按照预期输入数据,例如属性类型要求是字符串类型,但是用户输入却是数字。所以需要一种语法来声明请求体的数据内容,对请求体格式做最基本的校验,这里使用ReqBody
来声明。(ReqBody
声明类似JSON Schema官方文档的语法规范)
以如下请求体为例(假设UserName
是必填属性,且只允许取值Administrator
或root
):
{
"UserName": "root"
}
其对应的ReqBody配置应该为
"ReqBody": {
"Type": "object",
"Required": true,
"Properties": {
"UserName": {
"Type": "string",
"Required": true,
"Validator": [
{
"Type": "Enum",
"Formula": ["Administrator", "root"]
}
]
}
}
}
下面将详细说明请求体校验的的规则配置
完整性校验
关键字Required
用于声明请求体属性是否为必填参数,取值true/false
,未配置时取默认值false
。
"ReqBody": {
"Type": "object",
"Required": true,
"Properties": {
"PropA": {
"Required": true
},
"PropB": {
"Required": false
},
"PropC": {
"Type": "object",
"Properties": {
"Prop1": {
"Required": true
}
}
}
}
}
例如上述声明,请求体为{"PropA": 1}
或者{"PropA": 1, "PropB": 2}
是符合要求的(PropC
对象不存在时,不需要对PropC/Prop1
做完整性校验);请求体为{"PropA": 1, "PropC": {}}
或者{"PropB": 2}
是不符合要求的,前者缺少PropC/Prop1
,后者缺少PropA
。
数据类型校验
Type
关键字是数据类型校验的基础,可以是一个字符串或数组:
- 字符串,指定数据类型,可能取值
array、boolean、integer、number、null、object、string
- 字符串数组,允许多种类型,其中每个字符串是其中一种基本类型的名称,每个元素都是唯一的。在这种情况下,请求体数据与任一元素类型匹配上即可。
"ReqBody": {
"Type": "object",
"Required": true,
"Properties": {
"PropA": {
"Type": "string"
},
"PropB": {
"Type": ["number", "boolean"]
}
}
}
例如上述声明,请求体{"PropA": "str", "PropB": 1}
符合要求,{"PropA": 1, "PropB": "str"}
不符合要求。PropA
必须是字符串类型,PropB
可以是数字或者布尔类型。
除了boolean、integer、number、null、string
这些易于理解的基础类型外,下面对复合类型object、array
进行说明
object对象类型
对象(object
)是 JSON 中的映射类型,将“键”映射到“值”。在 JSON 中,“键”必须始终是字符串。这些对中的每一组通常被称为“属性”。 对象的属性(键值对)是使用Properties
关键字定义的 。Properties
的值是一个对象数组,数组的每个元素用于声明属性的特性,其内部同样也可以使用Type、Required
等关键字配置数据类型和完整性校验。
array数组类型
数组用于有序元素。在 JSON 中,数组中的每个元素可能是不同的类型。 JSON 中数组的使用一般有两种方式:
列表验证:任意长度的序列,其中每个元素都匹配相同的模式。
元组验证:一个固定长度的序列,其中每个元素可能有不同的模式。
列表验证 列表包含
Items、minItems、maxItems和uniqueItems
等关键字。Items
: 定义列表的单个元素校验,可以使用Type
等关键字。maxItems
: 定义列表的最大元素个数。缺省情况下不校验列表参数的最大元素个数。minItems
: 定义列表的最小元素个数。缺省情况下不校验列表参数的最小元素个数。uniqueItems
: 定义是否需要保证每个数组元素不相同。配置为true
,数组中的每一个元素必须唯一,配置为false
,则允许数组包含重复元素。缺省情况下为false
。json"ReqBody": { "Type": "object", "Required": true, "Properties": { "PropA": { "Type": "array", "Items": { "Type": "number" }, "minItems": 2, "maxItems": 5, "uniqueItems": true } } }
上述配置中属性
PropA
是一个数组,数组的每个元素都是数字,最少包含2个元素,最多包含5个元素,且数组中每个元素都不相同。元组验证
当数组是一个元素的集合时,元组验证很有用,每个元素可能有不同的模式。例如一个三元组 [编号、用户名、是否锁定],这些字段中的每一个都将具有不同的模式:
Id
:编号,必须是数字UserName
: 用户名,必须是字符串Locked
:是否锁定,必须是布尔值true/falseItems关键字设置为一个数组,其中每个元素都是一个模式,对应于元组的每个索引。也就是说,一个数组,其中第一个元素验证输入数组的第一个元素,第二个元素验证输入数组的第二个元素,依此类推。
json"ReqBody": [ { "Name": "PropA", "Type": "array", "Items": [ { "Type": "number" }, { "Type": "string" }, { "Type": "boolean" } ] }, ]
上述例子中,当PropA
的取值为[10086, 10001, true]
是不符合要求的,第二个元素必须是字符串,而10001
是一个数字。[10086, "root", true]
时是符合要求的,[10086, "root"]
和[10086, "root", true, "str"]
也是符合要求的。
数据内容校验Validator
有时需要对请求体属性内容做一些简单的校验,例如字符串长度符合范围或者限制为一组固定的值,这里使用关键字Validator
来声明相关内容校验,其中校验的种类由Type
字段来表征,当前支持的Type
可见下述各小节
{
"Name": "Prop",
"Validator": [
{
"Type": xxx,
"Formula": xxx
}
]
}
Enum
{
"Type": "Enum",
"Formula": ["Administrator", "root", "Admin"]
}
枚举校验,Formula
配置符合范围的取值数组,其中每个元素都唯一。可用于boolean、integer、number、null、string
简单数据类型的检验。(也可配置object
或者array
复杂结构,不推荐使用,复杂结构也要进行对应声明)
Length
{
"Type": "Length",
"Formula": [1, 16]
}
校验字符串长度。Formula
指定长度上下限(闭合区间),若上限或下限无要求,可配置为null
。
Nonempty
{
"Type": "Nonempty"
}
字符串非空校验,取值""
是不符合要求的。
Range
{
"Type": "Range",
"Formula": [1, 16]
}
数字范围校验,可用于integer
和number
数据类型,Formula
指定范围上下限(闭合区间),若上限或下限无要求,可配置为null
Regex
{
"Type": "Regex",
"Formula": "^xx[0-9]"
}
正则匹配校验(标准正则,非Lua
正则),可用于string
数据类型。上述例子中,取值"xx1"
符合要求,取值"xxx"
不符合要求
IPFormat
{
"Type": "IPFormat",
}
IP格式校验,,可用于string
数据类型。
Script
{
"Type": "Script",
"Formula": "if Input % 5 == 0 then local err = base_messages.PropertyValueFormatError(Input, PropertyName) err.RelatedProperties = {'#/' .. PropertyName} error(err) end return true"
}
复用数据处理中的Script
,具体使用规则可见数据处理statements章节。校验失败则执行error
抛出错误(必须是BMC定义的通用错误)。校验通过返回true或nil
。 相比较数据处理的Script,环境变量变化如下:
Input
:校验的属性取值PropertyName
: 新增变量,表示校验的属性名,例如Oem/openubmc/PropA
ProcessingFlow
: 校验时并未进行资源协作接口访问,故删除此变量
敏感信息打码Sensitive
当请求体存在敏感数据,错误Message中应该将该字段的值打码为******
。引入Sensitive
关键字,用于表示需要将该属性的取值打码,避免敏感数据直接呈现在Message
信息中。 敏感信息必须显示配置"Sensitive": true
,openUBMC涉及的敏感信息包括但不限于:用户密码、会话token等。
"ReqBody": {
"Type": "object",
"Required": true,
"Properties": {
"Password": {
"Type": "string",
"Sensitive": true
}
}
}
当请求体取值{"Password": 111}
,类型校验失败,其拿到的错误信息为:
"MessageId": "Base.1.0.PropertyValueTypeError",
"Message": "The value ****** for the property Password is of a different type than the property can accept.",
"MessageArgs": [
"******",
"Password"
],
...
RspBody定义
RspBody为接口响应体格式配置关键字,拼接从资源协作接口或者数据处理后获取的属性值,返回给用户侧,作为接口调用回显。
例如如下的RspBody配置:
"RspBody": {
"@odata.context": "/redfish/v1/$metadata#AccountService/Accounts/Members/$entity",
"Name": "User Account",
"UserName": "${ProcessingFlow[1]/Destination/UserName}",
"Oem": {
"openubmc": {
"LoginRule": null
}
}
}
其中${ProcessingFlow[1]/Destination/UserName}
表示数据需要从资源协作接口获取,详细规则可见下一章节资源协作接口映射配置ProcessingFlow,当其获取的值为"Administrator"
时,用户侧收到的响应体如下:
{
"@odata.context": "/redfish/v1/$metadata#AccountService/Accounts/Members/$entity",
"Name": "User Account",
"UserName": "Administrator",
"Oem": {
"openubmc": {
"LoginRule": null
}
}
}
资源协作接口映射配置ProcessingFlow
一般格式为:
"ProcessingFlow": [
{
"Type": "Property/Method/List/Task/Paging"
"Path": "xx",
"Interface": "xxx",
"Name": "xxx",
"Params": [xxx],
"Destination": {xxx},
"Source": {xxx},
"CallIf": {xxx},
"Foreach": "xxx"
},
xxx
]
通用字段说明:
ProcessingFlow
:取值必须为对象数组,每个对象指明一个资源协作接口映射,对象的映射数据按序执行Type
: 映射类型,当前支持Property/Method/List/Task/Paging
,具体使用方式可下述各小节(Paging仅CLI接口使用)Path
:MDS对象Interface
: 资源协作接口Name
: 方法名Params
:方法参数Destination
:返回值配置Source
: 设置属性时使用CallIf
:资源协作接口是否调用,适用于所有映射类型,规则可见CallIfForeach
: 资源协作接口多次调用,适用于所有映射类型,规则可见Foreach
Property属性
获取属性
json{ "Type": "Property", "Path": "/bmc/kepler/Managers/1/EthernetInterfaces/Ipv4", "Interface": "bmc.kepler.Managers.EthernetInterfaces.Ipv4", "Destination": { "IpMode": "IpModeIpv4", "IpAddr": "IpAddrIpv4", "SubnetMask": "SubnetMask" } }, { "Type": "Property", "Path": "/bmc/kepler/Managers/1/EthernetInterfaces/Ipv6", "Interface": "bmc.kepler.Managers.EthernetInterfaces.Ipv6", "Destination": { "IpMode": "IpModeIpv6" } }
Destination
配置了一系列键值对,key
表示资源协作接口上的属性名,获取该属性,该将属性名称重名为value
。
因为不同资源协作接口可能存在相同的属性名,有重命名的存在,就可以避免命名冲突。如上例,${ProcessingFlow[1]/Destination/IpModeIpv4}
说明获取的是IPv4资源协作接口的IpMode属性;${ProcessingFlow[2]/Destination/IpModeIpv6}
说明获取的是IPv6资源协作接口的IpMode属性设置属性
json{ "Type": "Property", "Path": "/bmc/kepler/Managers/1", "Interface": "bmc.kepler.Managers.Ntp", "Source": { "Preferred": "${ReqBody/PreferredServer}" } }
与获取属性的结构类似,关键字改为
Source
。上述例子中将请求体中的PreferredServer
属性值设置到资源协作接口的属性Preferred
中
Method
{
"Type": "Method",
"Path": "/bmc/kepler/Systems/Events",
"Interface": "bmc.kepler.Systems.Events",
"Name": "GetSelInfo",
"Params": [
"123"
],
"ContextParams": {
"SystemId": "1"
},
"Destination": {
"Version": "Version",
"CurrentEventNumber": "CurrentEventNumber",
"MaxEventNumber": "MaxEventNumber"
}
}
Name
: 方法名Params
:方法参数,可缺省,表示没有参数(不包括上下文参数)ContextParams
: 上下文参数配置,对应资源协作接口中的方法调用所需的a{ss}上下文参数Destination
:与获取属性配置类似,左边的key对应方法返回值的变量名,右边的value对应开发者配置的变量名
以上述配置为例,在实际环境使用busctl调用方法,有如下返回:
> busctl --user call bmc.kepler.event /bmc/kepler/Systems/Events bmc.kepler.Systems.Events GetSelInfo a{ss} 0
suu "1.0.0" 0 10000
返回为suu "1.0.0" 0 10000
,suu
表示后接数据类型依次是String、U32、U32
。此处并未能看出Version、CurrentEventNumber、MaxEventNumber
字段。
查看GetSelInfo
的声明(在mdb_interface
仓中有关于资源协作接口返回字段的声明),rsp
包含对返回字段的声明
"GetSelInfo" :{
"req": {},
"rsp": {
"Version": {
"baseType": "String"
},
"CurrentEventNumber": {
"baseType": "U32"
},
"MaxEventNumber": {
"baseType": "U32"
}
}
}
使用代理对象调用method
时,会对返回值做结构化封装,例如GetSelInfo
方法的返回值应该为:
{
Version = "1.0.0",
CurrentEventNumber = 0,
MaxEventNumber = 1000
}
List
{
"Type": "List",
"Path": "/bmc/kepler/Managers/1/NetworkProtocol",
"Interface": "bmc.kepler.Managers.NetworkProtocol.Protocol",
"Params": [1],
"Destination": {
"Members": "service_table"
}
}
获取资源协作接口集合。
Interface
:未配置时获取MDS子对象所有资源协作接口,配置时获取包含Interface的资源协作接口。Params
:可按需配置。Params[1]表征获取深度,例如1表示MDS子对象,2表示MDS子子对象。Params未配置时默认表示深度1。
上述例子中/bmc/kepler/Managers/1/NetworkProtocol资源有如下MDS子对象(子对象的资源协作接口均有bmc.kepler.Managers.NetworkProtocol.Protocol)
> busctl --user tree bmc.kepler.nsm
└─/bmc
└─/bmc/kepler
└─/bmc/kepler/Managers
│ └─/bmc/kepler/Managers/1
│ └─/bmc/kepler/Managers/1/NetworkProtocol
│ ├─/bmc/kepler/Managers/1/NetworkProtocol/HTTP
│ ├─/bmc/kepler/Managers/1/NetworkProtocol/HTTPS
│ ├─/bmc/kepler/Managers/1/NetworkProtocol/KVMIP
那么配置的结果为,Destination增加了如下键值对
"service_table": [
"/bmc/kepler/Managers/1/NetworkProtocol/HTTP",
"/bmc/kepler/Managers/1/NetworkProtocol/HTTPS",
"/bmc/kepler/Managers/1/NetworkProtocol/KVMIP",
......
]
Task
{
"Type": "Task",
"Path": "/bmc/kepler/Managers/1/LogServices",
"Interface": "bmc.kepler.Managers.LogServices",
"Name": "Dump",
"Params": [
0
],
"Destination": {
"TaskId": "TaskId"
},
"PostTaskProcess": [
{
"Type": "ChangeOwner",
"Params": [
"/tmp/web/operate.log"
]
}
]
}
Task
是一种特殊的Method,用于异步执行的方法。
PostTaskProcess
, 任务后置操作,用于配置在task执行完毕后执行指定操作。Type指明操作类型,目前已支持在文件导出任务后执行修改文件属主和权限。ChangeOwner
: 修改文件属主为当前用户,修改文件权限为600,参数:本地文件绝对路径(不支持远程文件路径)
补充:一个ProcessingFlow
中可以配置多个Task
,但是只应该有一个生效。可以通过CallIf
条件来控制具体执行的Task
CallIf
"CallIf": {
"${ReqBody/PropA}": "#WITH"
"${ReqBody/PropB}": "#WITHOUT",
"${ReqBody/PropC}": "str1",
"${Uri/id}": 1
}
只有当每个属性判断均成立,CallIf条件才成立,对应的Methods等才有必要执行。引入#WITH
和#WITHOUT
来表示属性是否存在。 上述例子表明,只有当ReqBody/PropA
存在、ReqBody/PropB
不存在、ReqBody/PropC
取值为"str1"、Uri/id
取值为1,四个条件都满足时CallIf条件才成立。
Foreach
"ReqBody": {
"Type": "object",
"Required": true,
"Properties": {
"SnmpTrapNotification": {
"Type": "object",
"Properties": {
"TrapServer": {
"Type": "array",
"Items": {
"Type": "object",
"Properties": {
"TrapServerPort": {
"Type": "number"
}
}
}
}
}
}
}
},
"ProcessingFlow": [
{
"Type": "Property",
"Path": "/bmc/kepler/EventService/Subscriptions/Snmp/Nmses/${#INDEX}",
"Interface": "bmc.kepler.EventService.Subscriptions.Snmp.Nms",
"Source": {
"Port": "${ReqBody/SnmpTrapNotification/TrapServer[#INDEX]/TrapServerPort}"
},
"Foreach": "${ReqBody/SnmpTrapNotification/TrapServer}"
}
]
上述配置例子,请求体中SnmpTrapNotification/TrapServer是一个数组,需要根据数组大小多次设置bmc.kepler.EventService.Subscriptions.Snmp.Nms的属性。
{
"SnmpTrapNotification": {
"TrapServer": [
{
"TrapServerPort": 3162
},
{
"TrapServerPort": 3163
},
{
"TrapServerPort": 3164
},
{
"TrapServerPort": 3165
}
]
}
}
假设请求体为上述配置,SnmpTrapNotification/TrapServer数组大小配置为4,通过语句"Foreach": "${ReqBody/SnmpTrapNotification/TrapServer}"
可断定需要设置属性4次(Foreach也可直接配置数字)。第一次时,Path为/bmc/kepler/EventService/Subscriptions/Snmp/Nmses/1
,数据源为${ReqBody/SnmpTrapNotification/TrapServer[1]/TrapServerPort} (#INDEX和Foreach配套使用,#INDEX会被替换成迭代时的次数)。
数据引用
数据引用:某些位置(DEST)需要使用来自外部的数据(SRC),例如响应体需要使用资源协作接口集合上的数据。
为了简化引用数据,以相同的格式来获取SRC数据,定义规则:${var}表示var的取值来自于外部数据SRC,例如${ReqBody/xxx}
表示引用请求体ReqBody
中的xxx属性 数据引用得到的数据类型是JsonObject,即有序json
数据来源
对于接口映射配置来说,作为外部数据的有:
Uri
: URI包含的动态字段Query
:查询参数ReqBody
:请求体中的数据ReqBodyOriginal
:请求体中的原始数据;ReqBody在校验时会删除异常数据,执行ProcessingFlow配置时获取不到,此时可获取ReqBodyOriginalReaHeader
: 请求头中的数据Context
: 上下文信息,包含的字段如下:- cli:Interface(取值CLI)、UserName、ClientIp、Privilege
- redfish用户密码鉴权方式:Interface(取值Redfish)、UserName、ClientIp、Privilege、RoleId、AccountId
- redfish会话鉴权方式:除用户密码鉴权方式的字段外,还额外支持AuthType、Token、SessionId
- web_backend:Interface(取值WEB)、UserName、ClientIp、Privilege、AccountId、RoleId、AuthType、Token、SessionId
- snmp:Interface(取值SNMP)、UserName、ClientIp、Privilege
ProcessingFlow[].Destination
:从资源协作接口中获取的数据
引用位置配置方式
- 1、属性写死
(非数据引用)理想的状态下,大部分引用位置的取值应该都是写死,是一个纯数据。
"RspBody": {
"Prop": "redfish/v1/Systems/1"
}
- 2、简易方式
框架会把${var}替换成外部数据中var的取值。
例如var取值为1,Prop的取值则为"redfish/v1/Systems/1"
"RspBody": {
"Prop" : "redfish/v1/Systems/${var}"
}
- 3、Statements方式
很多时候,拿到外部数据之后,并不能直接使用,而是需要做某些操作之后才可以使用。
这里定义"${Statements/Prop()}"
表示数据配置在Statements.Prop
中(注意:Statements/Prop
后接一对括号)。Statements.Prop
配置的含义为:引用var
的数据,经过多个Steps
处理后的取值。Statements
支持多种类型的配置,具体可见数据处理章节
"RspBody": {
"Prop" : "${Statements/Prop()}"
},
"Statements": {
"Prop": {
"Input": "${var}"
"Steps": [ {xxxx} ]
}
}
数据处理Statements
数据处理:拿到资源协作接口中的数据之后,需要做一些加工操作,才能使用
"Statements": {
"Prop": {
"Input": "${var}"
"Steps": [
{
"Type": "xxxx",
"Formula": "xxxxx"
},
{
"Type": "xxxx",
"Formula": "xxxxx"
},
]
}
}
Input:处理的数据源,取值必须是引用外部数据
Steps:数组类型,可配置多个处理逻辑。Input是Steps[1]的输入,Steps[1]的输出为Steps[2]的输入,依次类推;Steps[n]的输出为最终处理结果
Steps[].Type:指明数据处理的类型,可参考下列各小节说明
Steps[].Formula:数据处理的公式、规则。可嵌套配置数据引用
Convert
{
"Type": "Convert",
"Formula": "StringToNumber"
}
数据类型转换,上述中将字符串转换为数字类型,例如输入"12"
,输出为12
Formula指明转换的类型,当前支持的取值有:
NumberToBool
:数字转换为布尔值(0转换为false,非0转换为true)BoolToNumber
: 布尔值转换为数字(false转换为0,true转换为1)NumberToString
:数字转换为字符串StringToNumber
:字符串转换为数字FloatToInteger
:浮点型整数转换为整数。小数点后不为0,转化为nil,例如输入3.1,输出nilToHex
:数字转为16进制字符串,字母为大写Tohex
:数字转为16进制字符串,字母为小写
若输入数据类型与Formula声明的转换前类型不匹配时,输出为nil
Count
{
"Type": "Count"
}
输入数据必须为数组类型,计算该数组大小,无需Formula字段。 例如输入为["1", "2", "3"]
时,输出为3
。
Expand
{
"Type": "Expand",
"Formula": "1"
}
输入数据必须为可访问的URI字符串,Formula默认取值"1"。
Expand的作用是将URI原地替换为该URI对应的响应体。
注意:如果Expand的并非预期单独可访问的URI,不要以/redfish或者/UI/Rest开头,推荐以/expand或者/bmc/kepler开头,这样此URI在访问时会被nginx直接拦截,不会重定向到接口层业务层。
假如/redfish/v1/AccountService/Accounts/2
的响应体为
{
"UserName": "Administrator",
"RoleId": "Administrator",
"Locked": false,
}
那么输入为/redfish/v1/AccountService/Accounts/2
时,输出与上述响应体一致。
输入数据也可以是字符串数组,每个字符串都必须是可访问的URI,Expand操作会将每个字符串都原地替换为该URI对应的响应体。 假如/redfish/v1/AccountService/Accounts/3
的响应体为
{
"UserName": "Admin",
"RoleId": "Administrator",
"Locked": false,
}
那么输入为 ["/redfish/v1/AccountService/Accounts/2", "/redfish/v1/AccountService/Accounts/3"]
时,输出为:
[
{
"UserName": "Administrator",
"RoleId": "Administrator",
"Locked": false,
},
{
"UserName": "Admin",
"RoleId": "Administrator",
"Locked": false,
}
]
如果是资源协作接口路径遍历处理,可以添加资源协作接口的Uri,来获取响应数据
{
"Uri": "/bmc/kepler/AccountService/Accounts/:id",
"Interfaces": [
{
"Type": "Get",
"RspBody": {
"UserName": "${ProcessingFlow[1]/Destination/Id}",
"RoleId": "${ProcessingFlow[1]/Destination/Source}",
"Locked": "${ProcessingFlow[1]/Destination/Destination}",
},
"ProcessingFlow": [
{
"Type": "Property",
"Path": "/bmc/kepler/AccountService/Accounts/${Uri/id}",
"Interface": "bmc.kepler.AccountService.ManagerAccount",
"Destination": {
"UserName": "UserName",
"RoleId": "RoleId",
"Locked": "Locked"
}
}
]
}
]
}
输入数据也可以是对象数组,每个对象只有一个键值对,键必须为@odata.id
, 值必须为可访问的URI字符串,Expand
操作会将每个对象都原地替换为该URI对应的响应体。
L-Pair
{
"Type": "L-Pair",
"Formula": "@odata.id"
}
输入必须为数组,Formula
可取任意字符串。L-Pair
是将Formula
作为key
,数组元素作为value
,拼装成对象,替换原来的元素。
例如输入为["/redfish/v1/System/Blade1", "/redfish/v1/System/Blade2", "/redfish/v1/System/Blade3"]
,Formula
为@odata.id
时,输出为:
[
{"@odata.id": "/redfish/v1/System/Blade1"},
{"@odata.id": "/redfish/v1/System/Blade2"},
{"@odata.id": "/redfish/v1/System/Blade3"}
]
Prefix-Add
{
"Type": "Prefix-Add",
"Formula": "/redfish/v1/"
}
Formula为待增加的前缀字符串
- 输入为字符串或者数字时,输出为Formula + 输入组合成的字符串
- 输入为数组时,则为数组每个元素都增加Formula前缀作为输出
以Formula
取值/redfish/v1/
为例; 输入为"System"
时,输出为"/redfish/v1/System"
输入为["System/Blade1", "System/Blade2"]
时,输出为["/redfish/v1/System/1", "/redfish/v1/System/Blade2"]
Prefix-Trim
{
"Type": "Prefix-Trim",
"Formula": "/bmc/kepler/"
}
Prefix-Trim为删除前缀,使用规则与Prefix-Add类似
Suffix-Add
{
"Type": "Suffix-Add",
"Formula": "/Function/1"
}
Suffix-Add
为增加后缀,Formula
为待增加的后缀字符串,以上述配置为例: 输入为"PCIeCard"
时,输出为"/PCIeCard/Function/1"
输入为["PCIeCard1", "PCIeCard2"]
时,输出为["/PCIeCard1/Function/1", "/PCIeCard2/Function/1"]
Suffix-Trim
{
"Type": "Suffix-Trim",
"Formula": "/Function/1"
}
Suffix-Trim
为删除后缀,使用规则与Suffix-Add
类似
Switch
{
"Type": "Switch",
"Formula": [
{
"Case": "Administrator",
"To": 1
},
{
"Case": "root",
"To": 2
},
{
"Case": null,
"To": 3
},
{
"To": 0
}
]
}
数据替换,类似C/Java的switch语句,Case
为分支,To
对应替换的数据(默认带break
);
仅有To
配置对应default
分支(可缺省,配置需放置在最后)。
以上述配置为例:
当输入为"Administrator"
,输出为1
;
当输入为"root"
,输出为2
;
当输入为null
或者nil(空数据)
,输出为3
;
当输入为"Admin"
,输出为0
DateFormat
{
"Type": "DateFormat",
"Formula": ["%Y-%m-%dT%H:%M:%S", true]
}
输入为时间戳(数字或者字符串数字),输出为时间字符串。Formula[1]
指定时间格式(可参考Lua
语言os.date
函数的format
参数格式),未配置时或者配置为null
时取默认值"%Y-%m-%dT%H:%M:%S"
;Formula[2]
表征是否显示时区,默认值为false
。
上述例子,当输入为1
,系统时区为+8区时,输出为"1970-01-01T08:00:01+08:00"
Script
{
"Type": "Script",
"Formula": "return ReqBody.Option == 'all'"
}
Formula
的内容是一段Lua
脚本,可实现任何复杂逻辑的处理(不推荐与其他处理类型叠加使用)。
上述例子,请求体ReqBody
的Option
为"all"
时,输出为true
,否则输出为false
。
若脚本的内容较长,放置在一行不方便阅读,可将脚本外置在文件中,Formula
配置文件名即可,例如取值get_operate_log.lua
,那么在配置文件的同级目录下需要有文件script/get_operate_log.lua
。
注:除少部分复杂逻辑无法配置,尽量减少Script
的使用。接口映射配置的本质是将接口以配置方式实现,而Script
事实上就是代码实现。
Plugin
{
"Type": "Plugin",
"Formula": "orchestrator.bios.get_registry_version(Uri.systemid)"
}
Formula
的内容是一个函数,支持入参,入参可使用ReqBody
等数据来源。Plugin
可实现复杂逻辑的业务处理(不推荐与其他处理类型叠加使用)。
以Redfish
为例,当前Redfish
插件的存放目录为/opt/bmc/apps/redfish/interface_config/plugins
,那么上述配置要求,在存放目录下有文件orchestrator/bios.lua
,并且文件有函数get_registry_version
注:减少Plugin
的使用,Plugin
的本质是抽象函数供接口配置调用,也是代码实现。
Script和Plugin的区别
Script用于纯逻辑上的处理,Formula
配置的是一段Lua
脚本或文件名,不可复用。 Plugin用于编写跟业务相关的逻辑,Formula
配置的是函数调用,需要有配套的函数实现,可复用。 Script
和Plugin
中可使用的数据变量如表格所示:
变量 | 含义 | Script环境变量 | Plugin函数入参 | Plugin环境变量 |
---|---|---|---|---|
bus | dbus总线 | ✔ | ||
mdb | mdb自省接口 | ✔ | ||
require | Lua内置函数 | ✔ | ||
libroutemapper_utils | 接口映射配置封装的C库 | ✔ | ||
mapper_interface | 接口映射配置封装的Lua库 | ✔ | ||
string | Lua标准库 | ✔ | ✔ | |
math | Lua标准库 | ✔ | ✔ | |
type | Lua内置函数 | ✔ | ✔ | |
table | Lua标准库 | ✔ | ✔ | |
ipairs | Lua内置函数 | ✔ | ✔ | |
pairs | Lua内置函数 | ✔ | ✔ | |
next | Lua内置函数 | ✔ | ✔ | |
pcall | Lua内置函数 | ✔ | ||
xpcall | Lua内置函数 | ✔ | ||
error | Lua内置函数 | ✔ | ✔ | |
base_messages | 基础消息定义 | ✔ | ✔ | |
custom_messages | 自定义的错误消息 | ✔ | ✔ | |
tonumber | Lua内置函数 | ✔ | ✔ | |
tostring | Lua内置函数 | ✔ | ✔ | |
cjson | cjson库 | ✔ | ✔ | |
null | cjson.null | ✔ | ✔ | |
lua_nil | Lua的nil取值。RspBody属性获取失败会转为null,如果属性不呈现可以用lua_nil | ✔ | ✔ | |
Uri | 数据来源 | ✔ | ✔ | |
ReqBody | 数据来源 | ✔ | ✔ | |
Query | 数据来源 | ✔ | ✔ | |
Context | 数据来源 | ✔ | ✔ | |
ProcessingFlow | 数据来源 | ✔ | ✔ | |
Input | Steps[ ]的输入 | ✔ | ✔ |
系统锁定校验LockdownAllow
LockdownAllow
用于配置系统锁定状态下操作是否允许的配置选项,值为true
代表操作允许,值为false
表示不允许。支持接口级和属性级的配置,支持内层覆盖外层的配置,未配置的默认值为false
。配置样例如下:
{
"Uri": "/redfish/v1/AccountService",
"Interfaces": [
{
"Type": "PATCH",
"LockdownAllow": false,
"Properties": {
"Oem": {
"Type": "object",
"Properties": {
"openubmc": {
"Type": "object",
"Properties": {
"SystemLockDownEnabled": {
"LockdownAllow": true,
},
"SecurityBannerEnabled": {
"Type": "boolean",
}
}
}
}
}
}
}
]
}