Introduction to Persistence Mechanisms
In the openUBMC firmware, the data persistence mechanism refers to the process of writing critical configuration, status, and monitoring data from volatile memory to non-volatile storage media. This mechanism is implemented using the SQLite database, providing a unified and reliable structured data storage solution for various application components running on openUBMC.
Core Functions of the Persistence Mechanism
State retention and system reliability
All critical configurations and status information are not lost after the BMC restarts, powers off, or encounters a fault. After the system restarts, it can read from the database and recover to the state before the power failure, ensuring management continuity and system self-healing capabilities.
Data consistency and integrity
Through the transaction characteristics of the SQLite database, atomicity, consistency, isolation, and durability (ACID) are ensured during simultaneous data access by multiple processes and threads, effectively preventing data corruption or inconsistency.
Out-of-band management
Even when the server is powered off, administrators can still query historical sensor data, event logs, and configuration information stored in SQLite through the BMC, achieving independent system monitoring and management.
Performance and resource optimization
As a lightweight database, SQLite occupies minimal BMC CPU and memory resources while providing powerful functions, achieving an optimal balance between functionality and resource consumption.
Basic Concepts of Models and Automatically Generated Code
Module description source (MDS) model: MDS is the description source file for component models, providing unified modeling for component management, dependent data, and their expansion and customization capabilities.
mdb_interface: This collection of model definition interfaces for resource collaboration are implemented based on the D-Bus protocol. Through this mechanism, components can discover and call functional interfaces provided by other services, achieving cross-process resource access and collaboration.
Automatically generated code: openUBMC integrates the code generation capability of the Bingo tool. By running the bingo gen command, the tool can automatically transform abstract model definitions into directly callable component code. This process significantly improves development efficiency, allowing developers to focus on implementing service logic without manually writing a large amount of boilerplate code with fixed structures.
Generating database table creation code based on the MDS model definition Generating registration code for the resource collaboration interface based on the mdb_interface model definition
Persistence Mechanism
After the component data model definition is completed, persistent storage can be achieved using SQLite databases. The SQLite databases exist as .db files, and each database corresponds to an independent physical file.
The persistence configuration of a component uses the class defined in model.json of the MDS model as the basic unit. Each class is mapped to an independent data table in the database, and the properties declared as persistent in the class correspond to fields in the table.
Persistence Modes
The system configures the persistence implementation based on the tableLocation field in the class definition of the MDS model. The following two management modes are supported:
Local persistence
Data is stored in an exclusive persistence database named after the component. In this mode, the component directly performs read and write operations on the local persistence database to achieve independent data management.
Remote persistence
When a component starts, it creates an in-memory database as a temporary workspace, and all data operations are performed only on the in-memory database. The framework automatically captures change events in the in-memory database through a preset hook mechanism and forwards data change requests to the persistence service through RPC. Finally, the service uniformly writes the change data to the persistence database, achieving centralized data management.
Persistence Types
Persistence type refers to the storage media configuration for persistent data, set through the tableType field in the class definition or the usage field in the attribute definition in the MDS model.
Non-persistence (
Memory)Data resides exclusively in the in-memory database and will be lost when the process where the component resides restarts.
Temporary persistence (
TemporaryPer)Data that needs to be retained after a process restart is stored in the
tmpfsvirtual memory file system. The data will be cleared after the BMC resets.Reset persistence (
ResetPer)Data that needs to be retained after a BMC reset is stored in the
pramfile system. The data will be cleared after the BMC is powered off.Power-off persistence (
PoweroffPer)Data that needs to be retained after the BMC is powered off is stored in the
nandflashfile system. The data will be cleared after a BMC deep restore or factory reset.Permanent persistence (
PermanentPer)Data that needs to be retained after a BMC deep restore or factory reset is stored in character devices (not in database format).
Mapping Between Automatically Generated Code and Models
MDS Model
service.json: component service model, describing the basic information and dependencies of the componentmodel.json: component data model, defining the data structure managed by the component, including object paths, interfaces, and propertiesipmi.json: component IPMI command model, describing information such as IPMI command requests and response parameters implemented by the componenttypes.json: component custom type definitions, provided for reference by properties or method parameters inmodel.json
mdb_interface Model
- Interface definition file (in the
json/intfdirectory): definitions of interface properties, methods, and signals. - Path definition file (in the
json/pathdirectory): interfaces allowed to be configured under an object path. Ifpathconfigured for a class definition inmodel.jsonmatches this path, the class name must be the same as this file name, and the interfaces configured underinterfacesin the class definition must also be within the range of interfaces specified in this file.
| Directory | File Name | Basis File | Description |
| gen/class | model.lua | model.json and mdb_interface interface definition files | Contains validation information for attributes of each class (including resource tree properties and private attributes), as well as functions for registering service resource tree methods. |
| gen/class/types | ClassName.lua | model.json | Completes validation and default value configuration for private attributes and is referenced by model.lua. |
| gen/class/types | types.lua | types.json | Contains definitions of custom types such as enumerations, structures, and dictionaries. |
| gen/AppName | client.lua | service.json and mdb_interface interface definition files | Generated based on the interface dependencies defined in the required section, including functions for calling client resource tree interface methods, retrieving objects, and subscribing to signals. |
| gen/AppName | datas.lua | datas.yaml | Stores data loaded during the initialization of remote persistence in-memory databases or local persistence databases by default. |
| gen/AppName | db.lua | model.json | Table creation and database operation functions for remote persistence in-memory databases. |
| gen/AppName | local_db.lua | model.json | Table creation and database operation functions for local persistence databases (tables with "tableLocation": "Local" configured in model.json). |
| gen/AppName | orm_classes.lua | model.json | Initializes ORM objects for each class configured with remote persistence. |
| gen/AppName | service.lua | model.json, service.json, and mdb_interface interface definition files | Service entry, including application initialization, service resource tree method registration, and object creation. |
| gen/AppName/ipmi | ipmi.lua | ipmi.json | IPMI command definitions, serving as the entry point for the ipmi module. |
| gen/AppName/ipmi | ipmi_message.lua | ipmi.json | Summary of IPMI command requests and responses, referenced by ipmi.lua. |
| gen/AppName/ipmi/cmds | CommandName.lua | ipmi.json | Contains definitions and validation for request and response parameters, referenced by ipmi_message.lua. |
| gen/AppName/json_types | LastSegmentOfInterface.lua | model.json, service.json, and mdb_interface interface definition files | Contains definitions of resource tree interface registration properties and method parameter validation. |
Precautions
No permanent persistence support for local mode: Permanent persistence cannot be used in local persistence mode.
Storage capacity limit for permanent persistence: The total available space for permanent persistence is 2 MB, which is only suitable for storing critical data with small amount and stable content (such as MAC addresses).
Writing considerations for power-off persistence: When power-off persistence is configured, the data write volume and frequency must be evaluated to avoid affecting the service life of flash storage media.
Configuration constraints for local persistence: In local persistence mode, only full-table persistence types can be configured using
tableType. Persistence types defined at the attribute level is considered invalid.Default type for local persistence: If
tableTypeis not configured in local persistence mode, the system will use thePoweroffPer(power-off persistence) type by default.Type priority for remote persistence: In remote persistence mode, if persistence types are configured in both
tableTypeand theusageattribute, the system will prioritize the persistence type defined at the property level.Default behavior for remote persistence: If no persistence type is configured in remote persistence, data will not be stored persistently but only in the in-memory database.
Application Examples
Declare dependencies on interfaces of other components in the required section of service.json, and call methods for the dependent interfaces are generated in client.lua.
Declare the database table name in the tableName of model.json, which corresponds to the database table creation and operation functions in db.lua.
MDS Persistence Configuration Methods (model.json)
Class Definitions
tableName: database table name mapped through ORM for the class.tableLocation: persistence mode. Setting thetableLocationfield toLocalindicates local persistence. If thetableLocationfield is not configured or set to other values, it indicates remote persistence.tableType: persistence type for all properties of the class.tableMaxRows: maximum number of data records allowed in the database table.
Attribute Definitions
usage- Description: Defines the purpose of the property.
- Note: The value is an array, which can be used to specify the persistence type of the property.
primaryKey- Description: Identifies whether the property is the primary key of the database table.
- Value:
trueindicates that it is a primary key.falseor unconfigured indicates that it is not. - Rule: Generally, one or more properties that are unique within the table are selected as the primary key. When multiple properties are specified as the primary key, their combined value must be unique.
uniquekey- Description: Identifies whether the property is a unique key.
- Value:
trueindicates that it is a unique key.falseor unconfigured indicates that it is not. - Description: Unique key constraints are automatically imposed on properties defined as primary keys.
baseType- Description: Specifies the data type of the property.
- Value range: Includes
U8,U16,U32,U64,S8,S16,S32,S64, andString.
default- Description: Specifies the default value of the property.
- Rule: When a new data row is inserted into the database table and no value is assigned to the property, the default value defined here is used automatically.
notAllowNull- Description: Specifies whether the value of the property is allowed to be
NULL(empty) in the database. - Value:
trueindicates that the value cannot be empty.falseor unconfigured indicates that the value can be empty. - Default behavior: By default, the primary key properties cannot be empty, and non-primary key properties can be empty.
- Description: Specifies whether the value of the property is allowed to be
sensitive- Description: Identifies whether the property contains sensitive data.
- Value:
trueindicates that it has sensitive data.falseor unconfigured indicates that it has no sensitive data. - Security handling: When operations such as one-click collection and exporting persistent data are performed, property values marked as sensitive data will be replaced with
*****to ensure that actual data is not disclosed.
critical- Description: Identifies whether the property is critical data.
- Value:
trueindicates that it is critical data.falseor unconfigured indicates that it is not critical data. - Reliability assurance: For properties marked as critical data with the power-off persistence type, the system will synchronously write them to the backup database during each data update to enhance data reliability.
Demonstration Case
Adding an Interface and Configuring Property Persistence
Add an interface definition file json/intf/mdb/bmc/kepler/Example/Test.json in the mdb_interface repository:
{
"bmc.kepler.Example.Test": {
"properties": {
"Id": {
"baseType": "U8",
"description": "Test ID"
},
"Name": {
"baseType": "String",
"description": "Test Name"
}
},
"methods": {
"TestMethodA": {
"description": "Test Method A",
"req": {
"TestParam": {
"baseType": "String",
"description": "Parameter for Test Method A"
}
},
"rsp": {
"Response": {
"baseType": "String",
"description": "Function call result"
}
}
}
}
}
}Add a path definition file json/path/mdb/bmc/kepler/Example/TestA/TestA.json in the mdb_interface repository:
{
"TestA": {
"path": "/bmc/kepler/Example/TestA",
"interfaces": [
"bmc.kepler.Example.Test"
]
}
}Run the bingo gen command in the mdb_interface repository to generate gen/mdb/bmc/kepler/Example/TestA/TestA.lua.
Run the bingo build command in the mdb_interface repository and record the Conan package name.
Add the TestA class definition in the model.json of the sensor component repository.
Modify the mdb_interface dependency configured in service.json of the sensor component repository to the Conan package obtained in the previous step.
Run bingo gen in the sensor component repository to automatically generate code. After execution, the database table is created and operation functions are generated.
Run bingo build in the sensor component repository to compile the code and record the Conan package name.
Modify the versions of the sensor and mdb_interface repositories in manifest.yml and run bingo build.
After upgrading the BMC firmware, run busctl --user introspect bmc.kepler.sensor /bmc/kepler/Example/TestA in the BMC system.
Persistence Problem Location Methods
One-Click Collection
In the release version, you can only view the persistent database content through one-click collection. After collection is completed, the persistent data is stored in the dump_info/AppDump/persistence directory. Various types of persistent data will be exported as independent JSON files. You can search for and locate the data through table names.
Query on the CLI
Temporary persistence, reset persistence, and power-off persistence databases are implemented based on SQLite. You can run corresponding SQL statements through the sqlite3 CLI tool for data queries. Note that this operation requires root permissions, which can be obtained by upgrading the Telnet cracking package.
The storage paths for remote persistence data are as follows:
| Remote Persistence Type | Path |
|---|---|
Temporary Persistence (TemporaryPer) | /run/persistence/per_temporary.db |
Reset Persistence (ResetPer) | /opt/bmc/pram/persistence/per_reset.db |
Power-off Persistence (PoweroffPer) | /data/trust/persistence/per_poweroff.db |
Permanent Persistence (PermanentPer) | /dev/mmcblk0p8 |
The storage paths for local persistence (with "tableLocation": "local" configured) data are as follows:
| Local Persistence Type | Path |
|---|---|
Temporary Persistence (TemporaryPer) | /dev/shm/persistence.local/*.db |
Reset Persistence (ResetPer) | /opt/bmc/pram/persistence.local/*.db |
Power-off Persistence (PoweroffPer) (non-minimal system components) | /data/opt/bmc/persistence.local/*.db |
Power-off Persistence (PoweroffPer) (minimal system components) | /data/trust/persistence.local/*.db |
- You can view the list of minimal system components in the
/opt/bmc/trust/mini_system.jsonfile in the environment.
Temporary Persistence
Remote persistence:
bash/usr/sbin/sqlite3 /run/persistence/per_temporary.db "SELECT * FROM persist_table WHERE table_name = 't_iptables_nat' ;"Local persistence (the database file is named after the component):
bash/usr/sbin/sqlite3 /dev/shm/persistence.local/hwdiscovery.db "SELECT * FROM 't_hw_component' ;"
Reset Persistence
Remote persistence:
bash/usr/sbin/sqlite3 /opt/bmc/pram/persistence/per_reset.db "SELECT * FROM persist_table WHERE table_name = 't_soc_management' ;"Local persistence (the database filename is the component name):
bash/usr/sbin/sqlite3 /opt/bmc/pram/persistence.local/fault_diagnosis.db "select * from t_system_ras"
Power-off Persistence
Remote persistence:
Query data by table name:
bash/usr/sbin/sqlite3 /data/trust/persistence/per_poweroff.db "SELECT * FROM persist_table WHERE table_name = 't_banner_config';"Local persistence (the database filename is the component name):
Non-minimal system components:
bash/usr/sbin/sqlite3 /data/opt/bmc/persistence.local/cooling.db "select * from t_inlet_temp_record"Minimal system components:
bash/usr/sbin/sqlite3 /data/trust/persistence.local/maca.db "select * from t_package"
Permanent Persistence
Permanent persistence data is stored in the /dev/mmcblk0p8 character device in JSON format and can be viewed through the hexdump command. Permanent persistence does not support local persistence.
busybox hexdump -n 1048576 /dev/mmcblk0p8 -CViewing In-Memory Database Content
Under the premise that the property persistence feature based on MDS is enabled and the debug_console of the skynet process is turned on, you can view the component in-memory database content through code injection.
The following example shows how to view the t_time_config table in the bmc_time component in-memory database:
Create a Lua file (for example,
/data/test.lua) in the data partition and fill in the following content:lualocal c_object_manage = require 'mc.orm.object_manage' local cjson = require 'cjson' local sqlite3 = require 'lsqlite3' local db = c_object_manage.get_instance().db.db local vm = db:prepare('SELECT * FROM t_time_config') while vm:step() ~= sqlite3.DONE do print(cjson.encode(vm:get_named_values())) end vm:finalize()Find the
debug_consoleport number of the corresponding process.You can view the
launch_control.jsonfile of the corresponding subsystem in themanifestrepository.For example, the
bmc_timecomponent is in thebmc_coresubsystem, and its correspondingdebug_consoleport number is 40020.Connect to the
debug_consolethrough Telnet and find the service address.Use Telnet to the environment IP address and use the
debug_consoleport number found in step 2.Run the
listcommand first to find the service address corresponding to the component.Inject code and view the output.
Run
inject service_address lua_fileto inject code and view the in-memory database content.
Debugging Methods
Deleting Data
The following describes how to delete persistent data after obtaining environment permissions during development and debugging.
Remote Persistence
Remote persistence refers to the persistence method uniformly managed by the persistence service, where tableLocation: Local is not configured in the MDS class definition.
Using the sqlite3 CLI tool:
Components indirectly read and write remote persistence data through the in-memory database. If the sqlite CLI tool is used to directly delete persistent database content, the process where the component resides must be restarted for the deletion to take effect. (For example, for components under the
hardwareprocess, runsystemctl restart hardwareafter deletion.) For temporary, reset, and power-off persistence, run SQL statements on thepersist_tableof the corresponding database file to delete data.The following commands use the power-off persistence database as an example.
- Delete all data in a table (all data in the
t_banner_configtable):
bash/usr/sbin/sqlite3 /data/trust/persistence/per_poweroff.db "DELETE FROM persist_table WHERE table_name = 't_banner_config';"- Delete a record in a table (the record with
Idof 1 in thet_banner_configtable):
bash/usr/sbin/sqlite3 /data/trust/persistence/per_poweroff.db "DELETE FROM persist_table WHERE table_name = 't_banner_config' AND prime_key = 'Id:1';"- Delete a property value of a record in a table (the
Contentproperty value of the record withIdof 1 in thet_banner_configtable):
bash/usr/sbin/sqlite3 /data/trust/persistence/per_poweroff.db "DELETE FROM persist_table WHERE table_name = 't_banner_config' AND prime_key = 'Id:1' AND persist_param='Content';"Permanent persistence data is stored in character devices and cannot be deleted using sqlite commands. It can only be deleted through code injection.
- Delete all data in a table (all data in the
Deletion through code injection:
If the ORM feature is enabled for a component, you can use the Skynet debug console to inject code to operate the in-memory database to delete data. (The framework triggers synchronous deletion in the persistent database via hooks, and no process restart is required.)
For example, to delete data from the
t_iptables_ipv4_nattable of thebmc_networkcomponent, the following code can be injected for data deletion (see the previous section for specific code injection operations):
local c_object_manage = require 'mc.orm.object_manage'
local cjson = require 'cjson'
local sqlite3 = require 'lsqlite3'
local db = c_object_manage.get_instance().db
db:delete(db.tables['t_iptables_ipv4_nat']):exec()Local Persistence
Local persistence refers to the persistence method managed by the component itself, where tableLocation: Local is configured in the MDS class definition.
The component directly operates the local persistence database without in-memory database mapping. You can directly run sqlite commands on the local persistence database file to delete data, and the deletion takes effect immediately without process restart.
For example, deleting the power-off local persistence database table t_hw_component of the hwdiscovery component:
- Delete all data in a table (all data in the
t_hw_componenttable):
/usr/sbin/sqlite3 /data/trust/persistence.local/hwdiscovery.db "DELETE FROM t_hw_component;"- Delete a single record (the record with primary key value
Positionequal to01010Ain thet_hw_componenttable):
/usr/sbin/sqlite3 /data/trust/persistence.local/hwdiscovery.db "DELETE FROM t_hw_component WHERE Position='01010A';"Note: For local persistence data tables with new fields, the view name with the _v_ prefix plus the table name must be used for deletion. Otherwise, residual data exists in the extension table.
Because new fields in local persistence are stored in extension tables, the component needs to perform operations on the view composed of the main table and extension tables. When the code performs operations on the data table mapped by local_db.Class name, the framework has encapsulated the view. However, when the table name is used to delete data, the view name with the _v_ prefix must be used instead of the original table name.
New fields can be identified through the extend_field() in the automatically generated local_db.lua.
For example, the t_event_list_en table of the event component has new fields. The view name _v_t_event_list_en must be used for deletion:
/usr/sbin/sqlite3 /data/opt/bmc/persistence.local/event.db "DELETE FROM _v_t_event_list_en;"Restoring datas.yaml Preset Data That Has Been Deleted
Primary key records of data deleted in remote persistence are retained in the deleted_data_table in the database. When the component initializes and loads datas.yaml preset data into the in-memory database, deleted data will be removed. If you need to restore deleted datas.yaml preset data during debugging, delete the records of the corresponding table in the deleted_data_table.
For example, to restore the datas.yaml preset data of the power-off persistence table t_manager_account, run the following command and then restart the process where the component resides:
/usr/sbin/sqlite3 /data/trust/persistence/per_poweroff.db "DELETE FROM deleted_data_table WHERE table_name = 't_manager_account';"