Extend the BMC API
更新时间: 2026/06/26
在Gitcode上查看源码

In the previous chapter we created an app and brought its objects and properties into openUBMC through an MDB interface (resource collaboration interface). In some cases, though, you'll want to surface those properties to users outside the BMC system. This chapter shows how to expose your data through the BMC's external APIs.

IMPORTANT

This chapter builds on the my_app app you created in Create an App, so make sure you've completed it first.

Add an IPMI Command

IPMI (Intelligent Platform Management Interface) is one of the most widely used out-of-band management protocols for servers.

Define the IPMI Model

openUBMC describes IPMI models as part of the MDS model, so once you define an IPMI command in the MDS, the helper code that packs and parses that command is generated for you.

NOTE

For the full IPMI model definition, see MDS.

Open mds/ipmi.json (create it if it doesn't exist yet) and paste in the code below:

json
{
    "package": "MyAppIpmiCmds",
    "cmds": {
        "GetSecretNumber": {
            "netfn": "0x30",
            "cmd": "0x90",
            "priority": "OEM",
            "role": "Operator",
            "privilege": ["BasicSetting"],
            "req": [
                {"data": "SubCommand", "baseType": "U8", "len": "1B", "value": "0x88"},
                {"data": "ManufactureId", "baseType": "U32", "len": "3B", "value": "0x0007db"},
                {"data": "Action", "baseType": "U8", "len": "1B", "value": "0x00"}
            ],
            "rsp": [
                {"data": "CompletionCode", "baseType": "U8", "len": "1B"},
                {"data": "SecretNumber", "baseType": "U32", "len": "3B"}
            ]
        },
        "SetSecretNumber": {
            "netfn": "0x30",
            "cmd": "0x90",
            "priority": "OEM",
            "role": "Operator",
            "privilege": ["BasicSetting"],
            "req": [
                {"data": "SubCommand", "baseType": "U8", "len": "1B", "value": "0x88"},
                {"data": "ManufactureId", "baseType": "U32", "len": "3B", "value": "0x0007db"},
                {"data": "Action", "baseType": "U8", "len": "1B", "value": "0x01"},
                {"data": "Data", "baseType": "U32", "len": "3B"}
            ],
            "rsp": [
                {"data": "CompletionCode", "baseType": "U8", "len": "1B"}
            ]
        }
    }
}

Here we've defined two IPMI commands, both under NetFn 0x30, CMD 0x90:

0x30 0x90 0x88 0xdb 0x07 0x00 0x00 queries SecretNumber.

0x30 0x90 0x88 0xdb 0x07 0x00 0x01 sets SecretNumber.

With the definitions in place, run code generation:

bash
bingo gen

Write the Code

Code generation gives us the ability to parse, pack, and route IPMI commands, but we still need to write the business logic—returning data, or storing incoming data in memory.

Open my_app_app.lua.

First, load the generated IPMI files at the top:

lua
local class = require 'mc.class' -- Enhanced type system provided by the Lua development framework
local c_service = require 'my_app.service' -- App base class produced by code generation
-- Newly added IPMI files
local ipmi_struct = require 'my_app.ipmi.ipmi'
local ipmi_msg = require 'my_app.ipmi.ipmi_message'
local ipmi = require 'ipmi'

Then, in app:init(), register a callback for each IPMI command:

lua
function app:init() -- The app's initialization function
    app.super.init(self) -- Call the base class initializer first
    self.my_mds_model = self:CreateMyMDSModel(1, function(object) -- Create an MDS object instance
        object.ObjectName = "MyMDSModel_1"  -- Assign the object's properties inside the callback
        object.WelcomeMessage = "Hello OpenUBMC!"
        object.SecretNumber = 330
    end)
    self:register_ipmi()
end

function app:register_ipmi()
    self:register_ipmi_cmd(ipmi_struct.GetSecretNumber, function(req, ctx, ...)
        return ipmi_msg.GetSecretNumberRsp.new(ipmi.types.Cc.Success, self.my_mds_model.SecretNumber)
    end)
    self:register_ipmi_cmd(ipmi_struct.SetSecretNumber, function(req, ctx, ...)
        if req.Data > 630 then
            return ipmi_msg.SetSecretNumberRsp.new(ipmi.types.Cc.InvalidFieldRequest)
        end
        self.my_mds_model.SecretNumber = req.Data
        return ipmi_msg.SetSecretNumberRsp.new(ipmi.types.Cc.Success)
    end)
end

When the query command comes in, we return the SecretNumber stored in MyMDSModel.

When the set command comes in, we first check whether the value is valid; if it is, we update the in-memory data and return success.

Build the App

With the code written, we want to test it—starting with an app build.

Since we've changed the app's behavior, we bump its version to tell the builds apart.

Open mds/service.json and change the version field from 0.0.1 to 0.0.2.

bash
bingo build --stage=stable

When the build finishes, the log shows a new build tagged my_app/0.0.2@openubmc/stable. The earlier my_app/0.0.1@openubmc/stable is still there and untouched—if you ever need it, you can still depend on the my_app/0.0.1@openubmc/stable tag.

Build the Full Image

We were previously using my_app/0.0.1; now update the dependency to my_app/0.0.2, then build the full image.

bash
bingo build

When the build finishes, you'll find the package in manifest/output.

Test the Full Build

After upgrading with the full image, let's look at my_app's resources again:

bash
source /etc/profile
busctl --user tree bmc.kepler.my_app
bash
└─/bmc
  |─/bmc/demo
  | └─/bmc/demo/MyMDSModel
  |   └─/bmc/demo/MyMDSModel/1
  |─/bmc/kepler
    |─/bmc/kepler/IpmiCmds
    | └─/bmc/kepler/IpmiCmds/30
    |   └─/bmc/kepler/IpmiCmds/30/90
    |     |─/bmc/kepler/IpmiCmds/30/90/GetSecretNumber
    |     └─/bmc/kepler/IpmiCmds/30/90/SetSecretNumber
    └─/bmc/kepler/my_app
      └─/bmc/kepler/my_app/MicroComponent

Some new IPMI resources have appeared, confirming that IPMI was successfully registered on our app. During startup, the SDK walks these resources to update the system-wide IPMI routing table.

With ipmitool, we can quickly test whether the IPMI command is reachable.

powershell
> ./ipmitool.exe -H ip -I lanplus -p 623 -U TestUser -P OpenUBMC@123 -C 17 raw 0x30 0x90 0x88 0xdb 0x07 0x00 0x00
> 00 30 03

The command returns 330—exactly the SecretNumber we defined in the code.

Now use the set command to change SecretNumber to 630. ipmitool takes hex input, so 630 is 0x000276:

powershell
# SetSecretNumber
> ./ipmitool.exe -H ip -I lanplus -p 623 -U TestUser -P OpenUBMC@123 -C 17 raw 0x30 0x90 0x88 0xdb 0x07 0x00 0x01 0x76 0x02 0x00
>
# On success, no completion code is printed—the response is a blank line

# GetSecretNumber
> ./ipmitool.exe -H ip -I lanplus -p 623 -U TestUser -P OpenUBMC@123 -C 17 raw 0x30 0x90 0x88 0xdb 0x07 0x00 0x00
> 76 02 00

The set succeeds, and querying again returns 630—matching the value we just set.

Redfish Interface

The openUBMC SDK already provides full Redfish protocol management, along with a powerful mapping configurator that cuts down the work of adapting northbound interfaces.

NOTE

For details on interface mapping configuration, see Interface Mapping Configuration.

Add a Mapping Configuration

First, clone the repository that holds the Redfish interface mapping configurations:

bash
git clone https://gitcode.com/openUBMC/rackmount.git

Inside the rackmount folder, you'll see that under interface_config/redfish/mapping_config, each file corresponds to one Redfish path.

We want to add a Redfish interface at the path /redfish/v1/OpenUBMC.

Create a file named OpenUBMC.json under interface_config/redfish/mapping_config and paste in the code below:

json
{
    "Resources": [
        {
            "Uri": "/redfish/v1/OpenUBMC",
            "Interfaces": [
                {
                    "Type": "GET",
                    "RspBody": {
                        "@odata.id": "/redfish/v1/OpenUBMC",
                        "Id": "OpenUBMC",
                        "Name": "OpenUBMC",
                        "WelcomeMessage": "${ProcessingFlow[1]/Destination/WelcomeMessage}"
                    },
                    "ProcessingFlow": [
                        {
                            "Type": "Property",
                            "Path": "/bmc/demo/MyMDSModel/1",
                            "Interface": "bmc.demo.OpenUBMC.Community",
                            "Destination": {
                                "WelcomeMessage": "WelcomeMessage"
                            }
                        }
                    ]
                }
            ]
        }
    ]
}

Through this interface mapping configuration, we both define the Redfish uri resource and tie it to an openUBMC MDB interface—so you don't have to write any Redfish handler code yourself.

Build the App

Since we changed the rackmount repository, we need to rebuild it.

To distinguish it from the previous version, bump the version field in service.json.

bash
bingo build --stage=stable

Build the Full Image

In the manifest, find where rackmount is declared, update its version to the one you just built, then build the full image.

bash
bingo build

Test the Full Build

After upgrading with the full image, we can call the Redfish interface:

bash
curl -u username:password 'https://ip/redfish/v1/OpenUBMC' -k

The WelcomeMessage returned by the interface matches the value from the MDB interface.

bash
{"@odata.id":"\/redfish\/v1\/OpenUBMC","Id":"OpenUBMC","Name":"OpenUBMC","WelcomeMessage":"Hello OpenUBMC!"}