openUBMC offers a complete development platform that you can build on incrementally, tailoring it to your own business needs.
NOTE
An app (application) is the smallest functional unit in the openUBMC architecture. Each app owns its own resources and exposes its own interfaces.
For the detailed design, see App Development.
IMPORTANT
To get the most out of this chapter, make sure you've already completed Build Your Own BMC.
Scaffold a New App
The openUBMC CLI can scaffold a new app in a single command, taking the boilerplate out of setting up an app's structure.
Initialize the App
Use Bingo to create a new app named my_app, with openUBMC's Lua SDK as the app SDK:
# Enter the working directory
cd /home/workspace
# Create the new app
bingo new -n my_app -t application -l luaOnce it finishes, you'll find a new my_app folder in the working directory containing everything an app needs to get started.
> ls -l my_app
total 40
-rw-r--r-- 1 root root 0 Jan 17 07:22 CHANGELOG.md
-rw-r--r-- 1 root root 1273 Jan 17 07:22 CMakeLists.txt
-rw-r--r-- 1 root root 673 Jan 17 07:22 Makefile
-rw-r--r-- 1 root root 0 Jan 17 07:22 README.md
-rw-r--r-- 1 root root 102 Jan 17 07:22 conanfile.py
-rw-r--r-- 1 root root 141 Jan 17 07:22 config.cfg
drwxr-xr-x 2 root root 4096 Jan 17 07:22 mds
-rw-r--r-- 1 root root 624 Jan 17 07:22 permissions.ini
drwxr-xr-x 4 root root 4096 Jan 17 07:22 src
drwxr-xr-x 3 root root 4096 Jan 17 07:22 temp
drwxr-xr-x 4 root root 4096 Jan 17 07:22 test
drwxr-xr-x 3 root root 4096 Jan 17 07:22 user_confTo make version control easier down the line, initialize a git repository:
# Enter the my_app project directory
cd /home/workspace/my_app
# Initialize git
git init .Define an MDS Resource Object
NOTE
MDS (Model Description Source) describes the resource model.
For the detailed design, see MDS.
In openUBMC, every app owns its own business resources. By defining them against the MDS model, openUBMC can model the BMC's resources as a whole and enforce a consistent interaction contract across the system. Because everything shares the same modeling foundation, the openUBMC runtime also handles the inter-app interaction logic for you—as an app developer, you only need to write business logic around your own resource objects.
Open mds/model.json in your new my_app project and paste in the following configuration:
{
"MyMDSModel": {
"path": "/bmc/demo/MyMDSModel/${id}",
"interfaces": {
"bmc.demo.OpenUBMC.Community": {
"properties":{
"WelcomeMessage": {}
}
}
},
"properties": {
"SecretNumber": {
"baseType": "U32"
}
}
}
}Generate Model Code
NOTE
Model code generation:
Auto-generates helper code from the MDS model, so you write far less boilerplate to create, manage, and expose your model.
For the detailed design, see Code Generation.
mds/model.json only defines what the resource object means—it isn't usable on its own. To turn the model into code you can actually call, we generate that code automatically.
Generate Model Code from the CLI
bingo genWhen it finishes, a gen folder appears in your app directory, holding the helper code generated from the model you defined under mds. With this code, creating system resources in openUBMC becomes straightforward. For what the generated code does and a full walkthrough, see Code Generation.
Create a Resource Object
Code generation only produces the management scaffolding—it doesn't actually create any instances. For that, we create the instance object ourselves in the app's code.
Open src/lualib/my_app_app.lua and paste in the code below:
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
local app = class(c_service) -- Create an app class
function app:ctor() -- The app's constructor
end
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 named "1"
object.ObjectName = "MyMDSModel_1" -- Assign the object's properties inside the callback
object.WelcomeMessage = "Hello OpenUBMC!"
object.SecretNumber = 330
end)
end
return appopenUBMC builds an object-oriented programming framework on top of Lua to simplify creating and managing Lua classes.
The ctor function is the class constructor; it creates the concrete in-memory object instance.
The init function initializes the object's properties and runs immediately after ctor.
The app class is a subclass built on the openUBMC SDK framework, so it can call into the framework to work with MDS objects. self:CreateMyMDSModel is the helper code generated from the object defined in mds/model.json, letting you create objects quickly.
For more on the app SDK framework, see App Development.
Build the App
With the code written, we want to test it on a real device, which means packaging the my_app app into a BMC build.
openUBMC has a two-tier build system—app-level builds and product-level builds—and uses Conan, a well-established open-source package manager, to manage app build packages.
So we build the app first, then build the full image.
bingo build --stage=stableOn a successful build, you'll see my_app/0.0.1@openubmc/stable printed repeatedly—this is the build tag for version 0.0.1 of my_app. From here on, anywhere we refer to this app's build artifact, we use this tag to point to it.
This is a local build, so the artifact is only cached on your machine. You can find where it's stored with:
> conan cache path my_app/0.0.1@openubmc/stableNOTE
For app builds and a rundown of each file in an app, see BMC Studio CLI (bingo).
Build the Full Image
Because we just created a new app, the current manifest doesn't include it yet.
So the first step is to add the app to the manifest's component list.
First, locate the definition file for the product:
> cd /home/workspace/manifest
> ls build/product/
ca BMC
> ls build/product/BMC
openUBMC
> ls build/product/BMC/openUBMC
archive.ini manifest.yml permissions.ini rootfs update_ext4.cfg version.xmlmanifest/build/product/BMC/openUBMC/manifest.yml defines the product's basic information, including exactly which apps it bundles.
Open manifest.yml and add my_app under dependencies::
dependencies:
- conan: "my_app/0.0.1@openubmc/stable"NOTE
For what manifest.yml means and how the product repository is defined, see Product Build.
Once it's added, build the full image the same way as before:
bingo buildWhen the build finishes, you'll find the package in manifest/output.
Test the Full Build
After upgrading with the full image, we can inspect the app we just added. openUBMC is D-Bus compatible, so we can query it with the standard D-Bus command-line tools.
Query via the D-Bus Interface
source /etc/profile
busctl --user tree bmc.kepler.my_app└─/bmc
|─/bmc/demo
| └─/bmc/demo/MyMDSModel
| └─/bmc/demo/MyMDSModel/1
└─/bmc/kepler
└─/bmc/kepler/my_app
└─/bmc/kepler/my_app/MicroComponentThe busctl --user tree command lists the resource paths exposed by the my_app app—and they match exactly the path we defined in mds/model.json. In D-Bus, a resource path represents a resource object, so this path is our MDS object. We can inspect the object with busctl --user introspect:
busctl --user introspect bmc.kepler.my_app /bmc/demo/MyMDSModel/1NAME TYPE SIGNATURE RESULT/VALUE FLAGS
bmc.demo.OpenUBMC.Community interface - - -
.GetRepoURL method a{ss}u s -
.WelcomeMessage property s "Hello OpenUBMC!" emits-change writable
bmc.kepler.Object.Properties interface - - -
.GetAllWithContext method a{ss}s a{sv} -
.GetOptions method a{ss}ss a{ss} -
.GetPropertiesByNames method a{ss}sas a{sv}a{sv} -
.GetPropertiesByOptions method a{ss}sa{ss} as -
.GetWithContext method a{ss}ss v -
.SetWithContext method a{ss}ssv - -
.ClassName property s "MyMDSModel" emits-change
.ObjectIdentifier property (ysss) 1 "" "" "" emits-change
.ObjectName property s "MyMDSModel_1" emits-change
org.freedesktop.DBus.Introspectable interface - - -
.Introspect method - s -
org.freedesktop.DBus.ObjectManager interface - - -
.GetManagedObjects method - a{oa{sa{sv}}} -
org.freedesktop.DBus.Peer interface - - -
.GetMachineId method - s -
.Ping method - - -
org.freedesktop.DBus.Properties interface - - -
.Get method ss v -
.GetAll method s a{sv} -
.Set method ssv - -
.PropertiesChanged signal sa{sv}as - -You can see the bmc.demo.OpenUBMC.Community MDB interface (resource collaboration interface) and, under it, the WelcomeMessage property—exactly what we defined in mds/model.json. In this demo we defined bmc.demo.OpenUBMC.Community as our MDB interface; to change its properties or add an interface of your own, see MDS.
Sharp-eyed readers will notice that our other property, SecretNumber, isn't shown here. That's because it isn't declared in the MDB interface, so it can't be accessed from outside. We call this a private property of the MDS object: internal data that belongs to the app alone—other apps can neither see it nor read it from outside. To add a private property (like SecretNumber here), just add it directly; there's no need to declare it in the MDB interface.
Query with the mdbctl Tool
Beyond the native D-Bus tools, openUBMC provides another mature object-query tool: mdbctl. It queries more from the angle of MDS objects.
mdbctl lsprop MyMDSModel_1bmc.demo.OpenUBMC.Community
WelcomeMessage="Hello OpenUBMC!"
bmc.kepler.Object.Properties
ClassName="MyMDSModel"
ObjectIdentifier=[0,"","",""]
ObjectName="MyMDSModel_1"As before, mdbctl can only see data exposed through the MDB interface; private properties don't show up.