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

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

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

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

接口支持

接口说明
Redfish机机接口,用于与上层网管对接。
Web_backend为Web前端提供后端服务支持。
SNMP机机接口,多用于与早期网管系统对接,逐渐被Redfish替代。
CLI人机接口,供用户直接操作设备。

关键字

映射关键字

关键字类型适用范围说明
ResourcesarrayAll资源合集,每个数组元素描述一个资源。
Resources.UristringAll1. Redfish/Web_backend保持原uri不变。
2. CLI的uri以 /cli/v1 开头,-t-d 后的参数分别为路由的第3、4段,若不存在-t,则以 _ 代替。
3. SNMP的uri以/snmp/v1开头,oid为第三段,命令名称为第4段,读写方式为第5段。
Resources.IgnoreEtagsarrayRedfish不进行etag校验的属性集合。频繁变更属性需要使用该字段进行屏蔽,否则执行 PATCH 方法时会因属性变更而设置失败。
Resources.ResourceExistobjectAll资源存在性校验流程配置,一般用于校验 Uri 中的动态字段是否合法。
Key为数据来源的定义,Value为校验的预期结果。
Value为 #WITH 时,预期为数据存在。Value为 #WITHOUT 时,预期为数据不存在。其余字段代表实际预期结果。
Resources.SequencearraySNMP序列字段配置,用于SNMP接口中对象场景的数据定义。
Sequence.NamestringSNMP对象中某个属性的名称,属性名的取值和mib文件中定义的一致。
Sequence.TypestringSNMP对象中某个属性的类型,和mib文件中定义的一致,取值包含:stringnumberintegerobjectarrayboolean
Sequence.AccessstringSNMP对象中某个属性的访问权限,和mib文件中定义的一致,取值包含:Readonly、Readwrite。
Sequence.PrimarybooleanSNMP对象中某个属性是否为主键,同一个对象中可能存在多个属性被设置为主键,此字段配置信息和mib文件保持一致。
Resources.InterfacesarrayAll资源方法的集合,包含 GET/PATCH/POST/DELETE 四类方法,单个资源包含的方法映射类型按实际情况进行配置,单一资源中每类方法只能配置一个。
Interfaces.TypestringAll方法的类型,取值包含 GETPATCHPOSTDELETE 四类,不区分大小写,具体分类:
1. Redfish/Web_backend包含 GETPATCHPOSTDELETE 四类。
2. CLI包含 GETPATCH,其中 ipmcget 对应 GETipmcset 对应 PATCH
3. SNMP包含 GETPATCH,分别对应获取和设置操作。
Interfaces.LockdownAllowbooleanAll系统锁定状态下是否允许执行设置类接口(PATCH/POST/DELETE)。系统锁定后,该配置值为 true 时允许操作,否则不能执行。
Interfaces.UsagestringCLI当输入的CLI命令可以唯一匹配到指定命令,但是 -v 后的参数不匹配,需提示出该命令的具体使用方法,该字段用来描述命令的正确使用格式
Interfaces.BriefstringCLI当输入的CLI命令可以唯一匹配到指定命令,但是 -v 后的参数不匹配,需提示出该命令的简要描述,该提示信息在此字段配置,未配置则不显示。
Interfaces.ExamplesarrayCLI当输入的CLI命令可以唯一匹配到指定命令,但是 -v 后的参数不匹配,需提示出该命令的使用样例,该提示信息在此字段配置,可配置多个样例,未配置则不显示。
Interfaces.DescriptionstringCLI当输入的CLI命令未精确匹配到指定命令,则提示最接近当前输入的所有命令列表,每个命令的描述信息来源于此字段。
Interfaces.EchoesarrayCLI回显到客户端数据的模板路径。
Interfaces.ReqBodystringAll方法的请求体定义,属性支持嵌套配置,用关键字 Properties 描述下一级属性。
ReqBody.NamestringAll请求消息体中描述某属性的属性名,与对外接口的定义保持一致。
ReqBody.Type[string, array]All请求消息体中描述某属性的属性类型,与对外接口的定义保持一致,该字段取值范围:stringnumberintegerobjectarraybooleannull
若该字段为数组类型,则该属性的取值可为多种类型。
ReqBody.Items[object, array]All适用于请求消息体属性为数组的情况,用于定义数组内的元素。
List Validation模式数组(数组内元素定义一致)可采用object类型,该定义对数组内所有属性生效。
Tuple Validation模式数组(数组内元素有多种定义)可采用array类型,定义的索引与请求消息体属性中的索引一一对应。
ReqBody.maxItemsnumberAll适用于请求消息体属性为数组的情况,用于定义数组的最大元素数量。
ReqBody.minItemsnumberAll适用于请求消息体属性为数组的情况,用于定义数组的最小元素数量。
ReqBody.uniqueItemsbooleanAll适用于请求消息体属性为数组的情况,用于定义数组内的每个元素均不相同,默认允许数组包含重复元素。
ReqBody.PropertiesobjectAll请求消息体中,当前属性的下一级属性的集合。
ReqBody.LockdownAllowbooleanAllInterfaces.LockdownAllow 字段的功能一致,该字段用于修饰PATCH接口中的单个属性,而不是整个接口
ReqBody.RequiredbooleanAll请求消息体属性是否为必选属性。
ReqBody.DescriptionstringCLI当输入的CLI命令可以唯一匹配到指定命令,但是 -v 后的参数不匹配,需提示该命令 -v 后参数的描述信息,该提示信息在此字段配置,未配置则不显示。
ReqBody.SensitivebooleanAll请求消息体属性是否为敏感信息,敏感信息在错误信息中将替换为 ******
ReqBody.ValidatorarrayAll请求消息体属性的校验规则,同时满足数组中所有的校验规则,该属性的取值才有效
Interfaces.QueryobjectAll用于配置默认查询参数。
Redfish的查询参数存在使用限制。
Interfaces.RspBodyobjectAll方法的响应体。
Interfaces.StatementsobjectAll语句定义,用于数据的加工处理。
Statements.x.Input[string, array]All待加工处理的数据来源定义。
Statements.x.StepsarrayAll处理过程定义,按数组定义顺序有序执行,上一步的执行结果为下一步执行的输入。
Interfaces.ProcessingFlowarrayAll资源协作接口入口,按数组定义的顺序有序执行。
ProcessingFlow.PathstringAll资源协作接口Path,支持动态参数。
ProcessingFlow.InterfacestringAll资源协作接口Interface。
ProcessingFlow.DestinationobjectAll资源协作接口数据目的区,数据由资源协作接口存入暂存区。Key:资源协作接口的属性名,Value:数据暂存区属性名。
ProcessingFlow.SourceobjectAll资源协作接口数据来源区,数据从定义的来源存入资源协作接口。Key:资源协作接口的属性名,Value:数据来源定义。
ProcessingFlow.NamestringAll资源协作接口方法名称。
ProcessingFlow.ParamsarrayAll资源协作接口方法入参,数组定义参数的顺序与资源协作接口方法入参顺序一致。
ProcessingFlow.CallIf[object, string]All动态决定ProcessingFlow执行模式,包含 object 与 string 两种形态。
1. object类型,Key为数据来源的定义,Value为校验的预期结果。Value为 #WITH 时,预期为数据存在。Value为 #WITHOUT 时,预期为数据不存在。其余字段代表实际预期结果。该形态使用方式与 Resources.ResourceExist 相同。
2. string类型,使用预设执行模式,当前预设执行模式有:
· CheckUri:在资源存在性校验时执行,为资源存在性校验提供暂存区数据。
ProcessingFlow.ForeachstringAll用于迭代数组,#INDEX 表示当此迭代次数。可以通过 ${<数组路径>[#INDEX]} 引用当前迭代的数组元素。
Interfaces.AuthenticationobjectCLI描述该命令是否需要鉴权。若配置了该关键字,则对应命令会提示用户输入密码,然后自动进行认证处理。
Interfaces.ConfirmobjectCLI描述该命令是否需要二次确认。若配置了该关键字,则对应命令会提示用户进行确认。
Authentication.Description
Confirm.Description
stringCLI用户交互的提示信息。
Authentication.Condition
Confirm.Condition
stringCLI-v 后的参数做条件判断,若判断结果为 true,则进行交互行为,该字段默认为true。
若配置该字段为false,优先使用 Confirm 关键字。

映射符号

关键字说明
${...}数据引用,全路径方式引用到某个变量,引用的数据来源包含 UriReqBodyStatementsProcessingFlow[...]/DestinationForeach
使用样例:
· ${Uri/Id}
· ${ReqBody/IpAddr}
· ${Statements/GetId()}
· ${ProcessingFlow[1]/Destination/Name}
[...]数组引用,可对数组变量进行下标引用,若为 Foreach 场景,则用 #INDEX 表示当此迭代。
使用样例:${Statements/GetPath()[#INDEX]}
()语句调用,在调用Statements时使用。
/下级属性引用。
{{...}}全局数据引用,引用数据配置在接口的 config.json 文件中。
使用样例:{{OemIdentifier}}
#WITH资源存在,用于数据校验。
#WITHOUT资源不存在,用于数据校验。

Statements关键字

关键字说明
Prefix-Add增加 Formula 中配置的前缀。
Prefix-Trim删除 Formula 中配置的前缀。
Suffix-Add增加F Formula 中配置的后缀。
Suffix-Trim删除 Formula 中配置的后缀。
Count输入属性包含元素的个数。
Convert数据类型转换,转换形式在 Formula 中配置。转换形式:NumberToBoolBoolToNumberNumberToStringStringToNumberFloatToIntegerToHexTohex
DateFormat将时间戳转化为指定格式的日期类型,Formula 为数组类型,第一个参数为指定的日期格式,第二个参数为是否显示当地的时区。
L-Pair将数组类型数据与 Formula 作Pair处理,以 Formula 中的字符串为Key,数组元素为Value,拼装成对象。
Switch数据替换。Formula 中定义了替换规则数组,替换规则中 From 为匹配项,To 为替换项,没有匹配项的替换规则为默认规则(default规则)。
若输入数据满足任意匹配项,则转化为该替换规则中替换项。不满足任何匹配项的数据视为满足默认规则,没有默认规则时该数据不做替换。
Expand将输入数据作为uri进行资源展开,输入uri可以为内部uri。Formula 为展开层数,当前openUBMC仅支持一层展开(即 Formula 为1)。
Script脚本自定义规则。一般用于代码比较简单、不与业务耦合的场景。Formula 为代码或代码所在文件路径。
代码可使用的全局变量包含:stringmathtypetableipairspairsnexterrorbase_messagescustom_messagestonumbertostringcjsonnulllua_nilUriReqBodyQueryContextProcessingFlowInput
Plugin插件自定义规则。一般用于耦合业务、比较“重型”的场景。Formula 为 “FileName.FunctionName(...)” 的形式,其中 FileName 为插件文件名,FunctionName 为插件中定义的函数名,... 为函数入参。
函数入参支持的关键字包含:UriReqBodyQueryContextProcessingFlowInput
插件代码可使用的全局变量包含:busmdbrequirelibroutemapper_utilsmapper_interfacestringmathtypetableipairspairsnextpcallxpcallerrorbase_messagescustom_messagestonumbertostringcjsonnulllua_nil

Validator关键字

关键字说明
Range用于 integernumber 类型。校验属性的取值范围,取值范围包含左右边界。
Length用于 string 类型。校验字符串的长度限制,长度限制包含左右边界。
Enum用于 integernumberstring 类型。校验属性的枚举类型。
Nonempty用于 string 类型。校验属性为非空字符串。
IPFormat用于 string 类型。校验属性为IP格式
Regex用于 string 类型。校验属性满足正则匹配规则。
Script脚本自定义校验规则。

通用格式

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

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",
                                }
                            }
                        }
                    }
                }
            }
        }
    ]
}