Subscription Mechanism Guide Based on the Resource Collaboration Interface
Resource collaboration interface: mdb_interface
Introduction
When learning the subscription mechanism of the resource collaboration interface (mdb_interface), you are advised to first study the Resource Collaboration Interface Debugging Mechanism.
Overall Design and Implementation
The model part in mdb_interface is primarily implemented by the module description source (MDS), while the interface part mainly uses D-Bus. Similar to other inter-process communication (IPC) mechanisms used for communication between modules, resource definitions are generally described in JSON format, divided into two parts: path and intf.
When module A needs to obtain information from module B, the following two steps are required:
- Module A obtains the remote procedure call (RPC) entry point (
interface) of module B. - Module A calls the corresponding
propertiesormethodsof module B.
When module A needs to actively perceive property changes in module B, it must subscribe to the property changes of module B.
- Module A obtains the RPC entry point (
interface) of module B. - Module A subscribes to the
propertieschanges in module B.
Normally, if the interface of module B is listed in the required section of the service.json file of module A, the corresponding properties and methods under the interface of module B can be accessed through the methods in the automatically generated client.lua code of module A. This document primarily describes how to use the client mode for subscription.
Subscribing to Property Changes Under a Single Interface
The following is an example of the subscription mode in the existing code:
The PowerState property is defined in json\intf\mdb\bmc\kepler\Systems\FruCtrl.json of the mdb component:
"PowerState": {
"baseType": "String",
"readOnly": true,
"description": "Reflects the real-time power status."
},The emitsChangedSignal property is not defined here, so it uses the default value of true.
emitsChangedSignal indicates whether to send a property change signal. It defaults to true. (1) If it is set to false, no signal is sent when the property changes. (2) If it is set to const, the property never changes and no signal needs to be sent. (3) If it is set to true, a signal carrying the property value is sent when the property changes. (4) If it is set to invalidates, a signal is sent when the property changes, but it does not carry the property value.
When the PowerState and SysResetDetected properties are subscribed to in the service.json file of the bios component, the bios module can obtain these properties from module A through the client mode.
{
"path": "/bmc/kepler/Systems/${SystemId}/FruCtrl/${Id}",
"interface": "bmc.kepler.Systems.FruCtrl",
"properties": {
"PowerState": ["subscribe"],
"SysResetDetected": ["subscribe"]
}
},After bingo gen is executed, the bios component automatically generates a client.lua file. This file includes the following code, which calls the OnFruCtrlPropertiesChanged method in the client object to obtain the property value:
function bios_client:OnFruCtrlPropertiesChanged(cb, path_params)
local path_namespace = path_params and
('/bmc/kepler/Systems/' .. path_params['SystemId'] .. '/FruCtrl/' .. path_params['Id']) or
'/bmc/kepler/Systems/:SystemId/FruCtrl/:Id'
self.signal_slots[#self.signal_slots + 1] = subscribe_signal.on_properties_changed(self:get_bus(), path_namespace,
cb, 'bmc.kepler.Systems.FruCtrl', {'PowerState', 'SysResetDetected'})
endThe following code corresponds to the call for PowerState and SysResetDetected subscription and the related operation function:
client:OnFruCtrlPropertiesChanged(function(values, path)
if values[POWER_STATE_PROPERTY] or values[SYS_RESET_FLAG] then
......
end
UpgradeSignal.power_state_changed_callback()
end
end)The first parameter in the callback is values. values[POWER_STATE_PROPERTY] represents the property value PowerState, and values[SYS_RESET_FLAG] represents the property value SysResetDetected. The second parameter is path, which indicates the path where the property is located. Note that path contains SystemId and Id. These parameters need to be retrieved from the path. The third parameter is interface, representing the interface where the property resides.
Subscription Using Signals
The mdb_interface component defines signals in json\intf\mdb\bmc\kepler\Systems\FruCtrl.json:
"signals": {
"BeforePowerOnSignal": {
"Timeout": {
"baseType": "U8",
"default": 3,
"description": "Broadcast before power-on to notify other applications."
}
}
}The bios component subscribes to the relevant interface in its service.json file:
{
"path": "/bmc/kepler/Systems/${SystemId}/FruCtrl/${Id}",
"interface": "bmc.kepler.Systems.FruCtrl",
"properties": {
"PowerState": ["subscribe"],
"SysResetDetected": ["subscribe"]
}
},After bingo gen is executed, the following code is automatically generated in client.lua:
function bios_client:SubscribeFruCtrlFruCtrlBeforePowerOnSignal(cb)
local sig = match_rule.signal('BeforePowerOnSignal', 'bmc.kepler.Systems.FruCtrl'):with_path(
'/bmc/kepler/Systems/:SystemId/FruCtrl/:Id')
self.signal_slots[#self.signal_slots + 1] = self:get_bus():match(sig, function(msg)
cb(msg:read())
end)
endIn the signal.lua of the bios component, the code below calls BeforePowerOnSignal, with UpgradeSignal.verify_flash(timeout) serving as the callback function:
client:SubscribeFruCtrlFruCtrlBeforePowerOnSignal(UpgradeSignal.verify_flash)
log:notice('[bios]subscribe before power on signal success')The first parameter passed here should be ctx, and the second parameter is Timeout. Be careful when retrieving these values during use.
Subscription During Interface Creation and Deletion
Interfaces under json\intf\mdb\bmc\kepler\ of the mdb_interface component can be subscribed to.
For example, mds\service.json in the network code directory has the following definition:
{
"path": "*",
"interface": "bmc.kepler.Systems.SmBios",
"properties": {
"SmBiosStatus": ["subscribe"]
}
}After the network component executes bingo gen, the following code is automatically generated in gen\network_adapter\client.lua:
function network_adapter_client:OnSmBiosInterfacesAdded(cb)
self.signal_slots[#self.signal_slots + 1] = subscribe_signal.on_interfaces_added(self:get_bus(), '/bmc', cb,
'bmc.kepler.Systems.SmBios')
endIn src\lualib\device\class\network_adapter.lua, the following call is used, where the three parameters of the callback function are msg, path, and props:
client:OnSmBiosInterfacesAdded(function (_, _, props)
resource_obj.smbios_status = props.SmBiosStatus:value()
log:debug('updtate smbios_status=%s', resource_obj.smbios_status)
end)Subscription Directly via Lua Code (Non-Client Mode)
In the storage component, the c_bus_monitor_service:fru_monitor() function has the following implementation:
local fru_bus_sig = org_freedesktop_dbus.MatchRule.signal('PropertiesChanged'):with_interface(
org_freedesktop_dbus.SD_BUS_PROPERTIES):with_path_namespace(fru_path)
-- -- Registers response function.
local fru_slot = self.bus:match(fru_bus_sig, function(msg)
local _, values = msg:read('sa{sv}as')
......
end
end)This method is essentially the same as the client mode and can also achieve subscription functionality.
Precautions
1. Ensure that property values in mdb can be emitted. Properties such as the one below will not send a change signal when modified. By default, this is set to true, allowing signals to be sent.
"options": {
"emitsChangedSignal": "false"
},2. Use the busctl command to observe whether PropertiesChanged and on_interfaces_added events are triggered.
Connect to the BMC environment via telnet and execute:
source /etc/profile
busctl --user monitor bmc.kepler.xxxYou should see output similar to the following:
Type=signal Endian=l Flags=1 Version=1 Cookie=164 Timestamp="xxxxx UTC"
Sender=:1.41 Destination=:1.1 Path=/bmc/kepler/Systems/1/Bios Interface=org.freedesktop.DBus.Properties Member=PropertiesChanged
UniqueName=:1.41
MESSAGE "sa{sv}as" {
STRING "bmc.kepler.Systems.Bios";
ARRAY "{sv}" {
DICT_ENTRY "sv" {
STRING "Version";
VARIANT "s" {
STRING "KL4.41.001.xxxxxx.R";
};
};
};
ARRAY "s" {
};
};3. Currently, subscriptions using signals cannot be monitored. You can debug by adding print statements to the callback function.
For example, bios receive before power on UpgradeSignal might be printed in app.log, even though this type of signal is not found in busctl --user monitor. It can still be successfully called.