接口映射配置
更新时间:2024/12/19
在Gitcode上查看源码

openUBMC中北向接口(Redfish、Web-backend、CLI、SNMP)的设计,针对接口格式与资源协作接口的对接,提出配置、映射的概念,进而引入了映射接口配置的机制,框架解析接口配置,将接口请求映射到资源协作接口,拼装对应格式的返回数据。映射接口配置机制特点如下:

  1. 简易定制:封装接口协议,使用标准的资源协作接口实现接口配置
  2. 高效扩展:抽象接口数据模型,建立映射规则,扩展接口仅需修改配置
  3. 业务解耦:解耦接口与功能逻辑,避免接口错误调用影响业务功能

本文介绍公共接口映射配置规则,部分规则只在单一接口使用,可见其对应的接口配置指南。

通用格式

接口映射配置的通用格式:

json
{
    "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进行正确的接口访问

json
"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是必填属性,且只允许取值Administratorroot):

json
{
    "UserName": "root"
}

其对应的ReqBody配置应该为

json
"ReqBody": {
    "Type": "object",
    "Required": true,
    "Properties": {
        "UserName": {
            "Type": "string",
            "Required": true,
            "Validator": [
                {
                    "Type": "Enum",
                    "Formula": ["Administrator", "root"]
                }
            ]
        }
    }
}

下面将详细说明请求体校验的的规则配置

完整性校验

关键字Required用于声明请求体属性是否为必填参数,取值true/false,未配置时取默认值false

json
"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
  • 字符串数组,允许多种类型,其中每个字符串是其中一种基本类型的名称,每个元素都是唯一的。在这种情况下,请求体数据与任一元素类型匹配上即可。
json
"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/false

    Items关键字设置为一个数组,其中每个元素都是一个模式,对应于元组的每个索引。也就是说,一个数组,其中第一个元素验证输入数组的第一个元素,第二个元素验证输入数组的第二个元素,依此类推。

    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可见下述各小节

json
{
    "Name": "Prop",
    "Validator": [
        {
            "Type": xxx,
            "Formula": xxx
        }
    ]
}

Enum

json
{
    "Type": "Enum",
    "Formula": ["Administrator", "root", "Admin"]
}

枚举校验,Formula配置符合范围的取值数组,其中每个元素都唯一。可用于boolean、integer、number、null、string简单数据类型的检验。(也可配置object或者array复杂结构,不推荐使用,复杂结构也要进行对应声明)

Length

json
{
    "Type": "Length",
    "Formula": [1, 16]
}

校验字符串长度。Formula指定长度上下限(闭合区间),若上限或下限无要求,可配置为null

Nonempty

json
{
    "Type": "Nonempty"
}

字符串非空校验,取值""是不符合要求的。

Range

json
{
    "Type": "Range",
    "Formula": [1, 16]
}

数字范围校验,可用于integernumber数据类型,Formula指定范围上下限(闭合区间),若上限或下限无要求,可配置为null

Regex

json
{
    "Type": "Regex",
    "Formula": "^xx[0-9]"
}

正则匹配校验(标准正则,非Lua正则),可用于string数据类型。上述例子中,取值"xx1"符合要求,取值"xxx"不符合要求

IPFormat

json
{
    "Type": "IPFormat",
}

IP格式校验,,可用于string数据类型。

Script

json
{
    "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等。

json
"ReqBody": {
    "Type": "object",
    "Required": true,
    "Properties": {
        "Password": {
            "Type": "string",
            "Sensitive": true
        }
    }
}

当请求体取值{"Password": 111},类型校验失败,其拿到的错误信息为:

json
"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配置:

json
"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"时,用户侧收到的响应体如下:

json
{
    "@odata.context": "/redfish/v1/$metadata#AccountService/Accounts/Members/$entity",
    "Name": "User Account",
    "UserName": "Administrator",
    "Oem": {
        "openubmc": {
            "LoginRule": null
        }
    }
}

资源协作接口映射配置ProcessingFlow

一般格式为:

json
"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:资源协作接口是否调用,适用于所有映射类型,规则可见CallIf
  • Foreach: 资源协作接口多次调用,适用于所有映射类型,规则可见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

json
    {
        "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调用方法,有如下返回:

shell
> 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 10000suu表示后接数据类型依次是String、U32、U32。此处并未能看出Version、CurrentEventNumber、MaxEventNumber字段。
查看GetSelInfo的声明(在mdb_interface仓中有关于资源协作接口返回字段的声明),rsp包含对返回字段的声明

json
"GetSelInfo" :{
    "req": {},
    "rsp": {
        "Version": {
            "baseType": "String"
        },
        "CurrentEventNumber": {
            "baseType": "U32"
        },
        "MaxEventNumber": {
            "baseType": "U32"
        }
    }
}

使用代理对象调用method时,会对返回值做结构化封装,例如GetSelInfo方法的返回值应该为:

lua
{
    Version = "1.0.0",
    CurrentEventNumber = 0,
    MaxEventNumber = 1000
}

List

json
{
    "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)

shell
> 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增加了如下键值对

json
"service_table": [
    "/bmc/kepler/Managers/1/NetworkProtocol/HTTP",
    "/bmc/kepler/Managers/1/NetworkProtocol/HTTPS",
    "/bmc/kepler/Managers/1/NetworkProtocol/KVMIP",
    ......
]

Task

json
{
    "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

json
"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

json
"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的属性。

json
{
    "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配置时获取不到,此时可获取ReqBodyOriginal
  • ReaHeader: 请求头中的数据
  • 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、属性写死

(非数据引用)理想的状态下,大部分引用位置的取值应该都是写死,是一个纯数据。

json
"RspBody": {
    "Prop": "redfish/v1/Systems/1"
}
  • 2、简易方式

框架会把${var}替换成外部数据中var的取值。
例如var取值为1,Prop的取值则为"redfish/v1/Systems/1"

json
"RspBody": {
    "Prop" : "redfish/v1/Systems/${var}"
}
  • 3、Statements方式

很多时候,拿到外部数据之后,并不能直接使用,而是需要做某些操作之后才可以使用。
这里定义"${Statements/Prop()}"表示数据配置在Statements.Prop中(注意:Statements/Prop后接一对括号)。
Statements.Prop配置的含义为:引用var的数据,经过多个Steps处理后的取值。Statements支持多种类型的配置,具体可见数据处理章节

json
"RspBody": {
    "Prop" : "${Statements/Prop()}"
},
"Statements": {
    "Prop": {
        "Input": "${var}"
        "Steps": [ {xxxx} ]
    }
}

数据处理Statements

数据处理:拿到资源协作接口中的数据之后,需要做一些加工操作,才能使用

json
"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

json
{
    "Type": "Convert",
    "Formula": "StringToNumber"
}

数据类型转换,上述中将字符串转换为数字类型,例如输入"12",输出为12 Formula指明转换的类型,当前支持的取值有:

  • NumberToBool:数字转换为布尔值(0转换为false,非0转换为true)
  • BoolToNumber: 布尔值转换为数字(false转换为0,true转换为1)
  • NumberToString:数字转换为字符串
  • StringToNumber:字符串转换为数字
  • FloatToInteger:浮点型整数转换为整数。小数点后不为0,转化为nil,例如输入3.1,输出nil
  • ToHex:数字转为16进制字符串,字母为大写
  • Tohex:数字转为16进制字符串,字母为小写

若输入数据类型与Formula声明的转换前类型不匹配时,输出为nil

Count

json
{
    "Type": "Count"
}

输入数据必须为数组类型,计算该数组大小,无需Formula字段。 例如输入为["1", "2", "3"]时,输出为3

Expand

json
{
    "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的响应体为

json
{
    "UserName": "Administrator",
    "RoleId": "Administrator",
    "Locked": false,
}

那么输入为/redfish/v1/AccountService/Accounts/2时,输出与上述响应体一致。

输入数据也可以是字符串数组,每个字符串都必须是可访问的URI,Expand操作会将每个字符串都原地替换为该URI对应的响应体。 假如/redfish/v1/AccountService/Accounts/3的响应体为

json
{
    "UserName": "Admin",
    "RoleId": "Administrator",
    "Locked": false,
}

那么输入为 ["/redfish/v1/AccountService/Accounts/2", "/redfish/v1/AccountService/Accounts/3"]时,输出为:

json
[
    {
        "UserName": "Administrator",
        "RoleId": "Administrator",
        "Locked": false,
    }
    {
        "UserName": "Admin",
        "RoleId": "Administrator",
        "Locked": false,
    }
]

如果是资源协作接口路径遍历处理,可以添加资源协作接口的Uri,来获取响应数据

json
{
   "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

json
{
    "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时,输出为:

json
[
    {"@odata.id": "/redfish/v1/System/Blade1"},
    {"@odata.id": "/redfish/v1/System/Blade2"},
    {"@odata.id": "/redfish/v1/System/Blade3"}
]

Prefix-Add

json
{
    "Type": "Prefix-Add",
    "Formula": "/redfish/v1/"
}

Formula为待增加的前缀字符串

  1. 输入为字符串或者数字时,输出为Formula + 输入组合成的字符串
  2. 输入为数组时,则为数组每个元素都增加Formula前缀作为输出

Formula取值/redfish/v1/为例; 输入为"System"时,输出为"/redfish/v1/System" 输入为["System/Blade1", "System/Blade2"]时,输出为["/redfish/v1/System/1", "/redfish/v1/System/Blade2"]

Prefix-Trim

json
{
    "Type": "Prefix-Trim",
    "Formula": "/bmc/kepler/"
}

Prefix-Trim为删除前缀,使用规则与Prefix-Add类似

Suffix-Add

json
{
    "Type": "Suffix-Add",
    "Formula": "/Function/1"
}

Suffix-Add为增加后缀,Formula为待增加的后缀字符串,以上述配置为例: 输入为"PCIeCard"时,输出为"/PCIeCard/Function/1" 输入为["PCIeCard1", "PCIeCard2"]时,输出为["/PCIeCard1/Function/1", "/PCIeCard2/Function/1"]

Suffix-Trim

json
{
    "Type": "Suffix-Trim",
    "Formula": "/Function/1"
}

Suffix-Trim为删除后缀,使用规则与Suffix-Add类似

Switch

json
{
    "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

json
{
    "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

json
{
    "Type": "Script",
    "Formula": "return ReqBody.Option == 'all'"
}

Formula的内容是一段Lua脚本,可实现任何复杂逻辑的处理(不推荐与其他处理类型叠加使用)。
上述例子,请求体ReqBodyOption"all"时,输出为true,否则输出为false
若脚本的内容较长,放置在一行不方便阅读,可将脚本外置在文件中,Formula配置文件名即可,例如取值get_operate_log.lua,那么在配置文件的同级目录下需要有文件script/get_operate_log.lua

注:除少部分复杂逻辑无法配置,尽量减少Script的使用。接口映射配置的本质是将接口以配置方式实现,而Script事实上就是代码实现。

Plugin

json
{
    "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配置的是函数调用,需要有配套的函数实现,可复用ScriptPlugin中可使用的数据变量如表格所示:

变量含义Script环境变量Plugin函数入参Plugin环境变量
busdbus总线
mdbmdb自省接口
requireLua内置函数
libroutemapper_utils接口映射配置封装的C库
mapper_interface接口映射配置封装的Lua库
stringLua标准库
mathLua标准库
typeLua内置函数
tableLua标准库
ipairsLua内置函数
pairsLua内置函数
nextLua内置函数
pcallLua内置函数
xpcallLua内置函数
errorLua内置函数
base_messages基础消息定义
custom_messages自定义的错误消息
tonumberLua内置函数
tostringLua内置函数
cjsoncjson库
nullcjson.null
lua_nilLua的nil取值。RspBody属性获取失败会转为null,如果属性不呈现可以用lua_nil
Uri数据来源
ReqBody数据来源
Query数据来源
Context数据来源
ProcessingFlow数据来源
InputSteps[ ]的输入

系统锁定校验LockdownAllow

LockdownAllow用于配置系统锁定状态下操作是否允许的配置选项,值为true代表操作允许,值为false表示不允许。支持接口级和属性级的配置,支持内层覆盖外层的配置,未配置的默认值为false。配置样例如下:

json
{
    "Uri": "/redfish/v1/AccountService",
    "Interfaces": [
        {
            "Type": "PATCH",
            "LockdownAllow": false,
            "Properties": {
                "Oem": {
                    "Type": "object",
                    "Properties": {
                        "openubmc": {
                            "Type": "object",
                            "Properties": {
                                "SystemLockDownEnabled": {
                                    "LockdownAllow": true,
                                },
                                "SecurityBannerEnabled": {
                                    "Type": "boolean",
                                }
                            }
                        }
                    }
                }
            }
        }
    ]
}